aboutsummaryrefslogtreecommitdiff
path: root/freezed_deps/ecdsa/test_pyecdsa.py
diff options
context:
space:
mode:
Diffstat (limited to 'freezed_deps/ecdsa/test_pyecdsa.py')
-rw-r--r--freezed_deps/ecdsa/test_pyecdsa.py1445
1 files changed, 1445 insertions, 0 deletions
diff --git a/freezed_deps/ecdsa/test_pyecdsa.py b/freezed_deps/ecdsa/test_pyecdsa.py
new file mode 100644
index 0000000..d83eb01
--- /dev/null
+++ b/freezed_deps/ecdsa/test_pyecdsa.py
@@ -0,0 +1,1445 @@
+from __future__ import with_statement, division
+
+try:
+ import unittest2 as unittest
+except ImportError:
+ import unittest
+import os
+import time
+import shutil
+import subprocess
+import pytest
+from binascii import hexlify, unhexlify
+from hashlib import sha1, sha256, sha384, sha512
+import hashlib
+from functools import partial
+
+from hypothesis import given
+import hypothesis.strategies as st
+
+from six import b, print_, binary_type
+from .keys import SigningKey, VerifyingKey
+from .keys import BadSignatureError, MalformedPointError, BadDigestError
+from . import util
+from .util import sigencode_der, sigencode_strings
+from .util import sigdecode_der, sigdecode_strings
+from .util import number_to_string, encoded_oid_ecPublicKey, \
+ MalformedSignature
+from .curves import Curve, UnknownCurveError
+from .curves import NIST192p, NIST224p, NIST256p, NIST384p, NIST521p, \
+ SECP256k1, BRAINPOOLP160r1, BRAINPOOLP192r1, BRAINPOOLP224r1, \
+ BRAINPOOLP256r1, BRAINPOOLP320r1, BRAINPOOLP384r1, BRAINPOOLP512r1, \
+ curves
+from .ecdsa import curve_brainpoolp224r1, curve_brainpoolp256r1, \
+ curve_brainpoolp384r1, curve_brainpoolp512r1
+from .ellipticcurve import Point
+from . import der
+from . import rfc6979
+from . import ecdsa
+
+
+class SubprocessError(Exception):
+ pass
+
+
+def run_openssl(cmd):
+ OPENSSL = "openssl"
+ p = subprocess.Popen([OPENSSL] + cmd.split(),
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT)
+ stdout, ignored = p.communicate()
+ if p.returncode != 0:
+ raise SubprocessError("cmd '%s %s' failed: rc=%s, stdout/err was %s" %
+ (OPENSSL, cmd, p.returncode, stdout))
+ return stdout.decode()
+
+
+class ECDSA(unittest.TestCase):
+ def test_basic(self):
+ priv = SigningKey.generate()
+ pub = priv.get_verifying_key()
+
+ data = b("blahblah")
+ sig = priv.sign(data)
+
+ self.assertTrue(pub.verify(sig, data))
+ self.assertRaises(BadSignatureError, pub.verify, sig, data + b("bad"))
+
+ pub2 = VerifyingKey.from_string(pub.to_string())
+ self.assertTrue(pub2.verify(sig, data))
+
+ def test_deterministic(self):
+ data = b("blahblah")
+ secexp = int("9d0219792467d7d37b4d43298a7d0c05", 16)
+
+ priv = SigningKey.from_secret_exponent(secexp, SECP256k1, sha256)
+ pub = priv.get_verifying_key()
+
+ k = rfc6979.generate_k(
+ SECP256k1.generator.order(), secexp, sha256, sha256(data).digest())
+
+ sig1 = priv.sign(data, k=k)
+ self.assertTrue(pub.verify(sig1, data))
+
+ sig2 = priv.sign(data, k=k)
+ self.assertTrue(pub.verify(sig2, data))
+
+ sig3 = priv.sign_deterministic(data, sha256)
+ self.assertTrue(pub.verify(sig3, data))
+
+ self.assertEqual(sig1, sig2)
+ self.assertEqual(sig1, sig3)
+
+ def test_bad_usage(self):
+ # sk=SigningKey() is wrong
+ self.assertRaises(TypeError, SigningKey)
+ self.assertRaises(TypeError, VerifyingKey)
+
+ def test_lengths(self):
+ default = NIST192p
+ priv = SigningKey.generate()
+ pub = priv.get_verifying_key()
+ self.assertEqual(len(pub.to_string()), default.verifying_key_length)
+ sig = priv.sign(b("data"))
+ self.assertEqual(len(sig), default.signature_length)
+ for curve in (NIST192p, NIST224p, NIST256p, NIST384p, NIST521p,
+ BRAINPOOLP160r1, BRAINPOOLP192r1, BRAINPOOLP224r1,
+ BRAINPOOLP256r1, BRAINPOOLP320r1, BRAINPOOLP384r1,
+ BRAINPOOLP512r1):
+ start = time.time()
+ priv = SigningKey.generate(curve=curve)
+ pub1 = priv.get_verifying_key()
+ keygen_time = time.time() - start
+ pub2 = VerifyingKey.from_string(pub1.to_string(), curve)
+ self.assertEqual(pub1.to_string(), pub2.to_string())
+ self.assertEqual(len(pub1.to_string()),
+ curve.verifying_key_length)
+ start = time.time()
+ sig = priv.sign(b("data"))
+ sign_time = time.time() - start
+ self.assertEqual(len(sig), curve.signature_length)
+
+ def test_serialize(self):
+ seed = b("secret")
+ curve = NIST192p
+ secexp1 = util.randrange_from_seed__trytryagain(seed, curve.order)
+ secexp2 = util.randrange_from_seed__trytryagain(seed, curve.order)
+ self.assertEqual(secexp1, secexp2)
+ priv1 = SigningKey.from_secret_exponent(secexp1, curve)
+ priv2 = SigningKey.from_secret_exponent(secexp2, curve)
+ self.assertEqual(hexlify(priv1.to_string()),
+ hexlify(priv2.to_string()))
+ self.assertEqual(priv1.to_pem(), priv2.to_pem())
+ pub1 = priv1.get_verifying_key()
+ pub2 = priv2.get_verifying_key()
+ data = b("data")
+ sig1 = priv1.sign(data)
+ sig2 = priv2.sign(data)
+ self.assertTrue(pub1.verify(sig1, data))
+ self.assertTrue(pub2.verify(sig1, data))
+ self.assertTrue(pub1.verify(sig2, data))
+ self.assertTrue(pub2.verify(sig2, data))
+ self.assertEqual(hexlify(pub1.to_string()),
+ hexlify(pub2.to_string()))
+
+ def test_nonrandom(self):
+ s = b("all the entropy in the entire world, compressed into one line")
+
+ def not_much_entropy(numbytes):
+ return s[:numbytes]
+
+ # we control the entropy source, these two keys should be identical:
+ priv1 = SigningKey.generate(entropy=not_much_entropy)
+ priv2 = SigningKey.generate(entropy=not_much_entropy)
+ self.assertEqual(hexlify(priv1.get_verifying_key().to_string()),
+ hexlify(priv2.get_verifying_key().to_string()))
+ # likewise, signatures should be identical. Obviously you'd never
+ # want to do this with keys you care about, because the secrecy of
+ # the private key depends upon using different random numbers for
+ # each signature
+ sig1 = priv1.sign(b("data"), entropy=not_much_entropy)
+ sig2 = priv2.sign(b("data"), entropy=not_much_entropy)
+ self.assertEqual(hexlify(sig1), hexlify(sig2))
+
+ def assertTruePrivkeysEqual(self, priv1, priv2):
+ self.assertEqual(priv1.privkey.secret_multiplier,
+ priv2.privkey.secret_multiplier)
+ self.assertEqual(priv1.privkey.public_key.generator,
+ priv2.privkey.public_key.generator)
+
+ def test_privkey_creation(self):
+ s = b("all the entropy in the entire world, compressed into one line")
+
+ def not_much_entropy(numbytes):
+ return s[:numbytes]
+
+ priv1 = SigningKey.generate()
+ self.assertEqual(priv1.baselen, NIST192p.baselen)
+
+ priv1 = SigningKey.generate(curve=NIST224p)
+ self.assertEqual(priv1.baselen, NIST224p.baselen)
+
+ priv1 = SigningKey.generate(entropy=not_much_entropy)
+ self.assertEqual(priv1.baselen, NIST192p.baselen)
+ priv2 = SigningKey.generate(entropy=not_much_entropy)
+ self.assertEqual(priv2.baselen, NIST192p.baselen)
+ self.assertTruePrivkeysEqual(priv1, priv2)
+
+ priv1 = SigningKey.from_secret_exponent(secexp=3)
+ self.assertEqual(priv1.baselen, NIST192p.baselen)
+ priv2 = SigningKey.from_secret_exponent(secexp=3)
+ self.assertTruePrivkeysEqual(priv1, priv2)
+
+ priv1 = SigningKey.from_secret_exponent(secexp=4, curve=NIST224p)
+ self.assertEqual(priv1.baselen, NIST224p.baselen)
+
+ def test_privkey_strings(self):
+ priv1 = SigningKey.generate()
+ s1 = priv1.to_string()
+ self.assertEqual(type(s1), binary_type)
+ self.assertEqual(len(s1), NIST192p.baselen)
+ priv2 = SigningKey.from_string(s1)
+ self.assertTruePrivkeysEqual(priv1, priv2)
+
+ s1 = priv1.to_pem()
+ self.assertEqual(type(s1), binary_type)
+ self.assertTrue(s1.startswith(b("-----BEGIN EC PRIVATE KEY-----")))
+ self.assertTrue(s1.strip().endswith(b("-----END EC PRIVATE KEY-----")))
+ priv2 = SigningKey.from_pem(s1)
+ self.assertTruePrivkeysEqual(priv1, priv2)
+
+ s1 = priv1.to_der()
+ self.assertEqual(type(s1), binary_type)
+ priv2 = SigningKey.from_der(s1)
+ self.assertTruePrivkeysEqual(priv1, priv2)
+
+ priv1 = SigningKey.generate(curve=NIST256p)
+ s1 = priv1.to_pem()
+ self.assertEqual(type(s1), binary_type)
+ self.assertTrue(s1.startswith(b("-----BEGIN EC PRIVATE KEY-----")))
+ self.assertTrue(s1.strip().endswith(b("-----END EC PRIVATE KEY-----")))
+ priv2 = SigningKey.from_pem(s1)
+ self.assertTruePrivkeysEqual(priv1, priv2)
+
+ s1 = priv1.to_der()
+ self.assertEqual(type(s1), binary_type)
+ priv2 = SigningKey.from_der(s1)
+ self.assertTruePrivkeysEqual(priv1, priv2)
+
+ def test_privkey_strings_brainpool(self):
+ priv1 = SigningKey.generate(curve=BRAINPOOLP512r1)
+ s1 = priv1.to_pem()
+ self.assertEqual(type(s1), binary_type)
+ self.assertTrue(s1.startswith(b("-----BEGIN EC PRIVATE KEY-----")))
+ self.assertTrue(s1.strip().endswith(b("-----END EC PRIVATE KEY-----")))
+ priv2 = SigningKey.from_pem(s1)
+ self.assertTruePrivkeysEqual(priv1, priv2)
+
+ s1 = priv1.to_der()
+ self.assertEqual(type(s1), binary_type)
+ priv2 = SigningKey.from_der(s1)
+ self.assertTruePrivkeysEqual(priv1, priv2)
+
+ def assertTruePubkeysEqual(self, pub1, pub2):
+ self.assertEqual(pub1.pubkey.point, pub2.pubkey.point)
+ self.assertEqual(pub1.pubkey.generator, pub2.pubkey.generator)
+ self.assertEqual(pub1.curve, pub2.curve)
+
+ def test_pubkey_strings(self):
+ priv1 = SigningKey.generate()
+ pub1 = priv1.get_verifying_key()
+ s1 = pub1.to_string()
+ self.assertEqual(type(s1), binary_type)
+ self.assertEqual(len(s1), NIST192p.verifying_key_length)
+ pub2 = VerifyingKey.from_string(s1)
+ self.assertTruePubkeysEqual(pub1, pub2)
+
+ priv1 = SigningKey.generate(curve=NIST256p)
+ pub1 = priv1.get_verifying_key()
+ s1 = pub1.to_string()
+ self.assertEqual(type(s1), binary_type)
+ self.assertEqual(len(s1), NIST256p.verifying_key_length)
+ pub2 = VerifyingKey.from_string(s1, curve=NIST256p)
+ self.assertTruePubkeysEqual(pub1, pub2)
+
+ pub1_der = pub1.to_der()
+ self.assertEqual(type(pub1_der), binary_type)
+ pub2 = VerifyingKey.from_der(pub1_der)
+ self.assertTruePubkeysEqual(pub1, pub2)
+
+ self.assertRaises(der.UnexpectedDER,
+ VerifyingKey.from_der, pub1_der + b("junk"))
+ badpub = VerifyingKey.from_der(pub1_der)
+
+ class FakeGenerator:
+ def order(self):
+ return 123456789
+
+ badcurve = Curve("unknown", None, FakeGenerator(), (1, 2, 3, 4, 5, 6), None)
+ badpub.curve = badcurve
+ badder = badpub.to_der()
+ self.assertRaises(UnknownCurveError, VerifyingKey.from_der, badder)
+
+ pem = pub1.to_pem()
+ self.assertEqual(type(pem), binary_type)
+ self.assertTrue(pem.startswith(b("-----BEGIN PUBLIC KEY-----")), pem)
+ self.assertTrue(pem.strip().endswith(b("-----END PUBLIC KEY-----")), pem)
+ pub2 = VerifyingKey.from_pem(pem)
+ self.assertTruePubkeysEqual(pub1, pub2)
+
+ def test_pubkey_strings_brainpool(self):
+ priv1 = SigningKey.generate(curve=BRAINPOOLP512r1)
+ pub1 = priv1.get_verifying_key()
+ s1 = pub1.to_string()
+ self.assertEqual(type(s1), binary_type)
+ self.assertEqual(len(s1), BRAINPOOLP512r1.verifying_key_length)
+ pub2 = VerifyingKey.from_string(s1, curve=BRAINPOOLP512r1)
+ self.assertTruePubkeysEqual(pub1, pub2)
+
+ pub1_der = pub1.to_der()
+ self.assertEqual(type(pub1_der), binary_type)
+ pub2 = VerifyingKey.from_der(pub1_der)
+ self.assertTruePubkeysEqual(pub1, pub2)
+
+ def test_vk_to_der_with_invalid_point_encoding(self):
+ sk = SigningKey.generate()
+ vk = sk.verifying_key
+
+ with self.assertRaises(ValueError):
+ vk.to_der("raw")
+
+ def test_sk_to_der_with_invalid_point_encoding(self):
+ sk = SigningKey.generate()
+
+ with self.assertRaises(ValueError):
+ sk.to_der("raw")
+
+ def test_vk_from_der_garbage_after_curve_oid(self):
+ type_oid_der = encoded_oid_ecPublicKey
+ curve_oid_der = der.encode_oid(*(1, 2, 840, 10045, 3, 1, 1)) + \
+ b('garbage')
+ enc_type_der = der.encode_sequence(type_oid_der, curve_oid_der)
+ point_der = der.encode_bitstring(b'\x00\xff', None)
+ to_decode = der.encode_sequence(enc_type_der, point_der)
+
+ with self.assertRaises(der.UnexpectedDER):
+ VerifyingKey.from_der(to_decode)
+
+ def test_vk_from_der_invalid_key_type(self):
+ type_oid_der = der.encode_oid(*(1, 2, 3))
+ curve_oid_der = der.encode_oid(*(1, 2, 840, 10045, 3, 1, 1))
+ enc_type_der = der.encode_sequence(type_oid_der, curve_oid_der)
+ point_der = der.encode_bitstring(b'\x00\xff', None)
+ to_decode = der.encode_sequence(enc_type_der, point_der)
+
+ with self.assertRaises(der.UnexpectedDER):
+ VerifyingKey.from_der(to_decode)
+
+ def test_vk_from_der_garbage_after_point_string(self):
+ type_oid_der = encoded_oid_ecPublicKey
+ curve_oid_der = der.encode_oid(*(1, 2, 840, 10045, 3, 1, 1))
+ enc_type_der = der.encode_sequence(type_oid_der, curve_oid_der)
+ point_der = der.encode_bitstring(b'\x00\xff', None) + b('garbage')
+ to_decode = der.encode_sequence(enc_type_der, point_der)
+
+ with self.assertRaises(der.UnexpectedDER):
+ VerifyingKey.from_der(to_decode)
+
+ def test_vk_from_der_invalid_bitstring(self):
+ type_oid_der = encoded_oid_ecPublicKey
+ curve_oid_der = der.encode_oid(*(1, 2, 840, 10045, 3, 1, 1))
+ enc_type_der = der.encode_sequence(type_oid_der, curve_oid_der)
+ point_der = der.encode_bitstring(b'\x08\xff', None)
+ to_decode = der.encode_sequence(enc_type_der, point_der)
+
+ with self.assertRaises(der.UnexpectedDER):
+ VerifyingKey.from_der(to_decode)
+
+ def test_vk_from_der_with_invalid_length_of_encoding(self):
+ type_oid_der = encoded_oid_ecPublicKey
+ curve_oid_der = der.encode_oid(*(1, 2, 840, 10045, 3, 1, 1))
+ enc_type_der = der.encode_sequence(type_oid_der, curve_oid_der)
+ point_der = der.encode_bitstring(b'\xff'*64, 0)
+ to_decode = der.encode_sequence(enc_type_der, point_der)
+
+ with self.assertRaises(MalformedPointError):
+ VerifyingKey.from_der(to_decode)
+
+ def test_vk_from_der_with_raw_encoding(self):
+ type_oid_der = encoded_oid_ecPublicKey
+ curve_oid_der = der.encode_oid(*(1, 2, 840, 10045, 3, 1, 1))
+ enc_type_der = der.encode_sequence(type_oid_der, curve_oid_der)
+ point_der = der.encode_bitstring(b'\xff'*48, 0)
+ to_decode = der.encode_sequence(enc_type_der, point_der)
+
+ with self.assertRaises(der.UnexpectedDER):
+ VerifyingKey.from_der(to_decode)
+
+ def test_signature_strings(self):
+ priv1 = SigningKey.generate()
+ pub1 = priv1.get_verifying_key()
+ data = b("data")
+
+ sig = priv1.sign(data)
+ self.assertEqual(type(sig), binary_type)
+ self.assertEqual(len(sig), NIST192p.signature_length)
+ self.assertTrue(pub1.verify(sig, data))
+
+ sig = priv1.sign(data, sigencode=sigencode_strings)
+ self.assertEqual(type(sig), tuple)
+ self.assertEqual(len(sig), 2)
+ self.assertEqual(type(sig[0]), binary_type)
+ self.assertEqual(type(sig[1]), binary_type)
+ self.assertEqual(len(sig[0]), NIST192p.baselen)
+ self.assertEqual(len(sig[1]), NIST192p.baselen)
+ self.assertTrue(pub1.verify(sig, data, sigdecode=sigdecode_strings))
+
+ sig_der = priv1.sign(data, sigencode=sigencode_der)
+ self.assertEqual(type(sig_der), binary_type)
+ self.assertTrue(pub1.verify(sig_der, data, sigdecode=sigdecode_der))
+
+ def test_sig_decode_strings_with_invalid_count(self):
+ with self.assertRaises(MalformedSignature):
+ sigdecode_strings([b('one'), b('two'), b('three')], 0xff)
+
+ def test_sig_decode_strings_with_wrong_r_len(self):
+ with self.assertRaises(MalformedSignature):
+ sigdecode_strings([b('one'), b('two')], 0xff)
+
+ def test_sig_decode_strings_with_wrong_s_len(self):
+ with self.assertRaises(MalformedSignature):
+ sigdecode_strings([b('\xa0'), b('\xb0\xff')], 0xff)
+
+ def test_verify_with_too_long_input(self):
+ sk = SigningKey.generate()
+ vk = sk.verifying_key
+
+ with self.assertRaises(BadDigestError):
+ vk.verify_digest(None, b('\x00') * 128)
+
+ def test_sk_from_secret_exponent_with_wrong_sec_exponent(self):
+ with self.assertRaises(MalformedPointError):
+ SigningKey.from_secret_exponent(0)
+
+ def test_sk_from_string_with_wrong_len_string(self):
+ with self.assertRaises(MalformedPointError):
+ SigningKey.from_string(b('\x01'))
+
+ def test_sk_from_der_with_junk_after_sequence(self):
+ ver_der = der.encode_integer(1)
+ to_decode = der.encode_sequence(ver_der) + b('garbage')
+
+ with self.assertRaises(der.UnexpectedDER):
+ SigningKey.from_der(to_decode)
+
+ def test_sk_from_der_with_wrong_version(self):
+ ver_der = der.encode_integer(0)
+ to_decode = der.encode_sequence(ver_der)
+
+ with self.assertRaises(der.UnexpectedDER):
+ SigningKey.from_der(to_decode)
+
+ def test_sk_from_der_invalid_const_tag(self):
+ ver_der = der.encode_integer(1)
+ privkey_der = der.encode_octet_string(b('\x00\xff'))
+ curve_oid_der = der.encode_oid(*(1, 2, 3))
+ const_der = der.encode_constructed(1, curve_oid_der)
+ to_decode = der.encode_sequence(ver_der, privkey_der, const_der,
+ curve_oid_der)
+
+ with self.assertRaises(der.UnexpectedDER):
+ SigningKey.from_der(to_decode)
+
+ def test_sk_from_der_garbage_after_privkey_oid(self):
+ ver_der = der.encode_integer(1)
+ privkey_der = der.encode_octet_string(b('\x00\xff'))
+ curve_oid_der = der.encode_oid(*(1, 2, 3)) + b('garbage')
+ const_der = der.encode_constructed(0, curve_oid_der)
+ to_decode = der.encode_sequence(ver_der, privkey_der, const_der,
+ curve_oid_der)
+
+ with self.assertRaises(der.UnexpectedDER):
+ SigningKey.from_der(to_decode)
+
+ def test_sk_from_der_with_short_privkey(self):
+ ver_der = der.encode_integer(1)
+ privkey_der = der.encode_octet_string(b('\x00\xff'))
+ curve_oid_der = der.encode_oid(*(1, 2, 840, 10045, 3, 1, 1))
+ const_der = der.encode_constructed(0, curve_oid_der)
+ to_decode = der.encode_sequence(ver_der, privkey_der, const_der,
+ curve_oid_der)
+
+ sk = SigningKey.from_der(to_decode)
+ self.assertEqual(sk.privkey.secret_multiplier, 255)
+
+ def test_sign_with_too_long_hash(self):
+ sk = SigningKey.from_secret_exponent(12)
+
+ with self.assertRaises(BadDigestError):
+ sk.sign_digest(b('\xff') * 64)
+
+ def test_hashfunc(self):
+ sk = SigningKey.generate(curve=NIST256p, hashfunc=sha256)
+ data = b("security level is 128 bits")
+ sig = sk.sign(data)
+ vk = VerifyingKey.from_string(sk.get_verifying_key().to_string(),
+ curve=NIST256p, hashfunc=sha256)
+ self.assertTrue(vk.verify(sig, data))
+
+ sk2 = SigningKey.generate(curve=NIST256p)
+ sig2 = sk2.sign(data, hashfunc=sha256)
+ vk2 = VerifyingKey.from_string(sk2.get_verifying_key().to_string(),
+ curve=NIST256p, hashfunc=sha256)
+ self.assertTrue(vk2.verify(sig2, data))
+
+ vk3 = VerifyingKey.from_string(sk.get_verifying_key().to_string(),
+ curve=NIST256p)
+ self.assertTrue(vk3.verify(sig, data, hashfunc=sha256))
+
+ def test_public_key_recovery(self):
+ # Create keys
+ curve = NIST256p
+
+ sk = SigningKey.generate(curve=curve)
+ vk = sk.get_verifying_key()
+
+ # Sign a message
+ data = b("blahblah")
+ signature = sk.sign(data)
+
+ # Recover verifying keys
+ recovered_vks = VerifyingKey.from_public_key_recovery(signature, data, curve)
+
+ # Test if each pk is valid
+ for recovered_vk in recovered_vks:
+ # Test if recovered vk is valid for the data
+ self.assertTrue(recovered_vk.verify(signature, data))
+
+ # Test if properties are equal
+ self.assertEqual(vk.curve, recovered_vk.curve)
+ self.assertEqual(vk.default_hashfunc, recovered_vk.default_hashfunc)
+
+ # Test if original vk is the list of recovered keys
+ self.assertTrue(
+ vk.pubkey.point in [recovered_vk.pubkey.point for recovered_vk in recovered_vks])
+
+ def test_public_key_recovery_with_custom_hash(self):
+ # Create keys
+ curve = NIST256p
+
+ sk = SigningKey.generate(curve=curve, hashfunc=sha256)
+ vk = sk.get_verifying_key()
+
+ # Sign a message
+ data = b("blahblah")
+ signature = sk.sign(data)
+
+ # Recover verifying keys
+ recovered_vks = VerifyingKey.\
+ from_public_key_recovery(signature, data, curve,
+ hashfunc=sha256)
+
+ # Test if each pk is valid
+ for recovered_vk in recovered_vks:
+ # Test if recovered vk is valid for the data
+ self.assertTrue(recovered_vk.verify(signature, data))
+
+ # Test if properties are equal
+ self.assertEqual(vk.curve, recovered_vk.curve)
+ self.assertEqual(sha256, recovered_vk.default_hashfunc)
+
+ # Test if original vk is the list of recovered keys
+ self.assertTrue(vk.pubkey.point in
+ [recovered_vk.pubkey.point for recovered_vk in recovered_vks])
+
+ def test_encoding(self):
+ sk = SigningKey.from_secret_exponent(123456789)
+ vk = sk.verifying_key
+
+ exp = b('\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3'
+ '\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4'
+ 'z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*')
+ self.assertEqual(vk.to_string(), exp)
+ self.assertEqual(vk.to_string('raw'), exp)
+ self.assertEqual(vk.to_string('uncompressed'), b('\x04') + exp)
+ self.assertEqual(vk.to_string('compressed'), b('\x02') + exp[:24])
+ self.assertEqual(vk.to_string('hybrid'), b('\x06') + exp)
+
+ def test_decoding(self):
+ sk = SigningKey.from_secret_exponent(123456789)
+ vk = sk.verifying_key
+
+ enc = b('\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3'
+ '\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4'
+ 'z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*')
+
+ from_raw = VerifyingKey.from_string(enc)
+ self.assertEqual(from_raw.pubkey.point, vk.pubkey.point)
+
+ from_uncompressed = VerifyingKey.from_string(b('\x04') + enc)
+ self.assertEqual(from_uncompressed.pubkey.point, vk.pubkey.point)
+
+ from_compressed = VerifyingKey.from_string(b('\x02') + enc[:24])
+ self.assertEqual(from_compressed.pubkey.point, vk.pubkey.point)
+
+ from_uncompressed = VerifyingKey.from_string(b('\x06') + enc)
+ self.assertEqual(from_uncompressed.pubkey.point, vk.pubkey.point)
+
+ def test_decoding_with_malformed_uncompressed(self):
+ enc = b('\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3'
+ '\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4'
+ 'z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*')
+
+ with self.assertRaises(MalformedPointError):
+ VerifyingKey.from_string(b('\x02') + enc)
+
+ def test_decoding_with_malformed_compressed(self):
+ enc = b('\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3'
+ '\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4'
+ 'z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*')
+
+ with self.assertRaises(MalformedPointError):
+ VerifyingKey.from_string(b('\x01') + enc[:24])
+
+ def test_decoding_with_inconsistent_hybrid(self):
+ enc = b('\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3'
+ '\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4'
+ 'z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*')
+
+ with self.assertRaises(MalformedPointError):
+ VerifyingKey.from_string(b('\x07') + enc)
+
+ def test_decoding_with_point_not_on_curve(self):
+ enc = b('\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3'
+ '\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4'
+ 'z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*')
+
+ with self.assertRaises(MalformedPointError):
+ VerifyingKey.from_string(enc[:47] + b('\x00'))
+
+ def test_decoding_with_point_at_infinity(self):
+ # decoding it is unsupported, as it's not necessary to encode it
+ with self.assertRaises(MalformedPointError):
+ VerifyingKey.from_string(b('\x00'))
+
+ def test_not_lying_on_curve(self):
+ enc = number_to_string(NIST192p.curve.p(), NIST192p.curve.p()+1)
+
+ with self.assertRaises(MalformedPointError):
+ VerifyingKey.from_string(b('\x02') + enc)
+
+ def test_from_string_with_invalid_curve_too_short_ver_key_len(self):
+ # both verifying_key_length and baselen are calculated internally
+ # by the Curve constructor, but since we depend on them verify
+ # that inconsistent values are detected
+ curve = Curve("test", ecdsa.curve_192, ecdsa.generator_192, (1, 2))
+ curve.verifying_key_length = 16
+ curve.baselen = 32
+
+ with self.assertRaises(MalformedPointError):
+ VerifyingKey.from_string(b('\x00')*16, curve)
+
+ def test_from_string_with_invalid_curve_too_long_ver_key_len(self):
+ # both verifying_key_length and baselen are calculated internally
+ # by the Curve constructor, but since we depend on them verify
+ # that inconsistent values are detected
+ curve = Curve("test", ecdsa.curve_192, ecdsa.generator_192, (1, 2))
+ curve.verifying_key_length = 16
+ curve.baselen = 16
+
+ with self.assertRaises(MalformedPointError):
+ VerifyingKey.from_string(b('\x00')*16, curve)
+
+
+@pytest.mark.parametrize("val,even",
+ [(i, j) for i in range(256) for j in [True, False]])
+def test_VerifyingKey_decode_with_small_values(val, even):
+ enc = number_to_string(val, NIST192p.order)
+
+ if even:
+ enc = b('\x02') + enc
+ else:
+ enc = b('\x03') + enc
+
+ # small values can both be actual valid public keys and not, verify that
+ # only expected exceptions are raised if they are not
+ try:
+ vk = VerifyingKey.from_string(enc)
+ assert isinstance(vk, VerifyingKey)
+ except MalformedPointError:
+ assert True
+
+
+params = []
+for curve in curves:
+ for enc in ["raw", "uncompressed", "compressed", "hybrid"]:
+ params.append(pytest.param(curve, enc, id="{0}-{1}".format(
+ curve.name, enc)))
+
+
+@pytest.mark.parametrize("curve,encoding", params)
+def test_VerifyingKey_encode_decode(curve, encoding):
+ sk = SigningKey.generate(curve=curve)
+ vk = sk.verifying_key
+
+ encoded = vk.to_string(encoding)
+
+ from_enc = VerifyingKey.from_string(encoded, curve=curve)
+
+ assert vk.pubkey.point == from_enc.pubkey.point
+
+
+class OpenSSL(unittest.TestCase):
+ # test interoperability with OpenSSL tools. Note that openssl's ECDSA
+ # sign/verify arguments changed between 0.9.8 and 1.0.0: the early
+ # versions require "-ecdsa-with-SHA1", the later versions want just
+ # "-SHA1" (or to leave out that argument entirely, which means the
+ # signature will use some default digest algorithm, probably determined
+ # by the key, probably always SHA1).
+ #
+ # openssl ecparam -name secp224r1 -genkey -out privkey.pem
+ # openssl ec -in privkey.pem -text -noout # get the priv/pub keys
+ # openssl dgst -ecdsa-with-SHA1 -sign privkey.pem -out data.sig data.txt
+ # openssl asn1parse -in data.sig -inform DER
+ # data.sig is 64 bytes, probably 56b plus ASN1 overhead
+ # openssl dgst -ecdsa-with-SHA1 -prverify privkey.pem -signature data.sig data.txt ; echo $?
+ # openssl ec -in privkey.pem -pubout -out pubkey.pem
+ # openssl ec -in privkey.pem -pubout -outform DER -out pubkey.der
+
+ OPENSSL_SUPPORTED_CURVES = set(c.split(':')[0].strip() for c in
+ run_openssl("ecparam -list_curves")
+ .split('\n'))
+
+ def get_openssl_messagedigest_arg(self, hash_name):
+ v = run_openssl("version")
+ # e.g. "OpenSSL 1.0.0 29 Mar 2010", or "OpenSSL 1.0.0a 1 Jun 2010",
+ # or "OpenSSL 0.9.8o 01 Jun 2010"
+ vs = v.split()[1].split(".")
+ if vs >= ["1", "0", "0"]: # pragma: no cover
+ return "-{0}".format(hash_name)
+ else: # pragma: no cover
+ return "-ecdsa-with-{0}".format(hash_name)
+
+ # sk: 1:OpenSSL->python 2:python->OpenSSL
+ # vk: 3:OpenSSL->python 4:python->OpenSSL
+ # sig: 5:OpenSSL->python 6:python->OpenSSL
+
+ @pytest.mark.skipif("prime192v1" not in OPENSSL_SUPPORTED_CURVES,
+ reason="system openssl does not support prime192v1")
+ def test_from_openssl_nist192p(self):
+ return self.do_test_from_openssl(NIST192p)
+
+ @pytest.mark.skipif("prime192v1" not in OPENSSL_SUPPORTED_CURVES,
+ reason="system openssl does not support prime192v1")
+ def test_from_openssl_nist192p_sha256(self):
+ return self.do_test_from_openssl(NIST192p, "SHA256")
+
+ @pytest.mark.skipif("secp224r1" not in OPENSSL_SUPPORTED_CURVES,
+ reason="system openssl does not support secp224r1")
+ def test_from_openssl_nist224p(self):
+ return self.do_test_from_openssl(NIST224p)
+
+ @pytest.mark.skipif("prime256v1" not in OPENSSL_SUPPORTED_CURVES,
+ reason="system openssl does not support prime256v1")
+ def test_from_openssl_nist256p(self):
+ return self.do_test_from_openssl(NIST256p)
+
+ @pytest.mark.skipif("prime256v1" not in OPENSSL_SUPPORTED_CURVES,
+ reason="system openssl does not support prime256v1")
+ def test_from_openssl_nist256p_sha384(self):
+ return self.do_test_from_openssl(NIST256p, "SHA384")
+
+ @pytest.mark.skipif("prime256v1" not in OPENSSL_SUPPORTED_CURVES,
+ reason="system openssl does not support prime256v1")
+ def test_from_openssl_nist256p_sha512(self):
+ return self.do_test_from_openssl(NIST256p, "SHA512")
+
+ @pytest.mark.skipif("secp384r1" not in OPENSSL_SUPPORTED_CURVES,
+ reason="system openssl does not support secp384r1")
+ def test_from_openssl_nist384p(self):
+ return self.do_test_from_openssl(NIST384p)
+
+ @pytest.mark.skipif("secp521r1" not in OPENSSL_SUPPORTED_CURVES,
+ reason="system openssl does not support secp521r1")
+ def test_from_openssl_nist521p(self):
+ return self.do_test_from_openssl(NIST521p)
+
+ @pytest.mark.skipif("secp256k1" not in OPENSSL_SUPPORTED_CURVES,
+ reason="system openssl does not support secp256k1")
+ def test_from_openssl_secp256k1(self):
+ return self.do_test_from_openssl(SECP256k1)
+
+ @pytest.mark.skipif("brainpoolP160r1" not in OPENSSL_SUPPORTED_CURVES,
+ reason="system openssl does not support brainpoolP160r1")
+ def test_from_openssl_brainpoolp160r1(self):
+ return self.do_test_from_openssl(BRAINPOOLP160r1)
+
+ @pytest.mark.skipif("brainpoolP192r1" not in OPENSSL_SUPPORTED_CURVES,
+ reason="system openssl does not support brainpoolP192r1")
+ def test_from_openssl_brainpoolp192r1(self):
+ return self.do_test_from_openssl(BRAINPOOLP192r1)
+
+ @pytest.mark.skipif("brainpoolP224r1" not in OPENSSL_SUPPORTED_CURVES,
+ reason="system openssl does not support brainpoolP224r1")
+ def test_from_openssl_brainpoolp224r1(self):
+ return self.do_test_from_openssl(BRAINPOOLP224r1)
+
+ @pytest.mark.skipif("brainpoolP256r1" not in OPENSSL_SUPPORTED_CURVES,
+ reason="system openssl does not support brainpoolP256r1")
+ def test_from_openssl_brainpoolp256r1(self):
+ return self.do_test_from_openssl(BRAINPOOLP256r1)
+
+ @pytest.mark.skipif("brainpoolP320r1" not in OPENSSL_SUPPORTED_CURVES,
+ reason="system openssl does not support brainpoolP320r1")
+ def test_from_openssl_brainpoolp320r1(self):
+ return self.do_test_from_openssl(BRAINPOOLP320r1)
+
+ @pytest.mark.skipif("brainpoolP384r1" not in OPENSSL_SUPPORTED_CURVES,
+ reason="system openssl does not support brainpoolP384r1")
+ def test_from_openssl_brainpoolp384r1(self):
+ return self.do_test_from_openssl(BRAINPOOLP384r1)
+
+ @pytest.mark.skipif("brainpoolP512r1" not in OPENSSL_SUPPORTED_CURVES,
+ reason="system openssl does not support brainpoolP512r1")
+ def test_from_openssl_brainpoolp512r1(self):
+ return self.do_test_from_openssl(BRAINPOOLP512r1)
+
+ def do_test_from_openssl(self, curve, hash_name="SHA1"):
+ curvename = curve.openssl_name
+ assert curvename
+ # OpenSSL: create sk, vk, sign.
+ # Python: read vk(3), checksig(5), read sk(1), sign, check
+ mdarg = self.get_openssl_messagedigest_arg(hash_name)
+ if os.path.isdir("t"): # pragma: no cover
+ shutil.rmtree("t")
+ os.mkdir("t")
+ run_openssl("ecparam -name %s -genkey -out t/privkey.pem" % curvename)
+ run_openssl("ec -in t/privkey.pem -pubout -out t/pubkey.pem")
+ data = b("data")
+ with open("t/data.txt", "wb") as e:
+ e.write(data)
+ run_openssl("dgst %s -sign t/privkey.pem -out t/data.sig t/data.txt" % mdarg)
+ run_openssl("dgst %s -verify t/pubkey.pem -signature t/data.sig t/data.txt" % mdarg)
+ with open("t/pubkey.pem", "rb") as e:
+ pubkey_pem = e.read()
+ vk = VerifyingKey.from_pem(pubkey_pem) # 3
+ with open("t/data.sig", "rb") as e:
+ sig_der = e.read()
+ self.assertTrue(vk.verify(sig_der, data, # 5
+ hashfunc=partial(hashlib.new, hash_name),
+ sigdecode=sigdecode_der))
+
+ with open("t/privkey.pem") as e:
+ fp = e.read()
+ sk = SigningKey.from_pem(fp) # 1
+ sig = sk.sign(
+ data,
+ hashfunc=partial(hashlib.new, hash_name),
+ )
+ self.assertTrue(vk.verify(sig,
+ data,
+ hashfunc=partial(hashlib.new, hash_name)))
+
+ @pytest.mark.skipif("prime192v1" not in OPENSSL_SUPPORTED_CURVES,
+ reason="system openssl does not support prime192v1")
+ def test_to_openssl_nist192p(self):
+ self.do_test_to_openssl(NIST192p)
+
+ @pytest.mark.skipif("prime192v1" not in OPENSSL_SUPPORTED_CURVES,
+ reason="system openssl does not support prime192v1")
+ def test_to_openssl_nist192p_sha256(self):
+ self.do_test_to_openssl(NIST192p, "SHA256")
+
+ @pytest.mark.skipif("secp224r1" not in OPENSSL_SUPPORTED_CURVES,
+ reason="system openssl does not support secp224r1")
+ def test_to_openssl_nist224p(self):
+ self.do_test_to_openssl(NIST224p)
+
+ @pytest.mark.skipif("prime256v1" not in OPENSSL_SUPPORTED_CURVES,
+ reason="system openssl does not support prime256v1")
+ def test_to_openssl_nist256p(self):
+ self.do_test_to_openssl(NIST256p)
+
+ @pytest.mark.skipif("prime256v1" not in OPENSSL_SUPPORTED_CURVES,
+ reason="system openssl does not support prime256v1")
+ def test_to_openssl_nist256p_sha384(self):
+ self.do_test_to_openssl(NIST256p, "SHA384")
+
+ @pytest.mark.skipif("prime256v1" not in OPENSSL_SUPPORTED_CURVES,
+ reason="system openssl does not support prime256v1")
+ def test_to_openssl_nist256p_sha512(self):
+ self.do_test_to_openssl(NIST256p, "SHA512")
+
+ @pytest.mark.skipif("secp384r1" not in OPENSSL_SUPPORTED_CURVES,
+ reason="system openssl does not support secp384r1")
+ def test_to_openssl_nist384p(self):
+ self.do_test_to_openssl(NIST384p)
+
+ @pytest.mark.skipif("secp521r1" not in OPENSSL_SUPPORTED_CURVES,
+ reason="system openssl does not support secp521r1")
+ def test_to_openssl_nist521p(self):
+ self.do_test_to_openssl(NIST521p)
+
+ @pytest.mark.skipif("secp256k1" not in OPENSSL_SUPPORTED_CURVES,
+ reason="system openssl does not support secp256k1")
+ def test_to_openssl_secp256k1(self):
+ self.do_test_to_openssl(SECP256k1)
+
+ @pytest.mark.skipif("brainpoolP160r1" not in OPENSSL_SUPPORTED_CURVES,
+ reason="system openssl does not support brainpoolP160r1")
+ def test_to_openssl_brainpoolp160r1(se