refactor ed25519 helpers to functional style

eliminates the wrapper classes and uses some more-explicit
names throughout (e.g "sk" -> "signing_key")
This commit is contained in:
meejah 2019-06-13 22:17:58 -06:00
parent 49b7756a8b
commit 47ccdb0177
7 changed files with 300 additions and 186 deletions

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

@ -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):

View File

@ -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

View File

@ -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):

View File

@ -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,