From 47ccdb017700e925150397950612d089fe77604c Mon Sep 17 00:00:00 2001 From: meejah Date: Thu, 13 Jun 2019 22:17:58 -0600 Subject: [PATCH] refactor ed25519 helpers to functional style eliminates the wrapper classes and uses some more-explicit names throughout (e.g "sk" -> "signing_key") --- src/allmydata/client.py | 26 +-- src/allmydata/crypto/ed25519.py | 248 ++++++++++++++++++-------- src/allmydata/introducer/common.py | 28 ++- src/allmydata/scripts/admin.py | 16 +- src/allmydata/test/cli/test_cli.py | 13 +- src/allmydata/test/test_crypto.py | 53 +++--- src/allmydata/test/test_introducer.py | 102 ++++++----- 7 files changed, 300 insertions(+), 186 deletions(-) diff --git a/src/allmydata/client.py b/src/allmydata/client.py index 04df599d8..b7d097f3b 100644 --- a/src/allmydata/client.py +++ b/src/allmydata/client.py @@ -11,8 +11,7 @@ from twisted.python.filepath import FilePath from twisted.python.failure import Failure import allmydata -from allmydata.crypto.ed25519 import SigningKey -from allmydata.crypto import rsa +from allmydata.crypto import rsa, ed25519 from allmydata.storage.server import StorageServer from allmydata import storage_client from allmydata.immutable.upload import Uploader @@ -479,19 +478,20 @@ class _Client(node.Node, pollmixin.PollMixin): # we only create the key once. On all subsequent runs, we re-use the # existing key def _make_key(): - priv_key = SigningKey.generate() - return priv_key.encoded_key() + "\n" + private_key, _ = ed25519.create_signing_keypair() + return ed25519.string_from_signing_key(private_key) + "\n" - priv_key_str = self.config.get_or_create_private_config("node.privkey", _make_key) - priv_key = SigningKey.parse_encoded_key(priv_key_str) - pub_key_str = priv_key.public_key().encoded_key() - self.config.write_config_file("node.pubkey", pub_key_str + "\n") - self._node_key = priv_key + private_key_str = self.config.get_or_create_private_config("node.privkey", _make_key) + private_key, public_key = ed25519.signing_keypair_from_string(private_key_str) + public_key_str = ed25519.string_from_verifying_key(public_key) + self.config.write_config_file("node.pubkey", public_key_str + "\n") + self._node_private_key = private_key + self._node_public_key = public_key def get_long_nodeid(self): # this matches what IServer.get_longname() says about us elsewhere - vk_bytes = self._node_key.public_key().public_bytes() - return "v0-"+base32.b2a(vk_bytes) + vk_bytes = ed25519.bytes_from_verifying_key(self._node_public_key) + return "v0-" + base32.b2a(vk_bytes) def get_long_tubid(self): return idlib.nodeid_b2a(self.nodeid) @@ -512,7 +512,7 @@ class _Client(node.Node, pollmixin.PollMixin): else: # otherwise, we're free to use the more natural seed of our # pubkey-based serverid - vk_bytes = self._node_key.public_key().public_bytes() + vk_bytes = ed25519.bytes_from_verifying_key(self._node_public_key) seed = base32.b2a(vk_bytes) self.config.write_config_file("permutation-seed", seed+"\n") return seed.strip() @@ -583,7 +583,7 @@ class _Client(node.Node, pollmixin.PollMixin): "permutation-seed-base32": self._init_permutation_seed(ss), } for ic in self.introducer_clients: - ic.publish("storage", ann, self._node_key) + ic.publish("storage", ann, self._node_private_key) def init_client(self): helper_furl = self.config.get_config("client", "helper.furl", None) diff --git a/src/allmydata/crypto/ed25519.py b/src/allmydata/crypto/ed25519.py index 54ec5a3dc..bee43298d 100644 --- a/src/allmydata/crypto/ed25519.py +++ b/src/allmydata/crypto/ed25519.py @@ -44,95 +44,185 @@ PRIVATE_KEY_PREFIX = 'priv-v0-' PUBLIC_KEY_PREFIX = 'pub-v0-' -class SigningKey: +def create_signing_keypair(): + """ + Creates a new ed25519 keypair. - def __init__(self, priv_key): - if not isinstance(priv_key, Ed25519PrivateKey): - raise ValueError('priv_key must be an Ed25519PrivateKey') - self._priv_key = priv_key - - @classmethod - def generate(cls): - return cls(Ed25519PrivateKey.generate()) - - @classmethod - def from_private_bytes(cls, priv_bytes): - if not isinstance(priv_bytes, six.binary_type): - raise ValueError('priv_bytes must be bytes') - return SigningKey(Ed25519PrivateKey.from_private_bytes(priv_bytes)) - - def private_bytes(self): - return self._priv_key.private_bytes( - Encoding.Raw, - PrivateFormat.Raw, - NoEncryption(), - ) - - def public_key(self): - return VerifyingKey(self._priv_key.public_key()) - - def sign(self, data): - if not isinstance(data, six.binary_type): - raise ValueError('data must be bytes') - return self._priv_key.sign(data) - - @classmethod - def parse_encoded_key(cls, priv_str): - return cls.from_private_bytes(a2b(remove_prefix(priv_str, PRIVATE_KEY_PREFIX))) - - def encoded_key(self): - return PRIVATE_KEY_PREFIX + b2a(self.private_bytes()) - - def __eq__(self, other): - if isinstance(other, type(self)): - return self.private_bytes() == other.private_bytes() - else: - return False - - def __ne__(self, other): - return not self.__eq__(other) + :returns: 2-tuple of (private_key, public_key) + """ + private_key = Ed25519PrivateKey.generate() + return private_key, private_key.public_key() -class VerifyingKey: +def signing_keypair_from_bytes(private_bytes): + """ + Creates a ed25519 keypair from serialized bytes - def __init__(self, pub_key): - if not isinstance(pub_key, Ed25519PublicKey): - raise ValueError('pub_key must be an Ed25519PublicKey') - self._pub_key = pub_key + :returns: 2-tuple of (private_key, public_key) + """ - @classmethod - def from_public_bytes(cls, pub_bytes): - if not isinstance(pub_bytes, six.binary_type): - raise ValueError('pub_bytes must be bytes') - return cls(Ed25519PublicKey.from_public_bytes(pub_bytes)) + if not isinstance(private_bytes, six.binary_type): + raise ValueError('private_bytes must be bytes') + private_key = Ed25519PrivateKey.from_private_bytes(private_bytes) + return private_key, private_key.public_key() - def public_bytes(self): - return self._pub_key.public_bytes(Encoding.Raw, PublicFormat.Raw) - def verify(self, signature, data): - if not isinstance(signature, six.binary_type): - raise ValueError('signature must be bytes') +def bytes_from_signing_key(private_key): + """ + Turn a private signing key into serialized bytes + """ + _validate_private_key(private_key) + return private_key.private_bytes( + Encoding.Raw, + PrivateFormat.Raw, + NoEncryption(), + ) - if not isinstance(data, six.binary_type): - raise ValueError('data must be bytes') - try: - self._pub_key.verify(signature, data) - except InvalidSignature: - raise BadSignature +def verifying_key_from_signing_key(private_key): + """ + :returns: the public key associated to the given `private_key` + """ + _validate_private_key(private_key) + return private_key.public_key() - @classmethod - def parse_encoded_key(cls, pub_str): - return cls.from_public_bytes(a2b(remove_prefix(pub_str, PUBLIC_KEY_PREFIX))) - def encoded_key(self): - return PUBLIC_KEY_PREFIX + b2a(self.public_bytes()) +def sign_data(private_key, data): + """ + Sign the given data using the given private key - def __eq__(self, other): - if isinstance(other, type(self)): - return self.public_bytes() == other.public_bytes() - else: - return False + :param private_key: the private part returned from + `create_signing_keypair` or from + `signing_keypair_from_bytes` - def __ne__(self, other): - return not self.__eq__(other) + :param bytes data: the data to sign + + :returns: bytes representing the signature + """ + + _validate_private_key(private_key) + if not isinstance(data, six.binary_type): + raise ValueError('data must be bytes') + return private_key.sign(data) + + +def string_from_signing_key(private_key): + """ + Encode a private key to a string + + :param private_key: the private part returned from + `create_signing_keypair` or from + `signing_keypair_from_bytes` + + :returns: string representing this key + """ + _validate_private_key(private_key) + return PRIVATE_KEY_PREFIX + b2a(bytes_from_signing_key(private_key)) + + +def signing_keypair_from_string(private_key_str): + """ + Load a signing keypair from a string + + :returns: a 2-tuple of (private_key, public_key) + """ + + return signing_keypair_from_bytes( + a2b(remove_prefix(private_key_str, PRIVATE_KEY_PREFIX)) + ) + + +def verifying_key_from_string(public_key_str): + """ + Load a verifying key from a string + + :returns: a public_key + """ + + +def verifying_key_from_bytes(public_key_bytes): + """ + Load a verifying key from bytes + + :returns: a public_key + """ + if not isinstance(public_key_bytes, six.binary_type): + raise ValueError('public_key_bytes must be bytes') + return Ed25519PublicKey.from_public_bytes(public_key_bytes) + + +def bytes_from_verifying_key(public_key): + """ + Encode a verifying key to bytes. + + :param public_key: the public part of a key returned from + `create_signing_keypair` or from + `signing_keypair_from_bytes` + + :returns: bytes representing this key + """ + _validate_public_key(public_key) + return public_key.public_bytes( + Encoding.Raw, + PublicFormat.Raw, + ) + + +def verify_signature(public_key, alleged_signature, data): + """ + :param public_key: a verifying key + + :param bytes alleged_signature: the bytes of the alleged signature + + :param bytes data: the data which was allegedly signed + + :returns: None, or raises an exception if the signature is bad. + """ + + if not isinstance(alleged_signature, six.binary_type): + raise ValueError('alleged_signature must be bytes') + + if not isinstance(data, six.binary_type): + raise ValueError('data must be bytes') + + _validate_public_key(public_key) + try: + public_key.verify(alleged_signature, data) + except InvalidSignature: + raise BadSignature + + +def verifying_key_from_string(public_key_str): + return verifying_key_from_bytes( + a2b(remove_prefix(public_key_str, PUBLIC_KEY_PREFIX)) + ) + + +def string_from_verifying_key(public_key): + """ + Encode a public key to a string + + :param public_key: the public part of a keypair + + :returns: string representing this key + """ + _validate_public_key(public_key) + return PUBLIC_KEY_PREFIX + b2a(bytes_from_verifying_key(public_key)) + + +def _validate_public_key(public_key): + """ + Internal helper. Verify that `public_key` is an appropriate object + """ + if not isinstance(public_key, Ed25519PublicKey): + raise ValueError('public_key must be an Ed25519PublicKey') + return None + + +def _validate_private_key(private_key): + """ + Internal helper. Verify that `private_key` is an appropriate object + """ + if not isinstance(private_key, Ed25519PrivateKey): + raise ValueError('private_key must be an Ed25519PrivateKey') + return None diff --git a/src/allmydata/introducer/common.py b/src/allmydata/introducer/common.py index add5311e0..b41810b14 100644 --- a/src/allmydata/introducer/common.py +++ b/src/allmydata/introducer/common.py @@ -1,7 +1,7 @@ import re import json from allmydata import crypto -from allmydata.crypto.ed25519 import VerifyingKey +from allmydata.crypto import ed25519 from allmydata.util import base32, rrefutil @@ -15,14 +15,26 @@ def get_tubid_string(furl): return m.group(1).lower() -def sign_to_foolscap(ann, sk): +def sign_to_foolscap(announcement, signing_key): + """ + :param signing_key: a (private) signing key, as returned from + e.g. :func:`allmydata.crypto.ed25519.signing_keypair_from_string` + + :returns: 3-tuple of (msg, sig, vk) where msg is a UTF8 JSON + serialization of the `announcement`, sig is bytes (a signature of + msg) and vk is the verifying key + """ # return (bytes, sig-str, pubkey-str). A future HTTP-based serialization # will use JSON({msg:b64(JSON(msg).utf8), sig:v0-b64(sig), # pubkey:v0-b64(pubkey)}) . - msg = json.dumps(ann).encode("utf-8") - sig = "v0-"+base32.b2a(sk.sign(msg)) - vk_bytes = sk.public_key().public_bytes() - ann_t = (msg, sig, "v0-"+base32.b2a(vk_bytes)) + msg = json.dumps(announcement).encode("utf-8") + sig = "v0-" + base32.b2a( + ed25519.sign_data(signing_key, msg) + ) + verifying_key_bytes = ed25519.bytes_from_verifying_key( + ed25519.verifying_key_from_signing_key(signing_key) + ) + ann_t = (msg, sig, "v0-" + base32.b2a(verifying_key_bytes)) return ann_t @@ -39,9 +51,9 @@ def unsign_from_foolscap(ann_t): if not claimed_key_vs.startswith("v0-"): raise UnknownKeyError("only v0- keys recognized") - claimed_key = VerifyingKey.parse_encoded_key("pub-" + claimed_key_vs) + claimed_key = ed25519.verifying_key_from_string("pub-" + claimed_key_vs) sig_bytes = base32.a2b(crypto.remove_prefix(sig_vs, "v0-")) - claimed_key.verify(sig_bytes, msg) + ed25519.verify_signature(claimed_key, sig_bytes, msg) key_vs = claimed_key_vs ann = json.loads(msg.decode("utf-8")) return (ann, key_vs) diff --git a/src/allmydata/scripts/admin.py b/src/allmydata/scripts/admin.py index 8498dfce1..e472ffd8c 100644 --- a/src/allmydata/scripts/admin.py +++ b/src/allmydata/scripts/admin.py @@ -14,11 +14,11 @@ Generate a public/private keypair, dumped to stdout as two lines of ASCII.. return t def print_keypair(options): - from allmydata.crypto.ed25519 import SigningKey + from allmydata.crypto import ed25519 out = options.stdout - priv_key = SigningKey.generate() - print("private:", priv_key.encoded_key(), file=out) - print("public:", priv_key.public_key().encoded_key(), file=out) + private_key, public_key = ed25519.create_signing_keypair() + print("private:", ed25519.string_from_signing_key(private_key), file=out) + print("public:", ed25519.string_from_verifying_key(public_key), file=out) class DerivePubkeyOptions(BaseOptions): def parseArgs(self, privkey): @@ -38,11 +38,11 @@ generate-keypair, derive the public key and print it to stdout. def derive_pubkey(options): out = options.stdout - from allmydata.crypto.ed25519 import SigningKey + from allmydata.crypto import ed25519 privkey_vs = options.privkey - priv_key = SigningKey.parse_encoded_key(privkey_vs) - print("private:", priv_key.encoded_key(), file=out) - print("public:", priv_key.public_key().encoded_key(), file=out) + private_key, public_key = ed25519.signing_keypair_from_string(privkey_vs) + print("private:", ed25519.string_from_signing_key(private_key), file=out) + print("public:", ed25519.string_from_verifying_key(public_key), file=out) return 0 class AdminCommand(BaseOptions): diff --git a/src/allmydata/test/cli/test_cli.py b/src/allmydata/test/cli/test_cli.py index 3c9fbb23d..14530586b 100644 --- a/src/allmydata/test/cli/test_cli.py +++ b/src/allmydata/test/cli/test_cli.py @@ -735,16 +735,19 @@ class Admin(unittest.TestCase): self.failUnless(privkey_bits[1].startswith("priv-v0-"), lines[0]) self.failUnless(pubkey_bits[1].startswith("pub-v0-"), lines[1]) sk_bytes = base32.a2b(crypto.remove_prefix(privkey_bits[1], "priv-v0-")) - sk = ed25519.SigningKey.from_private_bytes(sk_bytes) + sk, pk = ed25519.signing_keypair_from_bytes(sk_bytes) vk_bytes = base32.a2b(crypto.remove_prefix(pubkey_bits[1], "pub-v0-")) - self.failUnlessEqual(sk.public_key().public_bytes(), vk_bytes) + self.failUnlessEqual( + ed25519.bytes_from_verifying_key(pk), + vk_bytes, + ) d.addCallback(_done) return d def test_derive_pubkey(self): - priv_key = ed25519.SigningKey.generate() - priv_key_str = priv_key.encoded_key() - pub_key_str = priv_key.public_key().encoded_key() + priv_key, pub_key = ed25519.create_signing_keypair() + priv_key_str = ed25519.string_from_signing_key(priv_key) + pub_key_str = ed25519.string_from_verifying_key(pub_key) d = run_cli("admin", "derive-pubkey", priv_key_str) def _done(args): (rc, stdout, stderr) = args diff --git a/src/allmydata/test/test_crypto.py b/src/allmydata/test/test_crypto.py index 2635e8806..afcf09fa3 100644 --- a/src/allmydata/test/test_crypto.py +++ b/src/allmydata/test/test_crypto.py @@ -194,15 +194,21 @@ class TestRegression(unittest.TestCase): b'\x86 ier!\xe8\xe5#*\x9d\x8c\x0bI\x02\xd90\x0e7\xbeW\xbf\xa3\xfe\xc1\x1c\xf5+\xe9)' b'\xa3\xde\xc9\xc6s\xc9\x90\xf7x\x08') - priv_key = ed25519.SigningKey.parse_encoded_key(priv_str) - pub_key = ed25519.VerifyingKey.parse_encoded_key(pub_str) + private_key, derived_public_key = ed25519.signing_keypair_from_string(priv_str) + public_key = ed25519.verifying_key_from_string(pub_str) - self.failUnlessEqual(priv_key.public_key(), pub_key) + self.failUnlessEqual( + ed25519.bytes_from_verifying_key(public_key), + ed25519.bytes_from_verifying_key(derived_public_key), + ) - new_sig = priv_key.sign(test_data) + new_sig = ed25519.sign_data(private_key, test_data) self.failUnlessEqual(new_sig, sig) - pub_key.verify(new_sig, test_data) + ed25519.verify_signature(public_key, new_sig, test_data) + ed25519.verify_signature(derived_public_key, new_sig, test_data) + ed25519.verify_signature(public_key, sig, test_data) + ed25519.verify_signature(derived_public_key, sig, test_data) def test_decode_rsa_keypair(self): ''' @@ -216,29 +222,34 @@ class TestRegression(unittest.TestCase): class TestEd25519(unittest.TestCase): def test_keys(self): - priv_key = ed25519.SigningKey.generate() - priv_key_str = priv_key.encoded_key() + private_key, public_key = ed25519.create_signing_keypair() + private_key_str = ed25519.string_from_signing_key(private_key) - self.assertIsInstance(priv_key_str, six.string_types) - self.assertIsInstance(priv_key.private_bytes(), six.binary_type) + self.assertIsInstance(private_key_str, six.string_types) - priv_key2 = ed25519.SigningKey.parse_encoded_key(priv_key_str) + private_key2, public_key2 = ed25519.signing_keypair_from_string(private_key_str) - self.failUnlessEqual(priv_key, priv_key2) + self.failUnlessEqual( + ed25519.bytes_from_signing_key(private_key), + ed25519.bytes_from_signing_key(private_key2), + ) + self.failUnlessEqual( + ed25519.bytes_from_verifying_key(public_key), + ed25519.bytes_from_verifying_key(public_key2), + ) - pub_key = priv_key.public_key() - pub_key2 = priv_key2.public_key() + public_key_str = ed25519.string_from_verifying_key(public_key) + public_key_bytes = ed25519.bytes_from_verifying_key(public_key) - self.failUnlessEqual(pub_key, pub_key2) + self.assertIsInstance(public_key_str, six.string_types) + self.assertIsInstance(public_key_bytes, six.binary_type) - pub_key_str = pub_key.encoded_key() + public_key2 = ed25519.verifying_key_from_string(public_key_str) - self.assertIsInstance(pub_key_str, six.string_types) - self.assertIsInstance(pub_key.public_bytes(), six.binary_type) - - pub_key2 = ed25519.VerifyingKey.parse_encoded_key(pub_key_str) - - self.failUnlessEqual(pub_key, pub_key2) + self.failUnlessEqual( + ed25519.bytes_from_verifying_key(public_key), + ed25519.bytes_from_verifying_key(public_key2), + ) class TestRsa(unittest.TestCase): diff --git a/src/allmydata/test/test_introducer.py b/src/allmydata/test/test_introducer.py index 540492f5d..83a389106 100644 --- a/src/allmydata/test/test_introducer.py +++ b/src/allmydata/test/test_introducer.py @@ -15,7 +15,7 @@ from twisted.python.filepath import FilePath from foolscap.api import Tub, Referenceable, fireEventually, flushEventualQueue from twisted.application import service from allmydata import crypto -from allmydata.crypto.ed25519 import SigningKey +from allmydata.crypto import ed25519 from allmydata.interfaces import InsufficientVersionError from allmydata.introducer.client import IntroducerClient from allmydata.introducer.server import IntroducerService, FurlFileConflictError @@ -202,21 +202,21 @@ class Client(AsyncTestCase): furl1a = "pb://62ubehyunnyhzs7r6vdonnm2hpi52w6y@127.0.0.1:7777/gydnp" furl2 = "pb://ttwwooyunnyhzs7r6vdonnm2hpi52w6y@127.0.0.1:36106/ttwwoo" - priv_key = SigningKey.generate() - pub_key_str = priv_key.public_key().encoded_key() - pubkey_s = crypto.remove_prefix(pub_key_str, "pub-") + private_key, public_key = ed25519.create_signing_keypair() + public_key_str = ed25519.string_from_verifying_key(public_key) + pubkey_s = crypto.remove_prefix(public_key_str, "pub-") # ann1: ic1, furl1 # ann1a: ic1, furl1a (same SturdyRef, different connection hints) # ann1b: ic2, furl1 # ann2: ic2, furl2 - self.ann1 = make_ann_t(ic1, furl1, priv_key, seqnum=10) - self.ann1old = make_ann_t(ic1, furl1, priv_key, seqnum=9) - self.ann1noseqnum = make_ann_t(ic1, furl1, priv_key, seqnum=None) - self.ann1b = make_ann_t(ic2, furl1, priv_key, seqnum=11) - self.ann1a = make_ann_t(ic1, furl1a, priv_key, seqnum=12) - self.ann2 = make_ann_t(ic2, furl2, priv_key, seqnum=13) + self.ann1 = make_ann_t(ic1, furl1, private_key, seqnum=10) + self.ann1old = make_ann_t(ic1, furl1, private_key, seqnum=9) + self.ann1noseqnum = make_ann_t(ic1, furl1, private_key, seqnum=None) + self.ann1b = make_ann_t(ic2, furl1, private_key, seqnum=11) + self.ann1a = make_ann_t(ic1, furl1a, private_key, seqnum=12) + self.ann2 = make_ann_t(ic2, furl2, private_key, seqnum=13) ic1.remote_announce_v2([self.ann1]) # queues eventual-send d = fireEventually() @@ -300,13 +300,13 @@ class Server(AsyncTestCase): FilePath(self.mktemp())) furl1 = "pb://62ubehyunnyhzs7r6vdonnm2hpi52w6y@127.0.0.1:36106/gydnp" - priv_key = SigningKey.generate() + private_key, _ = ed25519.create_signing_keypair() - ann1 = make_ann_t(ic1, furl1, priv_key, seqnum=10) - ann1_old = make_ann_t(ic1, furl1, priv_key, seqnum=9) - ann1_new = make_ann_t(ic1, furl1, priv_key, seqnum=11) - ann1_noseqnum = make_ann_t(ic1, furl1, priv_key, seqnum=None) - ann1_badseqnum = make_ann_t(ic1, furl1, priv_key, seqnum="not an int") + ann1 = make_ann_t(ic1, furl1, private_key, seqnum=10) + ann1_old = make_ann_t(ic1, furl1, private_key, seqnum=9) + ann1_new = make_ann_t(ic1, furl1, private_key, seqnum=11) + ann1_noseqnum = make_ann_t(ic1, furl1, private_key, seqnum=None) + ann1_badseqnum = make_ann_t(ic1, furl1, private_key, seqnum="not an int") i.remote_publish_v2(ann1, None) all = i.get_announcements() @@ -397,7 +397,7 @@ class Queue(SystemTestMixin, AsyncTestCase): u"nickname", "version", "oldest", {}, fakeseq, FilePath(self.mktemp())) furl1 = "pb://onug64tu@127.0.0.1:123/short" # base32("short") - priv_key = SigningKey.generate() + private_key, _ = ed25519.create_signing_keypair() d = introducer.disownServiceParent() @@ -405,7 +405,7 @@ class Queue(SystemTestMixin, AsyncTestCase): # now that the introducer server is offline, create a client and # publish some messages c.setServiceParent(self.parent) # this starts the reconnector - c.publish("storage", make_ann(furl1), priv_key) + c.publish("storage", make_ann(furl1), private_key) introducer.setServiceParent(self.parent) # restart the server # now wait for the messages to be delivered @@ -486,16 +486,16 @@ class SystemTest(SystemTestMixin, AsyncTestCase): expected_announcements[i] += 1 # all expect a 'storage' announcement node_furl = tub.registerReference(Referenceable()) - priv_key = SigningKey.generate() - pub_key_str = priv_key.public_key().encoded_key() - privkeys[i] = priv_key - pubkeys[i] = pub_key_str + private_key, public_key = ed25519.create_signing_keypair() + public_key_str = ed25519.string_from_verifying_key(public_key) + privkeys[i] = private_key + pubkeys[i] = public_key_str if i < NUM_STORAGE: # sign all announcements - c.publish("storage", make_ann(node_furl), priv_key) - assert pub_key_str.startswith("pub-") - printable_serverids[i] = pub_key_str[len("pub-"):] + c.publish("storage", make_ann(node_furl), private_key) + assert public_key_str.startswith("pub-") + printable_serverids[i] = public_key_str[len("pub-"):] publishing_clients.append(c) else: # the last one does not publish anything @@ -504,7 +504,7 @@ class SystemTest(SystemTestMixin, AsyncTestCase): if i == 2: # also publish something that nobody cares about boring_furl = tub.registerReference(Referenceable()) - c.publish("boring", make_ann(boring_furl), priv_key) + c.publish("boring", make_ann(boring_furl), private_key) c.setServiceParent(self.parent) clients.append(c) @@ -760,17 +760,16 @@ class Announcements(AsyncTestCase): fakeseq, FilePath(self.mktemp())) furl1 = "pb://62ubehyunnyhzs7r6vdonnm2hpi52w6y@127.0.0.1:0/swissnum" - priv_key = SigningKey.generate() - pub_key = priv_key.public_key() - pks = crypto.remove_prefix(pub_key.encoded_key(), "pub-") + private_key, public_key = ed25519.create_signing_keypair() + public_key_str = crypto.remove_prefix(ed25519.string_from_verifying_key(public_key), "pub-") - ann_t0 = make_ann_t(client_v2, furl1, priv_key, 10) + ann_t0 = make_ann_t(client_v2, furl1, private_key, 10) canary0 = Referenceable() introducer.remote_publish_v2(ann_t0, canary0) a = introducer.get_announcements() self.failUnlessEqual(len(a), 1) self.assertThat(a[0].canary, Is(canary0)) - self.failUnlessEqual(a[0].index, ("storage", pks)) + self.failUnlessEqual(a[0].index, ("storage", public_key_str)) self.failUnlessEqual(a[0].announcement["app-versions"], app_versions) self.failUnlessEqual(a[0].nickname, u"nick-v2") self.failUnlessEqual(a[0].service_name, "storage") @@ -800,11 +799,10 @@ class Announcements(AsyncTestCase): c = yield create_client(basedir) ic = c.introducer_clients[0] - priv_key = SigningKey.generate() - pub_key = priv_key.public_key() - pub_key_str = crypto.remove_prefix(pub_key.encoded_key(), "pub-") + private_key, public_key = ed25519.create_signing_keypair() + public_key_str = crypto.remove_prefix(ed25519.string_from_verifying_key(public_key), "pub-") furl1 = "pb://onug64tu@127.0.0.1:123/short" # base32("short") - ann_t = make_ann_t(ic, furl1, priv_key, 1) + ann_t = make_ann_t(ic, furl1, private_key, 1) ic.got_announcements([ann_t]) yield flushEventualQueue() @@ -812,7 +810,7 @@ class Announcements(AsyncTestCase): # check the cache for the announcement announcements = self._load_cache(cache_filepath) self.failUnlessEqual(len(announcements), 1) - self.failUnlessEqual(announcements[0]['key_s'], pub_key_str) + self.failUnlessEqual(announcements[0]['key_s'], public_key_str) ann = announcements[0]["ann"] self.failUnlessEqual(ann["anonymous-storage-FURL"], furl1) self.failUnlessEqual(ann["seqnum"], 1) @@ -820,29 +818,28 @@ class Announcements(AsyncTestCase): # a new announcement that replaces the first should replace the # cached entry, not duplicate it furl2 = furl1 + "er" - ann_t2 = make_ann_t(ic, furl2, priv_key, 2) + ann_t2 = make_ann_t(ic, furl2, private_key, 2) ic.got_announcements([ann_t2]) yield flushEventualQueue() announcements = self._load_cache(cache_filepath) self.failUnlessEqual(len(announcements), 1) - self.failUnlessEqual(announcements[0]['key_s'], pub_key_str) + self.failUnlessEqual(announcements[0]['key_s'], public_key_str) ann = announcements[0]["ann"] self.failUnlessEqual(ann["anonymous-storage-FURL"], furl2) self.failUnlessEqual(ann["seqnum"], 2) # but a third announcement with a different key should add to the # cache - priv_key2 = SigningKey.generate() - pub_key2 = priv_key2.public_key() - pub_key_str2 = crypto.remove_prefix(pub_key2.encoded_key(), "pub-") + private_key2, public_key2 = ed25519.create_signing_keypair() + public_key_str2 = crypto.remove_prefix(ed25519.string_from_verifying_key(public_key2), "pub-") furl3 = "pb://onug64tu@127.0.0.1:456/short" - ann_t3 = make_ann_t(ic, furl3, priv_key2, 1) + ann_t3 = make_ann_t(ic, furl3, private_key2, 1) ic.got_announcements([ann_t3]) yield flushEventualQueue() announcements = self._load_cache(cache_filepath) self.failUnlessEqual(len(announcements), 2) - self.failUnlessEqual(set([pub_key_str, pub_key_str2]), + self.failUnlessEqual(set([public_key_str, public_key_str2]), set([a["key_s"] for a in announcements])) self.failUnlessEqual(set([furl2, furl3]), set([a["ann"]["anonymous-storage-FURL"] @@ -860,17 +857,17 @@ class Announcements(AsyncTestCase): ic2._load_announcements() # normally happens when connection fails yield flushEventualQueue() - self.failUnless(pub_key_str in announcements) - self.failUnlessEqual(announcements[pub_key_str]["anonymous-storage-FURL"], + self.failUnless(public_key_str in announcements) + self.failUnlessEqual(announcements[public_key_str]["anonymous-storage-FURL"], furl2) - self.failUnlessEqual(announcements[pub_key_str2]["anonymous-storage-FURL"], + self.failUnlessEqual(announcements[public_key_str2]["anonymous-storage-FURL"], furl3) c2 = yield create_client(basedir) c2.introducer_clients[0]._load_announcements() yield flushEventualQueue() self.assertEqual(c2.storage_broker.get_all_serverids(), - frozenset([pub_key_str, pub_key_str2])) + frozenset([public_key_str, public_key_str2])) class ClientSeqnums(AsyncBrokenTestCase): @@ -899,7 +896,7 @@ class ClientSeqnums(AsyncBrokenTestCase): f.close() return int(seqnum) - ic.publish("sA", {"key": "value1"}, c._node_key) + ic.publish("sA", {"key": "value1"}, c._node_private_key) self.failUnlessEqual(read_seqnum(), 1) self.failUnless("sA" in outbound) self.failUnlessEqual(outbound["sA"]["seqnum"], 1) @@ -911,7 +908,7 @@ class ClientSeqnums(AsyncBrokenTestCase): # publishing a second service causes both services to be # re-published, with the next higher sequence number - ic.publish("sB", {"key": "value2"}, c._node_key) + ic.publish("sB", {"key": "value2"}, c._node_private_key) self.failUnlessEqual(read_seqnum(), 2) self.failUnless("sB" in outbound) self.failUnlessEqual(outbound["sB"]["seqnum"], 2) @@ -986,8 +983,9 @@ class Signatures(SyncTestCase): def test_sign(self): ann = {"key1": "value1"} - priv_key = SigningKey.generate() - ann_t = sign_to_foolscap(ann, priv_key) + private_key, public_key = ed25519.create_signing_keypair() + public_key_str = ed25519.string_from_verifying_key(public_key) + ann_t = sign_to_foolscap(ann, private_key) (msg, sig, key) = ann_t self.failUnlessEqual(type(msg), type("".encode("utf-8"))) # bytes self.failUnlessEqual(json.loads(msg.decode("utf-8")), ann) @@ -995,7 +993,7 @@ class Signatures(SyncTestCase): self.failUnless(key.startswith("v0-")) (ann2,key2) = unsign_from_foolscap(ann_t) self.failUnlessEqual(ann2, ann) - self.failUnlessEqual("pub-" + key2, priv_key.public_key().encoded_key()) + self.failUnlessEqual("pub-" + key2, public_key_str) # not signed self.failUnlessRaises(UnknownKeyError,