From 3abde25757bb3409f4448c50cce8d024fe4f88b6 Mon Sep 17 00:00:00 2001
From: Tom Prince <tom.prince@private.storage>
Date: Tue, 7 Dec 2021 14:12:38 -0700
Subject: [PATCH] Add `Pass.from_bytes`.

---
 src/_zkapauthorizer/_storage_server.py       |  9 ++++++---
 src/_zkapauthorizer/model.py                 |  4 ++++
 src/_zkapauthorizer/tests/storage_common.py  |  2 +-
 src/_zkapauthorizer/tests/test_controller.py |  7 ++++---
 src/_zkapauthorizer/tests/test_model.py      | 18 ++++++++++++++++++
 5 files changed, 33 insertions(+), 7 deletions(-)

diff --git a/src/_zkapauthorizer/_storage_server.py b/src/_zkapauthorizer/_storage_server.py
index 1b54113..b847499 100644
--- a/src/_zkapauthorizer/_storage_server.py
+++ b/src/_zkapauthorizer/_storage_server.py
@@ -49,6 +49,7 @@ from twisted.python.reflect import namedAny
 from zope.interface import implementer
 
 from .foolscap import RIPrivacyPassAuthorizedStorageServer, ShareStat
+from .model import Pass
 from .storage_common import (
     MorePassesRequired,
     add_lease_message,
@@ -112,9 +113,11 @@ class _ValidationResult(object):
         assert isinstance(message, unicode), "message %r not unicode" % (message,)
         assert isinstance(pass_, bytes), "pass %r not bytes" % (pass_,)
         try:
-            preimage_base64, signature_base64 = pass_.split(b" ")
-            preimage = TokenPreimage.decode_base64(preimage_base64)
-            proposed_signature = VerificationSignature.decode_base64(signature_base64)
+            parsed_pass = Pass.from_bytes(pass_)
+            preimage = TokenPreimage.decode_base64(parsed_pass.preimage)
+            proposed_signature = VerificationSignature.decode_base64(
+                parsed_pass.signature
+            )
             unblinded_token = signing_key.rederive_unblinded_token(preimage)
             verification_key = unblinded_token.derive_verification_key_sha512()
             invalid_pass = verification_key.invalid_sha512(
diff --git a/src/_zkapauthorizer/model.py b/src/_zkapauthorizer/model.py
index 594afc4..7530d2b 100644
--- a/src/_zkapauthorizer/model.py
+++ b/src/_zkapauthorizer/model.py
@@ -877,6 +877,10 @@ class Pass(object):
     def pass_bytes(self):
         return b" ".join((self.preimage, self.signature))
 
+    @classmethod
+    def from_bytes(cls, pass_):
+        return cls(*pass_.split(b" "))
+
 
 @attr.s(frozen=True)
 class RandomToken(object):
diff --git a/src/_zkapauthorizer/tests/storage_common.py b/src/_zkapauthorizer/tests/storage_common.py
index ab4f2be..a72cbb5 100644
--- a/src/_zkapauthorizer/tests/storage_common.py
+++ b/src/_zkapauthorizer/tests/storage_common.py
@@ -164,7 +164,7 @@ def get_passes(message, count, signing_key):
         and bound to the given message.
     """
     return list(
-        Pass(*pass_.split(b" "))
+        Pass.from_bytes(pass_)
         for pass_ in make_passes(
             signing_key,
             message,
diff --git a/src/_zkapauthorizer/tests/test_controller.py b/src/_zkapauthorizer/tests/test_controller.py
index fce655c..e4f08b1 100644
--- a/src/_zkapauthorizer/tests/test_controller.py
+++ b/src/_zkapauthorizer/tests/test_controller.py
@@ -76,6 +76,7 @@ from ..controller import (
 )
 from ..model import DoubleSpend as model_DoubleSpend
 from ..model import Error as model_Error
+from ..model import Pass
 from ..model import Pending as model_Pending
 from ..model import Redeemed as model_Redeemed
 from ..model import Redeeming as model_Redeeming
@@ -1016,10 +1017,10 @@ def ristretto_verify(signing_key, message, marshaled_passes):
     """
 
     def decode(marshaled_pass):
-        t, s = marshaled_pass.split(b" ")
+        pass_ = Pass.from_bytes(marshaled_pass)
         return (
-            TokenPreimage.decode_base64(t),
-            VerificationSignature.decode_base64(s),
+            TokenPreimage.decode_base64(pass_.preimage),
+            VerificationSignature.decode_base64(pass_.signature),
         )
 
     servers_passes = list(
diff --git a/src/_zkapauthorizer/tests/test_model.py b/src/_zkapauthorizer/tests/test_model.py
index bff6a60..93605cd 100644
--- a/src/_zkapauthorizer/tests/test_model.py
+++ b/src/_zkapauthorizer/tests/test_model.py
@@ -61,6 +61,7 @@ from ..model import (
     DoubleSpend,
     LeaseMaintenanceActivity,
     NotEnoughTokens,
+    Pass,
     Pending,
     Redeemed,
     StoreOpenError,
@@ -80,6 +81,7 @@ from .strategies import (
     voucher_counters,
     voucher_objects,
     vouchers,
+    zkaps,
 )
 
 
@@ -951,3 +953,19 @@ def store_for_test(testcase, get_config, get_now):
         memory_connect,
     )
     return store
+
+
+class PassTests(TestCase):
+    """
+    Tests for ``Pass``.
+    """
+
+    @given(zkaps())
+    def test_roundtrip(self, pass_):
+        """
+        ``Pass`` round-trips through ``Pass.from_bytes`` and ``Pass.pass_bytes``.
+        """
+        self.assertThat(
+            Pass.from_bytes(pass_.pass_bytes),
+            Equals(pass_),
+        )
-- 
GitLab