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(self): self.do_test_to_openssl(BRAINPOOLP160r1) @pytest.mark.skipif("brainpoolP192r1" not in OPENSSL_SUPPORTED_CURVES, reason="system openssl does not support brainpoolP192r1") def test_to_openssl_brainpoolp192r1(self): self.do_test_to_openssl(BRAINPOOLP192r1) @pytest.mark.skipif("brainpoolP224r1" not in OPENSSL_SUPPORTED_CURVES, reason="system openssl does not support brainpoolP224r1") def test_to_openssl_brainpoolp224r1(self): self.do_test_to_openssl(BRAINPOOLP224r1) @pytest.mark.skipif("brainpoolP256r1" not in OPENSSL_SUPPORTED_CURVES, reason="system openssl does not support brainpoolP256r1") def test_to_openssl_brainpoolp256r1(self): self.do_test_to_openssl(BRAINPOOLP256r1) @pytest.mark.skipif("brainpoolP320r1" not in OPENSSL_SUPPORTED_CURVES, reason="system openssl does not support brainpoolP320r1") def test_to_openssl_brainpoolp320r1(self): self.do_test_to_openssl(BRAINPOOLP320r1) @pytest.mark.skipif("brainpoolP384r1" not in OPENSSL_SUPPORTED_CURVES, reason="system openssl does not support brainpoolP384r1") def test_to_openssl_brainpoolp384r1(self): self.do_test_to_openssl(BRAINPOOLP384r1) @pytest.mark.skipif("brainpoolP512r1" not in OPENSSL_SUPPORTED_CURVES, reason="system openssl does not support brainpoolP512r1") def test_to_openssl_brainpoolp512r1(self): self.do_test_to_openssl(BRAINPOOLP512r1) def do_test_to_openssl(self, curve, hash_name="SHA1"): curvename = curve.openssl_name assert curvename # Python: create sk, vk, sign. # OpenSSL: read vk(4), checksig(6), read sk(2), sign, check mdarg = self.get_openssl_messagedigest_arg(hash_name) if os.path.isdir("t"): # pragma: no cover shutil.rmtree("t") os.mkdir("t") sk = SigningKey.generate(curve=curve) vk = sk.get_verifying_key() data = b("data") with open("t/pubkey.der", "wb") as e: e.write(vk.to_der()) # 4 with open("t/pubkey.pem", "wb") as e: e.write(vk.to_pem()) # 4 sig_der = sk.sign(data, hashfunc=partial(hashlib.new, hash_name), sigencode=sigencode_der) with open("t/data.sig", "wb") as e: e.write(sig_der) # 6 with open("t/data.txt", "wb") as e: e.write(data) with open("t/baddata.txt", "wb") as e: e.write(data + b("corrupt")) self.assertRaises(SubprocessError, run_openssl, "dgst %s -verify t/pubkey.der -keyform DER -signature t/data.sig t/baddata.txt" % mdarg) run_openssl("dgst %s -verify t/pubkey.der -keyform DER -signature t/data.sig t/data.txt" % mdarg) with open("t/privkey.pem", "wb") as e: e.write(sk.to_pem()) # 2 run_openssl("dgst %s -sign t/privkey.pem -out t/data.sig2 t/data.txt" % mdarg) run_openssl("dgst %s -verify t/pubkey.pem -signature t/data.sig2 t/data.txt" % mdarg) class DER(unittest.TestCase): def test_integer(self): self.assertEqual(der.encode_integer(0), b("\x02\x01\x00")) self.assertEqual(der.encode_integer(1), b("\x02\x01\x01")) self.assertEqual(der.encode_integer(127), b("\x02\x01\x7f")) self.assertEqual(der.encode_integer(128), b("\x02\x02\x00\x80")) self.assertEqual(der.encode_integer(256), b("\x02\x02\x01\x00")) # self.assertEqual(der.encode_integer(-1), b("\x02\x01\xff")) def s(n): return der.remove_integer(der.encode_integer(n) + b("junk")) self.assertEqual(s(0), (0, b("junk"))) self.assertEqual(s(1), (1, b("junk"))) self.assertEqual(s(127), (127, b("junk"))) self.assertEqual(s(128), (128, b("junk"))) self.assertEqual(s(256), (256, b("junk"))) self.assertEqual(s(1234567890123456789012345678901234567890), (1234567890123456789012345678901234567890, b("junk"))) def test_number(self): self.assertEqual(der.encode_number(0), b("\x00")) self.assertEqual(der.encode_number(127), b("\x7f")) self.assertEqual(der.encode_number(128), b("\x81\x00")) self.assertEqual(der.encode_number(3 * 128 + 7), b("\x83\x07")) # self.assertEqual(der.read_number("\x81\x9b" + "more"), (155, 2)) # self.assertEqual(der.encode_number(155), b("\x81\x9b")) for n in (0, 1, 2, 127, 128, 3 * 128 + 7, 840, 10045): # , 155): x = der.encode_number(n) + b("more") n1, llen = der.read_number(x) self.assertEqual(n1, n) self.assertEqual(x[llen:], b("more")) def test_length(self): self.assertEqual(der.encode_length(0), b("\x00")) self.assertEqual(der.encode_length(127), b("\x7f")) self.assertEqual(der.encode_length(128), b("\x81\x80")) self.assertEqual(der.encode_length(255), b("\x81\xff")) self.assertEqual(der.encode_length(256), b("\x82\x01\x00")) self.assertEqual(der.encode_length(3 * 256 + 7), b("\x82\x03\x07")) self.assertEqual(der.read_length(b("\x81\x9b") + b("more")), (155, 2)) self.assertEqual(der.encode_length(155), b("\x81\x9b")) for n in (0, 1, 2, 127, 128, 255, 256, 3 * 256 + 7, 155): x = der.encode_length(n) + b("more") n1, llen = der.read_length(x) self.assertEqual(n1, n) self.assertEqual(x[llen:], b("more")) def test_sequence(self): x = der.encode_sequence(b("ABC"), b("DEF")) + b("GHI") self.assertEqual(x, b("\x30\x06ABCDEFGHI")) x1, rest = der.remove_sequence(x) self.assertEqual(x1, b("ABCDEF")) self.assertEqual(rest, b("GHI")) def test_constructed(self): x = der.encode_constructed(0, NIST224p.encoded_oid) self.assertEqual(hexlify(x), b("a007") + b("06052b81040021")) x = der.encode_constructed(1, unhexlify(b("0102030a0b0c"))) self.assertEqual(hexlify(x), b("a106") + b("0102030a0b0c")) class Util(unittest.TestCase): def test_trytryagain(self): tta = util.randrange_from_seed__trytryagain for i in range(1000): seed = "seed-%d" % i for order in (2**8 - 2, 2**8 - 1, 2**8, 2**8 + 1, 2**8 + 2, 2**16 - 1, 2**16 + 1): n = tta(seed, order) self.assertTrue(1 <= n < order, (1, n, order)) # this trytryagain *does* provide long-term stability self.assertEqual(("%x" % (tta("seed", NIST224p.order))).encode(), b("6fa59d73bf0446ae8743cf748fc5ac11d5585a90356417e97155c3bc")) @given(st.integers(min_value=0, max_value=10**200)) def test_randrange(self, i): # util.randrange does not provide long-term stability: we might # change the algorithm in the future. entropy = util.PRNG("seed-%d" % i) for order in (2**8 - 2, 2**8 - 1, 2**8, 2**16 - 1, 2**16 + 1, ): # that oddball 2**16+1 takes half our runtime n = util.randrange(order, entropy=entropy) self.assertTrue(1 <= n < order, (1, n, order)) def OFF_test_prove_uniformity(self): # pragma: no cover order = 2**8 - 2 counts = dict([(i, 0) for i in range(1, order)]) assert 0 not in counts assert order not in counts for i in range(1000000): seed = "seed-%d" % i n = util.randrange_from_seed__trytryagain(seed, order) counts[n] += 1 # this technique should use the full range self.assertTrue(counts[order - 1]) for i in range(1, order): print_("%3d: %s" % (i, "*" * (counts[i] // 100))) class RFC6979(unittest.TestCase): # https://tools.ietf.org/html/rfc6979#appendix-A.1 def _do(self, generator, secexp, hsh, hash_func, expected): actual = rfc6979.generate_k(generator.order(), secexp, hash_func, hsh) self.assertEqual(expected, actual) def test_SECP256k1(self): '''RFC doesn't contain test vectors for SECP256k1 used in bitcoin. This vector has been computed by Golang reference implementation instead.''' self._do( generator=SECP256k1.generator, secexp=int("9d0219792467d7d37b4d43298a7d0c05", 16), hsh=sha256(b("sample")).digest(), hash_func=sha256, expected=int("8fa1f95d514760e498f28957b824ee6ec39ed64826ff4fecc2b5739ec45b91cd", 16)) def test_SECP256k1_2(self): self._do( generator=SECP256k1.generator, secexp=int("cca9fbcc1b41e5a95d369eaa6ddcff73b61a4efaa279cfc6567e8daa39cbaf50", 16), hsh=sha256(b("sample")).digest(), hash_func=sha256, expected=int("2df40ca70e639d89528a6b670d9d48d9165fdc0febc0974056bdce192b8e16a3", 16)) def test_SECP256k1_3(self): self._do( generator=SECP256k1.generator, secexp=0x1, hsh=sha256(b("Satoshi Nakamoto")).digest(), hash_func=sha256, expected=0x8F8A276C19F4149656B280621E358CCE24F5F52542772691EE69063B74F15D15) def test_SECP256k1_4(self): self._do( generator=SECP256k1.generator, secexp=0x1, hsh=sha256(b("All those moments will be lost in time, like tears in rain. Time to die...")).digest(), hash_func=sha256, expected=0x38AA22D72376B4DBC472E06C3BA403EE0A394DA63FC58D88686C611ABA98D6B3) def test_SECP256k1_5(self): self._do( generator=SECP256k1.generator, secexp=0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364140, hsh=sha256(b("Satoshi Nakamoto")).digest(), hash_func=sha256, expected=0x33A19B60E25FB6F4435AF53A3D42D493644827367E6453928554F43E49AA6F90) def test_SECP256k1_6(self): self._do( generator=SECP256k1.generator, secexp=0xf8b8af8ce3c7cca5e300d33939540c10d45ce001b8f252bfbc57ba0342904181, hsh=sha256(b("Alan Turing")).digest(), hash_func=sha256, expected=0x525A82B70E67874398067543FD84C83D30C175FDC45FDEEE082FE13B1D7CFDF1) def test_1(self): # Basic example of the RFC, it also tests 'try-try-again' from Step H of rfc6979 self._do( generator=Point(None, 0, 0, int("4000000000000000000020108A2E0CC0D99F8A5EF", 16)), secexp=int("09A4D6792295A7F730FC3F2B49CBC0F62E862272F", 16), hsh=unhexlify(b("AF2BDBE1AA9B6EC1E2ADE1D694F41FC71A831D0268E9891562113D8A62ADD1BF")), hash_func=sha256, expected=int("23AF4074C90A02B3FE61D286D5C87F425E6BDD81B", 16)) def test_2(self): self._do( generator=NIST192p.generator, secexp=int("6FAB034934E4C0FC9AE67F5B5659A9D7D1FEFD187EE09FD4", 16), hsh=sha1(b("sample")).digest(), hash_func=sha1, expected=int("37D7CA00D2C7B0E5E412AC03BD44BA837FDD5B28CD3B0021", 16)) def test_3(self): self._do( generator=NIST192p.generator, secexp=int("6FAB034934E4C0FC9AE67F5B5659A9D7D1FEFD187EE09FD4", 16), hsh=sha256(b("sample")).digest(), hash_func=sha256, expected=int("32B1B6D7D42A05CB449065727A84804FB1A3E34D8F261496", 16)) def test_4(self): self._do( generator=NIST192p.generator, secexp=int("6FAB034934E4C0FC9AE67F5B5659A9D7D1FEFD187EE09FD4", 16), hsh=sha512(b("sample")).digest(), hash_func=sha512, expected=int("A2AC7AB055E4F20692D49209544C203A7D1F2C0BFBC75DB1", 16)) def test_5(self): self._do( generator=NIST192p.generator, secexp=int("6FAB034934E4C0FC9AE67F5B5659A9D7D1FEFD187EE09FD4", 16), hsh=sha1(b("test")).digest(), hash_func=sha1, expected=int("D9CF9C3D3297D3260773A1DA7418DB5537AB8DD93DE7FA25", 16)) def test_6(self): self._do( generator=NIST192p.generator, secexp=int("6FAB034934E4C0FC9AE67F5B5659A9D7D1FEFD187EE09FD4", 16), hsh=sha256(b("test")).digest(), hash_func=sha256, expected=int("5C4CE89CF56D9E7C77C8585339B006B97B5F0680B4306C6C", 16)) def test_7(self): self._do( generator=NIST192p.generator, secexp=int("6FAB034934E4C0FC9AE67F5B5659A9D7D1FEFD187EE09FD4", 16), hsh=sha512(b("test")).digest(), hash_func=sha512, expected=int("0758753A5254759C7CFBAD2E2D9B0792EEE44136C9480527", 16)) def test_8(self): self._do( generator=NIST521p.generator, secexp=int("0FAD06DAA62BA3B25D2FB40133DA757205DE67F5BB0018FEE8C86E1B68C7E75CAA896EB32F1F47C70855836A6D16FCC1466F6D8FBEC67DB89EC0C08B0E996B83538", 16), hsh=sha1(b("sample")).digest(), hash_func=sha1, expected=int("089C071B419E1C2820962321787258469511958E80582E95D8378E0C2CCDB3CB42BEDE42F50E3FA3C71F5A76724281D31D9C89F0F91FC1BE4918DB1C03A5838D0F9", 16)) def test_9(self): self._do( generator=NIST521p.generator, secexp=int("0FAD06DAA62BA3B25D2FB40133DA757205DE67F5BB0018FEE8C86E1B68C7E75CAA896EB32F1F47C70855836A6D16FCC1466F6D8FBEC67DB89EC0C08B0E996B83538", 16), hsh=sha256(b("sample")).digest(), hash_func=sha256, expected=int("0EDF38AFCAAECAB4383358B34D67C9F2216C8382AAEA44A3DAD5FDC9C32575761793FEF24EB0FC276DFC4F6E3EC476752F043CF01415387470BCBD8678ED2C7E1A0", 16)) def test_10(self): self._do( generator=NIST521p.generator, secexp=int("0FAD06DAA62BA3B25D2FB40133DA757205DE67F5BB0018FEE8C86E1B68C7E75CAA896EB32F1F47C70855836A6D16FCC1466F6D8FBEC67DB89EC0C08B0E996B83538", 16), hsh=sha512(b("test")).digest(), hash_func=sha512, expected=int("16200813020EC986863BEDFC1B121F605C1215645018AEA1A7B215A564DE9EB1B38A67AA1128B80CE391C4FB71187654AAA3431027BFC7F395766CA988C964DC56D", 16)) class ECDH(unittest.TestCase): def _do(self, curve, generator, dA, x_qA, y_qA, dB, x_qB, y_qB, x_Z, y_Z): qA = dA * generator qB = dB * generator Z = dA * qB self.assertEqual(Point(curve, x_qA, y_qA), qA) self.assertEqual(Point(curve, x_qB, y_qB), qB) self.assertTrue((dA * qB) == (dA * dB * generator) == (dB * dA * generator) == (dB * qA)) self.assertEqual(Point(curve, x_Z, y_Z), Z) class RFC6932(ECDH): # https://tools.ietf.org/html/rfc6932#appendix-A.1 def test_brainpoolP224r1(self): self._do( curve=curve_brainpoolp224r1, generator=BRAINPOOLP224r1.generator, dA=int("7C4B7A2C8A4BAD1FBB7D79CC0955DB7C6A4660CA64CC4778159B495E", 16), x_qA=int("B104A67A6F6E85E14EC1825E1539E8ECDBBF584922367DD88C6BDCF2", 16), y_qA=int("46D782E7FDB5F60CD8404301AC5949C58EDB26BC68BA07695B750A94", 16), dB=int("63976D4AAE6CD0F6DD18DEFEF55D96569D0507C03E74D6486FFA28FB", 16), x_qB=int("2A97089A9296147B71B21A4B574E1278245B536F14D8C2B9D07A874E", 16), y_qB=int("9B900D7C77A709A797276B8CA1BA61BB95B546FC29F862E44D59D25B", 16), x_Z=int("312DFD98783F9FB77B9704945A73BEB6DCCBE3B65D0F967DCAB574EB", 16), y_Z=int("6F800811D64114B1C48C621AB3357CF93F496E4238696A2A012B3C98", 16)) def test_brainpoolP256r1(self): self._do( curve=curve_brainpoolp256r1, generator=BRAINPOOLP256r1.generator, dA=int("041EB8B1E2BC681BCE8E39963B2E9FC415B05283313DD1A8BCC055F11AE" "49699", 16), x_qA=int("78028496B5ECAAB3C8B6C12E45DB1E02C9E4D26B4113BC4F015F60C5C" "CC0D206", 16), y_qA=int("A2AE1762A3831C1D20F03F8D1E3C0C39AFE6F09B4D44BBE80CD100987" "B05F92B", 16), dB=int("06F5240EACDB9837BC96D48274C8AA834B6C87BA9CC3EEDD81F99A16B8D" "804D3", 16), x_qB=int("8E07E219BA588916C5B06AA30A2F464C2F2ACFC1610A3BE2FB240B635" "341F0DB", 16), y_qB=int("148EA1D7D1E7E54B9555B6C9AC90629C18B63BEE5D7AA6949EBBF47B2" "4FDE40D", 16), x_Z=int("05E940915549E9F6A4A75693716E37466ABA79B4BF2919877A16DD2CC2" "E23708", 16), y_Z=int("6BC23B6702BC5A019438CEEA107DAAD8B94232FFBBC350F3B137628FE6" "FD134C", 16)) def test_brainpoolP384r1(self): self._do( curve=curve_brainpoolp384r1, generator=BRAINPOOLP384r1.generator, dA=int("014EC0755B78594BA47FB0A56F6173045B4331E74BA1A6F47322E70D79D" "828D97E095884CA72B73FDABD5910DF0FA76A", 16), x_qA=int("45CB26E4384DAF6FB776885307B9A38B7AD1B5C692E0C32F012533277" "8F3B8D3F50CA358099B30DEB5EE69A95C058B4E", 16), y_qA=int("8173A1C54AFFA7E781D0E1E1D12C0DC2B74F4DF58E4A4E3AF7026C5D3" "2DC530A2CD89C859BB4B4B768497F49AB8CC859", 16), dB=int("6B461CB79BD0EA519A87D6828815D8CE7CD9B3CAA0B5A8262CBCD550A01" "5C90095B976F3529957506E1224A861711D54", 16), x_qB=int("01BF92A92EE4BE8DED1A911125C209B03F99E3161CFCC986DC7711383" "FC30AF9CE28CA3386D59E2C8D72CE1E7B4666E8", 16), y_qB=int("3289C4A3A4FEE035E39BDB885D509D224A142FF9FBCC5CFE5CCBB3026" "8EE47487ED8044858D31D848F7A95C635A347AC", 16), x_Z=int("04CC4FF3DCCCB07AF24E0ACC529955B36D7C807772B92FCBE48F3AFE9A" "2F370A1F98D3FA73FD0C0747C632E12F1423EC", 16), y_Z=int("7F465F90BD69AFB8F828A214EB9716D66ABC59F17AF7C75EE7F1DE22AB" "5D05085F5A01A9382D05BF72D96698FE3FF64E", 16)) def test_brainpoolP512r1(self): self._do( curve=curve_brainpoolp512r1, generator=BRAINPOOLP512r1.generator, dA=int("636B6BE0482A6C1C41AA7AE7B245E983392DB94CECEA2660A379CFE1595" "59E357581825391175FC195D28BAC0CF03A7841A383B95C262B98378287" "4CCE6FE333", 16), x_qA=int("0562E68B9AF7CBFD5565C6B16883B777FF11C199161ECC427A39D17EC" "2166499389571D6A994977C56AD8252658BA8A1B72AE42F4FB7532151" "AFC3EF0971CCDA", 16), y_qA=int("A7CA2D8191E21776A89860AFBC1F582FAA308D551C1DC6133AF9F9C3C" "AD59998D70079548140B90B1F311AFB378AA81F51B275B2BE6B7DEE97" "8EFC7343EA642E", 16), dB=int("0AF4E7F6D52EDD52907BB8DBAB3992A0BB696EC10DF11892FF205B66D38" "1ECE72314E6A6EA079CEA06961DBA5AE6422EF2E9EE803A1F236FB96A17" "99B86E5C8B", 16), x_qB=int("5A7954E32663DFF11AE24712D87419F26B708AC2B92877D6BFEE2BFC4" "3714D89BBDB6D24D807BBD3AEB7F0C325F862E8BADE4F74636B97EAAC" "E739E11720D323", 16), y_qB=int("96D14621A9283A1BED84DE8DD64836B2C0758B11441179DC0C54C0D49" "A47C03807D171DD544B72CAAEF7B7CE01C7753E2CAD1A861ECA55A719" "54EE1BA35E04BE", 16), x_Z=int("1EE8321A4BBF93B9CF8921AB209850EC9B7066D1984EF08C2BB7232362" "08AC8F1A483E79461A00E0D5F6921CE9D360502F85C812BEDEE23AC5B2" "10E5811B191E", 16), y_Z=int("2632095B7B936174B41FD2FAF369B1D18DCADEED7E410A7E251F083109" "7C50D02CFED02607B6A2D5ADB4C0006008562208631875B58B54ECDA5A" "4F9FE9EAABA6", 16)) class RFC7027(ECDH): # https://tools.ietf.org/html/rfc7027#appendix-A def test_brainpoolP256r1(self): self._do( curve=curve_brainpoolp256r1, generator=BRAINPOOLP256r1.generator, dA=int("81DB1EE100150FF2EA338D708271BE38300CB54241D79950F77B0630398" "04F1D", 16), x_qA=int("44106E913F92BC02A1705D9953A8414DB95E1AAA49E81D9E85F929A8E" "3100BE5", 16), y_qA=int("8AB4846F11CACCB73CE49CBDD120F5A900A69FD32C272223F789EF10E" "B089BDC", 16), dB=int("55E40BC41E37E3E2AD25C3C6654511FFA8474A91A0032087593852D3E7D" "76BD3", 16), x_qB=int("8D2D688C6CF93E1160AD04CC4429117DC2C41825E1E9FCA0ADDD34E6F" "1B39F7B", 16), y_qB=int("990C57520812BE512641E47034832106BC7D3E8DD0E4C7F1136D70065" "47CEC6A", 16), x_Z=int("89AFC39D41D3B327814B80940B042590F96556EC91E6AE7939BCE31F3A" "18BF2B", 16), y_Z=int("49C27868F4ECA2179BFD7D59B1E3BF34C1DBDE61AE12931648F43E5963" "2504DE", 16)) def test_brainpoolP384r1(self): self._do( curve=curve_brainpoolp384r1, generator=BRAINPOOLP384r1.generator, dA=int("1E20F5E048A5886F1F157C74E91BDE2B98C8B52D58E5003D57053FC4B0B" "D65D6F15EB5D1EE1610DF870795143627D042", 16), x_qA=int("68B665DD91C195800650CDD363C625F4E742E8134667B767B1B476793" "588F885AB698C852D4A6E77A252D6380FCAF068", 16), y_qA=int("55BC91A39C9EC01DEE36017B7D673A931236D2F1F5C83942D049E3FA2" "0607493E0D038FF2FD30C2AB67D15C85F7FAA59", 16), dB=int("032640BC6003C59260F7250C3DB58CE647F98E1260ACCE4ACDA3DD869F7" "4E01F8BA5E0324309DB6A9831497ABAC96670", 16), x_qB=int("4D44326F269A597A5B58BBA565DA5556ED7FD9A8A9EB76C25F46DB69D" "19DC8CE6AD18E404B15738B2086DF37E71D1EB4", 16), y_qB=int("62D692136DE56CBE93BF5FA3188EF58BC8A3A0EC6C1E151A21038A42E" "9185329B5B275903D192F8D4E1F32FE9CC78C48", 16), x_Z=int("0BD9D3A7EA0B3D519D09D8E48D0785FB744A6B355E6304BC51C229FBBC" "E239BBADF6403715C35D4FB2A5444F575D4F42", 16), y_Z=int("0DF213417EBE4D8E40A5F76F66C56470C489A3478D146DECF6DF0D94BA" "E9E598157290F8756066975F1DB34B2324B7BD", 16)) def test_brainpoolP512r1(self): self._do( curve=curve_brainpoolp512r1, generator=BRAINPOOLP512r1.generator, dA=int("16302FF0DBBB5A8D733DAB7141C1B45ACBC8715939677F6A56850A38BD8" "7BD59B09E80279609FF333EB9D4C061231FB26F92EEB04982A5F1D1764C" "AD57665422", 16), x_qA=int("0A420517E406AAC0ACDCE90FCD71487718D3B953EFD7FBEC5F7F27E28" "C6149999397E91E029E06457DB2D3E640668B392C2A7E737A7F0BF044" "36D11640FD09FD", 16), y_qA=int("72E6882E8DB28AAD36237CD25D580DB23783961C8DC52DFA2EC138AD4" "72A0FCEF3887CF62B623B2A87DE5C588301EA3E5FC269B373B60724F5" "E82A6AD147FDE7", 16), dB=int("230E18E1BCC88A362FA54E4EA3902009292F7F8033624FD471B5D8ACE49" "D12CFABBC19963DAB8E2F1EBA00BFFB29E4D72D13F2224562F405CB8050" "3666B25429", 16), x_qB=int("9D45F66DE5D67E2E6DB6E93A59CE0BB48106097FF78A081DE781CDB31" "FCE8CCBAAEA8DD4320C4119F1E9CD437A2EAB3731FA9668AB268D871D" "EDA55A5473199F", 16), y_qB=int("2FDC313095BCDD5FB3A91636F07A959C8E86B5636A1E930E8396049CB" "481961D365CC11453A06C719835475B12CB52FC3C383BCE35E27EF194" "512B71876285FA", 16), x_Z=int("A7927098655F1F9976FA50A9D566865DC530331846381C87256BAF3226" "244B76D36403C024D7BBF0AA0803EAFF405D3D24F11A9B5C0BEF679FE1" "454B21C4CD1F", 16), y_Z=int("7DB71C3DEF63212841C463E881BDCF055523BD368240E6C3143BD8DEF8" "B3B3223B95E0F53082FF5E412F4222537A43DF1C6D25729DDB51620A83" "2BE6A26680A2", 16)) # https://tools.ietf.org/html/rfc4754#page-5 @pytest.mark.parametrize("w, gwx, gwy, k, msg, md, r, s, curve", [pytest.param( "DC51D3866A15BACDE33D96F992FCA99DA7E6EF0934E7097559C27F1614C88A7F", "2442A5CC0ECD015FA3CA31DC8E2BBC70BF42D60CBCA20085E0822CB04235E970", "6FC98BD7E50211A4A27102FA3549DF79EBCB4BF246B80945CDDFE7D509BBFD7D", "9E56F509196784D963D1C0A401510EE7ADA3DCC5DEE04B154BF61AF1D5A6DECE", b"abc", sha256, "CB28E0999B9C7715FD0A80D8E47A77079716CBBF917DD72E97566EA1C066957C", "86FA3BB4E26CAD5BF90B7F81899256CE7594BB1EA0C89212748BFF3B3D5B0315", NIST256p, id="ECDSA-256"), pytest.param( "0BEB646634BA87735D77AE4809A0EBEA865535DE4C1E1DCB692E84708E81A5AF" "62E528C38B2A81B35309668D73524D9F", "96281BF8DD5E0525CA049C048D345D3082968D10FEDF5C5ACA0C64E6465A97EA" "5CE10C9DFEC21797415710721F437922", "447688BA94708EB6E2E4D59F6AB6D7EDFF9301D249FE49C33096655F5D502FAD" "3D383B91C5E7EDAA2B714CC99D5743CA", "B4B74E44D71A13D568003D7489908D564C7761E229C58CBFA18950096EB7463B" "854D7FA992F934D927376285E63414FA", b'abc', sha384, "FB017B914E29149432D8BAC29A514640B46F53DDAB2C69948084E2930F1C8F7E" "08E07C9C63F2D21A07DCB56A6AF56EB3", "B263A1305E057F984D38726A1B46874109F417BCA112674C528262A40A629AF1" "CBB9F516CE0FA7D2FF630863A00E8B9F", NIST384p, id="ECDSA-384"), pytest.param( "0065FDA3409451DCAB0A0EAD45495112A3D813C17BFD34BDF8C1209D7DF58491" "20597779060A7FF9D704ADF78B570FFAD6F062E95C7E0C5D5481C5B153B48B37" "5FA1", "0151518F1AF0F563517EDD5485190DF95A4BF57B5CBA4CF2A9A3F6474725A35F" "7AFE0A6DDEB8BEDBCD6A197E592D40188901CECD650699C9B5E456AEA5ADD190" "52A8", "006F3B142EA1BFFF7E2837AD44C9E4FF6D2D34C73184BBAD90026DD5E6E85317" "D9DF45CAD7803C6C20035B2F3FF63AFF4E1BA64D1C077577DA3F4286C58F0AEA" "E643", "00C1C2B305419F5A41344D7E4359933D734096F556197A9B244342B8B62F46F9" "373778F9DE6B6497B1EF825FF24F42F9B4A4BD7382CFC3378A540B1B7F0C1B95" "6C2F", b'abc', sha512, "0154FD3836AF92D0DCA57DD5341D3053988534FDE8318FC6AAAAB68E2E6F4339" "B19F2F281A7E0B22C269D93CF8794A9278880ED7DBB8D9362CAEACEE54432055" "2251", "017705A7030290D1CEB605A9A1BB03FF9CDD521E87A696EC926C8C10C8362DF4" "975367101F67D1CF9BCCBF2F3D239534FA509E70AAC851AE01AAC68D62F86647" "2660", NIST521p, id="ECDSA-521") ]) def test_RFC4754_vectors(w, gwx, gwy, k, msg, md, r, s, curve): sk = SigningKey.from_string(unhexlify(w), curve) vk = VerifyingKey.from_string(unhexlify(gwx + gwy), curve) assert sk.verifying_key == vk sig = sk.sign(msg, hashfunc=md, sigencode=sigencode_strings, k=int(k, 16)) assert sig == (unhexlify(r), unhexlify(s)) assert vk.verify(sig, msg, md, sigdecode_strings)