# 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))
# 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])
# 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
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'))
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)
# 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: assertTrue
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)))
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)
@pytest.mark.skipif("prime192v1"notin 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"notin 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"notin 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"notin 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"notin 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"notin 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"notin 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"notin 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"notin 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"notin 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"notin 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"notin 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"notin 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"notin 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"notin 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"notin OPENSSL_SUPPORTED_CURVES,
reason="system openssl does not support brainpoolP512r1") def test_from_openssl_brainpoolp512r1(self): return self.do_test_from_openssl(BRAINPOOLP512r1)
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"notin 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"notin 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"notin 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"notin 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"notin 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"notin 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"notin 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"notin 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"notin 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"notin 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"notin 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"notin 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"notin 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"notin 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"notin 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"notin 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"))
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 notin counts assert order notin 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_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_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))
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.