aboutsummaryrefslogtreecommitdiff
path: root/frozen_deps/ecdsa/test_pyecdsa.py
diff options
context:
space:
mode:
Diffstat (limited to 'frozen_deps/ecdsa/test_pyecdsa.py')
-rw-r--r--frozen_deps/ecdsa/test_pyecdsa.py1989
1 files changed, 1989 insertions, 0 deletions
diff --git a/frozen_deps/ecdsa/test_pyecdsa.py b/frozen_deps/ecdsa/test_pyecdsa.py
new file mode 100644
index 0000000..65b6716
--- /dev/null
+++ b/frozen_deps/ecdsa/test_pyecdsa.py
@@ -0,0 +1,1989 @@
+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_sk_from_p8_der_with_wrong_version(self):
+ ver_der = der.encode_integer(2)
+ algorithm_der = der.encode_sequence(
+ der.encode_oid(1, 2, 840, 10045, 2, 1),
+ der.encode_oid(1, 2, 840, 10045, 3, 1, 1),
+ )
+ privkey_der = der.encode_octet_string(
+ der.encode_sequence(
+ der.encode_integer(1), der.encode_octet_string(b"\x00\xff")
+ )
+ )
+ to_decode = der.encode_sequence(ver_der, algorithm_der, privkey_der)
+
+ with self.assertRaises(der.UnexpectedDER):
+ SigningKey.from_der(to_decode)
+
+ def test_sk_from_p8_der_with_wrong_algorithm(self):
+ ver_der = der.encode_integer(1)
+ algorithm_der = der.encode_sequence(
+ der.encode_oid(1, 2, 3), der.encode_oid(1, 2, 840, 10045, 3, 1, 1)
+ )
+ privkey_der = der.encode_octet_string(
+ der.encode_sequence(
+ der.encode_integer(1), der.encode_octet_string(b"\x00\xff")
+ )
+ )
+ to_decode = der.encode_sequence(ver_der, algorithm_der, privkey_der)
+
+ with self.assertRaises(der.UnexpectedDER):
+ SigningKey.from_der(to_decode)
+
+ def test_sk_from_p8_der_with_trailing_junk_after_algorithm(self):
+ ver_der = der.encode_integer(1)
+ algorithm_der = der.encode_sequence(
+ der.encode_oid(1, 2, 840, 10045, 2, 1),
+ der.encode_oid(1, 2, 840, 10045, 3, 1, 1),
+ der.encode_octet_string(b"junk"),
+ )
+ privkey_der = der.encode_octet_string(
+ der.encode_sequence(
+ der.encode_integer(1), der.encode_octet_string(b"\x00\xff")
+ )
+ )
+ to_decode = der.encode_sequence(ver_der, algorithm_der, privkey_der)
+
+ with self.assertRaises(der.UnexpectedDER):
+ SigningKey.from_der(to_decode)
+
+ def test_sk_from_p8_der_with_trailing_junk_after_key(self):
+ ver_der = der.encode_integer(1)
+ algorithm_der = der.encode_sequence(
+ der.encode_oid(1, 2, 840, 10045, 2, 1),
+ der.encode_oid(1, 2, 840, 10045, 3, 1, 1),
+ )
+ privkey_der = der.encode_octet_string(
+ der.encode_sequence(
+ der.encode_integer(1), der.encode_octet_string(b"\x00\xff")
+ )
+ + der.encode_integer(999)
+ )
+ to_decode = der.encode_sequence(
+ ver_der,
+ algorithm_der,
+ privkey_der,
+ der.encode_octet_string(b"junk"),
+ )
+
+ with self.assertRaises(der.UnexpectedDER):
+ SigningKey.from_der(to_decode)
+
+ 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):