diff --git a/src/_zkapauthorizer/tests/storage_common.py b/src/_zkapauthorizer/tests/storage_common.py index d00a580c29adf51f1d39583012fbe09b11555678..c882a4917a92f6954b00bfb4d64b1c7acfcf3f56 100644 --- a/src/_zkapauthorizer/tests/storage_common.py +++ b/src/_zkapauthorizer/tests/storage_common.py @@ -16,6 +16,10 @@ ``allmydata.storage``-related helpers shared across the test suite. """ +from functools import ( + partial, +) + from os import ( SEEK_CUR, ) @@ -23,15 +27,34 @@ from struct import ( pack, ) +from itertools import ( + count, + islice, +) + +import attr + from twisted.python.filepath import ( FilePath, ) +from challenge_bypass_ristretto import ( + RandomToken, +) + from .strategies import ( # Not really a strategy... bytes_for_share, ) +from .privacypass import ( + make_passes, +) + +from ..model import ( + Pass, +) + # Hard-coded in Tahoe-LAFS LEASE_INTERVAL = 60 * 60 * 24 * 31 @@ -133,3 +156,85 @@ def whitebox_write_sparse_share(sharepath, version, size, leases, now): in leases ), ) + + +def integer_passes(): + """ + :return: Return a function which can be used to get a number of passes. + The function accepts a unicode request-binding message and an integer + number of passes. It returns a list of integers which serve as passes. + Successive calls to the function return unique pass values. + """ + counter = count(0) + def get_passes(message, num_passes): + return list(islice(counter, num_passes)) + return get_passes + + +def get_passes(message, count, signing_key): + """ + :param unicode message: Request-binding message for PrivacyPass. + + :param int count: The number of passes to get. + + :param SigningKEy signing_key: The key to use to sign the passes. + + :return list[Pass]: ``count`` new random passes signed with the given key + and bound to the given message. + """ + return list( + Pass(*pass_.split(u" ")) + for pass_ + in make_passes( + signing_key, + message, + list(RandomToken.create() for n in range(count)), + ) + ) + + +def privacypass_passes(signing_key): + """ + Get a PrivacyPass issuing function. + + :param SigningKey signing_key: The key to use to issue passes. + + :return: Return a function which can be used to get a number of passes. + The function accepts a unicode request-binding message and an integer + number of passes. It returns a list of real pass values signed by the + given key. Successive calls to the function return unique passes. + """ + return partial(get_passes, signing_key=signing_key) + + +def pass_factory(get_passes=None): + """ + Get a new factory for passes. + + :param (unicode -> int -> [pass]) get_passes: A function the factory can + use to get new passes. + """ + if get_passes is None: + get_passes = integer_passes() + return _PassFactory(get_passes=get_passes) + + +@attr.s +class _PassFactory(object): + """ + A stateful pass issuer. + + :ivar (unicode -> int -> [bytes]) _get_passes: A function for getting + passes. + + :ivar set[int] issued: All of the passes ever given out. + + """ + _get_passes = attr.ib() + issued = attr.ib(default=attr.Factory(set), init=False) + + def get(self, message, num_passes): + passes = [] + passes.extend(self._get_passes(message, num_passes)) + self.issued.update(passes) + return passes diff --git a/src/_zkapauthorizer/tests/test_storage_protocol.py b/src/_zkapauthorizer/tests/test_storage_protocol.py index bb79fb25e919110d0454595fe55cb81203dfbac9..4ddb289408f50e042cdb67bb40e8c8b064cc3ce2 100644 --- a/src/_zkapauthorizer/tests/test_storage_protocol.py +++ b/src/_zkapauthorizer/tests/test_storage_protocol.py @@ -67,7 +67,6 @@ from foolscap.referenceable import ( ) from challenge_bypass_ristretto import ( - RandomToken, random_signing_key, ) @@ -79,9 +78,6 @@ from .common import ( skipIf, ) -from .privacypass import ( - make_passes, -) from .strategies import ( storage_indexes, lease_renew_secrets, @@ -107,6 +103,9 @@ from .storage_common import ( cleanup_storage_server, write_toy_shares, whitebox_write_sparse_share, + get_passes, + privacypass_passes, + pass_factory, ) from .foolscap import ( LocalRemote, @@ -122,9 +121,6 @@ from ..storage_common import ( get_implied_data_length, required_passes, ) -from ..model import ( - Pass, -) from ..foolscap import ( ShareStat, ) @@ -167,36 +163,12 @@ class RequiredPassesTests(TestCase): ) -def get_passes(message, count, signing_key): - """ - :param unicode message: Request-binding message for PrivacyPass. - - :param int count: The number of passes to get. - - :param SigningKEy signing_key: The key to use to sign the passes. - - :return list[Pass]: ``count`` new random passes signed with the given key - and bound to the given message. - """ - return list( - Pass(*pass_.split(u" ")) - for pass_ - in make_passes( - signing_key, - message, - list(RandomToken.create() for n in range(count)), - ) - ) - - class ShareTests(TestCase): """ Tests for interaction with shares. - :ivar int spent_passes: The number of passes which have been spent so far - in the course of a single test (in the case of Hypothesis, every - iteration of the test so far, probably; so make relative comparisons - instead of absolute ones). + :ivar pass_factory: An object which is responsible for creating passes + which are used by these tests. """ pass_value = 128 * 1024 @@ -205,11 +177,8 @@ class ShareTests(TestCase): self.canary = LocalReferenceable(None) self.anonymous_storage_server = self.useFixture(AnonymousStorageServer()).storage_server self.signing_key = random_signing_key() - self.spent_passes = 0 - def counting_get_passes(message, count): - self.spent_passes += count - return get_passes(message, count, self.signing_key) + self.pass_factory = pass_factory(get_passes=privacypass_passes(self.signing_key)) self.server = ZKAPAuthorizerStorageServer( self.anonymous_storage_server, @@ -220,7 +189,7 @@ class ShareTests(TestCase): self.client = ZKAPAuthorizerStorageClient( self.pass_value, get_rref=lambda: self.local_remote_server, - get_passes=counting_get_passes, + get_passes=self.pass_factory.get, ) def test_get_version(self): @@ -826,12 +795,12 @@ class ShareTests(TestCase): u"Server gave back read results when we asked for none.", ) # Now we can read it back without spending any more passes. - before_spent_passes = self.spent_passes + before_passes = len(self.pass_factory.issued) assert_read_back_data(self, storage_index, secrets, test_and_write_vectors_for_shares) - after_spent_passes = self.spent_passes + after_passes = len(self.pass_factory.issued) self.assertThat( - before_spent_passes, - Equals(after_spent_passes), + before_passes, + Equals(after_passes), ) @given(