update code/test to use new ed25512 module

This commit is contained in:
heartsucker 2019-05-17 19:27:30 +02:00 committed by meejah
parent 3a5a0fb572
commit 9e31bfe2f4
12 changed files with 159 additions and 116 deletions

View File

@ -12,14 +12,14 @@ from twisted.python.failure import Failure
from pycryptopp.publickey import rsa from pycryptopp.publickey import rsa
import allmydata import allmydata
from allmydata.crypto.ed25519 import SigningKey
from allmydata.storage.server import StorageServer from allmydata.storage.server import StorageServer
from allmydata import storage_client from allmydata import storage_client
from allmydata.immutable.upload import Uploader from allmydata.immutable.upload import Uploader
from allmydata.immutable.offloaded import Helper from allmydata.immutable.offloaded import Helper
from allmydata.control import ControlServer from allmydata.control import ControlServer
from allmydata.introducer.client import IntroducerClient from allmydata.introducer.client import IntroducerClient
from allmydata.util import (hashutil, base32, pollmixin, log, keyutil, idlib, from allmydata.util import (hashutil, base32, pollmixin, log, idlib, yamlutil)
yamlutil)
from allmydata.util.encodingutil import (get_filesystem_encoding, from allmydata.util.encodingutil import (get_filesystem_encoding,
from_utf8_or_none) from_utf8_or_none)
from allmydata.util.abbreviate import parse_abbreviated_size from allmydata.util.abbreviate import parse_abbreviated_size
@ -480,12 +480,14 @@ class _Client(node.Node, pollmixin.PollMixin):
# we only create the key once. On all subsequent runs, we re-use the # we only create the key once. On all subsequent runs, we re-use the
# existing key # existing key
def _make_key(): def _make_key():
sk_vs,vk_vs = keyutil.make_keypair() priv_key = SigningKey.generate()
return sk_vs+"\n" return priv_key.encoded_key() + "\n"
sk_vs = self.config.get_or_create_private_config("node.privkey", _make_key)
sk,vk_vs = keyutil.parse_privkey(sk_vs.strip()) priv_key_str = self.config.get_or_create_private_config("node.privkey", _make_key)
self.config.write_config_file("node.pubkey", vk_vs+"\n") priv_key = SigningKey.parse_encoded_key(priv_key_str)
self._node_key = sk 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
def get_long_nodeid(self): def get_long_nodeid(self):
# this matches what IServer.get_longname() says about us elsewhere # this matches what IServer.get_longname() says about us elsewhere

View File

@ -1 +1,8 @@
class BadPrefixError(Exception):
pass
def remove_prefix(s_bytes, prefix):
if not s_bytes.startswith(prefix):
raise BadPrefixError("did not see expected '%s' prefix" % (prefix,))
return s_bytes[len(prefix):]

View File

@ -28,22 +28,13 @@ from cryptography.exceptions import InvalidSignature
BadSignatureError = InvalidSignature BadSignatureError = InvalidSignature
del InvalidSignature del InvalidSignature
from allmydata.crypto import remove_prefix
from allmydata.util.base32 import a2b, b2a from allmydata.util.base32 import a2b, b2a
_PRIV_PREFIX = 'priv-v0-' _PRIV_PREFIX = 'priv-v0-'
_PUB_PREFIX = 'pub-v0-' _PUB_PREFIX = 'pub-v0-'
class BadPrefixError(Exception):
pass
def _remove_prefix(s_bytes, prefix):
if not s_bytes.startswith(prefix):
raise BadPrefixError("did not see expected '%s' prefix" % (prefix,))
return s_bytes[len(prefix):]
class SigningKey: class SigningKey:
def __init__(self, priv_key): def __init__(self, priv_key):
@ -79,11 +70,11 @@ class SigningKey:
@classmethod @classmethod
def parse_encoded_key(cls, priv_str): def parse_encoded_key(cls, priv_str):
global _PRIV_PREFIX global _PRIV_PREFIX
return cls.from_private_bytes(a2b(_remove_prefix(priv_str, _PRIV_PREFIX))) return cls.from_private_bytes(a2b(remove_prefix(priv_str, _PRIV_PREFIX)))
def encoded_key(self): def encoded_key(self):
global _PRIV_PREFIX global _PRIV_PREFIX
return _PRIV_PREFIX + a2b(self.private_bytes()) return _PRIV_PREFIX + b2a(self.private_bytes())
def __eq__(self, other): def __eq__(self, other):
if isinstance(other, type(self)): if isinstance(other, type(self)):
@ -123,11 +114,11 @@ class VerifyingKey:
@classmethod @classmethod
def parse_encoded_key(cls, pub_str): def parse_encoded_key(cls, pub_str):
global _PUB_PREFIX global _PUB_PREFIX
return cls.from_public_bytes(a2b(_remove_prefix(pub_str, _PUB_PREFIX))) return cls.from_public_bytes(a2b(remove_prefix(pub_str, _PUB_PREFIX)))
def encoded_key(self): def encoded_key(self):
global _PUB_PREFIX global _PUB_PREFIX
return _PUB_PREFIX + a2b(self.public_bytes()) return _PUB_PREFIX + b2a(self.public_bytes())
def __eq__(self, other): def __eq__(self, other):
if isinstance(other, type(self)): if isinstance(other, type(self)):

View File

@ -1,4 +1,3 @@
import time import time
from zope.interface import implementer from zope.interface import implementer
from twisted.application import service from twisted.application import service
@ -10,7 +9,7 @@ from allmydata.introducer.common import sign_to_foolscap, unsign_from_foolscap,\
get_tubid_string_from_ann get_tubid_string_from_ann
from allmydata.util import log, yamlutil, connection_status from allmydata.util import log, yamlutil, connection_status
from allmydata.util.rrefutil import add_version_to_remote_reference from allmydata.util.rrefutil import add_version_to_remote_reference
from allmydata.util.keyutil import BadSignatureError from allmydata.crypto.ed25519 import BadSignatureError
from allmydata.util.assertutil import precondition from allmydata.util.assertutil import precondition
class InvalidCacheError(Exception): class InvalidCacheError(Exception):

View File

@ -1,7 +1,9 @@
import re import re
import json import json
from allmydata.util import keyutil, base32, rrefutil from allmydata import crypto
from allmydata.crypto.ed25519 import VerifyingKey
from allmydata.util import base32, rrefutil
def get_tubid_string_from_ann(ann): def get_tubid_string_from_ann(ann):
return get_tubid_string(str(ann.get("anonymous-storage-FURL") return get_tubid_string(str(ann.get("anonymous-storage-FURL")
@ -23,9 +25,11 @@ def sign_to_foolscap(ann, sk):
ann_t = (msg, sig, "v0-"+base32.b2a(vk_bytes)) ann_t = (msg, sig, "v0-"+base32.b2a(vk_bytes))
return ann_t return ann_t
class UnknownKeyError(Exception): class UnknownKeyError(Exception):
pass pass
def unsign_from_foolscap(ann_t): def unsign_from_foolscap(ann_t):
(msg, sig_vs, claimed_key_vs) = ann_t (msg, sig_vs, claimed_key_vs) = ann_t
if not sig_vs or not claimed_key_vs: if not sig_vs or not claimed_key_vs:
@ -34,8 +38,9 @@ def unsign_from_foolscap(ann_t):
raise UnknownKeyError("only v0- signatures recognized") raise UnknownKeyError("only v0- signatures recognized")
if not claimed_key_vs.startswith("v0-"): if not claimed_key_vs.startswith("v0-"):
raise UnknownKeyError("only v0- keys recognized") raise UnknownKeyError("only v0- keys recognized")
claimed_key = keyutil.parse_pubkey("pub-"+claimed_key_vs)
sig_bytes = base32.a2b(keyutil.remove_prefix(sig_vs, "v0-")) claimed_key = VerifyingKey.parse_encoded_key("pub-" + claimed_key_vs)
sig_bytes = base32.a2b(crypto.remove_prefix(sig_vs, "v0-"))
claimed_key.verify(sig_bytes, msg) claimed_key.verify(sig_bytes, msg)
key_vs = claimed_key_vs key_vs = claimed_key_vs
ann = json.loads(msg.decode("utf-8")) ann = json.loads(msg.decode("utf-8"))

View File

@ -14,11 +14,11 @@ Generate a public/private keypair, dumped to stdout as two lines of ASCII..
return t return t
def print_keypair(options): def print_keypair(options):
from allmydata.util.keyutil import make_keypair from allmydata.crypto.ed25519 import SigningKey
out = options.stdout out = options.stdout
privkey_vs, pubkey_vs = make_keypair() priv_key = SigningKey.generate()
print("private:", privkey_vs, file=out) print("private:", priv_key.encoded_key(), file=out)
print("public:", pubkey_vs, file=out) print("public:", priv_key.public_key().encoded_key(), file=out)
class DerivePubkeyOptions(BaseOptions): class DerivePubkeyOptions(BaseOptions):
def parseArgs(self, privkey): def parseArgs(self, privkey):
@ -38,11 +38,11 @@ generate-keypair, derive the public key and print it to stdout.
def derive_pubkey(options): def derive_pubkey(options):
out = options.stdout out = options.stdout
from allmydata.util import keyutil from allmydata.crypto.ed25519 import SigningKey
privkey_vs = options.privkey privkey_vs = options.privkey
sk, pubkey_vs = keyutil.parse_privkey(privkey_vs) priv_key = SigningKey.parse_encoded_key(privkey_vs)
print("private:", privkey_vs, file=out) print("private:", priv_key.encoded_key(), file=out)
print("public:", pubkey_vs, file=out) print("public:", priv_key.public_key().encoded_key(), file=out)
return 0 return 0
class AdminCommand(BaseOptions): class AdminCommand(BaseOptions):

View File

@ -10,8 +10,9 @@ from twisted.internet import task
from twisted.python.filepath import FilePath from twisted.python.filepath import FilePath
import allmydata import allmydata
from allmydata import crypto
from allmydata.crypto import ed25519 from allmydata.crypto import ed25519
from allmydata.util import fileutil, hashutil, base32, keyutil from allmydata.util import fileutil, hashutil, base32
from allmydata.util.namespace import Namespace from allmydata.util.namespace import Namespace
from allmydata import uri from allmydata import uri
from allmydata.immutable import upload from allmydata.immutable import upload
@ -34,10 +35,10 @@ from allmydata.scripts.common import DEFAULT_ALIAS, get_aliases, get_alias, \
DefaultAliasMarker DefaultAliasMarker
from allmydata.scripts import cli, debug, runner from allmydata.scripts import cli, debug, runner
from ..common_util import (ReallyEqualMixin, skip_if_cannot_represent_filename, from allmydata.test.common_util import (ReallyEqualMixin, skip_if_cannot_represent_filename,
run_cli) run_cli)
from ..no_network import GridTestMixin from allmydata.test.no_network import GridTestMixin
from .common import CLITestMixin, parse_options from allmydata.test.cli.common import CLITestMixin, parse_options
from twisted.python import usage from twisted.python import usage
from allmydata.util.encodingutil import listdir_unicode, get_io_encoding from allmydata.util.encodingutil import listdir_unicode, get_io_encoding
@ -733,16 +734,18 @@ class Admin(unittest.TestCase):
self.failUnlessEqual(pubkey_bits[0], vk_header, lines[1]) self.failUnlessEqual(pubkey_bits[0], vk_header, lines[1])
self.failUnless(privkey_bits[1].startswith("priv-v0-"), lines[0]) self.failUnless(privkey_bits[1].startswith("priv-v0-"), lines[0])
self.failUnless(pubkey_bits[1].startswith("pub-v0-"), lines[1]) self.failUnless(pubkey_bits[1].startswith("pub-v0-"), lines[1])
sk_bytes = base32.a2b(keyutil.remove_prefix(privkey_bits[1], "priv-v0-")) sk_bytes = base32.a2b(crypto.remove_prefix(privkey_bits[1], "priv-v0-"))
sk = ed25519.SigningKey.from_private_bytes(sk_bytes) sk = ed25519.SigningKey.from_private_bytes(sk_bytes)
vk_bytes = base32.a2b(keyutil.remove_prefix(pubkey_bits[1], "pub-v0-")) vk_bytes = base32.a2b(crypto.remove_prefix(pubkey_bits[1], "pub-v0-"))
self.failUnlessEqual(sk.public_key().public_bytes(), vk_bytes) self.failUnlessEqual(sk.public_key().public_bytes(), vk_bytes)
d.addCallback(_done) d.addCallback(_done)
return d return d
def test_derive_pubkey(self): def test_derive_pubkey(self):
priv1,pub1 = keyutil.make_keypair() priv_key = ed25519.SigningKey.generate()
d = run_cli("admin", "derive-pubkey", priv1) priv_key_str = priv_key.encoded_key()
pub_key_str = priv_key.public_key().encoded_key()
d = run_cli("admin", "derive-pubkey", priv_key_str)
def _done(args): def _done(args):
(rc, stdout, stderr) = args (rc, stdout, stderr) = args
lines = stdout.split("\n") lines = stdout.split("\n")
@ -752,8 +755,8 @@ class Admin(unittest.TestCase):
vk_header = "public: pub-v0-" vk_header = "public: pub-v0-"
self.failUnless(privkey_line.startswith(sk_header), privkey_line) self.failUnless(privkey_line.startswith(sk_header), privkey_line)
self.failUnless(pubkey_line.startswith(vk_header), pubkey_line) self.failUnless(pubkey_line.startswith(vk_header), pubkey_line)
pub2 = pubkey_line[len(vk_header):] pub_key_str2 = pubkey_line[len(vk_header):]
self.failUnlessEqual("pub-v0-"+pub2, pub1) self.failUnlessEqual("pub-v0-" + pub_key_str2, pub_key_str)
d.addCallback(_done) d.addCallback(_done)
return d return d

View File

@ -174,3 +174,31 @@ class TestRegression(unittest.TestCase):
self.failUnlessEqual(new_sig, sig) self.failUnlessEqual(new_sig, sig)
pub_key.verify(new_sig, test_data) pub_key.verify(new_sig, test_data)
class TestEd25519(unittest.TestCase):
def test_keys(self):
priv_key = SigningKey.generate()
priv_key_str = priv_key.encoded_key()
self.assertIsInstance(priv_key_str, six.string_types)
self.assertIsInstance(priv_key.private_bytes(), six.binary_type)
priv_key2 = SigningKey.parse_encoded_key(priv_key_str)
self.failUnlessEqual(priv_key, priv_key2)
pub_key = priv_key.public_key()
pub_key2 = priv_key2.public_key()
self.failUnlessEqual(pub_key, pub_key2)
pub_key_str = pub_key.encoded_key()
self.assertIsInstance(pub_key_str, six.string_types)
self.assertIsInstance(pub_key.public_bytes(), six.binary_type)
pub_key2 = VerifyingKey.parse_encoded_key(pub_key_str)
self.failUnlessEqual(pub_key, pub_key2)

View File

@ -14,6 +14,9 @@ from twisted.python.filepath import FilePath
from foolscap.api import Tub, Referenceable, fireEventually, flushEventualQueue from foolscap.api import Tub, Referenceable, fireEventually, flushEventualQueue
from twisted.application import service from twisted.application import service
from allmydata import crypto
from allmydata.crypto import ed25519
from allmydata.crypto.ed25519 import SigningKey
from allmydata.interfaces import InsufficientVersionError from allmydata.interfaces import InsufficientVersionError
from allmydata.introducer.client import IntroducerClient from allmydata.introducer.client import IntroducerClient
from allmydata.introducer.server import IntroducerService, FurlFileConflictError from allmydata.introducer.server import IntroducerService, FurlFileConflictError
@ -31,12 +34,12 @@ from allmydata.client import (
create_client, create_client,
create_introducer_clients, create_introducer_clients,
) )
from allmydata.util import pollmixin, keyutil, idlib, fileutil, yamlutil from allmydata.util import pollmixin, idlib, fileutil, yamlutil
from allmydata.util.iputil import ( from allmydata.util.iputil import (
listenOnUnused, listenOnUnused,
) )
import allmydata.test.common_util as testutil import allmydata.test.common_util as testutil
from .common import ( from allmydata.test.common import (
SyncTestCase, SyncTestCase,
AsyncTestCase, AsyncTestCase,
AsyncBrokenTestCase, AsyncBrokenTestCase,
@ -200,21 +203,21 @@ class Client(AsyncTestCase):
furl1a = "pb://62ubehyunnyhzs7r6vdonnm2hpi52w6y@127.0.0.1:7777/gydnp" furl1a = "pb://62ubehyunnyhzs7r6vdonnm2hpi52w6y@127.0.0.1:7777/gydnp"
furl2 = "pb://ttwwooyunnyhzs7r6vdonnm2hpi52w6y@127.0.0.1:36106/ttwwoo" furl2 = "pb://ttwwooyunnyhzs7r6vdonnm2hpi52w6y@127.0.0.1:36106/ttwwoo"
privkey_s, pubkey_vs = keyutil.make_keypair() priv_key = SigningKey.generate()
privkey, _ignored = keyutil.parse_privkey(privkey_s) pub_key_str = priv_key.public_key().encoded_key()
pubkey_s = keyutil.remove_prefix(pubkey_vs, "pub-") pubkey_s = crypto.remove_prefix(pub_key_str, "pub-")
# ann1: ic1, furl1 # ann1: ic1, furl1
# ann1a: ic1, furl1a (same SturdyRef, different connection hints) # ann1a: ic1, furl1a (same SturdyRef, different connection hints)
# ann1b: ic2, furl1 # ann1b: ic2, furl1
# ann2: ic2, furl2 # ann2: ic2, furl2
self.ann1 = make_ann_t(ic1, furl1, privkey, seqnum=10) self.ann1 = make_ann_t(ic1, furl1, priv_key, seqnum=10)
self.ann1old = make_ann_t(ic1, furl1, privkey, seqnum=9) self.ann1old = make_ann_t(ic1, furl1, priv_key, seqnum=9)
self.ann1noseqnum = make_ann_t(ic1, furl1, privkey, seqnum=None) self.ann1noseqnum = make_ann_t(ic1, furl1, priv_key, seqnum=None)
self.ann1b = make_ann_t(ic2, furl1, privkey, seqnum=11) self.ann1b = make_ann_t(ic2, furl1, priv_key, seqnum=11)
self.ann1a = make_ann_t(ic1, furl1a, privkey, seqnum=12) self.ann1a = make_ann_t(ic1, furl1a, priv_key, seqnum=12)
self.ann2 = make_ann_t(ic2, furl2, privkey, seqnum=13) self.ann2 = make_ann_t(ic2, furl2, priv_key, seqnum=13)
ic1.remote_announce_v2([self.ann1]) # queues eventual-send ic1.remote_announce_v2([self.ann1]) # queues eventual-send
d = fireEventually() d = fireEventually()
@ -298,14 +301,13 @@ class Server(AsyncTestCase):
FilePath(self.mktemp())) FilePath(self.mktemp()))
furl1 = "pb://62ubehyunnyhzs7r6vdonnm2hpi52w6y@127.0.0.1:36106/gydnp" furl1 = "pb://62ubehyunnyhzs7r6vdonnm2hpi52w6y@127.0.0.1:36106/gydnp"
privkey_s, _ = keyutil.make_keypair() priv_key = SigningKey.generate()
privkey, _ = keyutil.parse_privkey(privkey_s)
ann1 = make_ann_t(ic1, furl1, privkey, seqnum=10) ann1 = make_ann_t(ic1, furl1, priv_key, seqnum=10)
ann1_old = make_ann_t(ic1, furl1, privkey, seqnum=9) ann1_old = make_ann_t(ic1, furl1, priv_key, seqnum=9)
ann1_new = make_ann_t(ic1, furl1, privkey, seqnum=11) ann1_new = make_ann_t(ic1, furl1, priv_key, seqnum=11)
ann1_noseqnum = make_ann_t(ic1, furl1, privkey, seqnum=None) ann1_noseqnum = make_ann_t(ic1, furl1, priv_key, seqnum=None)
ann1_badseqnum = make_ann_t(ic1, furl1, privkey, seqnum="not an int") ann1_badseqnum = make_ann_t(ic1, furl1, priv_key, seqnum="not an int")
i.remote_publish_v2(ann1, None) i.remote_publish_v2(ann1, None)
all = i.get_announcements() all = i.get_announcements()
@ -396,22 +398,24 @@ class Queue(SystemTestMixin, AsyncTestCase):
u"nickname", "version", "oldest", {}, fakeseq, u"nickname", "version", "oldest", {}, fakeseq,
FilePath(self.mktemp())) FilePath(self.mktemp()))
furl1 = "pb://onug64tu@127.0.0.1:123/short" # base32("short") furl1 = "pb://onug64tu@127.0.0.1:123/short" # base32("short")
sk_s, vk_s = keyutil.make_keypair() priv_key = SigningKey.generate()
sk, _ignored = keyutil.parse_privkey(sk_s)
d = introducer.disownServiceParent() d = introducer.disownServiceParent()
def _offline(ign): def _offline(ign):
# now that the introducer server is offline, create a client and # now that the introducer server is offline, create a client and
# publish some messages # publish some messages
c.setServiceParent(self.parent) # this starts the reconnector c.setServiceParent(self.parent) # this starts the reconnector
c.publish("storage", make_ann(furl1), sk) c.publish("storage", make_ann(furl1), priv_key)
introducer.setServiceParent(self.parent) # restart the server introducer.setServiceParent(self.parent) # restart the server
# now wait for the messages to be delivered # now wait for the messages to be delivered
def _got_announcement(): def _got_announcement():
return bool(introducer.get_announcements()) return bool(introducer.get_announcements())
return self.poll(_got_announcement) return self.poll(_got_announcement)
d.addCallback(_offline) d.addCallback(_offline)
def _done(ign): def _done(ign):
v = introducer.get_announcements()[0] v = introducer.get_announcements()[0]
furl = v.announcement["anonymous-storage-FURL"] furl = v.announcement["anonymous-storage-FURL"]
@ -427,6 +431,7 @@ class Queue(SystemTestMixin, AsyncTestCase):
return False return False
return True return True
return self.poll(_idle) return self.poll(_idle)
d.addCallback(_wait_until_idle) d.addCallback(_wait_until_idle)
return d return d
@ -482,16 +487,16 @@ class SystemTest(SystemTestMixin, AsyncTestCase):
expected_announcements[i] += 1 # all expect a 'storage' announcement expected_announcements[i] += 1 # all expect a 'storage' announcement
node_furl = tub.registerReference(Referenceable()) node_furl = tub.registerReference(Referenceable())
privkey_s, pubkey_s = keyutil.make_keypair() priv_key = SigningKey.generate()
privkey, _ignored = keyutil.parse_privkey(privkey_s) pub_key_str = priv_key.public_key().encoded_key()
privkeys[i] = privkey privkeys[i] = priv_key
pubkeys[i] = pubkey_s pubkeys[i] = pub_key_str
if i < NUM_STORAGE: if i < NUM_STORAGE:
# sign all announcements # sign all announcements
c.publish("storage", make_ann(node_furl), privkey) c.publish("storage", make_ann(node_furl), priv_key)
assert pubkey_s.startswith("pub-") assert pub_key_str.startswith("pub-")
printable_serverids[i] = pubkey_s[len("pub-"):] printable_serverids[i] = pub_key_str[len("pub-"):]
publishing_clients.append(c) publishing_clients.append(c)
else: else:
# the last one does not publish anything # the last one does not publish anything
@ -500,13 +505,12 @@ class SystemTest(SystemTestMixin, AsyncTestCase):
if i == 2: if i == 2:
# also publish something that nobody cares about # also publish something that nobody cares about
boring_furl = tub.registerReference(Referenceable()) boring_furl = tub.registerReference(Referenceable())
c.publish("boring", make_ann(boring_furl), privkey) c.publish("boring", make_ann(boring_furl), priv_key)
c.setServiceParent(self.parent) c.setServiceParent(self.parent)
clients.append(c) clients.append(c)
tubs[c] = tub tubs[c] = tub
def _wait_for_connected(ign): def _wait_for_connected(ign):
def _connected(): def _connected():
for c in clients: for c in clients:
@ -746,6 +750,7 @@ class ClientInfo(AsyncTestCase):
self.failUnlessEqual(s0.nickname, NICKNAME % u"v2") self.failUnlessEqual(s0.nickname, NICKNAME % u"v2")
self.failUnlessEqual(s0.version, "my_version") self.failUnlessEqual(s0.version, "my_version")
class Announcements(AsyncTestCase): class Announcements(AsyncTestCase):
def test_client_v2_signed(self): def test_client_v2_signed(self):
introducer = IntroducerService() introducer = IntroducerService()
@ -755,10 +760,12 @@ class Announcements(AsyncTestCase):
"my_version", "oldest", app_versions, "my_version", "oldest", app_versions,
fakeseq, FilePath(self.mktemp())) fakeseq, FilePath(self.mktemp()))
furl1 = "pb://62ubehyunnyhzs7r6vdonnm2hpi52w6y@127.0.0.1:0/swissnum" furl1 = "pb://62ubehyunnyhzs7r6vdonnm2hpi52w6y@127.0.0.1:0/swissnum"
sk_s, vk_s = keyutil.make_keypair()
sk, _ignored = keyutil.parse_privkey(sk_s) priv_key = SigningKey.generate()
pks = keyutil.remove_prefix(vk_s, "pub-") pub_key = priv_key.public_key()
ann_t0 = make_ann_t(client_v2, furl1, sk, 10) pks = crypto.remove_prefix(pub_key.encoded_key(), "pub-")
ann_t0 = make_ann_t(client_v2, furl1, priv_key, 10)
canary0 = Referenceable() canary0 = Referenceable()
introducer.remote_publish_v2(ann_t0, canary0) introducer.remote_publish_v2(ann_t0, canary0)
a = introducer.get_announcements() a = introducer.get_announcements()
@ -786,20 +793,19 @@ class Announcements(AsyncTestCase):
# during startup (although the announcement will wait in a queue # during startup (although the announcement will wait in a queue
# until the introducer connection is established). To avoid getting # until the introducer connection is established). To avoid getting
# confused by this, disable storage. # confused by this, disable storage.
f = open(os.path.join(basedir, "tahoe.cfg"), "w") with open(os.path.join(basedir, "tahoe.cfg"), "w") as f:
f.write("[client]\n") f.write("[client]\n")
f.write("introducer.furl = nope\n") f.write("introducer.furl = nope\n")
f.write("[storage]\n") f.write("[storage]\n")
f.write("enabled = false\n") f.write("enabled = false\n")
f.close()
c = yield create_client(basedir) c = yield create_client(basedir)
ic = c.introducer_clients[0] ic = c.introducer_clients[0]
sk_s, vk_s = keyutil.make_keypair() priv_key = SigningKey.generate()
sk, _ignored = keyutil.parse_privkey(sk_s) pub_key = priv_key.public_key()
pub1 = keyutil.remove_prefix(vk_s, "pub-") pub_key_str = crypto.remove_prefix(pub_key.encoded_key(), "pub-")
furl1 = "pb://onug64tu@127.0.0.1:123/short" # base32("short") furl1 = "pb://onug64tu@127.0.0.1:123/short" # base32("short")
ann_t = make_ann_t(ic, furl1, sk, 1) ann_t = make_ann_t(ic, furl1, priv_key, 1)
ic.got_announcements([ann_t]) ic.got_announcements([ann_t])
yield flushEventualQueue() yield flushEventualQueue()
@ -807,7 +813,7 @@ class Announcements(AsyncTestCase):
# check the cache for the announcement # check the cache for the announcement
announcements = self._load_cache(cache_filepath) announcements = self._load_cache(cache_filepath)
self.failUnlessEqual(len(announcements), 1) self.failUnlessEqual(len(announcements), 1)
self.failUnlessEqual(announcements[0]['key_s'], pub1) self.failUnlessEqual(announcements[0]['key_s'], pub_key_str)
ann = announcements[0]["ann"] ann = announcements[0]["ann"]
self.failUnlessEqual(ann["anonymous-storage-FURL"], furl1) self.failUnlessEqual(ann["anonymous-storage-FURL"], furl1)
self.failUnlessEqual(ann["seqnum"], 1) self.failUnlessEqual(ann["seqnum"], 1)
@ -815,29 +821,29 @@ class Announcements(AsyncTestCase):
# a new announcement that replaces the first should replace the # a new announcement that replaces the first should replace the
# cached entry, not duplicate it # cached entry, not duplicate it
furl2 = furl1 + "er" furl2 = furl1 + "er"
ann_t2 = make_ann_t(ic, furl2, sk, 2) ann_t2 = make_ann_t(ic, furl2, priv_key, 2)
ic.got_announcements([ann_t2]) ic.got_announcements([ann_t2])
yield flushEventualQueue() yield flushEventualQueue()
announcements = self._load_cache(cache_filepath) announcements = self._load_cache(cache_filepath)
self.failUnlessEqual(len(announcements), 1) self.failUnlessEqual(len(announcements), 1)
self.failUnlessEqual(announcements[0]['key_s'], pub1) self.failUnlessEqual(announcements[0]['key_s'], pub_key_str)
ann = announcements[0]["ann"] ann = announcements[0]["ann"]
self.failUnlessEqual(ann["anonymous-storage-FURL"], furl2) self.failUnlessEqual(ann["anonymous-storage-FURL"], furl2)
self.failUnlessEqual(ann["seqnum"], 2) self.failUnlessEqual(ann["seqnum"], 2)
# but a third announcement with a different key should add to the # but a third announcement with a different key should add to the
# cache # cache
sk_s2, vk_s2 = keyutil.make_keypair() priv_key2 = SigningKey.generate()
sk2, _ignored = keyutil.parse_privkey(sk_s2) pub_key2 = priv_key2.public_key()
pub2 = keyutil.remove_prefix(vk_s2, "pub-") pub_key_str2 = crypto.remove_prefix(pub_key2.encoded_key(), "pub-")
furl3 = "pb://onug64tu@127.0.0.1:456/short" furl3 = "pb://onug64tu@127.0.0.1:456/short"
ann_t3 = make_ann_t(ic, furl3, sk2, 1) ann_t3 = make_ann_t(ic, furl3, priv_key2, 1)
ic.got_announcements([ann_t3]) ic.got_announcements([ann_t3])
yield flushEventualQueue() yield flushEventualQueue()
announcements = self._load_cache(cache_filepath) announcements = self._load_cache(cache_filepath)
self.failUnlessEqual(len(announcements), 2) self.failUnlessEqual(len(announcements), 2)
self.failUnlessEqual(set([pub1, pub2]), self.failUnlessEqual(set([pub_key_str, pub_key_str2]),
set([a["key_s"] for a in announcements])) set([a["key_s"] for a in announcements]))
self.failUnlessEqual(set([furl2, furl3]), self.failUnlessEqual(set([furl2, furl3]),
set([a["ann"]["anonymous-storage-FURL"] set([a["ann"]["anonymous-storage-FURL"]
@ -855,17 +861,17 @@ class Announcements(AsyncTestCase):
ic2._load_announcements() # normally happens when connection fails ic2._load_announcements() # normally happens when connection fails
yield flushEventualQueue() yield flushEventualQueue()
self.failUnless(pub1 in announcements) self.failUnless(pub_key_str in announcements)
self.failUnlessEqual(announcements[pub1]["anonymous-storage-FURL"], self.failUnlessEqual(announcements[pub_key_str]["anonymous-storage-FURL"],
furl2) furl2)
self.failUnlessEqual(announcements[pub2]["anonymous-storage-FURL"], self.failUnlessEqual(announcements[pub_key_str2]["anonymous-storage-FURL"],
furl3) furl3)
c2 = yield create_client(basedir) c2 = yield create_client(basedir)
c2.introducer_clients[0]._load_announcements() c2.introducer_clients[0]._load_announcements()
yield flushEventualQueue() yield flushEventualQueue()
self.assertEqual(c2.storage_broker.get_all_serverids(), self.assertEqual(c2.storage_broker.get_all_serverids(),
frozenset([pub1, pub2])) frozenset([pub_key_str, pub_key_str2]))
class ClientSeqnums(AsyncBrokenTestCase): class ClientSeqnums(AsyncBrokenTestCase):
@ -978,11 +984,11 @@ class DecodeFurl(SyncTestCase):
self.failUnlessEqual(nodeid, "\x9fM\xf2\x19\xcckU0\xbf\x03\r\x10\x99\xfb&\x9b-\xc7A\x1d") self.failUnlessEqual(nodeid, "\x9fM\xf2\x19\xcckU0\xbf\x03\r\x10\x99\xfb&\x9b-\xc7A\x1d")
class Signatures(SyncTestCase): class Signatures(SyncTestCase):
def test_sign(self): def test_sign(self):
ann = {"key1": "value1"} ann = {"key1": "value1"}
sk_s,vk_s = keyutil.make_keypair() priv_key = SigningKey.generate()
sk,ignored = keyutil.parse_privkey(sk_s) ann_t = sign_to_foolscap(ann, priv_key)
ann_t = sign_to_foolscap(ann, sk)
(msg, sig, key) = ann_t (msg, sig, key) = ann_t
self.failUnlessEqual(type(msg), type("".encode("utf-8"))) # bytes self.failUnlessEqual(type(msg), type("".encode("utf-8"))) # bytes
self.failUnlessEqual(json.loads(msg.decode("utf-8")), ann) self.failUnlessEqual(json.loads(msg.decode("utf-8")), ann)
@ -990,7 +996,7 @@ class Signatures(SyncTestCase):
self.failUnless(key.startswith("v0-")) self.failUnless(key.startswith("v0-"))
(ann2,key2) = unsign_from_foolscap(ann_t) (ann2,key2) = unsign_from_foolscap(ann_t)
self.failUnlessEqual(ann2, ann) self.failUnlessEqual(ann2, ann)
self.failUnlessEqual("pub-"+key2, vk_s) self.failUnlessEqual("pub-" + key2, priv_key.public_key().encoded_key())
# not signed # not signed
self.failUnlessRaises(UnknownKeyError, self.failUnlessRaises(UnknownKeyError,
@ -1000,14 +1006,14 @@ class Signatures(SyncTestCase):
# bad signature # bad signature
bad_ann = {"key1": "value2"} bad_ann = {"key1": "value2"}
bad_msg = json.dumps(bad_ann).encode("utf-8") bad_msg = json.dumps(bad_ann).encode("utf-8")
self.failUnlessRaises(keyutil.BadSignatureError, self.failUnlessRaises(ed25519.BadSignatureError,
unsign_from_foolscap, (bad_msg,sig,key)) unsign_from_foolscap, (bad_msg, sig, key))
# unrecognized signatures # unrecognized signatures
self.failUnlessRaises(UnknownKeyError, self.failUnlessRaises(UnknownKeyError,
unsign_from_foolscap, (bad_msg,"v999-sig",key)) unsign_from_foolscap, (bad_msg, "v999-sig", key))
self.failUnlessRaises(UnknownKeyError, self.failUnlessRaises(UnknownKeyError,
unsign_from_foolscap, (bad_msg,sig,"v999-key")) unsign_from_foolscap, (bad_msg, sig, "v999-key"))
# add tests of StorageFarmBroker: if it receives duplicate announcements, it # add tests of StorageFarmBroker: if it receives duplicate announcements, it

View File

@ -1,4 +1,3 @@
import time, os.path, platform, stat, re, json, struct, shutil import time, os.path, platform, stat, re, json, struct, shutil
from twisted.trial import unittest from twisted.trial import unittest

View File

@ -1,4 +1,5 @@
# from the Python Standard Library # from the Python Standard Library
import six
import string import string
from allmydata.util.assertutil import precondition from allmydata.util.assertutil import precondition
@ -179,13 +180,13 @@ def init_s5():
s5 = init_s5() s5 = init_s5()
def could_be_base32_encoded(s, s8=s8, tr=string.translate, identitytranstable=identitytranstable, chars=chars): def could_be_base32_encoded(s, s8=s8, tr=string.translate, identitytranstable=identitytranstable, chars=chars):
precondition(isinstance(s, str), s) precondition(isinstance(s, six.binary_type), s)
if s == '': if s == '':
return True return True
return s8[len(s)%8][ord(s[-1])] and not tr(s, identitytranstable, chars) return s8[len(s)%8][ord(s[-1])] and not tr(s, identitytranstable, chars)
def could_be_base32_encoded_l(s, lengthinbits, s5=s5, tr=string.translate, identitytranstable=identitytranstable, chars=chars): def could_be_base32_encoded_l(s, lengthinbits, s5=s5, tr=string.translate, identitytranstable=identitytranstable, chars=chars):
precondition(isinstance(s, str), s) precondition(isinstance(s, six.binary_type), s)
if s == '': if s == '':
return True return True
assert lengthinbits%5 < len(s5), lengthinbits assert lengthinbits%5 < len(s5), lengthinbits
@ -201,7 +202,7 @@ def a2b(cs):
@param cs the base-32 encoded data (a string) @param cs the base-32 encoded data (a string)
""" """
precondition(could_be_base32_encoded(cs), "cs is required to be possibly base32 encoded data.", cs=cs) precondition(could_be_base32_encoded(cs), "cs is required to be possibly base32 encoded data.", cs=cs)
precondition(isinstance(cs, str), cs) precondition(isinstance(cs, six.binary_type), cs)
return a2b_l(cs, num_octets_that_encode_to_this_many_quintets(len(cs))*8) return a2b_l(cs, num_octets_that_encode_to_this_many_quintets(len(cs))*8)
@ -226,7 +227,7 @@ def a2b_l(cs, lengthinbits):
@return the data encoded in cs @return the data encoded in cs
""" """
precondition(could_be_base32_encoded_l(cs, lengthinbits), "cs is required to be possibly base32 encoded data.", cs=cs, lengthinbits=lengthinbits) precondition(could_be_base32_encoded_l(cs, lengthinbits), "cs is required to be possibly base32 encoded data.", cs=cs, lengthinbits=lengthinbits)
precondition(isinstance(cs, str), cs) precondition(isinstance(cs, six.binary_type), cs)
if cs == '': if cs == '':
return '' return ''

View File

@ -9,11 +9,13 @@ from allmydata.web.common import (
from allmydata.util.abbreviate import abbreviate_space from allmydata.util.abbreviate import abbreviate_space
from allmydata.util import time_format, idlib from allmydata.util import time_format, idlib
def remove_prefix(s, prefix): def remove_prefix(s, prefix):
if not s.startswith(prefix): if not s.startswith(prefix):
return None return None
return s[len(prefix):] return s[len(prefix):]
class StorageStatus(MultiFormatPage): class StorageStatus(MultiFormatPage):
docFactory = getxmlfile("storage_status.xhtml") docFactory = getxmlfile("storage_status.xhtml")
# the default 'data' argument is the StorageServer instance # the default 'data' argument is the StorageServer instance