from __future__ import with_statement, division, print_function
try:
import unittest2 as unittest
except ImportError:
import unittest
import os
import shutil
import subprocess
import pytest
import sys
from binascii import hexlify, unhexlify
import hashlib
from functools import partial
from hypothesis import given, settings
import hypothesis.strategies as st
from six import binary_type
from .keys import SigningKey, VerifyingKey
from .keys import BadSignatureError, MalformedPointError, BadDigestError
from . import util
from .util import (
sigencode_der,
sigencode_strings,
sigencode_strings_canonize,
sigencode_string_canonize,
sigencode_der_canonize,
)
from .util import sigdecode_der, sigdecode_strings, sigdecode_string
from .util import number_to_string, encoded_oid_ecPublicKey, MalformedSignature
from .curves import Curve, UnknownCurveError
from .curves import (
SECP112r1,
SECP112r2,
SECP128r1,
SECP160r1,
NIST192p,
NIST224p,
NIST256p,
NIST384p,
NIST521p,
SECP256k1,
BRAINPOOLP160r1,
BRAINPOOLP192r1,
BRAINPOOLP224r1,
BRAINPOOLP256r1,
BRAINPOOLP320r1,
BRAINPOOLP384r1,
BRAINPOOLP512r1,
BRAINPOOLP160t1,
BRAINPOOLP192t1,
BRAINPOOLP224t1,
BRAINPOOLP256t1,
BRAINPOOLP320t1,
BRAINPOOLP384t1,
BRAINPOOLP512t1,
Ed25519,
Ed448,
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
HYP_SETTINGS = {}
if "--fast" in sys.argv: HYP_SETTINGS["max_examples"] = 2
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, hashlib.sha256
)
pub = priv.get_verifying_key()
k = rfc6979.generate_k(
SECP256k1.generator.order(),
secexp,
hashlib.sha256,
hashlib.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, hashlib.sha256)
self.assertTrue(pub.verify(sig3, data))
self.assertEqual(sig1, sig2)
self.assertEqual(sig1, sig3)
def test_bad_usage(self):
self.assertRaises(TypeError, SigningKey)
self.assertRaises(TypeError, VerifyingKey)
def test_lengths_default(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)
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]
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()),
)
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
class FakeCurveFp:
def p(self):
return int(
"6525534529039240705020950546962731340"
"4541085228058844382513856749047873406763"
)
badcurve = Curve(
"unknown", FakeCurveFp(), 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_sigencode_string_canonize_no_change(self):
r = 12
s = 400
order = SECP112r1.order
new_r, new_s = sigdecode_string(
sigencode_string_canonize(r, s, order), order
)
self.assertEqual(r, new_r)
self.assertEqual(s, new_s)
def test_sigencode_string_canonize(self):
r = 12
order = SECP112r1.order
s = order - 10
new_r, new_s = sigdecode_string(
sigencode_string_canonize(r, s, order), order
)
self.assertEqual(r, new_r)
self.assertEqual(order - s, new_s)
def test_sigencode_strings_canonize_no_change(self):
r = 12
s = 400
order = SECP112r1.order
new_r, new_s = sigdecode_strings(
sigencode_strings_canonize(r, s, order), order
)
self.assertEqual(r, new_r)
self.assertEqual(s, new_s)
def test_sigencode_strings_canonize(self):
r = 12
order = SECP112r1.order
s = order - 10
new_r, new_s = sigdecode_strings(
sigencode_strings_canonize(r, s, order), order
)
self.assertEqual(r, new_r)
self.assertEqual(order - s, new_s)
def test_sigencode_der_canonize_no_change(self):
r = 13
s = 200
order = SECP112r1.order
new_r, new_s = sigdecode_der(
sigencode_der_canonize(r, s, order), order
)
self.assertEqual(r, new_r)
self.assertEqual(s, new_s)
def test_sigencode_der_canonize(self):
r = 13
order = SECP112r1.order
s = order - 14
new_r, new_s = sigdecode_der(
sigencode_der_canonize(r, s, order), order
)
self.assertEqual(r, new_r)
self.assertEqual(order - s, new_s)
def test_sigencode_der_canonize_with_close_to_half_order(self):
r = 13
order = SECP112r1.order
s = order // 2 + 1
regular_encode = sigencode_der(r, s, order)
canonical_encode = sigencode_der_canonize(r, s, order)
self.assertNotEqual(regular_encode, canonical_encode)
new_r, new_s = sigdecode_der(
sigencode_der_canonize(r, s, order), order
)
self.assertEqual(r, new_r)
self.assertEqual(order - s, new_s)
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=hashlib.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=hashlib.sha256,
)
self.assertTrue(vk.verify(sig, data))
sk2 = SigningKey.generate(curve=NIST256p)
sig2 = sk2.sign(data, hashfunc=hashlib.sha256)
vk2 = VerifyingKey.from_string(
sk2.get_verifying_key().to_string(),
curve=NIST256p,
hashfunc=hashlib.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=hashlib.sha256))
def test_public_key_recovery(self):
curve = BRAINPOOLP160r1
sk = SigningKey.generate(curve=curve)
vk = sk.get_verifying_key()
data = b"blahblah"
signature = sk.sign(data)
recovered_vks = VerifyingKey.from_public_key_recovery(
signature, data, curve
)
for recovered_vk in recovered_vks:
self.assertTrue(recovered_vk.verify(signature, data))
self.assertEqual(vk.curve, recovered_vk.curve)
self.assertEqual(
vk.default_hashfunc, recovered_vk.default_hashfunc
)
self.assertIn(
vk.pubkey.point,
[recovered_vk.pubkey.point for recovered_vk in recovered_vks],
)
def test_public_key_recovery_with_custom_hash(self):
curve = BRAINPOOLP160r1
sk = SigningKey.generate(curve=curve, hashfunc=hashlib.sha256)
vk = sk.get_verifying_key()
data = b"blahblah"
signature = sk.sign(data)
recovered_vks = VerifyingKey.from_public_key_recovery(
signature,
data,
curve,
hashfunc=hashlib.sha256,
allow_truncate=True,
)
for recovered_vk in recovered_vks:
self.assertTrue(recovered_vk.verify(signature, data))
self.assertEqual(vk.curve, recovered_vk.curve)
self.assertEqual(hashlib.sha256, recovered_vk.default_hashfunc)
self.assertIn(
vk.pubkey.point,
[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"
b"\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4"
b"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"
b"\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4"
b"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_uncompressed_decoding_as_only_alowed(self):
enc = (
b"\x04"
b"\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3"
b"\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4"
b"z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*"
)
vk = VerifyingKey.from_string(enc, valid_encodings=("uncompressed",))
sk = SigningKey.from_secret_exponent(123456789)
self.assertEqual(vk, sk.verifying_key)
def test_raw_decoding_with_blocked_format(self):
enc = (
b"\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3"
b"\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4"
b"z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*"
)
with self.assertRaises(MalformedPointError) as exp:
VerifyingKey.from_string(enc, valid_encodings=("hybrid",))
self.assertIn("hybrid", str(exp.exception))
def test_decoding_with_unknown_format(self):
with self.assertRaises(ValueError) as e:
VerifyingKey.from_string(b"", valid_encodings=("raw", "foobar"))
self.assertIn("Only uncompressed, compressed", str(e.exception))
def test_uncompressed_decoding_with_blocked_format(self):
enc = (
b"\x04"
b"\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3"
b"\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4"
b"z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*"
)
with self.assertRaises(MalformedPointError) as exp:
VerifyingKey.from_string(enc, valid_encodings=("hybrid",))
self.assertIn("Invalid X9.62 encoding", str(exp.exception))
def test_hybrid_decoding_with_blocked_format(self):
enc = (
b"\x06"
b"\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3"
b"\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4"
b"z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*"
)
with self.assertRaises(MalformedPointError) as exp:
VerifyingKey.from_string(enc, valid_encodings=("uncompressed",))
self.assertIn("Invalid X9.62 encoding", str(exp.exception))
def test_hybrid_decoding_with_inconsistent_encoding_and_no_validation(
self,
):
sk = SigningKey.from_secret_exponent(123456789)
vk = sk.verifying_key
enc = vk.to_string("hybrid")
self.assertEqual(enc[:1], b"\x06")
enc = b"\x07" + enc[1:]
b = VerifyingKey.from_string(
enc, valid_encodings=("hybrid",), validate_point=False
)
self.assertEqual(vk, b)
def test_compressed_decoding_with_blocked_format(self):
enc = (
b"\x02"
b"\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3"
b"\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4"
b"z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*"
)[:25]
with self.assertRaises(MalformedPointError) as exp:
VerifyingKey.from_string(enc, valid_encodings=("hybrid", "raw"))
self.assertIn("(hybrid, raw)", str(exp.exception))
def test_decoding_with_malformed_uncompressed(self):
enc = (
b"\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3"
b"\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4"
b"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"
b"\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4"
b"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"
b"\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4"
b"z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*"
)
with self.assertRaises(MalformedPointError):
VerifyingKey.from_string(b"\x07" + enc)
def test_decoding_with_inconsistent_hybrid_odd_point(self):
sk = SigningKey.from_secret_exponent(123456791)
vk = sk.verifying_key
enc = vk.to_string("hybrid")
self.assertEqual(enc[:1], b"\x07")
enc = b"\x06" + enc[1:]
with self.assertRaises(MalformedPointError):
b = VerifyingKey.from_string(enc, valid_encodings=("hybrid",))
def test_decoding_with_point_not_on_curve(self):
enc = (
b"\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3"
b"\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4"
b"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):
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):
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):
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
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
if "--fast" in sys.argv: params = [NIST192p, BRAINPOOLP160r1]
else:
params = curves
@pytest.mark.parametrize("curve", params)
def test_lengths(curve):
priv = SigningKey.generate(curve=curve)
pub1 = priv.get_verifying_key()
pub2 = VerifyingKey.from_string(pub1.to_string(), curve)
assert pub1.to_string() == pub2.to_string()
assert len(pub1.to_string()) == curve.verifying_key_length
sig = priv.sign(b"data")
assert len(sig) == curve.signature_length
@pytest.mark.slow
class OpenSSL(unittest.TestCase):
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")
vs = v.split()[1].split(".")
if vs >= ["1", "0", "0"]: return "-{0}".format(hash_name)
else: return "-ecdsa-with-{0}".format(hash_name)
@pytest.mark.slow
@pytest.mark.skipif(
"secp112r1" not in OPENSSL_SUPPORTED_CURVES,
reason="system openssl does not support secp112r1",
)
def test_from_openssl_secp112r1(self):
return self.do_test_from_openssl(SECP112r1)
@pytest.mark.slow
@pytest.mark.skipif(
"secp112r2" not in OPENSSL_SUPPORTED_CURVES,
reason="system openssl does not support secp112r2",
)
def test_from_openssl_secp112r2(self):
return self.do_test_from_openssl(SECP112r2)
@pytest.mark.slow
@pytest.mark.skipif(
"secp128r1" not in OPENSSL_SUPPORTED_CURVES,
reason="system openssl does not support secp128r1",
)
def test_from_openssl_secp128r1(self):
return self.do_test_from_openssl(SECP128r1)
@pytest.mark.slow
@pytest.mark.skipif(
"secp160r1" not in OPENSSL_SUPPORTED_CURVES,
reason="system openssl does not support secp160r1",
)
def test_from_openssl_secp160r1(self):
return self.do_test_from_openssl(SECP160r1)
@pytest.mark.slow
@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.slow
@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.slow
@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.slow
@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.slow
@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.slow
@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.slow
@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.slow
@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.slow
@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.slow
@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.slow
@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.slow
@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.slow
@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.slow
@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.slow
@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.slow
@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)
@pytest.mark.slow
@pytest.mark.skipif(
"brainpoolP160t1" not in OPENSSL_SUPPORTED_CURVES,
reason="system openssl does not support brainpoolP160t1",
)
def test_from_openssl_brainpoolp160t1(self):
return self.do_test_from_openssl(BRAINPOOLP160t1)
@pytest.mark.slow
@pytest.mark.skipif(
"brainpoolP192t1" not in OPENSSL_SUPPORTED_CURVES,
reason="system openssl does not support brainpoolP192t1",
)
def test_from_openssl_brainpoolp192t1(self):
return self.do_test_from_openssl(BRAINPOOLP192t1)
@pytest.mark.slow
@pytest.mark.skipif(
"brainpoolP224t1" not in OPENSSL_SUPPORTED_CURVES,
reason="system openssl does not support brainpoolP224t1",
)
def test_from_openssl_brainpoolp224t1(self):
return self.do_test_from_openssl(BRAINPOOLP224t1)
@pytest.mark.slow
@pytest.mark.skipif(
"brainpoolP256t1" not in OPENSSL_SUPPORTED_CURVES,
reason="system openssl does not support brainpoolP256t1",
)
def test_from_openssl_brainpoolp256t1(self):
return self.do_test_from_openssl(BRAINPOOLP256t1)
@pytest.mark.slow
@pytest.mark.skipif(
"brainpoolP320t1" not in OPENSSL_SUPPORTED_CURVES,
reason="system openssl does not support brainpoolP320t1",
)
def test_from_openssl_brainpoolp320t1(self):
return self.do_test_from_openssl(BRAINPOOLP320t1)
@pytest.mark.slow
@pytest.mark.skipif(
"brainpoolP384t1" not in OPENSSL_SUPPORTED_CURVES,
reason="system openssl does not support brainpoolP384t1",
)
def test_from_openssl_brainpoolp384t1(self):
return self.do_test_from_openssl(BRAINPOOLP384t1)
@pytest.mark.slow
@pytest.mark.skipif(
"brainpoolP512t1" not in OPENSSL_SUPPORTED_CURVES,
reason="system openssl does not support brainpoolP512t1",
)
def test_from_openssl_brainpoolp512t1(self):
return self.do_test_from_openssl(BRAINPOOLP512t1)
def do_test_from_openssl(self, curve, hash_name="SHA1"):
curvename = curve.openssl_name
assert curvename
mdarg = self.get_openssl_messagedigest_arg(hash_name)
if os.path.isdir("t"): 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) with open("t/data.sig", "rb") as e:
sig_der = e.read()
self.assertTrue(
vk.verify(
sig_der,
data, hashfunc=partial(hashlib.new, hash_name),
sigdecode=sigdecode_der,
)
)
with open("t/privkey.pem") as e:
fp = e.read()
sk = SigningKey.from_pem(fp) sig = sk.sign(data, hashfunc=partial(hashlib.new, hash_name))
self.assertTrue(
vk.verify(sig, data, hashfunc=partial(hashlib.new, hash_name))
)
run_openssl(
"pkcs8 -topk8 -nocrypt "
"-in t/privkey.pem -outform pem -out t/privkey-p8.pem"
)
with open("t/privkey-p8.pem", "rb") as e:
privkey_p8_pem = e.read()
sk_from_p8 = SigningKey.from_pem(privkey_p8_pem)
self.assertEqual(sk, sk_from_p8)
@pytest.mark.slow
@pytest.mark.skipif(
"secp112r1" not in OPENSSL_SUPPORTED_CURVES,
reason="system openssl does not support secp112r1",
)
def test_to_openssl_secp112r1(self):
self.do_test_to_openssl(SECP112r1)
@pytest.mark.slow
@pytest.mark.skipif(
"secp112r2" not in OPENSSL_SUPPORTED_CURVES,
reason="system openssl does not support secp112r2",
)
def test_to_openssl_secp112r2(self):
self.do_test_to_openssl(SECP112r2)
@pytest.mark.slow
@pytest.mark.skipif(
"secp128r1" not in OPENSSL_SUPPORTED_CURVES,
reason="system openssl does not support secp128r1",
)
def test_to_openssl_secp128r1(self):
self.do_test_to_openssl(SECP128r1)
@pytest.mark.slow
@pytest.mark.skipif(
"secp160r1" not in OPENSSL_SUPPORTED_CURVES,
reason="system openssl does not support secp160r1",
)
def test_to_openssl_secp160r1(self):
self.do_test_to_openssl(SECP160r1)
@pytest.mark.slow
@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.slow
@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.slow
@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.slow
@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.slow
@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.slow
@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.slow
@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.slow
@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.slow
@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.slow
@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.slow
@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.slow
@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.slow
@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.slow
@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.slow
@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.slow
@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)
@pytest.mark.slow
@pytest.mark.skipif(
"brainpoolP160t1" not in OPENSSL_SUPPORTED_CURVES,
reason="system openssl does not support brainpoolP160t1",
)
def test_to_openssl_brainpoolp160t1(self):
self.do_test_to_openssl(BRAINPOOLP160t1)
@pytest.mark.slow
@pytest.mark.skipif(
"brainpoolP192t1" not in OPENSSL_SUPPORTED_CURVES,
reason="system openssl does not support brainpoolP192t1",
)
def test_to_openssl_brainpoolp192t1(self):
self.do_test_to_openssl(BRAINPOOLP192t1)
@pytest.mark.slow
@pytest.mark.skipif(
"brainpoolP224t1" not in OPENSSL_SUPPORTED_CURVES,
reason="system openssl does not support brainpoolP224t1",
)
def test_to_openssl_brainpoolp224t1(self):
self.do_test_to_openssl(BRAINPOOLP224t1)
@pytest.mark.slow
@pytest.mark.skipif(
"brainpoolP256t1" not in OPENSSL_SUPPORTED_CURVES,
reason="system openssl does not support brainpoolP256t1",
)
def test_to_openssl_brainpoolp256t1(self):
self.do_test_to_openssl(BRAINPOOLP256t1)
@pytest.mark.slow
@pytest.mark.skipif(
"brainpoolP320t1" not in OPENSSL_SUPPORTED_CURVES,
reason="system openssl does not support brainpoolP320t1",
)
def test_to_openssl_brainpoolp320t1(self):
self.do_test_to_openssl(BRAINPOOLP320t1)
@pytest.mark.slow
@pytest.mark.skipif(
"brainpoolP384t1" not in OPENSSL_SUPPORTED_CURVES,
reason="system openssl does not support brainpoolP384t1",
)
def test_to_openssl_brainpoolp384t1(self):
self.do_test_to_openssl(BRAINPOOLP384t1)
@pytest.mark.slow
@pytest.mark.skipif(
"brainpoolP512t1" not in OPENSSL_SUPPORTED_CURVES,
reason="system openssl does not support brainpoolP512t1",
)
def test_to_openssl_brainpoolp512t1(self):
self.do_test_to_openssl(BRAINPOOLP512t1)
def do_test_to_openssl(self, curve, hash_name="SHA1"):
curvename = curve.openssl_name
assert curvename
mdarg = self.get_openssl_messagedigest_arg(hash_name)
if os.path.isdir("t"): 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()) with open("t/pubkey.pem", "wb") as e:
e.write(vk.to_pem()) 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) 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()) 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
)
with open("t/privkey-explicit.pem", "wb") as e:
e.write(sk.to_pem(curve_parameters_encoding="explicit"))
run_openssl(
"dgst %s -sign t/privkey-explicit.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
)
with open("t/privkey-p8.pem", "wb") as e:
e.write(sk.to_pem(format="pkcs8"))
run_openssl(
"dgst %s -sign t/privkey-p8.pem -out t/data.sig3 t/data.txt"
% mdarg
)
run_openssl(
"dgst %s -verify t/pubkey.pem -signature t/data.sig3 t/data.txt"
% mdarg
)
with open("t/privkey-p8-explicit.pem", "wb") as e:
e.write(
sk.to_pem(format="pkcs8", curve_parameters_encoding="explicit")
)
run_openssl(
"dgst %s -sign t/privkey-p8-explicit.pem -out t/data.sig3 t/data.txt"
% mdarg
)
run_openssl(
"dgst %s -verify t/pubkey.pem -signature t/data.sig3 t/data.txt"
% mdarg
)
OPENSSL_SUPPORTED_TYPES = set()
try:
if "-rawin" in run_openssl("pkeyutl -help"):
OPENSSL_SUPPORTED_TYPES = set( c.lower()
for c in ("ED25519", "ED448")
if c in run_openssl("list -public-key-methods")
)
except SubprocessError: pass
def do_eddsa_test_to_openssl(self, curve):
if os.path.isdir("t"):
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())
with open("t/pubkey.pem", "wb") as e:
e.write(vk.to_pem())
sig = sk.sign(data)
with open("t/data.sig", "wb") as e:
e.write(sig)
with open("t/data.txt", "wb") as e:
e.write(data)
with open("t/baddata.txt", "wb") as e:
e.write(data + b"corrupt")
with self.assertRaises(SubprocessError):
run_openssl(
"pkeyutl -verify -pubin -inkey t/pubkey.pem -rawin "
"-in t/baddata.txt -sigfile t/data.sig"
)
run_openssl(
"pkeyutl -verify -pubin -inkey t/pubkey.pem -rawin "
"-in t/data.txt -sigfile t/data.sig"
)
shutil.rmtree("t")
@pytest.mark.slow
@pytest.mark.skipif(
"ed25519" not in OPENSSL_SUPPORTED_TYPES,
reason="system openssl does not support signing with Ed25519",
)
def test_to_openssl_ed25519(self):
return self.do_eddsa_test_to_openssl(Ed25519)
@pytest.mark.slow
@pytest.mark.skipif(
"ed448" not in OPENSSL_SUPPORTED_TYPES,
reason="system openssl does not support signing with Ed448",
)
def test_to_openssl_ed448(self):
return self.do_eddsa_test_to_openssl(Ed448)
def do_eddsa_test_from_openssl(self, curve):
curvename = curve.name
if os.path.isdir("t"):
shutil.rmtree("t")
os.mkdir("t")
data = b"data"
run_openssl(
"genpkey -algorithm {0} -outform PEM -out t/privkey.pem".format(
curvename
)
)
run_openssl(
"pkey -outform PEM -pubout -in t/privkey.pem -out t/pubkey.pem"
)
with open("t/data.txt", "wb") as e:
e.write(data)
run_openssl(
"pkeyutl -sign -inkey t/privkey.pem "
"-rawin -in t/data.txt -out t/data.sig"
)
with open("t/data.sig", "rb") as e:
sig = e.read()
with open("t/pubkey.pem", "rb") as e:
vk = VerifyingKey.from_pem(e.read())
self.assertIs(vk.curve, curve)
vk.verify(sig, data)
shutil.rmtree("t")
@pytest.mark.slow
@pytest.mark.skipif(
"ed25519" not in OPENSSL_SUPPORTED_TYPES,
reason="system openssl does not support signing with Ed25519",
)
def test_from_openssl_ed25519(self):
return self.do_eddsa_test_from_openssl(Ed25519)
@pytest.mark.slow
@pytest.mark.skipif(
"ed448" not in OPENSSL_SUPPORTED_TYPES,
reason="system openssl does not support signing with Ed448",
)
def test_from_openssl_ed448(self):
return self.do_eddsa_test_from_openssl(Ed448)
class TooSmallCurve(unittest.TestCase):
OPENSSL_SUPPORTED_CURVES = set(
c.split(":")[0].strip()
for c in run_openssl("ecparam -list_curves").split("\n")
)
@pytest.mark.skipif(
"prime192v1" not in OPENSSL_SUPPORTED_CURVES,
reason="system openssl does not support prime192v1",
)
def test_sign_too_small_curve_dont_allow_truncate_raises(self):
sk = SigningKey.generate(curve=NIST192p)
data = b"data"
with self.assertRaises(BadDigestError):
sk.sign(
data,
hashfunc=partial(hashlib.new, "SHA256"),
sigencode=sigencode_der,
allow_truncate=False,
)
@pytest.mark.skipif(
"prime192v1" not in OPENSSL_SUPPORTED_CURVES,
reason="system openssl does not support prime192v1",
)
def test_verify_too_small_curve_dont_allow_truncate_raises(self):
sk = SigningKey.generate(curve=NIST192p)
vk = sk.get_verifying_key()
data = b"data"
sig_der = sk.sign(
data,
hashfunc=partial(hashlib.new, "SHA256"),
sigencode=sigencode_der,
allow_truncate=True,
)
with self.assertRaises(BadDigestError):
vk.verify(
sig_der,
data,
hashfunc=partial(hashlib.new, "SHA256"),
sigdecode=sigdecode_der,
allow_truncate=False,
)
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")
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")
for n in (0, 1, 2, 127, 128, 3 * 128 + 7, 840, 10045): 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):
@pytest.mark.slow
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))
self.assertEqual(
("%x" % (tta("seed", NIST224p.order))).encode(),
b"6fa59d73bf0446ae8743cf748fc5ac11d5585a90356417e97155c3bc",
)
def test_trytryagain_single(self):
tta = util.randrange_from_seed__trytryagain
order = 2**8 - 2
seed = b"text"
n = tta(seed, order)
if sys.version_info < (3, 0): self.assertEqual(n, 228)
else: self.assertEqual(n, 18)
@settings(**HYP_SETTINGS)
@given(st.integers(min_value=0, max_value=10**200))
def test_randrange(self, i):
entropy = util.PRNG("seed-%d" % i)
for order in (
2**8 - 2,
2**8 - 1,
2**8,
2**16 - 1,
2**16 + 1,
):
n = util.randrange(order, entropy=entropy)
self.assertTrue(1 <= n < order, (1, n, order))
def OFF_test_prove_uniformity(self): 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
self.assertTrue(counts[order - 1])
for i in range(1, order):
print("%3d: %s" % (i, "*" * (counts[i] // 100)))
class RFC6979(unittest.TestCase):
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):
self._do(
generator=SECP256k1.generator,
secexp=int("9d0219792467d7d37b4d43298a7d0c05", 16),
hsh=hashlib.sha256(b"sample").digest(),
hash_func=hashlib.sha256,
expected=int(
"8fa1f95d514760e498f28957b824ee6ec39ed64826ff4fecc2b5739ec45b91cd",
16,
),
)
def test_SECP256k1_2(self):
self._do(
generator=SECP256k1.generator,
secexp=int(
"cca9fbcc1b41e5a95d369eaa6ddcff73b61a4efaa279cfc6567e8daa39cbaf50",
16,
),
hsh=hashlib.sha256(b"sample").digest(),
hash_func=hashlib.sha256,
expected=int(
"2df40ca70e639d89528a6b670d9d48d9165fdc0febc0974056bdce192b8e16a3",
16,
),
)
def test_SECP256k1_3(self):
self._do(
generator=SECP256k1.generator,
secexp=0x1,
hsh=hashlib.sha256(b"Satoshi Nakamoto").digest(),
hash_func=hashlib.sha256,
expected=0x8F8A276C19F4149656B280621E358CCE24F5F52542772691EE69063B74F15D15,
)
def test_SECP256k1_4(self):
self._do(
generator=SECP256k1.generator,
secexp=0x1,
hsh=hashlib.sha256(
b"All those moments will be lost in time, like tears in rain. Time to die..."
).digest(),
hash_func=hashlib.sha256,
expected=0x38AA22D72376B4DBC472E06C3BA403EE0A394DA63FC58D88686C611ABA98D6B3,
)
def test_SECP256k1_5(self):
self._do(
generator=SECP256k1.generator,
secexp=0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364140,
hsh=hashlib.sha256(b"Satoshi Nakamoto").digest(),
hash_func=hashlib.sha256,
expected=0x33A19B60E25FB6F4435AF53A3D42D493644827367E6453928554F43E49AA6F90,
)
def test_SECP256k1_6(self):
self._do(
generator=SECP256k1.generator,
secexp=0xF8B8AF8CE3C7CCA5E300D33939540C10D45CE001B8F252BFBC57BA0342904181,
hsh=hashlib.sha256(b"Alan Turing").digest(),
hash_func=hashlib.sha256,
expected=0x525A82B70E67874398067543FD84C83D30C175FDC45FDEEE082FE13B1D7CFDF1,
)
def test_1(self):
self._do(
generator=Point(
None,
0,
0,
int("4000000000000000000020108A2E0CC0D99F8A5EF", 16),
),
secexp=int("09A4D6792295A7F730FC3F2B49CBC0F62E862272F", 16),
hsh=unhexlify(
b"AF2BDBE1AA9B6EC1E2ADE1D694F41FC71A831D0268E9891562113D8A62ADD1BF"
),
hash_func=hashlib.sha256,
expected=int("23AF4074C90A02B3FE61D286D5C87F425E6BDD81B", 16),
)
def test_2(self):
self._do(
generator=NIST192p.generator,
secexp=int("6FAB034934E4C0FC9AE67F5B5659A9D7D1FEFD187EE09FD4", 16),
hsh=hashlib.sha1(b"sample").digest(),
hash_func=hashlib.sha1,
expected=int(
"37D7CA00D2C7B0E5E412AC03BD44BA837FDD5B28CD3B0021", 16
),
)
def test_3(self):
self._do(
generator=NIST192p.generator,
secexp=int("6FAB034934E4C0FC9AE67F5B5659A9D7D1FEFD187EE09FD4", 16),
hsh=hashlib.sha256(b"sample").digest(),
hash_func=hashlib.sha256,
expected=int(
"32B1B6D7D42A05CB449065727A84804FB1A3E34D8F261496", 16
),
)
def test_4(self):
self._do(
generator=NIST192p.generator,
secexp=int("6FAB034934E4C0FC9AE67F5B5659A9D7D1FEFD187EE09FD4", 16),
hsh=hashlib.sha512(b"sample").digest(),
hash_func=hashlib.sha512,
expected=int(
"A2AC7AB055E4F20692D49209544C203A7D1F2C0BFBC75DB1", 16
),
)
def test_5(self):
self._do(
generator=NIST192p.generator,
secexp=int("6FAB034934E4C0FC9AE67F5B5659A9D7D1FEFD187EE09FD4", 16),
hsh=hashlib.sha1(b"test").digest(),
hash_func=hashlib.sha1,
expected=int(
"D9CF9C3D3297D3260773A1DA7418DB5537AB8DD93DE7FA25", 16
),
)
def test_6(self):
self._do(
generator=NIST192p.generator,
secexp=int("6FAB034934E4C0FC9AE67F5B5659A9D7D1FEFD187EE09FD4", 16),
hsh=hashlib.sha256(b"test").digest(),
hash_func=hashlib.sha256,
expected=int(
"5C4CE89CF56D9E7C77C8585339B006B97B5F0680B4306C6C", 16
),
)
def test_7(self):
self._do(
generator=NIST192p.generator,
secexp=int("6FAB034934E4C0FC9AE67F5B5659A9D7D1FEFD187EE09FD4", 16),
hsh=hashlib.sha512(b"test").digest(),
hash_func=hashlib.sha512,
expected=int(
"0758753A5254759C7CFBAD2E2D9B0792EEE44136C9480527", 16
),
)
def test_8(self):
self._do(
generator=NIST521p.generator,
secexp=int(
"0FAD06DAA62BA3B25D2FB40133DA757205DE67F5BB0018FEE8C86E1B68C7E75CAA896EB32F1F47C70855836A6D16FCC1466F6D8FBEC67DB89EC0C08B0E996B83538",
16,
),
hsh=hashlib.sha1(b"sample").digest(),
hash_func=hashlib.sha1,
expected=int(
"089C071B419E1C2820962321787258469511958E80582E95D8378E0C2CCDB3CB42BEDE42F50E3FA3C71F5A76724281D31D9C89F0F91FC1BE4918DB1C03A5838D0F9",
16,
),
)
def test_9(self):
self._do(
generator=NIST521p.generator,
secexp=int(
"0FAD06DAA62BA3B25D2FB40133DA757205DE67F5BB0018FEE8C86E1B68C7E75CAA896EB32F1F47C70855836A6D16FCC1466F6D8FBEC67DB89EC0C08B0E996B83538",
16,
),
hsh=hashlib.sha256(b"sample").digest(),
hash_func=hashlib.sha256,
expected=int(
"0EDF38AFCAAECAB4383358B34D67C9F2216C8382AAEA44A3DAD5FDC9C32575761793FEF24EB0FC276DFC4F6E3EC476752F043CF01415387470BCBD8678ED2C7E1A0",
16,
),
)
def test_10(self):
self._do(
generator=NIST521p.generator,
secexp=int(
"0FAD06DAA62BA3B25D2FB40133DA757205DE67F5BB0018FEE8C86E1B68C7E75CAA896EB32F1F47C70855836A6D16FCC1466F6D8FBEC67DB89EC0C08B0E996B83538",
16,
),
hsh=hashlib.sha512(b"test").digest(),
hash_func=hashlib.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):
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,
),
)
@pytest.mark.slow
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,
),
)
@pytest.mark.slow
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):
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,
),
)
@pytest.mark.slow
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,
),
)
@pytest.mark.slow
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,
),
)
@pytest.mark.parametrize(
"w, gwx, gwy, k, msg, md, r, s, curve",
[
pytest.param(
"DC51D3866A15BACDE33D96F992FCA99DA7E6EF0934E7097559C27F1614C88A7F",
"2442A5CC0ECD015FA3CA31DC8E2BBC70BF42D60CBCA20085E0822CB04235E970",
"6FC98BD7E50211A4A27102FA3549DF79EBCB4BF246B80945CDDFE7D509BBFD7D",
"9E56F509196784D963D1C0A401510EE7ADA3DCC5DEE04B154BF61AF1D5A6DECE",
b"abc",
hashlib.sha256,
"CB28E0999B9C7715FD0A80D8E47A77079716CBBF917DD72E97566EA1C066957C",
"86FA3BB4E26CAD5BF90B7F81899256CE7594BB1EA0C89212748BFF3B3D5B0315",
NIST256p,
id="ECDSA-256",
),
pytest.param(
"0BEB646634BA87735D77AE4809A0EBEA865535DE4C1E1DCB692E84708E81A5AF"
"62E528C38B2A81B35309668D73524D9F",
"96281BF8DD5E0525CA049C048D345D3082968D10FEDF5C5ACA0C64E6465A97EA"
"5CE10C9DFEC21797415710721F437922",
"447688BA94708EB6E2E4D59F6AB6D7EDFF9301D249FE49C33096655F5D502FAD"
"3D383B91C5E7EDAA2B714CC99D5743CA",
"B4B74E44D71A13D568003D7489908D564C7761E229C58CBFA18950096EB7463B"
"854D7FA992F934D927376285E63414FA",
b"abc",
hashlib.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",
hashlib.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)