diff --git a/src/_zkapauthorizer/controller.py b/src/_zkapauthorizer/controller.py index 2b005112013de69ae1bbc6b47d9c44e53812d447..32a52e94ad2f0721a8daca5381111721e2fb078d 100644 --- a/src/_zkapauthorizer/controller.py +++ b/src/_zkapauthorizer/controller.py @@ -35,6 +35,7 @@ from datetime import ( ) from base64 import ( b64encode, + b64decode, ) import attr @@ -301,9 +302,13 @@ class DummyRedeemer(object): :return: An already-fired ``Deferred`` that has a list of ``UnblindedToken`` instances wrapping meaningless values. """ + def dummy_unblinded_token(random_token): + random_value = b64decode(random_token.token_value.encode("ascii")) + unblinded_value = random_value + b"x" * (96 - len(random_value)) + return UnblindedToken(b64encode(unblinded_value).decode("ascii")) return succeed( list( - UnblindedToken(token.token_value) + dummy_unblinded_token(token) for token in random_tokens ), @@ -311,7 +316,7 @@ class DummyRedeemer(object): def tokens_to_passes(self, message, unblinded_tokens): return list( - Pass(token.text) + Pass(token.unblinded_token) for token in unblinded_tokens ) @@ -463,7 +468,7 @@ class RistrettoRedeemer(object): assert isinstance(unblinded_tokens, list) assert all(isinstance(element, UnblindedToken) for element in unblinded_tokens) unblinded_tokens = list( - privacypass.UnblindedToken.decode_base64(token.text.encode("ascii")) + privacypass.UnblindedToken.decode_base64(token.unblinded_token.encode("ascii")) for token in unblinded_tokens ) diff --git a/src/_zkapauthorizer/model.py b/src/_zkapauthorizer/model.py index 9088f040f5bc6f8581b374c4c23891f01afc740e..7b309a30c12527062998ec283de3472149c2d00f 100644 --- a/src/_zkapauthorizer/model.py +++ b/src/_zkapauthorizer/model.py @@ -371,7 +371,7 @@ class VoucherStore(object): INSERT INTO [unblinded-tokens] VALUES (?) """, list( - (t.text,) + (t.unblinded_token,) for t in unblinded_tokens ), @@ -646,12 +646,18 @@ class UnblindedToken(object): and can be used to construct a privacy-preserving pass which can be exchanged for service. - :ivar unicode text: The base64 encoded serialized form of the unblinded - token. This can be used to reconstruct a + :ivar unicode unblinded_token: The base64 encoded serialized form of the + unblinded token. This can be used to reconstruct a ``privacypass.UnblindedToken`` using that class's ``decode_base64`` method. """ - text = attr.ib(validator=attr.validators.instance_of(unicode)) + unblinded_token = attr.ib( + validator=attr.validators.and_( + attr.validators.instance_of(unicode), + is_base64_encoded(), + has_length(128), + ), + ) @attr.s(frozen=True) diff --git a/src/_zkapauthorizer/tests/strategies.py b/src/_zkapauthorizer/tests/strategies.py index c0953e40ac1a319b5852d80cd46a7344306d41f0..d62b84025900f9866983abef6b8f39633f3a14f7 100644 --- a/src/_zkapauthorizer/tests/strategies.py +++ b/src/_zkapauthorizer/tests/strategies.py @@ -83,6 +83,8 @@ from ..model import ( # The length of a `Token`, in bytes. _TOKEN_LENGTH = 96 +# The length of a `UnblindedToken`, in bytes. +_UNBLINDED_TOKEN_LENGTH = 96 def _merge_dictionaries(dictionaries): result = {} @@ -356,11 +358,12 @@ def unblinded_tokens(): base64 encode data. You cannot use these in the PrivacyPass cryptographic protocol but you can put them into the database and take them out again. """ - return binary( - min_size=32, - max_size=32, + return byte_strings( + label=b"unblinded-tokens", + length=_UNBLINDED_TOKEN_LENGTH, + entropy=4, ).map( - urlsafe_b64encode, + b64encode, ).map( lambda zkap: UnblindedToken(zkap.decode("ascii")), )