diff --git a/authentication.py b/authentication.py index 6191627b..085c1553 100644 --- a/authentication.py +++ b/authentication.py @@ -157,7 +157,7 @@ def has_valid_signature_for(self, placeholder, payload): return self._member.verify(payload, self._signature) def _is_sig_empty(self): - return self._signature == "" or self._signature == "\x00" * self._member.signature_length + return not self._signature or self._signature == "\x00" * self._member.signature_length def __init__(self, encoding="default"): diff --git a/community.py b/community.py index b4b9fc26..073a937c 100644 --- a/community.py +++ b/community.py @@ -20,6 +20,7 @@ from twisted.internet.task import LoopingCall, deferLater from twisted.python.threadable import isInIOThread +from crypto import DispersyPublicKey, DispersyCrypto from .authentication import NoAuthentication, MemberAuthentication, DoubleMemberAuthentication from .bloomfilter import BloomFilter from .candidate import Candidate, WalkCandidate @@ -481,13 +482,14 @@ def _download_master_member_identity(self): self._logger.debug("using dummy master member") try: - public_key, = self._dispersy.database.execute(u"SELECT public_key FROM member WHERE id = ?", (self._master_member.database_id,)).next() + public_key_binary, = self._dispersy.database.execute(u"SELECT public_key FROM member WHERE id = ?", (self._master_member.database_id,)).next() + public_key = DispersyPublicKey.from_bytes(public_key_binary) except StopIteration: pass else: if public_key: self._logger.debug("%s found master member", self._cid.encode("HEX")) - self._master_member = self._dispersy.get_member(public_key=str(public_key)) + self._master_member = self._dispersy.get_member(public_key=public_key) assert self._master_member.public_key self.cancel_pending_task("download master member identity") else: @@ -1842,8 +1844,11 @@ def get_member(self, *argv, **kwargs): assert isinstance(public_key, str) assert isinstance(private_key, str) assert not mid or len(mid) == 20 - assert not public_key or self._dispersy.crypto.is_valid_public_bin(public_key) - assert not private_key or self._dispersy.crypto.is_valid_private_bin(private_key) + assert not public_key or DispersyCrypto.is_valid_public_key(public_key) + assert not private_key or DispersyCrypto.is_valid_private_key(private_key) + + public_key = DispersyPublicKey.from_bytes(public_key) if public_key else None + private_key = DispersyPublicKey.from_bytes(private_key) if private_key else None member = self._dispersy.get_member(mid=mid, public_key=public_key, private_key=private_key) # We only need to check if this member has an identity message in this community if we still don't have the full diff --git a/conversion.py b/conversion.py index 5138b614..98865d73 100644 --- a/conversion.py +++ b/conversion.py @@ -6,6 +6,7 @@ from M2Crypto.EC import ECError +from crypto import DispersyCrypto, DispersyPublicKey from .authentication import Authentication, NoAuthentication, MemberAuthentication, DoubleMemberAuthentication from .bloomfilter import BloomFilter from .candidate import Candidate @@ -54,6 +55,7 @@ def __init__(self, community, dispersy_version, community_version): # the messages that this instance can handle, and that this instance produces, is identified # by _prefix. self._prefix = dispersy_version + community_version + community.cid + # 22 == hashlength + 2 assert len(self._prefix) == 22 # when this assumption changes, we need to ensure the # dispersy_version and community_version properties are # returned correctly @@ -84,9 +86,10 @@ def can_decode_message(self, data): """ # at least a length of 23, as we need the prefix + 1 byte messagetype assert isinstance(data, str), type(data) - assert len(data) >= 23 + prefix_length = 22 + assert len(data) > prefix_length - return (len(data) >= 23 and data[:22] == self._prefix) + return len(data) > prefix_length and data[:prefix_length] == self._prefix @abstractmethod def decode_meta_message(self, data): @@ -469,7 +472,7 @@ def _decode_authorize(self, placeholder, offset, data): key = data[offset:offset + key_length] try: - member = self._community.dispersy.get_member(public_key=key) + member = self._community.dispersy.get_member(public_key=DispersyPublicKey.from_bytes(key)) except: raise DropPacket("Invalid cryptographic key (_decode_authorize)") offset += key_length @@ -906,7 +909,7 @@ def _encode_member_authentication(self, container, message): container.append(message.authentication.member.mid) elif encoding == "bin": assert message.authentication.member.public_key - assert self._community.dispersy.crypto.is_valid_public_bin(message.authentication.member.public_key), message.authentication.member.public_key.encode("HEX") + assert DispersyCrypto.is_valid_public_key(message.authentication.member.public_key), message.authentication.member.public_key.encode("HEX") container.extend((self._struct_H.pack(len(message.authentication.member.public_key)), message.authentication.member.public_key)) else: raise NotImplementedError(encoding) @@ -1080,10 +1083,15 @@ def _decode_member_authentication(self, placeholder): except ECError: raise DropPacket("Invalid member authentication") + pk_offset = offset + 12 + 2 + key_length, = self._struct_H.unpack_from(data, offset + 12) + nr_messages_offset = pk_offset + key_length + nr_messages, = self._struct_B.unpack_from(data, nr_messages_offset) + # If signatures and verification are enabled, verify that the signature matches the member sha1 identifier if member: placeholder.offset = offset - placeholder.first_signature_offset = len(data) - member.signature_length + placeholder.first_signature_offset = nr_messages_offset + 1 + nr_messages * 2 placeholder.authentication = MemberAuthentication.Implementation(authentication, member, data[-member.signature_length:]) else: raise DelayPacketByMissingMember(self._community, member_id) @@ -1105,8 +1113,8 @@ def _decode_member_authentication(self, placeholder): if member: placeholder.offset = offset - placeholder.first_signature_offset = len(data) - member.signature_length - placeholder.authentication = MemberAuthentication.Implementation(authentication, member, data[-member.signature_length:]) + placeholder.first_signature_offset = offset + 8 + placeholder.authentication = MemberAuthentication.Implementation(authentication, member, data[placeholder.first_signature_offset:]) else: raise DropPacket("Invalid cryptographic key (_decode_member_authentication)") else: @@ -1164,10 +1172,11 @@ def can_decode_message(self, data): """ Returns True when DATA can be decoded using this conversion. """ + prefix_length = 22 assert isinstance(data, str), type(data) - return (len(data) >= 23 and - data[:22] == self._prefix and - data[22] in self._decode_message_map) + return (len(data) > prefix_length and + data[:prefix_length] == self._prefix and + data[prefix_length] in self._decode_message_map) def decode_meta_message(self, data): """ diff --git a/crypto.py b/crypto.py index 00d0b8ce..da598589 100644 --- a/crypto.py +++ b/crypto.py @@ -1,424 +1,272 @@ -from hashlib import sha1 +""" +All cryptography for Dispersy. +""" from math import ceil -from struct import Struct -import logging -from M2Crypto import EC, BIO +from cryptography.exceptions import InvalidSignature, UnsupportedAlgorithm +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives.asymmetric.ec import SECT233K1, SECT409K1, SECT571R1, generate_private_key, ECDSA +from cryptography.hazmat.primitives.hashes import SHA256, SHA512, SHA384, Hash, SHA1 +from cryptography.hazmat.primitives.serialization import load_pem_public_key, load_der_public_key, Encoding, \ + load_pem_private_key, load_der_private_key, PublicFormat, PrivateFormat, BestAvailableEncryption, NoEncryption -# Add libnacl submodule to the python path -import sys -import os -sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'libnacl')) +DEFAULT_SECURITY_LEVELS = {"low": SECT233K1, + "medium": SECT409K1, + "high": SECT571R1} -import libnacl.dual -from .util import attach_runtime_statistics -from libnacl.encode import hex_encode - -_STRUCT_L = Struct(">L") - -# Allow all available curves. -# Niels: 16-12-2013, if it starts with NID_ -_CURVES = dict((unicode(curve), (getattr(EC, curve), "M2Crypto")) for curve in dir(EC) if curve.startswith("NID_")) - -# We want to provide a few default curves. We will change these curves as new become available and -# old ones to small to provide sufficient security. -_CURVES.update({u"very-low": (EC.NID_sect163k1, "M2Crypto"), - u"low": (EC.NID_sect233k1, "M2Crypto"), - u"medium": (EC.NID_sect409k1, "M2Crypto"), - u"high": (EC.NID_sect571r1, "M2Crypto")}) - -# Add custom curves, not provided by M2Crypto -_CURVES.update({u'curve25519': (None, "libnacl")}) - -logger = logging.getLogger(__name__) - -class DispersyCrypto(object): - - @property - def security_levels(self): - """ - Returns the different security levels supported by this crypto class - @rtype: [unicode] - """ - raise NotImplementedError() - - def generate_key(self, security_level): +class DispersyCrypto: + """ + Cryptography helper methods for Dispersy. + """ + @staticmethod + def is_valid_public_key(string): """ - Generate a new key using the specified security_level - @param security_level: Level of security, supported levels can be obtained using .security_levels. - @type security_level: unicode + Verify if this binary string contains a public key. - @rtype key + :param string: a byte string possibly containing a public key + :return: a boolean """ - raise NotImplementedError() - - def key_to_bin(self, key): - "Convert a key to the binary format." - raise NotImplementedError() + for encoding in ["PEM", "DER"]: + try: + DispersyPublicKey.from_bytes(string, encoding) + return True + except (ValueError, UnsupportedAlgorithm): + pass - def key_to_hash(self, key): - "Get a hash representation from a key." - raise NotImplementedError() - - def key_from_public_bin(self, string): - "Convert a public key stored in the binary format to a key object." - raise NotImplementedError() - - def key_from_private_bin(self, string): - "Convert a public/private keypair stored in the binary format to a key object." - raise NotImplementedError() - - def is_valid_public_bin(self, string): - "Verify if this binary string contains a public key." - raise NotImplementedError() - - def is_valid_private_bin(self, string): - "Verify if this binary string contains a public/private keypair." - raise NotImplementedError() + return False - def is_valid_signature(self, key, string, signature): - "Verify if the signature matches the one generated by key/string pair." - raise NotImplementedError() + @staticmethod + def is_valid_private_key(string): + """ + Verify if this binary string contains a public/private keypair. - def create_signature(self, key, string): - "Create a signature using this key for this string." - raise NotImplementedError() + :param string: a byte string possible containing a private key + :return: a boolean + """ + for encoding in DispersyKey.ENCODINGS: + try: + DispersyPrivateKey.from_bytes(string, encoding) + return True + except (ValueError, UnsupportedAlgorithm): + pass - def get_signature_length(self, key): - "Get the length of a signature created using this key in bytes." - raise NotImplementedError() + return False -class ECCrypto(DispersyCrypto): +class DispersyKey(object): """ - A crypto object which provides a layer between Dispersy and low level eccrypographic features. - - Most methods are implemented by: - @author: Boudewijn Schoon - @organization: Technical University Delft - @contact: dispersy@frayja.com - - However since then, most functionality was completely rewritten by: - @author: Niels Zeilemaker + A wrapper around a key (pair). """ + ENCODINGS = {"DER", "PEM"} + HASH_ALGORITHMS = {"SHA1": SHA1, + "SHA256": SHA256, + "SHA384": SHA384, + "SHA512": SHA512} - def _progress(self, *args): - "Called when no feedback needs to be given." - pass - - @property - def security_levels(self): - """ - Returns the names of all available curves. - @rtype: [unicode] + def __init__(self, key): """ - return _CURVES.keys() + Create a new DispersyPublicKey instance. - @attach_runtime_statistics(u"{0.__class__.__name__}.{function_name}") - def generate_key(self, security_level): + :param key: an EllipticCurve instance """ - Generate a new Elliptic Curve object with a new public / private key pair. + self.key = key - Security can be u'low', u'medium', or u'high' depending on how secure you need your Elliptic - Curve to be. Currently these values translate into: - - very-low: NID_sect163k1 ~42 byte signatures - - low: NID_sect233k1 ~60 byte signatures - - medium: NID_sect409k1 ~104 byte signatures - - high: NID_sect571r1 ~144 byte signatures - - Besides these predefined curves, all other curves provided by M2Crypto are also available. For - a full list of available curves, see ec_get_curves(). - - @param security_level: Level of security {u'very-low', u'low', u'medium', or u'high'}. - @type security_level: unicode + @property + def curve(self): """ - assert isinstance(security_level, unicode) - assert security_level in _CURVES + The curve instance by which the key is backed. - curve = _CURVES[security_level] - if curve[1] == "M2Crypto": - return M2CryptoSK(curve[0]) + :return: a + """ + return self.key.curve - if curve[1] == "libnacl": - return LibNaCLSK() + @property + def curve_name(self): + """ + The name of the curve backing the key instance. - def key_to_bin(self, ec): - "Convert the key to a binary format." - assert isinstance(ec, DispersyKey), ec - return ec.key_to_bin() + :return: a string + """ + return self.key.curve.name - def key_to_hash(self, ec): - "Get a hash representation from a key." - assert isinstance(ec, DispersyKey), ec - return ec.key_to_hash() + def has_private_key(self): + """ + Whether this is a DispersyPublicKey or DispersyPrivateKey instance. - @attach_runtime_statistics(u"{0.__class__.__name__}.{function_name}") - def is_valid_private_bin(self, string): - "Returns True if the input is a valid public/private keypair stored in a binary format" - try: - self.key_from_private_bin(string) - except: - return False - return True + :return: a boolean + """ + raise NotImplementedError() - @attach_runtime_statistics(u"{0.__class__.__name__}.{function_name}") - def is_valid_public_bin(self, string): - "Returns True if the input is a valid public key" - try: - self.key_from_public_bin(string) - except: - return False - return True + def hash(self, hash_algorithm="SHA1"): + """ + Get a hash of the public part of this key. - def key_from_private_bin(self, string): - "Get the EC from a public/private keypair stored in a binary format." - if string.startswith("LibNaCLSK:"): - return LibNaCLSK(string[10:]) - return M2CryptoSK(keystring=string) + :param hash_algorithm: one of HASH_ALGORITHMS + :return: a binary string + """ + digest = Hash(self.HASH_ALGORITHMS[hash_algorithm](), default_backend()) + digest.update(self.public_bytes()) + return digest.finalize() - def key_from_public_bin(self, string): - "Get the EC from a public key in binary format." - if string.startswith("LibNaCLPK:"): - return LibNaCLPK(string[10:]) - return M2CryptoPK(keystring=string) + @property + def key_size(self): + return self.key.curve.key_size - def get_signature_length(self, ec): - """ - Returns the length, in bytes, of each signature made using EC. + @property + def signature_length(self): """ - assert isinstance(ec, DispersyKey), ec - return ec.get_signature_length() + TODO: Determine the signature length. See conversion.py, L1111. - def create_signature(self, ec, data): + These seem somehow longer than expected. For example: 233 bits -> 60 bytes, but actually is 64 bytes. + :return: """ - Returns the signature of DIGEST made using EC. + # temporary_key = generate_private_key(self.curve, default_backend()) + # signature = temporary_key.sign(b"AFDF", ECDSA(SHA1())) + # length = len(signature) + length = int(ceil(self.key_size / 8.0) * 2) + return length + + @staticmethod + def verify(key, signature, data, hash_algorithm="SHA1"): """ - assert isinstance(ec, DispersyKey), ec - assert isinstance(data, str), type(data) - return ec.signature(data) + Verify whether the data was signed for this public key. - def is_valid_signature(self, ec, data, signature): - """ - Returns True when SIGNATURE matches the DIGEST made using EC. + :param key: a public key to check against + :param signature: a bytes object with DER encoded contents (See RFC 3279) + :param data: the signed data + :param hash_algorithm: the hash algorithm used in signing + :return: a boolean """ - assert isinstance(ec, DispersyKey), ec - assert isinstance(data, str), type(data) - assert isinstance(signature, str), type(signature) - assert len(signature) == self.get_signature_length(ec), [len(signature), self.get_signature_length(ec)] - + algorithm = ECDSA(DispersyKey.HASH_ALGORITHMS[hash_algorithm]()) try: - return ec.verify(signature, data) - except: + key.verify(signature, data, algorithm) + except InvalidSignature: return False - -class NoVerifyCrypto(ECCrypto): - """ - A crypto object which assumes all signatures are valid. Usefull to reduce CPU overhead. - - """ - def is_valid_signature(self, ec, digest, signature): return True -class NoCrypto(NoVerifyCrypto): +class DispersyPublicKey(DispersyKey): """ - A crypto object which does not create a valid signatures, and assumes all signatures are valid. - Usefull to reduce CPU overhead. + A wrapper around a public key. """ + AVAILABLE_FORMATS = PublicFormat.__members__.keys() - def create_signature(self, ec, digest): - return "0" * self.get_signature_length(ec) - - -class DispersyKey(object): - - def pub(self): - raise NotImplementedError() - - def has_secret_key(self): - raise NotImplementedError() - - def key_to_bin(self): - raise NotImplementedError() - - def key_to_hash(self): - if self.has_secret_key(): - return sha1(self.pub().key_to_bin()).digest() - return sha1(self.key_to_bin()).digest() - - -class M2CryptoPK(DispersyKey): + def __init__(self, key): + """ + Create a new DispersyPublicKey instance. - def __init__(self, ec_pub=None, keystring=None): - if ec_pub: - self.ec = ec_pub - elif keystring: - self.ec = self.key_from_pem("-----BEGIN PUBLIC KEY-----\n%s-----END PUBLIC KEY-----\n" % keystring.encode("BASE64")) + :param key: an EllipticCurvePublicKey instance + """ + super(DispersyPublicKey, self).__init__(key) + self.public_key = self - def pub(self): - return self + def public_bytes(self, encoding="PEM", encoding_format="SubjectPublicKeyInfo"): + return self.key.public_bytes(Encoding[encoding], PublicFormat[encoding_format]) - def has_secret_key(self): + @property + def has_private_key(self): return False - @attach_runtime_statistics(u"{0.__class__.__name__}.{function_name}") - def pem_to_bin(self, pem): - """ - Convert a key in the PEM format into a key in the binary format. - @note: Enrcypted pem's are NOT supported and will silently fail. + def verify(self, signature, data, hash_algorithm="SHA1"): + return DispersyKey.verify(self.key, signature, data, hash_algorithm) + + @staticmethod + def from_bytes(string, encoding="PEM"): """ - return "".join(pem.split("\n")[1:-2]).decode("BASE64") - - @attach_runtime_statistics(u"{0.__class__.__name__}.{function_name}") - def key_to_pem(self): - "Convert a key to the PEM format." - bio = BIO.MemoryBuffer() - self.ec.save_pub_key_bio(bio) - return bio.read_all() - - @attach_runtime_statistics(u"{0.__class__.__name__}.{function_name}") - def key_from_pem(self, pem): - "Get the EC from a public PEM." - return EC.load_pub_key_bio(BIO.MemoryBuffer(pem)) - - @attach_runtime_statistics(u"{0.__class__.__name__}.{function_name}") - def key_to_bin(self): - return self.pem_to_bin(self.key_to_pem()) - - def get_signature_length(self): - return int(ceil(len(self.ec) / 8.0)) * 2 - - @attach_runtime_statistics(u"{0.__class__.__name__}.{function_name}") - def verify(self, signature, data): - length = len(signature) / 2 - r = signature[:length] - # remove all "\x00" prefixes - while r and r[0] == "\x00": - r = r[1:] - # prepend "\x00" when the most significant bit is set - if ord(r[0]) & 128: - r = "\x00" + r - - s = signature[length:] - # remove all "\x00" prefixes - while s and s[0] == "\x00": - s = s[1:] - # prepend "\x00" when the most significant bit is set - if ord(s[0]) & 128: - s = "\x00" + s - - mpi_r = _STRUCT_L.pack(len(r)) + r - mpi_s = _STRUCT_L.pack(len(s)) + s - - # mpi_r3 = bn_to_mpi(bin_to_bn(signature[:length])) - # mpi_s3 = bn_to_mpi(bin_to_bn(signature[length:])) - - # if not mpi_r == mpi_r3: - # raise RuntimeError([mpi_r.encode("HEX"), mpi_r3.encode("HEX")]) - # if not mpi_s == mpi_s3: - # raise RuntimeError([mpi_s.encode("HEX"), mpi_s3.encode("HEX")]) - - digest = sha1(data).digest() - return bool(self.ec.verify_dsa(digest, mpi_r, mpi_s)) - - -class M2CryptoSK(M2CryptoPK): - - def __init__(self, curve=None, keystring=None, filename=None): - if curve: - self.ec = EC.gen_params(curve) - self.ec.gen_key() - - elif keystring: - self.ec = self.key_from_pem("-----BEGIN EC PRIVATE KEY-----\n%s-----END EC PRIVATE KEY-----\n" % keystring.encode("BASE64")) - - elif filename: - # this workaround is needed to run Tribler on Windows 64 bit - membuf = BIO.MemoryBuffer(open(filename, 'rb').read()) - self.ec = EC.load_key_bio(membuf) - membuf.close() - - def pub(self): - return M2CryptoPK(ec_pub=self.ec.pub()) - - def has_secret_key(self): - return True + Load a DispersyPublicKey from a byte string. - @attach_runtime_statistics(u"{0.__class__.__name__}.{function_name}") - def key_to_pem(self): - "Convert a key to the PEM format." - bio = BIO.MemoryBuffer() - self.ec.save_key_bio(bio, None, lambda *args: "") - return bio.read_all() + :param string: the byte string containing the public key + :param encoding: the encoding used. One of "PEM" or "DER" + :return: a new DispersyPublicKey instance + """ + if Encoding[encoding] is Encoding.PEM: + loaded_key = load_pem_public_key(string, backend=default_backend()) + elif Encoding[encoding] is Encoding.DER: + loaded_key = load_der_public_key(string, backend=default_backend()) + else: + raise UnknownKeyEncodingException(encoding) - @attach_runtime_statistics(u"{0.__class__.__name__}.{function_name}") - def key_from_pem(self, pem): - "Get the EC from a public/private keypair stored in the PEM." - def get_password(*args): - return "" - return EC.load_key_bio(BIO.MemoryBuffer(pem), get_password) + return DispersyPublicKey(loaded_key) - @attach_runtime_statistics(u"{0.__class__.__name__}.{function_name}") - def signature(self, msg): - length = int(ceil(len(self.ec) / 8.0)) - digest = sha1(msg).digest() - mpi_r, mpi_s = self.ec.sign_dsa(digest) - length_r, = _STRUCT_L.unpack_from(mpi_r) - r = mpi_r[-min(length, length_r):] - length_s, = _STRUCT_L.unpack_from(mpi_s) - s = mpi_s[-min(length, length_s):] +class DispersyPrivateKey(DispersyKey): + """ + A wrapper around a key pair. + """ + AVAILABLE_FORMATS = PrivateFormat.__members__.keys() - return "".join(("\x00" * (length - len(r)), r, "\x00" * (length - len(s)), s)) + def __init__(self, key=None, security_level="high"): + """ + Create a new private-public key pair. + :param key: the private key + :param security_level: a curve to generate a new key from, if one is not provided + """ + key = key or generate_private_key(DEFAULT_SECURITY_LEVELS[security_level], backend=default_backend()) + super(DispersyPrivateKey, self).__init__(key) -class LibNaCLPK(DispersyKey): + self.public_key = DispersyPublicKey(self.key.public_key()) - def __init__(self, binarykey="", pk=None, hex_vk=None): - if binarykey: - pk, vk = binarykey[:libnacl.crypto_box_SECRETKEYBYTES], binarykey[libnacl.crypto_box_SECRETKEYBYTES: libnacl.crypto_box_SECRETKEYBYTES + libnacl.crypto_sign_SEEDBYTES] - hex_vk = hex_encode(vk) + def public_bytes(self, encoding="PEM", encoding_format="SubjectPublicKeyInfo"): + return self.public_key.public_bytes(encoding, encoding_format) - self.key = libnacl.public.PublicKey(pk) - self.veri = libnacl.sign.Verifier(hex_vk) + def private_bytes(self, encoding="PEM", encoding_format="PKCS8", password=None): + """ + Get this private key in byte form. - def pub(self): - return self + :param encoding: either "PEM" or "DER" + :param encoding_format: either "X.509 subjectPublicKeyInfo with PKCS#1", "Raw PKCS#1" or "OpenSSH" + :param password: an optional password in case the private key is to be encoded + :return: a byte string + """ + encryption = BestAvailableEncryption(password) if password else NoEncryption() + return self.key.private_bytes(Encoding[encoding], PrivateFormat[encoding_format], encryption) - def has_secret_key(self): - return False + @property + def has_private_key(self): + return True - @attach_runtime_statistics(u"{0.__class__.__name__}.{function_name}") - def verify(self, signature, msg): - return self.veri.verify(signature + msg) + def sign(self, data, hash_algorithm="SHA1"): + """ + Sign the provided data. - def key_to_bin(self): - return "LibNaCLPK:" + self.key.pk + self.veri.vk + Note that in order to verify the signature created by this method, the + hash algorithm used in creating the signature needs to be known. It is + therefore advised not to deviate from the default algorithm. - def get_signature_length(self): - return libnacl.crypto_sign_BYTES + :param data: the data to sign + :param hash_algorithm: the hash algorithm to use in signing + :return: a bytes object with DER encoded contents (see RFC 3279) + """ + algorithm = ECDSA(self.HASH_ALGORITHMS[hash_algorithm]()) + return self.key.sign(data, algorithm) + def verify(self, signature, data, hash_algorithm="SHA1"): + return DispersyKey.verify(self.key.public_key(), signature, data, hash_algorithm) -class LibNaCLSK(LibNaCLPK): + @staticmethod + def from_bytes(string, encoding="PEM", password=None): + """ + Load a private key from a byte string. - def __init__(self, binarykey=""): - if binarykey: - crypt, seed = binarykey[:libnacl.crypto_box_SECRETKEYBYTES], binarykey[libnacl.crypto_box_SECRETKEYBYTES : libnacl.crypto_box_SECRETKEYBYTES + libnacl.crypto_sign_SEEDBYTES] - self.key = libnacl.dual.DualSecret(crypt, seed) + :param string: the byte string containing the private key + :param encoding: the encoding used + :param password: an optional password in case the private key is encoded + :return: a new DispersyPrivateKey instance + """ + if Encoding[encoding] is Encoding.PEM: + loaded_key = load_pem_private_key(string, password=password, backend=default_backend()) + elif Encoding[encoding] is Encoding.DER: + loaded_key = load_der_private_key(string, password=password, backend=default_backend()) else: - self.key = libnacl.dual.DualSecret() - self.veri = libnacl.sign.Verifier(self.key.hex_vk()) - - def pub(self): - return LibNaCLPK(pk=self.key.pk, hex_vk=self.veri.hex_vk()) + raise UnknownKeyEncodingException(encoding) - def has_secret_key(self): - return True + return DispersyPrivateKey(loaded_key) - @attach_runtime_statistics(u"{0.__class__.__name__}.{function_name}") - def signature(self, msg): - return self.key.signature(msg) - def key_to_bin(self): - return "LibNaCLSK:" + self.key.sk + self.key.seed +class UnknownKeyEncodingException(Exception): + """ + Thrown when a key encoding wasn't recognised. + """ + pass diff --git a/dispersy.py b/dispersy.py index 83acdc92..874d3f93 100644 --- a/dispersy.py +++ b/dispersy.py @@ -53,10 +53,10 @@ from twisted.python.failure import Failure from twisted.python.threadable import isInIOThread +from crypto import DispersyPrivateKey, DispersyCrypto, DispersyPublicKey from .authentication import MemberAuthentication, DoubleMemberAuthentication from .candidate import LoopbackCandidate, WalkCandidate, Candidate from .community import Community -from .crypto import DispersyCrypto, ECCrypto from .destination import CommunityDestination, CandidateDestination from .discovery.community import DiscoveryCommunity from .dispersydatabase import DispersyDatabase @@ -85,7 +85,7 @@ class Dispersy(TaskManager): outgoing data for, possibly, multiple communities. """ - def __init__(self, endpoint, working_directory, database_filename=u"dispersy.db", crypto=ECCrypto()): + def __init__(self, endpoint, working_directory, database_filename=u"dispersy.db"): """ Initialise a Dispersy instance. @@ -101,7 +101,6 @@ def __init__(self, endpoint, working_directory, database_filename=u"dispersy.db" assert isinstance(endpoint, Endpoint), type(endpoint) assert isinstance(working_directory, unicode), type(working_directory) assert isinstance(database_filename, unicode), type(database_filename) - assert isinstance(crypto, DispersyCrypto), type(crypto) super(Dispersy, self).__init__() self._logger = logging.getLogger(self.__class__.__name__) @@ -125,8 +124,6 @@ def __init__(self, endpoint, working_directory, database_filename=u"dispersy.db" database_filename = os.path.join(database_directory, database_filename) self._database = DispersyDatabase(database_filename) - self._crypto = crypto - # indicates what our connection type is. currently it can be u"unknown", u"public", or # u"symmetric-NAT" self._connection_type = u"unknown" @@ -358,14 +355,6 @@ def database(self): """ return self._database - @property - def crypto(self): - """ - The Dispersy crypto singleton. - @rtype: DispersyCrypto - """ - return self._crypto - @property def statistics(self): """ @@ -445,15 +434,13 @@ def detach_progress_handler(self, func): def get_progress_handlers(self): return self._progress_handlers - def get_member(self, mid="", public_key="", private_key=""): + def get_member(self, mid="", public_key=None, private_key=None): """Returns a Member instance associated with public_key. Since we have the public_key, we can create this user if it doesn't yet. Hence, this method always succeeds. @param public_key: The public key of the member we want to obtain. @param private_key: The public/private key pair of the member we want to obtain. - @type public_key: string - @type private_key: string @return: The Member instance associated with public_key. @rtype: Member @@ -461,81 +448,76 @@ def get_member(self, mid="", public_key="", private_key=""): assert sum(map(bool, (mid, public_key, private_key))) == 1, \ "Only one of the three optional arguments may be passed: %s" % str((mid, public_key, private_key)) assert isinstance(mid, str) - assert isinstance(public_key, str) - assert isinstance(private_key, str) assert not mid or len(mid) == 20, (mid.encode("HEX"), len(mid)) - assert not public_key or self.crypto.is_valid_public_bin(public_key) - assert not private_key or self.crypto.is_valid_private_bin(private_key) if not mid: if public_key: - mid = sha1(public_key).digest() + mid = public_key.hash() elif private_key: - _key = self.crypto.key_from_private_bin(private_key) - mid = self.crypto.key_to_hash(_key.pub()) + mid = private_key.hash() member = self._member_cache_by_hash.get(mid) if member: return member if private_key: - key = self.crypto.key_from_private_bin(private_key) - public_key = self.crypto.key_to_bin(key.pub()) + public_key = private_key.public_key - elif public_key: - key = self.crypto.key_from_public_bin(public_key) - - # both public and private keys are valid at this point + # both public and private keys are valid from this point # The member is not cached, let's try to get it from the database row = self.database.execute(u"SELECT id, public_key, private_key FROM member WHERE mid = ? LIMIT 1", (buffer(mid),)).fetchone() + private_key_bytes = private_key.private_bytes() + public_key_bytes = public_key.public_bytes() + if row: database_id, public_key_from_db, private_key_from_db = row - public_key_from_db = "" if public_key_from_db is None else str(public_key_from_db) - private_key_from_db = "" if private_key_from_db is None else str(private_key_from_db) # the private key that was passed as an argument overrules everything, update db if neccesary if private_key: assert public_key - if private_key_from_db != private_key: + if private_key_from_db != private_key_bytes: self.database.execute(u"UPDATE member SET public_key = ?, private_key = ? WHERE id = ?", - (buffer(public_key), buffer(private_key), database_id)) + (buffer(public_key_bytes), buffer(private_key_bytes), database_id)) else: # the private key from the database overrules the public key argument if private_key_from_db: - key = self.crypto.key_from_private_bin(private_key_from_db) + key = DispersyPrivateKey.from_bytes(private_key_from_db) # the public key argument overrules anything in the database elif public_key: - if public_key_from_db != public_key: + if public_key_from_db != public_key_bytes: self.database.execute(u"UPDATE member SET public_key = ? WHERE id = ?", - (buffer(public_key), database_id)) + (buffer(public_key_bytes), database_id)) # no priv/pubkey arguments passed, maybe use the public key from the database elif public_key_from_db: - key = self.crypto.key_from_public_bin(public_key_from_db) + key = DispersyPublicKey.from_bytes(public_key_from_db) else: return DummyMember(self, database_id, mid) # the member is not in the database, insert it elif public_key or private_key: + if private_key: assert public_key + # The MID or public/private keys are not in the database, store them. database_id = self.database.execute( u"INSERT INTO member (mid, public_key, private_key) VALUES (?, ?, ?)", - (buffer(mid), buffer(public_key), buffer(private_key)), get_lastrowid=True) + (buffer(mid), buffer(public_key_bytes), buffer(private_key_bytes)), get_lastrowid=True) else: # We could't find the key on the DB, nothing else to do database_id = self.database.execute(u"INSERT INTO member (mid) VALUES (?)", - (buffer(mid),), get_lastrowid=True) + (buffer(mid),), get_lastrowid=True) return DummyMember(self, database_id, mid) - member = Member(self, key, database_id, mid) + key = private_key or public_key + member = Member(self, key, database_id) # store in cache self._member_cache_by_hash[member.mid] = member @@ -546,13 +528,12 @@ def get_member(self, mid="", public_key="", private_key=""): return member - def get_new_member(self, securitylevel=u"medium"): + def get_new_member(self, security_level=u"medium"): """ Returns a Member instance created from a newly generated public key. """ - assert isinstance(securitylevel, unicode), type(securitylevel) - key = self.crypto.generate_key(securitylevel) - return self.get_member(private_key=self.crypto.key_to_bin(key)) + key = DispersyPrivateKey(security_level=security_level) + return self.get_member(private_key=key) def get_member_from_database_id(self, database_id): """ diff --git a/libnacl b/libnacl deleted file mode 160000 index 64faa347..00000000 --- a/libnacl +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 64faa34715a42d7c581492a78d9e18e8dd355df6 diff --git a/member.py b/member.py index c53d2001..a9208454 100644 --- a/member.py +++ b/member.py @@ -9,6 +9,7 @@ def __init__(self, dispersy, database_id, mid): assert isinstance(dispersy, Dispersy), type(dispersy) assert isinstance(database_id, int), type(database_id) assert isinstance(mid, str), type(mid) + # The SHA1 hash is 20 bytes in size assert len(mid) == 20, len(mid) self._logger = logging.getLogger(self.__class__.__name__) @@ -70,7 +71,7 @@ def __str__(self): class Member(DummyMember): - def __init__(self, dispersy, key, database_id, mid=None): + def __init__(self, dispersy, key, database_id): """ Create a new Member instance. """ @@ -80,23 +81,11 @@ def __init__(self, dispersy, key, database_id, mid=None): assert isinstance(key, DispersyKey), type(key) assert isinstance(database_id, int), type(database_id) - if not mid: - mid = dispersy.crypto.key_to_hash(key.pub()) - super(Member, self).__init__(dispersy, database_id, mid) + super(Member, self).__init__(dispersy, database_id, mid=key.hash()) - public_key = dispersy.crypto.key_to_bin(key.pub()) + self._key = key - if key.has_secret_key(): - private_key = key - else: - private_key = None - - self._crypto = dispersy.crypto self._database = dispersy.database - self._public_key = public_key - self._private_key = private_key - self._ec = key - self._signature_length = self._crypto.get_signature_length(self._ec) self._has_identity = set() @property @@ -106,7 +95,7 @@ def public_key(self): This is binary representation of the public key. """ - return self._public_key + return self._key.public_bytes() @property def private_key(self): @@ -118,14 +107,14 @@ def private_key(self): It may be an empty string when the private key is not yet available. In this case the sign method will raise a RuntimeError. """ - return self._private_key + return self._key.private_bytes() @property def signature_length(self): """ The length, in bytes, of a signature. """ - return self._signature_length + return self._key.signature_length def add_identity(self, community): self._has_identity.add(community.cid) @@ -164,8 +153,7 @@ def verify(self, data, signature, offset=0, length=0): # DATA is to small, we expect len(DATA[OFFSET:OFFSET+LENGTH]) to be LENGTH return False - if self._public_key and self._signature_length == len(signature): - return self._crypto.is_valid_signature(self._ec, data[offset:offset + length], signature) + return self._key.verify(signature, data[offset:offset + length]) def sign(self, data, offset=0, length=0): """ @@ -186,8 +174,8 @@ def sign(self, data, offset=0, length=0): # DATA is to small, we expect len(DATA[OFFSET:OFFSET+LENGTH]) to be LENGTH raise ValueError("LENGTH is larger than the available DATA") - if self._private_key: - return self._crypto.create_signature(self._ec, data[offset:offset + length]) + if self._key.has_private_key: + return self._key.sign(data[offset:offset + length]) else: raise RuntimeError("unable to sign data without the private key") @@ -210,7 +198,7 @@ def __hash__(self): """ Allows Member classes to be used as keys in a dictionary. """ - return self._public_key.__hash__() + return hash(self._key) def __str__(self): """ diff --git a/tests/debugcommunity/node.py b/tests/debugcommunity/node.py index 03702af2..fdc2ac12 100644 --- a/tests/debugcommunity/node.py +++ b/tests/debugcommunity/node.py @@ -30,14 +30,14 @@ class DebugNode(object): node.init_my_member() """ - def __init__(self, testclass, dispersy, communityclass=DebugCommunity, c_master_member=None, curve=u"low"): + def __init__(self, testclass, dispersy, communityclass=DebugCommunity, c_master_member=None, security_level=u"low"): super(DebugNode, self).__init__() self._logger = logging.getLogger(self.__class__.__name__) self._testclass = testclass self._dispersy = dispersy - self._my_member = self._dispersy.get_new_member(curve) - self._my_pub_member = Member(self._dispersy, self._my_member._ec.pub(), self._my_member.database_id) + self._my_member = self._dispersy.get_new_member(security_level) + self._my_pub_member = Member(self._dispersy, self._my_member._key.public_key, self._my_member.database_id) if c_master_member == None: self._community = communityclass.create_community(self._dispersy, self._my_member) diff --git a/tests/test_crypto.py b/tests/test_crypto.py index 3e6feb5a..9f88ea9e 100644 --- a/tests/test_crypto.py +++ b/tests/test_crypto.py @@ -1,78 +1,95 @@ from unittest import TestCase -from ..crypto import ECCrypto +from ..crypto import DispersyPrivateKey, DEFAULT_SECURITY_LEVELS, DispersyPublicKey -class TestLowLevelCrypto(TestCase): +class TestDispersyKey(TestCase): - @classmethod - def setUpClass(cls): - super(TestLowLevelCrypto, cls).setUpClass() - cls.crypto = ECCrypto() + def setUp(self): + self.data = "".join(chr(i % 256) for i in range(1024)) def test_sign_and_verify(self): """ - Creates each curve, signs some data, and finally verifies the signature. + Creates a curve for each security_level, signs some data, and finally verifies the signature. """ - data = "".join(chr(i % 256) for i in xrange(1024)) - for curve in self.crypto.security_levels: - ec = self.crypto.generate_key(curve) - signature = self.crypto.create_signature(ec, data) - self.assertEqual(len(signature), self.crypto.get_signature_length(ec)) - self.assertTrue(self.crypto.is_valid_signature(ec, data, signature)) - - self.assertFalse(self.crypto.is_valid_signature(ec, data, "-" * self.crypto.get_signature_length(ec))) - self.assertFalse(self.crypto.is_valid_signature(ec, "---", signature)) - - for i in xrange(len(signature)): - # invert one bit in the ith character of the signature - invalid_signature = list(signature) - invalid_signature[i] = chr(ord(invalid_signature[i]) ^ 1) - invalid_signature = "".join(invalid_signature) - self.assertNotEqual(signature, invalid_signature) - self.assertFalse(self.crypto.is_valid_signature(ec, data, invalid_signature)) - - def test_serialise_binary(self): + for security_level in DEFAULT_SECURITY_LEVELS.keys(): + key = DispersyPrivateKey(security_level=security_level) + signature = key.sign(self.data) + + self.assertTrue(key.verify(signature, self.data)) + + def test_serialise_private(self): + """ + Create, serialise and deserialize a key for each curve. + """ + for security_level in DEFAULT_SECURITY_LEVELS.keys(): + key = DispersyPrivateKey(security_level=security_level) + binary = key.private_bytes() + key2 = DispersyPrivateKey.from_bytes(binary) + + self.assertEqual(key2.hash(), key.hash()) + + def test_serialise_public(self): """ Creates and serialises each curve. """ - data = "".join(chr(i % 256) for i in xrange(1024)) - for curve in self.crypto.security_levels: - ec = self.crypto.generate_key(curve) - ec_pub = ec.pub() - - signature = self.crypto.create_signature(ec, data) - self.assertEqual(len(signature), self.crypto.get_signature_length(ec)) - self.assertTrue(self.crypto.is_valid_signature(ec, data, signature)) - - public = self.crypto.key_to_bin(ec_pub) - self.assertTrue(self.crypto.is_valid_public_bin(public), public) - self.assertEqual(public, self.crypto.key_to_bin(ec_pub)) - - private = self.crypto.key_to_bin(ec) - self.assertTrue(self.crypto.is_valid_private_bin(private), private) - self.assertEqual(private, self.crypto.key_to_bin(ec)) - - ec_clone = self.crypto.key_from_public_bin(public) - self.assertTrue(self.crypto.is_valid_signature(ec_clone, data, signature)) - ec_clone = self.crypto.key_from_private_bin(private) - self.assertTrue(self.crypto.is_valid_signature(ec_clone, data, signature)) - - def test_performance(self): - from time import time - import sys - import os - - ec = self.crypto.generate_key(u"very-low") - - data = [os.urandom(1024) for i in xrange(1000)] - for curve in [u"very-low", u"low", u"medium", u"high", u"curve25519"]: - t1 = time() - ec = self.crypto.generate_key(curve) - t2 = time() - signatures = [self.crypto.create_signature(ec, msg) for msg in data] - t3 = time() - verfified = [self.crypto.is_valid_signature(ec, msg, signature) for msg, signature in zip(data, signatures)] - # print >> sys.stderr, curve, "verify", time() - t3, "sign", t3 - t2, "genkey", t2 - t1 - - assert all(verfified) + for security_level in DEFAULT_SECURITY_LEVELS.keys(): + key = DispersyPrivateKey(security_level=security_level) + public_key = key.public_key + signature = key.sign(self.data) + + binary = public_key.public_bytes() + key2 = DispersyPublicKey.from_bytes(binary) + + self.assertTrue(key2.verify(signature, self.data)) + + def test_sign_not_valid(self): + """ + A signature created with another key shouldn't be valid. + """ + for security_level in DEFAULT_SECURITY_LEVELS.keys(): + key1 = DispersyPrivateKey(security_level=security_level) + key2 = DispersyPrivateKey(security_level=security_level) + signature1 = key1.sign(self.data) + signature2 = key2.sign(self.data) + + self.assertFalse(key1.verify(signature2, self.data)) + self.assertFalse(key2.verify(signature1, self.data)) + + def test_sign_verify_with_public(self): + """ + Signature verification should be possible with a public key only. + """ + for security_level in DEFAULT_SECURITY_LEVELS.keys(): + key = DispersyPrivateKey(security_level=security_level) + public_key = key.public_key + signature = key.sign(self.data) + + self.assertTrue(public_key.verify(signature, self.data)) + + def test_sign_verify_with_other_public(self): + """ + Signature verification with another public key should fail. + """ + for security_level in DEFAULT_SECURITY_LEVELS.keys(): + key1 = DispersyPrivateKey(security_level=security_level) + key2 = DispersyPrivateKey(security_level=security_level) + public_key1 = key1.public_key + public_key2 = key2.public_key + signature1 = key1.sign(self.data) + signature2 = key2.sign(self.data) + + self.assertFalse(public_key1.verify(signature2, self.data)) + self.assertFalse(public_key2.verify(signature1, self.data)) + + def test_hash(self): + """ + Test taking a hash. + """ + key = DispersyPrivateKey() + h = key.hash() + self.assertEqual(len(h), 20) + + public_key = key.public_key + h = public_key.hash() + self.assertEqual(len(h), 20) diff --git a/tests/test_member.py b/tests/test_member.py index 37681198..d0506d06 100644 --- a/tests/test_member.py +++ b/tests/test_member.py @@ -1,3 +1,4 @@ +from crypto import DispersyPrivateKey from .dispersytestclass import DispersyTestFunc from ..util import call_on_reactor_thread @@ -6,72 +7,70 @@ class TestMember(DispersyTestFunc): def test_verify(self): self._test_verify(u"medium") - self._test_verify(u"curve25519") @call_on_reactor_thread - def _test_verify(self, curve): + def _test_verify(self, security_level): """ Test test member.verify assuming create_signature works properly. """ - ec = self._dispersy.crypto.generate_key(curve) - member = self._dispersy.get_member(private_key=self._dispersy.crypto.key_to_bin(ec)) + key = DispersyPrivateKey(security_level=security_level) + member = self._dispersy.get_member(private_key=key) # sign and verify "0123456789"[0:10] - self.assertTrue(member.verify("0123456789", self._dispersy.crypto.create_signature(ec, "0123456789"))) - self.assertTrue(member.verify("0123456789", self._dispersy.crypto.create_signature(ec, "0123456789"), offset=0, length=0)) - self.assertFalse(member.verify("0123456789E", self._dispersy.crypto.create_signature(ec, "0123456789"), offset=0, length=0)) - self.assertFalse(member.verify("0123456789", self._dispersy.crypto.create_signature(ec, "0123456789"), offset=0, length=9)) - self.assertFalse(member.verify("0123456789E", self._dispersy.crypto.create_signature(ec, "0123456789"), offset=0, length=9)) - self.assertTrue(member.verify("0123456789", self._dispersy.crypto.create_signature(ec, "0123456789"), offset=0, length=10)) - self.assertTrue(member.verify("0123456789E", self._dispersy.crypto.create_signature(ec, "0123456789"), offset=0, length=10)) - self.assertFalse(member.verify("0123456789", self._dispersy.crypto.create_signature(ec, "0123456789"), offset=0, length=11)) - self.assertFalse(member.verify("0123456789E", self._dispersy.crypto.create_signature(ec, "0123456789"), offset=0, length=11)) - self.assertFalse(member.verify("0123456789", self._dispersy.crypto.create_signature(ec, "0123456789"), offset=0, length=666)) - self.assertFalse(member.verify("0123456789E", self._dispersy.crypto.create_signature(ec, "0123456789"), offset=0, length=666)) + self.assertTrue(member.verify("0123456789", self._dispersy.crypto.create_signature(key, "0123456789"))) + self.assertTrue(member.verify("0123456789", self._dispersy.crypto.create_signature(key, "0123456789"), offset=0, length=0)) + self.assertFalse(member.verify("0123456789E", self._dispersy.crypto.create_signature(key, "0123456789"), offset=0, length=0)) + self.assertFalse(member.verify("0123456789", self._dispersy.crypto.create_signature(key, "0123456789"), offset=0, length=9)) + self.assertFalse(member.verify("0123456789E", self._dispersy.crypto.create_signature(key, "0123456789"), offset=0, length=9)) + self.assertTrue(member.verify("0123456789", self._dispersy.crypto.create_signature(key, "0123456789"), offset=0, length=10)) + self.assertTrue(member.verify("0123456789E", self._dispersy.crypto.create_signature(key, "0123456789"), offset=0, length=10)) + self.assertFalse(member.verify("0123456789", self._dispersy.crypto.create_signature(key, "0123456789"), offset=0, length=11)) + self.assertFalse(member.verify("0123456789E", self._dispersy.crypto.create_signature(key, "0123456789"), offset=0, length=11)) + self.assertFalse(member.verify("0123456789", self._dispersy.crypto.create_signature(key, "0123456789"), offset=0, length=666)) + self.assertFalse(member.verify("0123456789E", self._dispersy.crypto.create_signature(key, "0123456789"), offset=0, length=666)) # sign and verify "0123456789"[1:10] - self.assertTrue(member.verify("123456789", self._dispersy.crypto.create_signature(ec, "123456789"))) - self.assertTrue(member.verify("0123456789", self._dispersy.crypto.create_signature(ec, "123456789"), offset=1, length=0)) - self.assertFalse(member.verify("0123456789E", self._dispersy.crypto.create_signature(ec, "123456789"), offset=1, length=0)) - self.assertFalse(member.verify("0123456789", self._dispersy.crypto.create_signature(ec, "123456789"), offset=1, length=8)) - self.assertFalse(member.verify("0123456789E", self._dispersy.crypto.create_signature(ec, "123456789"), offset=1, length=8)) - self.assertTrue(member.verify("0123456789", self._dispersy.crypto.create_signature(ec, "123456789"), offset=1, length=9)) - self.assertTrue(member.verify("0123456789E", self._dispersy.crypto.create_signature(ec, "123456789"), offset=1, length=9)) - self.assertFalse(member.verify("0123456789", self._dispersy.crypto.create_signature(ec, "123456789"), offset=1, length=10)) - self.assertFalse(member.verify("0123456789E", self._dispersy.crypto.create_signature(ec, "123456789"), offset=1, length=10)) - self.assertFalse(member.verify("0123456789", self._dispersy.crypto.create_signature(ec, "123456789"), offset=1, length=666)) - self.assertFalse(member.verify("0123456789E", self._dispersy.crypto.create_signature(ec, "123456789"), offset=1, length=666)) + self.assertTrue(member.verify("123456789", self._dispersy.crypto.create_signature(key, "123456789"))) + self.assertTrue(member.verify("0123456789", self._dispersy.crypto.create_signature(key, "123456789"), offset=1, length=0)) + self.assertFalse(member.verify("0123456789E", self._dispersy.crypto.create_signature(key, "123456789"), offset=1, length=0)) + self.assertFalse(member.verify("0123456789", self._dispersy.crypto.create_signature(key, "123456789"), offset=1, length=8)) + self.assertFalse(member.verify("0123456789E", self._dispersy.crypto.create_signature(key, "123456789"), offset=1, length=8)) + self.assertTrue(member.verify("0123456789", self._dispersy.crypto.create_signature(key, "123456789"), offset=1, length=9)) + self.assertTrue(member.verify("0123456789E", self._dispersy.crypto.create_signature(key, "123456789"), offset=1, length=9)) + self.assertFalse(member.verify("0123456789", self._dispersy.crypto.create_signature(key, "123456789"), offset=1, length=10)) + self.assertFalse(member.verify("0123456789E", self._dispersy.crypto.create_signature(key, "123456789"), offset=1, length=10)) + self.assertFalse(member.verify("0123456789", self._dispersy.crypto.create_signature(key, "123456789"), offset=1, length=666)) + self.assertFalse(member.verify("0123456789E", self._dispersy.crypto.create_signature(key, "123456789"), offset=1, length=666)) # sign and verify "0123456789"[0:9] - self.assertTrue(member.verify("012345678", self._dispersy.crypto.create_signature(ec, "012345678"))) - self.assertFalse(member.verify("0123456789", self._dispersy.crypto.create_signature(ec, "012345678"), offset=0, length=0)) - self.assertFalse(member.verify("0123456789E", self._dispersy.crypto.create_signature(ec, "012345678"), offset=0, length=0)) - self.assertFalse(member.verify("0123456789", self._dispersy.crypto.create_signature(ec, "012345678"), offset=0, length=8)) - self.assertFalse(member.verify("0123456789E", self._dispersy.crypto.create_signature(ec, "012345678"), offset=0, length=8)) - self.assertTrue(member.verify("0123456789", self._dispersy.crypto.create_signature(ec, "012345678"), offset=0, length=9)) - self.assertTrue(member.verify("0123456789E", self._dispersy.crypto.create_signature(ec, "012345678"), offset=0, length=9)) - self.assertFalse(member.verify("0123456789", self._dispersy.crypto.create_signature(ec, "012345678"), offset=0, length=10)) - self.assertFalse(member.verify("0123456789E", self._dispersy.crypto.create_signature(ec, "012345678"), offset=0, length=10)) - self.assertFalse(member.verify("0123456789", self._dispersy.crypto.create_signature(ec, "012345678"), offset=0, length=666)) - self.assertFalse(member.verify("0123456789E", self._dispersy.crypto.create_signature(ec, "012345678"), offset=0, length=666)) + self.assertTrue(member.verify("012345678", self._dispersy.crypto.create_signature(key, "012345678"))) + self.assertFalse(member.verify("0123456789", self._dispersy.crypto.create_signature(key, "012345678"), offset=0, length=0)) + self.assertFalse(member.verify("0123456789E", self._dispersy.crypto.create_signature(key, "012345678"), offset=0, length=0)) + self.assertFalse(member.verify("0123456789", self._dispersy.crypto.create_signature(key, "012345678"), offset=0, length=8)) + self.assertFalse(member.verify("0123456789E", self._dispersy.crypto.create_signature(key, "012345678"), offset=0, length=8)) + self.assertTrue(member.verify("0123456789", self._dispersy.crypto.create_signature(key, "012345678"), offset=0, length=9)) + self.assertTrue(member.verify("0123456789E", self._dispersy.crypto.create_signature(key, "012345678"), offset=0, length=9)) + self.assertFalse(member.verify("0123456789", self._dispersy.crypto.create_signature(key, "012345678"), offset=0, length=10)) + self.assertFalse(member.verify("0123456789E", self._dispersy.crypto.create_signature(key, "012345678"), offset=0, length=10)) + self.assertFalse(member.verify("0123456789", self._dispersy.crypto.create_signature(key, "012345678"), offset=0, length=666)) + self.assertFalse(member.verify("0123456789E", self._dispersy.crypto.create_signature(key, "012345678"), offset=0, length=666)) # sign and verify "0123456789"[1:9] - self.assertTrue(member.verify("12345678", self._dispersy.crypto.create_signature(ec, "12345678"))) - self.assertFalse(member.verify("0123456789", self._dispersy.crypto.create_signature(ec, "12345678"), offset=1, length=0)) - self.assertFalse(member.verify("0123456789E", self._dispersy.crypto.create_signature(ec, "12345678"), offset=1, length=0)) - self.assertFalse(member.verify("0123456789", self._dispersy.crypto.create_signature(ec, "12345678"), offset=1, length=7)) - self.assertFalse(member.verify("0123456789E", self._dispersy.crypto.create_signature(ec, "12345678"), offset=1, length=7)) - self.assertTrue(member.verify("0123456789", self._dispersy.crypto.create_signature(ec, "12345678"), offset=1, length=8)) - self.assertTrue(member.verify("0123456789E", self._dispersy.crypto.create_signature(ec, "12345678"), offset=1, length=8)) - self.assertFalse(member.verify("0123456789", self._dispersy.crypto.create_signature(ec, "12345678"), offset=1, length=9)) - self.assertFalse(member.verify("0123456789E", self._dispersy.crypto.create_signature(ec, "12345678"), offset=1, length=9)) - self.assertFalse(member.verify("0123456789", self._dispersy.crypto.create_signature(ec, "12345678"), offset=1, length=666)) - self.assertFalse(member.verify("0123456789E", self._dispersy.crypto.create_signature(ec, "12345678"), offset=1, length=666)) + self.assertTrue(member.verify("12345678", self._dispersy.crypto.create_signature(key, "12345678"))) + self.assertFalse(member.verify("0123456789", self._dispersy.crypto.create_signature(key, "12345678"), offset=1, length=0)) + self.assertFalse(member.verify("0123456789E", self._dispersy.crypto.create_signature(key, "12345678"), offset=1, length=0)) + self.assertFalse(member.verify("0123456789", self._dispersy.crypto.create_signature(key, "12345678"), offset=1, length=7)) + self.assertFalse(member.verify("0123456789E", self._dispersy.crypto.create_signature(key, "12345678"), offset=1, length=7)) + self.assertTrue(member.verify("0123456789", self._dispersy.crypto.create_signature(key, "12345678"), offset=1, length=8)) + self.assertTrue(member.verify("0123456789E", self._dispersy.crypto.create_signature(key, "12345678"), offset=1, length=8)) + self.assertFalse(member.verify("0123456789", self._dispersy.crypto.create_signature(key, "12345678"), offset=1, length=9)) + self.assertFalse(member.verify("0123456789E", self._dispersy.crypto.create_signature(key, "12345678"), offset=1, length=9)) + self.assertFalse(member.verify("0123456789", self._dispersy.crypto.create_signature(key, "12345678"), offset=1, length=666)) + self.assertFalse(member.verify("0123456789E", self._dispersy.crypto.create_signature(key, "12345678"), offset=1, length=666)) def test_sign(self): self._test_sign(u"medium") - self._test_sign(u"curve25519") @call_on_reactor_thread def _test_sign(self, curve): diff --git a/tool/createkey.py b/tool/createkey.py deleted file mode 100755 index 3fbea814..00000000 --- a/tool/createkey.py +++ /dev/null @@ -1,73 +0,0 @@ -#!/usr/bin/env python2 - -""" -Create one or more Elliptic Curves. - -Typically the following curves are used: -- very-low: NID_sect163k1 ~42 byte signatures -- low: NID_sect233k1 ~60 byte signatures -- medium: NID_sect409k1 ~104 byte signatures -- high: NID_sect571r1 ~144 byte signatures -""" - -import argparse -import time -from hashlib import sha1 - -from M2Crypto import EC -# From: http://docs.python.org/2/tutorial/modules.html#intra-package-references -# Note that both explicit and implicit relative imports are based on the name of the current -# module. Since the name of the main module is always "__main__", modules intended for use as the -# main module of a Python application should always use absolute imports. -from dispersy.crypto import ECCrypto, _CURVES - -def ec_name(eccrypto, curve): - assert isinstance(curve, unicode) - curve_id = _CURVES[curve] - - for name in dir(EC): - value = getattr(EC, name) - if isinstance(value, int) and value == curve_id: - return name - -def create_key(eccrypto, curves): - for index, curve in enumerate(curves): - if index > 0: - print - - private_pem = "" - public_pem = "" - - ec = eccrypto.generate_key(curve) - if hasattr(ec, 'key_to_pem'): - print "KEP" - private_pem = ec.key_to_pem() - public_pem = ec.pub().key_to_pem() - - private_bin = eccrypto.key_to_bin(ec) - public_bin = eccrypto.key_to_bin(ec.pub()) - print "generated:", time.ctime() - print "curve:", ec_name(eccrypto, curve) - print "len:", len(ec.ec), "bits ~", eccrypto.get_signature_length(ec), "bytes signature" - print "pub:", len(public_bin), public_bin.encode("HEX") - print "prv:", len(private_bin), private_bin.encode("HEX") - print "pub-sha1", sha1(public_bin).digest().encode("HEX") - print "prv-sha1", sha1(private_bin).digest().encode("HEX") - print public_pem.strip() - print private_pem.strip() - -def main(): - eccrypto = ECCrypto() - - parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) - parser.add_argument("curves", - metavar="CURVE", - nargs="+", - choices=sorted([str(curve) for curve in eccrypto.security_levels]), - help="EC curves to create") - args = parser.parse_args() - - create_key(eccrypto, (unicode(curve) for curve in args.curves)) - -if __name__ == "__main__": - main() diff --git a/twisted/plugins/tracker_plugin.py b/twisted/plugins/tracker_plugin.py index 3488e54a..d15afcdc 100644 --- a/twisted/plugins/tracker_plugin.py +++ b/twisted/plugins/tracker_plugin.py @@ -40,7 +40,6 @@ sys.path.insert(0, DISPERSY_ROOT_PATH) from dispersy.candidate import LoopbackCandidate -from dispersy.crypto import NoVerifyCrypto, NoCrypto from dispersy.discovery.community import DiscoveryCommunity from dispersy.dispersy import Dispersy from dispersy.endpoint import StandaloneEndpoint @@ -64,8 +63,8 @@ class TrackerDispersy(Dispersy): - def __init__(self, endpoint, working_directory, silent=False, crypto=NoVerifyCrypto()): - super(TrackerDispersy, self).__init__(endpoint, working_directory, u":memory:", crypto) + def __init__(self, endpoint, working_directory, silent=False): + super(TrackerDispersy, self).__init__(endpoint, working_directory, u":memory:") # location of persistent storage self._persistent_storage_filename = os.path.join(working_directory, "persistent-storage.data") @@ -201,12 +200,6 @@ def makeService(self, options): tracker_service = TrackerMultiService(options["logfile"], options["statedir"]) tracker_service.setName("Dispersy Tracker") - # crypto - if options["crypto"] == 'NoCrypto': - crypto = NoCrypto() - else: - crypto = NoVerifyCrypto() - container = [None] manhole_namespace = {} if options["manhole"]: @@ -225,8 +218,7 @@ def run(): dispersy = TrackerDispersy(StandaloneEndpoint(options["port"], options["ip"]), unicode(options["statedir"]), - bool(options["silent"]), - crypto) + bool(options["silent"])) container[0] = dispersy manhole_namespace['dispersy'] = dispersy