diff --git a/README.rst b/README.rst index 2002802..a8095db 100644 --- a/README.rst +++ b/README.rst @@ -86,101 +86,68 @@ You can run the (fast) test suite, the (slower) known-answer-tests, and the spee python setup.py speed -Prefixes and Encodings ----------------------- - -The basic keypair/sign/verify operations work on binary bytestrings: signing -keys are created with a 32-byte seed or a 64-byte expanded form, verifying -keys are serialized as 32-byte binary strings, and signatures are 64-byte -binary strings. - -All methods that generate or accept bytestrings take a prefix= argument, -which is simply prepended to the output or stripped from the input. This can -be used for a cheap version check: if you use e.g. prefix="pubkey0-" when -handling verifying keys, and later update your application to use a different -kind of key (and update to "pubkey1-"), then older receivers will throw a -clean error when faced with a key format that they cannot handle. - -These methods also accept an encoding= argument, which makes them return an -ASCII string instead of a binary bytestring. This makes it convenient to -display verifying keys or signatures to cut-and-paste or encode into JSON -messages. Be careful when encouraging users to cut-and-paste signing keys, -since you might enable them to accidentally reveal those keys: in general, it -should require slightly more attention to handle signing keys than verifying -keys. - -encoding= can be set to one of "base64", "base32", "base16", or "hex" (an -alias for "base16"). The strings are stripped of trailing "=" markers and -lowercased (for base32/base16). - - Usage ----- -The first step is to create a signing key and store it. The safest way to -generate a key is with the create_keypair() function, which uses 32 bytes of -random data from os.urandom() (although you can provide an alternative -entropy source with the entropy= argument):: - - import ed25519 - signing_key, verifying_key = ed25519.create_keypair() - open("my-secret-key","wb").write(signing_key.to_bytes()) - vkey_hex = verifying_key.to_ascii(encoding="hex") - print "the public key is", vkey_hex +The first step is to generate a signing key and store it. At the same time, +you'll probably need to derive the verifying key and give it to someone else. +Signing keys are generated from 32-byte uniformly-random seeds. The safest +way to generate a key seed is with os.urandom(32):: -The private signing key string produced by to_bytes() is 64 bytes long, and -includes a copy of the public key (to avoid the 1.9ms needed to recalculate -it later). If you want to store less data (and recompute the public key -later), you can store just the 32 byte seed instead:: + import os, ed25519 + from binascii import hexlify, unhexlify - open("my-secret-seed","wb").write(signing_key.to_seed()) + sk_bytes = os.urandom(32) + signing_key = ed25519.SigningKey(sk_bytes) + open("my-secret-key","wb").write(sk_bytes) -The signing key is an instance of the ed25519.SigningKey class. To -reconstruct this instance from a serialized form, the constructor accepts the -output of either .to_bytes() or .to_seed():: + vkey_hex = hexlify(sk.get_verifying_key_bytes()) + print "the public key is", vkey_hex - keydata = open("my-secret-key","rb").read() - signing_key = ed25519.SigningKey(keydata) +To reconstruct the same key from the stored form later, just pass it back +into SigningKey:: - seed = open("my-secret-seed","rb").read() - signing_key2 = ed25519.SigningKey(seed) - assert signing_key == signing_key2 + sk_bytes = open("my-secret-key","rb").read() + signing_key = ed25519.SigningKey(sk_bytes) Special-purpose applications may want to derive keypairs from existing secrets; any 32-byte uniformly-distributed random string can be provided as a -seed:: +seed. The safest approach is to feed a string with at least 256 bits of +entropy into a cryptographic hash function like SHA256, or to use a +well-known protocol like HKDF:: import os, hashlib master = os.urandom(87) - seed = hashlib.sha256(master).digest() - signing_key = ed25519.SigningKey(seed) + sk_bytes = hashlib.sha256(master).digest() + signing_key = ed25519.SigningKey(sk_bytes) Once you have the SigningKey instance, use its .sign() method to sign a message. The signature is 64 bytes, but can be generated in printable form with the encoding= argument:: - sig = signing_key.sign("hello world", encoding="base64") - print "sig is:", sig + sig = signing_key.sign("hello world") + print "sig is:", hexlify(sig) On the verifying side, the receiver first needs to construct a -ed25519.VerifyingKey instance from the serialized string, then use its +ed25519.VerifyingKey instance from the serialized form, then use its .verify() method on the signature and message:: vkey_hex = "1246b84985e1ab5f83f4ec2bdf271114666fd3d9e24d12981a3c861b9ed523c6" - verifying_key = ed25519.VerifyingKey(vkey_hex, encoding="hex") + verifying_key = ed25519.VerifyingKey(unhexlify(vkey_hex) try: - verifying_key.verify(sig, "hello world", encoding="base64") + verifying_key.verify(sig, "hello world") print "signature is good" except ed25519.BadSignatureError: print "signature is bad!" If you happen to have the SigningKey but not the corresponding VerifyingKey, -you can derive it with .get_verifying_key(). This allows the sending side to -hold just 32 bytes of data and derive everything else from that:: +you can derive it with .get_verifying_key_bytes(). This allows the sending +side to hold just 32 bytes of data and derive everything else from that. +Deriving a verifying key takes about 1.9ms:: - keydata = open("my-secret-seed","rb").read() - signing_key = ed25519.SigningKey(keydata) - verifying_key = signing_key.get_verifying_key() + sk_bytes = open("my-secret-seed","rb").read() + signing_key = ed25519.SigningKey(sk_bytes) + verifying_key = ed25519.VerifyingKey(signing_key.get_verifying_key_bytes()) There is also a basic command-line keygen/sign/verify tool in bin/edsig . @@ -190,23 +157,13 @@ API Summary The complete API is summarized here:: - sk,vk = ed25519.create_keypair(entropy=os.urandom) - vk = sk.get_verifying_key() - - signature = sk.sign(message, prefix=, encoding=) - vk.verify(signature, message, prefix=, encoding=) - - seed = sk.to_seed(prefix=) - sk = SigningKey(seed, prefix=) - bytes = sk.to_bytes(prefix=) - sk = SigningKey(bytes, prefix=) - ascii = sk.to_ascii(prefix=, encoding=) # encodes seed - sk = SigningKey(ascii, prefix=, encoding=) + sk_bytes = os.urandom(32) + sk = SigningKey(sk_bytes) + vk_bytes = sk.get_verifying_key_bytes() + vk = VerifyingKey(vk_bytes) - bytes = vk.to_bytes(prefix=) - vk = VerifyingKey(bytes, prefix=) - ascii = vk.to_ascii(prefix=, encoding=) - vk = VerifyingKey(ascii, prefix=, encoding=) + signature = sk.sign(message) + vk.verify(signature, message) footnotes diff --git a/bin/edsig b/bin/edsig index d7d9c4f..be30063 100755 --- a/bin/edsig +++ b/bin/edsig @@ -1,6 +1,7 @@ #! /usr/bin/python import os, sys +from binascii import hexlify, unhexlify import ed25519 from hashlib import sha256 @@ -54,35 +55,37 @@ def message_rep(msg_arg): if len(sys.argv) < 2: help() elif sys.argv[1] == "generate": - sk,vk = ed25519.create_keypair() + sk_bytes = os.urandom(32) + sk = ed25519.SigningKey(sk_bytes) + vk_bytes = sk.get_verifying_key_bytes() if len(sys.argv) > 2: sk_outfile = sys.argv[2]+".signing.key" vk_outfile = sys.argv[2]+".verifying.key" else: sk_outfile = "signing.key" vk_outfile = "verifying.key" - sk_s = sk.to_seed(prefix="sign0-") - vk_s = vk.to_ascii("verf0-", "base32") - open(sk_outfile,"w").write(sk_s) + vk_s = "verf0-"+hexlify(vk_bytes) + open(sk_outfile,"w").write("sign0-"+sk_bytes) open(vk_outfile,"w").write(vk_s+"\n") print "wrote private signing key to", sk_outfile print "write public verifying key to", vk_outfile elif sys.argv[1] == "sign": sk_arg = sys.argv[2] msg_arg = sys.argv[3] - sk = ed25519.SigningKey(data_from_arg(sk_arg, "sign0-", 52, False), - prefix="sign0-") - sig = sk.sign(message_rep(msg_arg), prefix="sig0-", encoding="base32") + sk_s = data_from_arg(sk_arg, "sign0-", 64, False) + sk = ed25519.SigningKey(remove_prefix("sign0-", sk_s)) + sig = "sig0-"+hexlify(sk.sign(message_rep(msg_arg))) print sig elif sys.argv[1] == "verify": vk_arg = sys.argv[2] msg_arg = sys.argv[3] sig_arg = sys.argv[4] - vk = ed25519.VerifyingKey(data_from_arg(vk_arg, "verf0-", 52, True), - prefix="verf0-", encoding="base32") - sig = data_from_arg(sig_arg, "sig0-", 103, True) - vk.verify(sig, message_rep(msg_arg), - prefix="sig0-", encoding="base32") # could raise BadSignature + vk_s = data_from_arg(vk_arg, "verf0-", 64, True).strip() + vk_bytes = unhexlify(remove_prefix("verf0-", vk_s)) + vk = ed25519.VerifyingKey(vk_bytes) + sig_s = data_from_arg(sig_arg, "sig0-", 128, True).strip() + sig_bytes = unhexlify(remove_prefix("sig0-", sig_s)) + vk.verify(sig_bytes, message_rep(msg_arg)) # could raise BadSignature print "good signature!" else: help() diff --git a/src/ed25519/__init__.py b/src/ed25519/__init__.py index 54d266c..f399769 100644 --- a/src/ed25519/__init__.py +++ b/src/ed25519/__init__.py @@ -1,10 +1,6 @@ -from keys import (BadSignatureError, BadPrefixError, - create_keypair, SigningKey, VerifyingKey, - remove_prefix, to_ascii, from_ascii) +from keys import (BadSignatureError, SigningKey, VerifyingKey) -(BadSignatureError, BadPrefixError, - create_keypair, SigningKey, VerifyingKey, - remove_prefix, to_ascii, from_ascii) # hush pyflakes +(BadSignatureError, SigningKey, VerifyingKey) # hush pyflakes from _version import get_versions __version__ = get_versions()['version'] diff --git a/src/ed25519/keys.py b/src/ed25519/keys.py index 8820a30..fe980d7 100644 --- a/src/ed25519/keys.py +++ b/src/ed25519/keys.py @@ -1,159 +1,71 @@ import os -import base64 import _ed25519 BadSignatureError = _ed25519.BadSignatureError -def create_keypair(entropy=os.urandom): - SEEDLEN = _ed25519.SECRETKEYBYTES/2 - assert SEEDLEN == 32 - seed = entropy(SEEDLEN) - sk = SigningKey(seed) - vk = sk.get_verifying_key() - return sk, vk - -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):] - -def to_ascii(s_bytes, prefix="", encoding="base64"): - """Return a version-prefixed ASCII representation of the given binary - string. 'encoding' indicates how to do the encoding, and can be one of: - * base64 - * base32 - * base16 (or hex) - - This function handles bytes, not bits, so it does not append any trailing - '=' (unlike standard base64.b64encode). It also lowercases the base32 - output. - - 'prefix' will be prepended to the encoded form, and is useful for - distinguishing the purpose and version of the binary string. E.g. you - could prepend 'pub0-' to a VerifyingKey string to allow the receiving - code to raise a useful error if someone pasted in a signature string by - mistake. - """ - if encoding == "base64": - s_ascii = base64.b64encode(s_bytes).rstrip("=") - elif encoding == "base32": - s_ascii = base64.b32encode(s_bytes).rstrip("=").lower() - elif encoding in ("base16", "hex"): - s_ascii = base64.b16encode(s_bytes).lower() - else: - raise NotImplementedError - return prefix+s_ascii - -def from_ascii(s_ascii, prefix="", encoding="base64"): - """This is the opposite of to_ascii. It will throw BadPrefixError if - the prefix is not found. - """ - s_ascii = remove_prefix(s_ascii.strip(), prefix) - if encoding == "base64": - s_ascii += "="*((4 - len(s_ascii)%4)%4) - s_bytes = base64.b64decode(s_ascii) - elif encoding == "base32": - s_ascii += "="*((8 - len(s_ascii)%8)%8) - s_bytes = base64.b32decode(s_ascii.upper()) - elif encoding in ("base16", "hex"): - s_bytes = base64.b16decode(s_ascii.upper()) - else: - raise NotImplementedError - return s_bytes - class SigningKey(object): - # this can only be used to reconstruct a key created by create_keypair(). - def __init__(self, sk_s, prefix="", encoding=None): - assert isinstance(sk_s, type("")) # string, really bytes - sk_s = remove_prefix(sk_s, prefix) - if encoding is not None: - sk_s = from_ascii(sk_s, encoding=encoding) - if len(sk_s) == 32: - # create from seed - vk_s, sk_s = _ed25519.publickey(sk_s) - else: - if len(sk_s) != 32+32: - raise ValueError("SigningKey takes 32-byte seed or 64-byte string") - self.sk_s = sk_s # seed+pubkey - self.vk_s = sk_s[32:] # just pubkey - - def to_bytes(self, prefix=""): - return prefix+self.sk_s - - def to_ascii(self, prefix="", encoding=None): - assert encoding - return to_ascii(self.to_seed(), prefix, encoding) - - def to_seed(self, prefix=""): - return prefix+self.sk_s[:32] + # this is how all keys are created + def __init__(self, sk_bytes): + if not isinstance(sk_bytes, type("")): + raise TypeError("must be bytes, not %s" % type(sk_bytes)) + if len(sk_bytes) != 32: + raise ValueError("must be exactly 32 bytes") + vk_bytes, sk_and_vk = _ed25519.publickey(sk_bytes) + assert sk_and_vk[:32] == sk_bytes + assert vk_bytes == sk_and_vk[32:] + self.vk_bytes = vk_bytes + self.sk_and_vk = sk_and_vk def __eq__(self, them): if not isinstance(them, object): return False return (them.__class__ == self.__class__ - and them.sk_s == self.sk_s) + and them.sk_and_vk == self.sk_and_vk) - def get_verifying_key(self): - return VerifyingKey(self.vk_s) + def get_verifying_key_bytes(self): + return self.vk_bytes - def sign(self, msg, prefix="", encoding=None): - sig_and_msg = _ed25519.sign(msg, self.sk_s) + def sign(self, msg): + sig_and_msg = _ed25519.sign(msg, self.sk_and_vk) # the response is R+S+msg sig_R = sig_and_msg[0:32] sig_S = sig_and_msg[32:64] msg_out = sig_and_msg[64:] sig_out = sig_R + sig_S assert msg_out == msg - if encoding: - return to_ascii(sig_out, prefix, encoding) - return prefix+sig_out + return sig_out class VerifyingKey(object): - def __init__(self, vk_s, prefix="", encoding=None): - assert isinstance(vk_s, type("")) # string, really bytes - vk_s = remove_prefix(vk_s, prefix) - if encoding is not None: - vk_s = from_ascii(vk_s, encoding=encoding) - - assert len(vk_s) == 32 - self.vk_s = vk_s - - def to_bytes(self, prefix=""): - return prefix+self.vk_s - - def to_ascii(self, prefix="", encoding=None): - assert encoding - return to_ascii(self.vk_s, prefix, encoding) + def __init__(self, vk_bytes): + if not isinstance(vk_bytes, type("")): + raise TypeError("must be bytes, not %s" % type(vk_bytes)) + if len(vk_bytes) != 32: + raise ValueError("must be exactly 32 bytes") + self.vk_bytes = vk_bytes def __eq__(self, them): if not isinstance(them, object): return False return (them.__class__ == self.__class__ - and them.vk_s == self.vk_s) + and them.vk_bytes == self.vk_bytes) - def verify(self, sig, msg, prefix="", encoding=None): + def verify(self, sig, msg): assert isinstance(sig, type("")) # string, really bytes - if encoding: - sig = from_ascii(sig, prefix, encoding) - else: - sig = remove_prefix(sig, prefix) assert len(sig) == 64 sig_R = sig[:32] sig_S = sig[32:] sig_and_msg = sig_R + sig_S + msg # this might raise BadSignatureError - msg2 = _ed25519.open(sig_and_msg, self.vk_s) + msg2 = _ed25519.open(sig_and_msg, self.vk_bytes) assert msg2 == msg def selftest(): + from binascii import unhexlify, hexlify message = "crypto libraries should always test themselves at powerup" - sk = SigningKey("priv0-VIsfn5OFGa09Un2MR6Hm7BQ5++xhcQskU2OGXG8jSJl4cWLZrRrVcSN2gVYMGtZT+3354J5jfmqAcuRSD9KIyg", - prefix="priv0-", encoding="base64") - vk = VerifyingKey("pub0-eHFi2a0a1XEjdoFWDBrWU/t9+eCeY35qgHLkUg/SiMo", - prefix="pub0-", encoding="base64") - assert sk.get_verifying_key() == vk - sig = sk.sign(message, prefix="sig0-", encoding="base64") - assert sig == "sig0-E/QrwtSF52x8+q0l4ahA7eJbRKc777ClKNg217Q0z4fiYMCdmAOI+rTLVkiFhX6k3D+wQQfKdJYMxaTUFfv1DQ", sig - vk.verify(sig, message, prefix="sig0-", encoding="base64") + sk_bytes = unhexlify("548b1f9f938519ad3d527d8c47a1e6ec1439fbec61710b245363865c6f234899") + sk = SigningKey(sk_bytes) + vk_bytes = unhexlify("787162d9ad1ad571237681560c1ad653fb7df9e09e637e6a8072e4520fd288ca") + vk = VerifyingKey(vk_bytes) + assert sk.get_verifying_key_bytes() == vk_bytes + sig = sk.sign(message) + assert hexlify(sig) == "13f42bc2d485e76c7cfaad25e1a840ede25b44a73befb0a528d836d7b434cf87e260c09d980388fab4cb564885857ea4dc3fb04107ca74960cc5a4d415fbf50d" + vk.verify(sig, message) selftest() diff --git a/src/ed25519/test_ed25519.py b/src/ed25519/test_ed25519.py index 840909d..4f8aba9 100644 --- a/src/ed25519/test_ed25519.py +++ b/src/ed25519/test_ed25519.py @@ -1,4 +1,5 @@ +import os import unittest import time from binascii import hexlify, unhexlify @@ -82,186 +83,67 @@ def test_raw(self): raw.open, flip_bit(sig, in_byte=33)+msg, vk_s) - def test_keypair(self): - sk, vk = ed25519.create_keypair() - self.failUnless(isinstance(sk, ed25519.SigningKey), sk) - self.failUnless(isinstance(vk, ed25519.VerifyingKey), vk) - sk2, vk2 = ed25519.create_keypair() - self.failIfEqual(hexlify(sk.to_bytes()), hexlify(sk2.to_bytes())) - # you can control the entropy source - def not_so_random(length): - return "4"*length - sk1, vk1 = ed25519.create_keypair(entropy=not_so_random) - self.failUnlessEqual(sk1.to_ascii(encoding="base64"), - "NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ") - self.failUnlessEqual(vk1.to_ascii(encoding="base64"), - "6yzxO/euOl9hQWih+wknLTl3HsS4UjcngV5GbK+O4WM") - sk2, vk2 = ed25519.create_keypair(entropy=not_so_random) - self.failUnlessEqual(sk1.to_ascii(encoding="base64"), - sk2.to_ascii(encoding="base64")) - self.failUnlessEqual(vk1.to_ascii(encoding="base64"), - vk2.to_ascii(encoding="base64")) + def test_publickey(self): + sk_bytes = unhexlify("4ba96b0b5303328c7405220598a587c4" + "acb06ed9a9601d149f85400195f1ec3d") + sk = ed25519.SigningKey(sk_bytes) + self.failUnlessRaises(ValueError, ed25519.SigningKey, "wrong length") + vk_bytes = sk.get_verifying_key_bytes() + self.failUnlessEqual(hexlify(vk_bytes), + "a66d161e090652b054740748f059f92a" + "5b731f1c27b05571f6d942e4f8b7b264") - def test_publickey(self): - seed = unhexlify("4ba96b0b5303328c7405220598a587c4" - "acb06ed9a9601d149f85400195f1ec3d") - sk = ed25519.SigningKey(seed) - self.failUnlessEqual(hexlify(sk.to_bytes()), - ("4ba96b0b5303328c7405220598a587c4" - "acb06ed9a9601d149f85400195f1ec3d" - "a66d161e090652b054740748f059f92a" - "5b731f1c27b05571f6d942e4f8b7b264")) - self.failUnlessEqual(hexlify(sk.to_seed()), - ("4ba96b0b5303328c7405220598a587c4" - "acb06ed9a9601d149f85400195f1ec3d")) - self.failUnlessRaises(ValueError, - ed25519.SigningKey, "wrong length") - sk2 = ed25519.SigningKey(seed) - self.failUnlessEqual(sk, sk2) + vk = ed25519.VerifyingKey(vk_bytes) + self.failUnlessRaises(ValueError, ed25519.VerifyingKey, "wrong length") def test_OOP(self): - sk_s = unhexlify("4ba96b0b5303328c7405220598a587c4" - "acb06ed9a9601d149f85400195f1ec3d" - "a66d161e090652b054740748f059f92a" - "5b731f1c27b05571f6d942e4f8b7b264") - sk = ed25519.SigningKey(sk_s) - self.failUnlessEqual(len(sk.to_bytes()), 64) - self.failUnlessEqual(sk.to_bytes(), sk_s) - - sk2_seed = unhexlify("4ba96b0b5303328c7405220598a587c4" + sk_bytes = unhexlify("4ba96b0b5303328c7405220598a587c4" "acb06ed9a9601d149f85400195f1ec3d") - sk2 = ed25519.SigningKey(sk2_seed) - self.failUnlessEqual(sk2.to_bytes(), sk.to_bytes()) + sk = ed25519.SigningKey(sk_bytes) + + self.failUnlessEqual(hexlify(sk.get_verifying_key_bytes()), + "a66d161e090652b054740748f059f92a" + "5b731f1c27b05571f6d942e4f8b7b264") + vk = ed25519.VerifyingKey(sk.get_verifying_key_bytes()) - vk = sk.get_verifying_key() - self.failUnlessEqual(len(vk.to_bytes()), 32) - exp_vks = unhexlify("a66d161e090652b054740748f059f92a" - "5b731f1c27b05571f6d942e4f8b7b264") - self.failUnlessEqual(vk.to_bytes(), exp_vks) - self.failUnlessEqual(ed25519.VerifyingKey(vk.to_bytes()), vk) msg = "hello world" sig = sk.sign(msg) self.failUnlessEqual(len(sig), 64) - exp_sig = unhexlify("6eaffe94f2972b35158b6aaa9b69c1da" - "97f0896aca29c41b1dd7b32e6c9e2ff6" - "76fc8d8b034709cdcc37d8aeb86bebfb" - "173ace3c319e211ea1d7e8d8884c1808") - self.failUnlessEqual(sig, exp_sig) + self.failUnlessEqual(hexlify(sig), + "6eaffe94f2972b35158b6aaa9b69c1da" + "97f0896aca29c41b1dd7b32e6c9e2ff6" + "76fc8d8b034709cdcc37d8aeb86bebfb" + "173ace3c319e211ea1d7e8d8884c1808") self.failUnlessEqual(vk.verify(sig, msg), None) # also, don't throw self.failUnlessRaises(ed25519.BadSignatureError, vk.verify, sig, msg+".. NOT!") def test_object_identity(self): - sk1_s = unhexlify("ef32972ae3f1252a5aa1395347ea008c" - "bd2fed0773a4ea45e2d2d06c8cf8fbd4" - "c024601a9c5b854fb100ff3116cf4f22" - "a311565f027391cb49d3bbe11c44399d") - sk2_s = unhexlify("3d550c158900b4c2922b6656d2f80572" - "89de4ee65043745179685ae7d29b944d" - "672b8a2cb23f9e75e1d46ce249cd9c04" - "68f816f1c734a102822b60e18b41eacd") - sk1a = ed25519.SigningKey(sk1_s) - sk1b = ed25519.SigningKey(sk1_s) - vk1a = sk1a.get_verifying_key() - vk1b = sk1b.get_verifying_key() - sk2 = ed25519.SigningKey(sk2_s) - vk2 = sk2.get_verifying_key() + sk1_bytes = unhexlify("ef32972ae3f1252a5aa1395347ea008c" + "bd2fed0773a4ea45e2d2d06c8cf8fbd4") + sk2_bytes = unhexlify("3d550c158900b4c2922b6656d2f80572" + "89de4ee65043745179685ae7d29b944d") + sk1a = ed25519.SigningKey(sk1_bytes) + sk1b = ed25519.SigningKey(sk1_bytes) + sk2 = ed25519.SigningKey(sk2_bytes) self.failUnlessEqual(sk1a, sk1b) self.failIfEqual(sk1a, sk2) + + vk1_bytes = sk1a.get_verifying_key_bytes() + self.failUnlessEqual(vk1_bytes, sk1b.get_verifying_key_bytes()) + vk2_bytes = sk2.get_verifying_key_bytes() + vk1a = ed25519.VerifyingKey(vk1_bytes) + vk1b = ed25519.VerifyingKey(vk1_bytes) + vk2 = ed25519.VerifyingKey(vk2_bytes) self.failUnlessEqual(vk1a, vk1b) self.failIfEqual(vk1a, vk2) + # exercise compare-against-other-type self.failIfEqual(sk2, "not a SigningKey") self.failIfEqual(vk2, "not a VerifyingKey") - def test_prefix(self): - sk1,vk1 = ed25519.create_keypair() - PREFIX = "private0-" - p = sk1.to_bytes(PREFIX) - # that gives us a binary string with a prefix - self.failUnless(p.startswith(PREFIX), repr(p)) - sk2 = ed25519.SigningKey(p, prefix=PREFIX) - self.failUnlessEqual(sk1, sk2) - self.failUnlessEqual(repr(sk1.to_bytes()), repr(sk2.to_bytes())) - self.failUnlessRaises(ed25519.BadPrefixError, - ed25519.SigningKey, p, prefix="WRONG-") - # SigningKey.to_seed() can do a prefix too - p = sk1.to_seed(PREFIX) - self.failUnless(p.startswith(PREFIX), repr(p)) - sk3 = ed25519.SigningKey(p, prefix=PREFIX) - self.failUnlessEqual(sk1, sk3) - self.failUnlessEqual(repr(sk1.to_bytes()), repr(sk3.to_bytes())) - self.failUnlessRaises(ed25519.BadPrefixError, - ed25519.SigningKey, p, prefix="WRONG-") - - # verifying keys can do this too - PREFIX = "public0-" - p = vk1.to_bytes(PREFIX) - self.failUnless(p.startswith(PREFIX), repr(p)) - vk2 = ed25519.VerifyingKey(p, prefix=PREFIX) - self.failUnlessEqual(vk1, vk2) - self.failUnlessEqual(repr(vk1.to_bytes()), repr(vk2.to_bytes())) - self.failUnlessRaises(ed25519.BadPrefixError, - ed25519.VerifyingKey, p, prefix="WRONG-") - - # and signatures - PREFIX = "sig0-" - p = sk1.sign("msg", PREFIX) - self.failUnless(p.startswith(PREFIX), repr(p)) - vk1.verify(p, "msg", PREFIX) - self.failUnlessRaises(ed25519.BadPrefixError, - vk1.verify, p, "msg", prefix="WRONG-") - - def test_ascii(self): - b2a = ed25519.to_ascii - a2b = ed25519.from_ascii - for prefix in ("", "prefix-"): - for length in range(0, 100): - b1 = "a"*length - for base in ("base64", "base32", "base16", "hex"): - a = b2a(b1, prefix, base) - b2 = a2b(a, prefix, base) - self.failUnlessEqual(b1, b2) - - def test_encoding(self): - sk_s = "\x88" * 32 # usually urandom(32) - sk1 = ed25519.SigningKey(sk_s) - vk1 = sk1.get_verifying_key() - - def check1(encoding, expected): - PREFIX = "private0-" - p = sk1.to_ascii(PREFIX, encoding) - self.failUnlessEqual(p, expected) - sk2 = ed25519.SigningKey(p, prefix=PREFIX, encoding=encoding) - self.failUnlessEqual(repr(sk1.to_bytes()), repr(sk2.to_bytes())) - self.failUnlessEqual(sk1, sk2) - check1("base64", "private0-iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIg") - check1("base32", "private0-rceirceirceirceirceirceirceirceirceirceirceirceircea") - check1("hex", "private0-8888888888888888888888888888888888888888888888888888888888888888") - - def check2(encoding, expected): - PREFIX="public0-" - p = vk1.to_ascii(PREFIX, encoding) - self.failUnlessEqual(p, expected) - vk2 = ed25519.VerifyingKey(p, prefix=PREFIX, encoding=encoding) - self.failUnlessEqual(repr(vk1.to_bytes()), repr(vk2.to_bytes())) - self.failUnlessEqual(vk1, vk2) - check2("base64", "public0-skkdlQKuKGMKK6yy4MdFEP/N0yjDNP8+E5PnWy0x59w") - check2("base32", "public0-wjer3ficvyuggcrlvszobr2fcd743uziym2p6pqtsptvwljr47oa") - check2("hex", "public0-b2491d9502ae28630a2bacb2e0c74510ffcdd328c334ff3e1393e75b2d31e7dc") - - def check3(encoding, expected): - msg = "msg" - PREFIX="sig0-" - sig = sk1.sign(msg, PREFIX, encoding) - self.failUnlessEqual(sig, expected) - vk1.verify(sig, msg, PREFIX, encoding) - check3("base64", "sig0-MNfdUir6tMlaYQ+/p8KANJ5d+bk8g2al76v5MeJCo6RiywxURda3sU580CyiW2FBG/Q7kDRswgYqxbkQw3o5CQ") - check3("base32", "sig0-gdl52urk7k2mswtbb672pquagspf36nzhsbwnjppvp4tdyscuosgfsymkrc5nn5rjz6nalfclnqucg7uhoidi3gcayvmloiqyn5dsci") - check3("hex", "sig0-30d7dd522afab4c95a610fbfa7c280349e5df9b93c8366a5efabf931e242a3a462cb0c5445d6b7b14e7cd02ca25b61411bf43b90346cc2062ac5b910c37a3909") - if __name__ == '__main__': unittest.main()