Skip to content

Commit 25378c0

Browse files
Athan Massourasdbluhm
authored andcommitted
fix: add serialization/deserialization to bitstring-status-list, fix import statements
Signed-off-by: Athan Massouras <athan@indicio.tech>
1 parent 1ec49e7 commit 25378c0

File tree

6 files changed

+94
-18
lines changed

6 files changed

+94
-18
lines changed

src/bitstring_status_list/issuer.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1-
from issuer import *
2-
from bit_array import *
1+
from src.issuer import Issuer
2+
from src.bit_array import BitArray, IndexAllocator, N, LinearIndexAllocator, RandomIndexAllocator, Bits, dict_to_b64
33

44
from typing import (
55
List,
66
Literal,
77
Optional,
88
Tuple,
9+
Protocol,
10+
Union,
911
)
1012

1113
MIN_LIST_LENGTH = 131072

src/bitstring_status_list/verifier.py

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
from typing import (
2-
Literal,
32
Optional,
43
Protocol,
54
)
65

76
from aiohttp import ClientSession
87
import json
98

10-
from bit_array import BitArray, b64url_decode, b64url_encode, dict_to_b64
11-
from bitstring_status_list.issuer import MIN_LIST_LENGTH, StatusListLengthError
9+
from src.bit_array import BitArray, b64url_decode, b64url_encode, dict_to_b64
10+
from src.bitstring_status_list.issuer import MIN_LIST_LENGTH, StatusListLengthError
1211

1312
class EnvelopingTokenVerifier(Protocol):
1413
"""Protocol defining the verifying callable for enveloping signatures."""
@@ -207,4 +206,45 @@ def get_status(self, idx: Optional[int] = None):
207206
raise StatusVerificationError(f"statusMessage is malformed or not present: {k}")
208207

209208
raise StatusVerificationError(f"Status {status} not found in message list: {self.credential_status["statusMessage"]}")
209+
210+
def serialize_verifier(self) -> dict:
211+
"""
212+
Utility function: serialize a BitstringStatusListVerifier for storing.
213+
214+
Returns:
215+
A dictionary with headers and payload, as well as relevant metadata.
216+
"""
217+
218+
return {
219+
"credential_status": self.credential_status,
220+
**({"headers": self.headers} if self.headers else {}),
221+
"payload": self.payload,
222+
}
223+
224+
225+
@classmethod
226+
def deserialize_verifier(cls, seralized_verifier: dict) -> "BitstringStatusListVerifier":
227+
"""
228+
Utility function: deserializes a seralized TokenStatusListVerifier, which must be in the
229+
same format as the return type of seralize_verifier. Returns a TokenStatusListVerifier type
230+
with fields populated and the status list stored as a BitArray.
231+
232+
Args:
233+
serialized_verifier: REQUIRED. Serialized verifier type which must be in the same format
234+
as TokenStatusListVerifier.serialize_verifier.
235+
236+
Returns:
237+
A TokenStatusListVerifier instance with relevant fields populated.
238+
"""
239+
240+
bits = seralized_verifier["credential_status"].get("statusSize")
241+
return cls(
242+
credential_status=seralized_verifier["credential_status"],
243+
headers=seralized_verifier.get("headers"),
244+
payload=seralized_verifier["payload"],
245+
bit_array=BitArray.from_b64(
246+
bits=1 if bits is None else bits,
247+
value=seralized_verifier["payload"]["credentialSubject"]["encodedList"]
248+
)
249+
)
210250

tests/test_bitstring_status_list.py

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
from google.auth.crypt.es256 import ES256Signer, ES256Verifier
44
from cryptography.hazmat.primitives.asymmetric import ec
55

6-
from bit_array import b64url_encode, b64url_decode
7-
from bitstring_status_list.issuer import BitstringStatusListIssuer, EmbeddingTokenSigner, EnvelopingTokenSigner, MIN_LIST_LENGTH
8-
from bitstring_status_list.verifier import BitstringStatusListVerifier, EmbeddingTokenVerifier, EnvelopingTokenVerifier
6+
from src.bit_array import b64url_encode, b64url_decode
7+
from src.bitstring_status_list.issuer import BitstringStatusListIssuer, EmbeddingTokenSigner, EnvelopingTokenSigner, MIN_LIST_LENGTH
8+
from src.bitstring_status_list.verifier import BitstringStatusListVerifier, EmbeddingTokenVerifier, EnvelopingTokenVerifier
99

1010
@pytest.fixture
1111
def status():
@@ -257,4 +257,38 @@ def test_verify_es256_embedding(
257257
assert verifier.get_status(i) == {
258258
"status": status[i],
259259
"valid": not bool(status[i])
260-
}
260+
}
261+
262+
def test_serialization(status: BitstringStatusListIssuer):
263+
encoded_jwt = status.sign_jwt_enveloping(
264+
signer=trivial_enveloping_signer,
265+
alg="ES256",
266+
kid="12",
267+
status_purpose="revocation",
268+
)
269+
270+
credential_status = {
271+
"id": "https://example.com/credentials/status/3#94567",
272+
"type": "BitstringStatusListEntry",
273+
"statusPurpose": "revocation",
274+
"statusListIndex": "0",
275+
"statusListCredential": "https://example.com/credentials/status/3"
276+
}
277+
278+
verifier = BitstringStatusListVerifier.from_jwt(
279+
token=encoded_jwt,
280+
credential_status=credential_status,
281+
verifier=trivial_embedding_verifier,
282+
)
283+
284+
serialized_verifier = verifier.serialize_verifier()
285+
unserialized_verifier = BitstringStatusListVerifier.deserialize_verifier(serialized_verifier)
286+
287+
assert verifier.credential_status == unserialized_verifier.credential_status
288+
289+
assert verifier.headers == unserialized_verifier.headers
290+
assert verifier.payload == unserialized_verifier.payload
291+
292+
# Check that values match
293+
for i in range(status.status_list.size):
294+
assert verifier.get_status(i) == unserialized_verifier.get_status(i)

tests/test_token_status_list_issuer.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22

33
import json
44
import pytest
5-
from bit_array import BitArray, b64url_decode
6-
from token_status_list.issuer import TokenStatusListIssuer
5+
from src.bit_array import BitArray, b64url_decode
6+
from src.token_status_list.issuer import TokenStatusListIssuer
77

88

99
@pytest.fixture

tests/test_token_status_list_verifier.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@
66
from google.auth.crypt.es256 import ES256Signer, ES256Verifier
77
from cryptography.hazmat.primitives.asymmetric import ec
88

9-
from bit_array import BitArray
10-
from token_status_list.issuer import TokenStatusListIssuer, ALG, KID, TYP, ISS, SUB, AUD, EXP, NBF, IAT, CTI, STATUS_LIST, TTL, KNOWN_ALGS_TO_CWT_ALG
11-
from token_status_list.verifier import TokenStatusListVerifier
9+
from src.bit_array import BitArray
10+
from src.token_status_list.issuer import TokenStatusListIssuer, ALG, KID, TYP, ISS, SUB, EXP, IAT, STATUS_LIST, KNOWN_ALGS_TO_CWT_ALG
11+
from src.token_status_list.verifier import TokenStatusListVerifier
1212

1313
@pytest.fixture
1414
def status():

tests/test_token_status_list_webserver.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@
77
from cryptography.hazmat.primitives.asymmetric import ec
88
import requests as r
99

10-
from token_status_list.verifier import TokenStatusListVerifier
11-
from token_status_list.issuer import TokenStatusListIssuer
12-
from token_status_list.issuer import ALG, KID, TYP, ISS, SUB, AUD, EXP, NBF, IAT, CTI, STATUS_LIST, TTL, KNOWN_ALGS_TO_CWT_ALG
13-
from bit_array import BitArray
10+
from src.token_status_list.verifier import TokenStatusListVerifier
11+
from src.token_status_list.issuer import TokenStatusListIssuer
12+
from src.token_status_list.issuer import ALG, KID, TYP, ISS, SUB, EXP, IAT, STATUS_LIST, KNOWN_ALGS_TO_CWT_ALG
13+
from src.bit_array import BitArray
1414

1515
ISSUER = "http://localhost:3001"
1616

0 commit comments

Comments
 (0)