From 132ddd02f2aa52b3b352c1038550b1f869eb5553 Mon Sep 17 00:00:00 2001 From: Anton Kueltz Date: Tue, 7 Apr 2020 23:31:17 -0700 Subject: [PATCH 1/2] Move encoding tests into own package under tests --- fastecdsa/tests/encoding/__init__.py | 0 fastecdsa/tests/{ => encoding}/test_asn1.py | 4 +- .../test_der.py} | 42 ++++---- fastecdsa/tests/encoding/test_sec1.py | 100 ++++++++++++++++++ fastecdsa/tests/test_key_encoding.py | 72 ------------- 5 files changed, 126 insertions(+), 92 deletions(-) create mode 100644 fastecdsa/tests/encoding/__init__.py rename fastecdsa/tests/{ => encoding}/test_asn1.py (85%) rename fastecdsa/tests/{test_signature_encoding.py => encoding/test_der.py} (64%) create mode 100644 fastecdsa/tests/encoding/test_sec1.py delete mode 100644 fastecdsa/tests/test_key_encoding.py diff --git a/fastecdsa/tests/encoding/__init__.py b/fastecdsa/tests/encoding/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/fastecdsa/tests/test_asn1.py b/fastecdsa/tests/encoding/test_asn1.py similarity index 85% rename from fastecdsa/tests/test_asn1.py rename to fastecdsa/tests/encoding/test_asn1.py index c17eef0..efa571e 100644 --- a/fastecdsa/tests/test_asn1.py +++ b/fastecdsa/tests/encoding/test_asn1.py @@ -1,8 +1,8 @@ from os import remove from unittest import TestCase -from ..curve import P256 -from ..keys import export_key, import_key, gen_keypair +from fastecdsa.curve import P256 +from fastecdsa.keys import export_key, import_key, gen_keypair class TestAsn1(TestCase): diff --git a/fastecdsa/tests/test_signature_encoding.py b/fastecdsa/tests/encoding/test_der.py similarity index 64% rename from fastecdsa/tests/test_signature_encoding.py rename to fastecdsa/tests/encoding/test_der.py index 85f221e..cc03cbd 100644 --- a/fastecdsa/tests/test_signature_encoding.py +++ b/fastecdsa/tests/encoding/test_der.py @@ -1,24 +1,28 @@ from unittest import TestCase -from ..encoding.der import DEREncoder, InvalidDerSignature +from fastecdsa._ecdsa import sign +from fastecdsa.encoding.der import DEREncoder, InvalidDerSignature +from fastecdsa.util import RFC6979 -class TestSignatureEncoding(TestCase): - def test_encode_der_signature(self): - self.assertEqual(DEREncoder.encode_signature(r=1, s=2), b"\x30" # SEQUENCE - b"\x06" # Length of Sequence - b"\x02" # INTEGER - b"\x01" # Length of r - b"\x01" # r - b"\x02" # INTEGER - b"\x01" # Length of s - b"\x02") # s +class TestDEREncoder(TestCase): + def test_encode_one_byte_signature(self): + self.assertEqual( + DEREncoder.encode_signature(r=1, s=2), + b"\x30" # SEQUENCE + b"\x06" # Length of Sequence + b"\x02" # INTEGER + b"\x01" # Length of r + b"\x01" # r + b"\x02" # INTEGER + b"\x01" # Length of s + b"\x02", # s + ) # Check that we add a zero byte when the number's highest bit is set - self.assertEqual(DEREncoder.encode_signature(r=128, s=128), - b"0\x08\x02\x02\x00\x80\x02\x02\x00\x80") + self.assertEqual(DEREncoder.encode_signature(r=128, s=128), b"0\x08\x02\x02\x00\x80\x02\x02\x00\x80") - def test_decode_der_signature(self): + def test_decode_signature(self): with self.assertRaises(InvalidDerSignature): DEREncoder.decode_signature(b"") # length to shot with self.assertRaises(InvalidDerSignature): @@ -47,7 +51,9 @@ def test_decode_der_signature(self): DEREncoder.decode_signature(b"\x30\x07\x02\x01\x01\x02\x02\x00\x02") # value of s starts with a zero byte self.assertEqual(DEREncoder.decode_signature(b"\x30\x06\x02\x01\x01\x02\x01\x02"), (1, 2)) - self.assertEqual(DEREncoder.decode_signature(b"0\x08\x02\x02\x00\x80\x02\x02\x00\x80"), - (128, 128)) # verify zero bytes - self.assertEqual(DEREncoder.decode_signature(b"0\x08\x02\x02\x03\xE8\x02\x02\x03\xE8"), - (1000, 1000)) # verify byte order \ No newline at end of file + self.assertEqual( + DEREncoder.decode_signature(b"0\x08\x02\x02\x00\x80\x02\x02\x00\x80"), (128, 128) + ) # verify zero bytes + self.assertEqual( + DEREncoder.decode_signature(b"0\x08\x02\x02\x03\xE8\x02\x02\x03\xE8"), (1000, 1000) + ) # verify byte order diff --git a/fastecdsa/tests/encoding/test_sec1.py b/fastecdsa/tests/encoding/test_sec1.py new file mode 100644 index 0000000..df3934b --- /dev/null +++ b/fastecdsa/tests/encoding/test_sec1.py @@ -0,0 +1,100 @@ +from binascii import hexlify, unhexlify +from unittest import TestCase + +from fastecdsa.curve import P256, secp192k1, secp256k1 +from fastecdsa.encoding.sec1 import InvalidSEC1PublicKey, SEC1Encoder +from fastecdsa.point import Point + + +class TestSEC1Encoder(TestCase): + def test_encode_public_key(self): + # 1/ PrivateKey generated using openssl "openssl ecparam -name secp256k1 -genkey -out ec-priv.pem" + # 2/ Printed using "openssl ec -in ec-priv.pem -text -noout" and converted to numeric using "asn1._bytes_to_int" + priv_key = 7002880736699640265110069622773736733141182416793484574964618597954446769264 + pubkey_compressed = hexlify(SEC1Encoder.encode_public_key(secp256k1.G * priv_key)) + pubkey_uncompressed = hexlify(SEC1Encoder.encode_public_key(secp256k1.G * priv_key, compressed=False)) + + # 3/ PublicKey extracted using "openssl ec -in ec-priv.pem -pubout -out ec-pub.pem" + # 4/ Encoding verified using openssl "openssl ec -in ec-pub.pem -pubin -text -noout -conv_form compressed" + self.assertEqual(pubkey_compressed, b"02e5e2c01985aafb6e2c3ad49f3db5ccc54b2e63343af405b521303d0f35835062") + self.assertEqual( + pubkey_uncompressed, + b"04e5e2c01985aafb6e2c3ad49f3db5ccc54b2e63343af405b521303d0f3583506" + b"23dad76df888abde5ed0cc5af1b83968edffcae5d70bedb24fdc18bb5f79499d0", + ) + + # Same with P256 Curve + priv_P256 = 807015861248675637760562792774171551137308512372870683367415858378856470633 + pubkey_compressed = hexlify(SEC1Encoder.encode_public_key(P256.G * priv_P256)) + pubkey_uncompressed = hexlify(SEC1Encoder.encode_public_key(P256.G * priv_P256, compressed=False)) + self.assertEqual(pubkey_compressed, b"0212c9ddf64b0d1f1d91d9bd729abfb880079fa889d66604cc0b78c9cbc271824c") + self.assertEqual( + pubkey_uncompressed, + b"0412c9ddf64b0d1f1d91d9bd729abfb880079fa889d66604cc0b78c9cbc271824" + b"c9a7d581bcf2aba680b53cedbade03be62fe95869da04a168a458f369ac6a823e", + ) + + # And secp192k1 Curve + priv_secp192k1 = 5345863567856687638748079156318679969014620278806295592453 + pubkey_compressed = hexlify(SEC1Encoder.encode_public_key(secp192k1.G * priv_secp192k1)) + pubkey_uncompressed = hexlify(SEC1Encoder.encode_public_key(secp192k1.G * priv_secp192k1, compressed=False)) + self.assertEqual(pubkey_compressed, b"03a3bec5fba6d13e51fb55bd88dd097cb9b04f827bc151d22d") + self.assertEqual( + pubkey_uncompressed, + b"04a3bec5fba6d13e51fb55bd88dd097cb9b04f827bc151d22df07a73819149e8d903aa983e52ab1cff38f0d381f940d361", + ) + + def test_decode_public_key(self): + expected_public = Point( + x=0xE5E2C01985AAFB6E2C3AD49F3DB5CCC54B2E63343AF405B521303D0F35835062, + y=0x3DAD76DF888ABDE5ED0CC5AF1B83968EDFFCAE5D70BEDB24FDC18BB5F79499D0, + curve=secp256k1, + ) + public_from_compressed = SEC1Encoder.decode_public_key( + unhexlify(b"02e5e2c01985aafb6e2c3ad49f3db5ccc54b2e63343af405b521303d0f35835062"), secp256k1 + ) + public_from_uncompressed = SEC1Encoder.decode_public_key( + unhexlify( + b"04e5e2c01985aafb6e2c3ad49f3db5ccc54b2e63343af405b521303d0f3583506" + b"23dad76df888abde5ed0cc5af1b83968edffcae5d70bedb24fdc18bb5f79499d0" + ), + secp256k1, + ) + + # Same values as in `test_encode_public_key`, verified using openssl + self.assertEqual(public_from_compressed, expected_public) + self.assertEqual(public_from_uncompressed, expected_public) + with self.assertRaises(InvalidSEC1PublicKey) as e: + SEC1Encoder.decode_public_key(b"\x02", secp256k1) # invalid compressed length + self.assertEqual(e.exception.args[0], "A compressed public key must be 33 bytes long") + with self.assertRaises(InvalidSEC1PublicKey) as e: + SEC1Encoder.decode_public_key(b"\x04", secp256k1) # invalid uncompressed length + self.assertEqual(e.exception.args[0], "An uncompressed public key must be 65 bytes long") + with self.assertRaises(InvalidSEC1PublicKey) as e: + # invalid prefix value + SEC1Encoder.decode_public_key( + unhexlify(b"05e5e2c01985aafb6e2c3ad49f3db5ccc54b2e63343af405b521303d0f35835062"), secp256k1 + ) + self.assertEqual(e.exception.args[0], "Wrong key format") + + # With P256, same values as in `test_encode_public_key`, verified using openssl + expected_P256 = Point( + x=0x12C9DDF64B0D1F1D91D9BD729ABFB880079FA889D66604CC0B78C9CBC271824C, + y=0x9A7D581BCF2ABA680B53CEDBADE03BE62FE95869DA04A168A458F369AC6A823E, + curve=P256, + ) + public_from_compressed = SEC1Encoder.decode_public_key( + unhexlify(b"0212c9ddf64b0d1f1d91d9bd729abfb880079fa889d66604cc0b78c9cbc271824c"), P256 + ) + self.assertEqual(public_from_compressed, expected_P256) + + # With secp192k1, same values as in `test_encode_public_key`, verified using openssl + expected_secp192k1 = Point( + x=0xA3BEC5FBA6D13E51FB55BD88DD097CB9B04F827BC151D22D, + y=0xF07A73819149E8D903AA983E52AB1CFF38F0D381F940D361, + curve=secp192k1, + ) + public_from_compressed = SEC1Encoder.decode_public_key( + unhexlify(b"03a3bec5fba6d13e51fb55bd88dd097cb9b04f827bc151d22d"), secp192k1 + ) + self.assertEqual(public_from_compressed, expected_secp192k1) diff --git a/fastecdsa/tests/test_key_encoding.py b/fastecdsa/tests/test_key_encoding.py deleted file mode 100644 index 30ff066..0000000 --- a/fastecdsa/tests/test_key_encoding.py +++ /dev/null @@ -1,72 +0,0 @@ -from binascii import hexlify, unhexlify -from unittest import TestCase - -from ..curve import P256, secp192k1, secp256k1 -from ..encoding.sec1 import InvalidSEC1PublicKey, SEC1Encoder -from ..point import Point - - -class TestEncodePublicKey(TestCase): - def test_SEC1_encode_public_key(self): - # 1/ PrivateKey generated using openssl "openssl ecparam -name secp256k1 -genkey -out ec-priv.pem" - # 2/ Printed using "openssl ec -in ec-priv.pem -text -noout" and converted to numeric using "asn1._bytes_to_int" - priv_key = 7002880736699640265110069622773736733141182416793484574964618597954446769264 - pubkey_compressed = hexlify(SEC1Encoder.encode_public_key(secp256k1.G * priv_key)) - pubkey_uncompressed = hexlify(SEC1Encoder.encode_public_key(secp256k1.G * priv_key, compressed=False)) - # 3/ PublicKey extracted using "openssl ec -in ec-priv.pem -pubout -out ec-pub.pem" - # 4/ Encoding verified using openssl "openssl ec -in ec-pub.pem -pubin -text -noout -conv_form compressed" - self.assertEqual(pubkey_compressed, b'02e5e2c01985aafb6e2c3ad49f3db5ccc54b2e63343af405b521303d0f35835062') - self.assertEqual(pubkey_uncompressed, b'04e5e2c01985aafb6e2c3ad49f3db5ccc54b2e63343af405b521303d0f3583506' - b'23dad76df888abde5ed0cc5af1b83968edffcae5d70bedb24fdc18bb5f79499d0') - # Same with P256 Curve - priv_P256 = 807015861248675637760562792774171551137308512372870683367415858378856470633 - pubkey_compressed = hexlify(SEC1Encoder.encode_public_key(P256.G * priv_P256)) - pubkey_uncompressed = hexlify(SEC1Encoder.encode_public_key(P256.G * priv_P256, compressed=False)) - self.assertEqual(pubkey_compressed, b'0212c9ddf64b0d1f1d91d9bd729abfb880079fa889d66604cc0b78c9cbc271824c') - self.assertEqual(pubkey_uncompressed, b'0412c9ddf64b0d1f1d91d9bd729abfb880079fa889d66604cc0b78c9cbc271824' - b'c9a7d581bcf2aba680b53cedbade03be62fe95869da04a168a458f369ac6a823e') - # And secp192k1 Curve - priv_secp192k1 = 5345863567856687638748079156318679969014620278806295592453 - pubkey_compressed = hexlify(SEC1Encoder.encode_public_key(secp192k1.G * priv_secp192k1)) - pubkey_uncompressed = hexlify(SEC1Encoder.encode_public_key(secp192k1.G * priv_secp192k1, compressed=False)) - self.assertEqual(pubkey_compressed, b'03a3bec5fba6d13e51fb55bd88dd097cb9b04f827bc151d22d') - self.assertEqual(pubkey_uncompressed, b'04a3bec5fba6d13e51fb55bd88dd097cb9b04f827bc151d22' - b'df07a73819149e8d903aa983e52ab1cff38f0d381f940d361') - - def test_SEC1_decode_public_key(self): - expected_public = Point(x=0xe5e2c01985aafb6e2c3ad49f3db5ccc54b2e63343af405b521303d0f35835062, - y=0x3dad76df888abde5ed0cc5af1b83968edffcae5d70bedb24fdc18bb5f79499d0, - curve=secp256k1) - public_from_compressed = SEC1Encoder.decode_public_key( - unhexlify(b'02e5e2c01985aafb6e2c3ad49f3db5ccc54b2e63343af405b521303d0f35835062'), secp256k1) - public_from_uncompressed = SEC1Encoder.decode_public_key( - unhexlify(b'04e5e2c01985aafb6e2c3ad49f3db5ccc54b2e63343af405b521303d0f3583506' - b'23dad76df888abde5ed0cc5af1b83968edffcae5d70bedb24fdc18bb5f79499d0'), secp256k1) - # Same values as in "test_SEC1_encode_public_key", verified using openssl - self.assertEqual(public_from_compressed, expected_public) - self.assertEqual(public_from_uncompressed, expected_public) - with self.assertRaises(InvalidSEC1PublicKey) as e: - SEC1Encoder.decode_public_key(b'\x02', secp256k1) # invalid compressed length - self.assertEqual(e.exception.args[0], "A compressed public key must be 33 bytes long") - with self.assertRaises(InvalidSEC1PublicKey) as e: - SEC1Encoder.decode_public_key(b'\x04', secp256k1) # invalid uncompressed length - self.assertEqual(e.exception.args[0], "An uncompressed public key must be 65 bytes long") - with self.assertRaises(InvalidSEC1PublicKey) as e: - # invalid prefix value - SEC1Encoder.decode_public_key( - unhexlify(b'05e5e2c01985aafb6e2c3ad49f3db5ccc54b2e63343af405b521303d0f35835062'), secp256k1) - self.assertEqual(e.exception.args[0], "Wrong key format") - # With P256, same values as in "test_SEC1_encode_public_key", verified using openssl - expected_P256 = Point(x=0x12c9ddf64b0d1f1d91d9bd729abfb880079fa889d66604cc0b78c9cbc271824c, - y=0x9a7d581bcf2aba680b53cedbade03be62fe95869da04a168a458f369ac6a823e, - curve=P256) - public_from_compressed = SEC1Encoder.decode_public_key( - unhexlify(b'0212c9ddf64b0d1f1d91d9bd729abfb880079fa889d66604cc0b78c9cbc271824c'), P256) - self.assertEqual(public_from_compressed, expected_P256) - # With P256, same values as in "test_SEC1_encode_public_key", verified using openssl - expected_secp192k1 = Point(x=0xa3bec5fba6d13e51fb55bd88dd097cb9b04f827bc151d22d, - y=0xf07a73819149e8d903aa983e52ab1cff38f0d381f940d361, - curve=secp192k1) - public_from_compressed = SEC1Encoder.decode_public_key( - unhexlify(b'03a3bec5fba6d13e51fb55bd88dd097cb9b04f827bc151d22d'), secp192k1) - self.assertEqual(public_from_compressed, expected_secp192k1) \ No newline at end of file From 033ab59f9b5672015a2c59b8c58c5ff4299964ec Mon Sep 17 00:00:00 2001 From: Anton Kueltz Date: Wed, 8 Apr 2020 01:10:13 -0700 Subject: [PATCH 2/2] Fix issue with rfc6979 when prehashed ecdsa is used --- fastecdsa/ecdsa.py | 14 +++++----- fastecdsa/tests/encoding/test_der.py | 41 ++++++++++++++++++++++++++-- fastecdsa/util.py | 21 ++++++++------ 3 files changed, 58 insertions(+), 18 deletions(-) diff --git a/fastecdsa/ecdsa.py b/fastecdsa/ecdsa.py index a9122ff..617cea9 100644 --- a/fastecdsa/ecdsa.py +++ b/fastecdsa/ecdsa.py @@ -1,3 +1,4 @@ +from binascii import hexlify from hashlib import sha256 from typing import TypeVar @@ -25,19 +26,18 @@ def sign(msg: MsgTypes, d: int, curve: Curve = P256, hashfunc=sha256, prehashed: | d (int): The ECDSA private key of the signer. | curve (fastecdsa.curve.Curve): The curve to be used to sign the message. | hashfunc (_hashlib.HASH): The hash function used to compress the message. + | prehashed (bool): The message being passed has already been hashed by :code:`hashfunc`. """ # generate a deterministic nonce per RFC6979 - rfc6979 = RFC6979(msg, d, curve.q, hashfunc) + rfc6979 = RFC6979(msg, d, curve.q, hashfunc, prehashed=prehashed) k = rfc6979.gen_nonce() - # add a prehash option - if not prehashed: - hashed = hashfunc(msg_bytes(msg)).hexdigest() + if prehashed: + hex_digest = hexlify(msg).decode() else: - hashed = msg - + hex_digest = hashfunc(msg_bytes(msg)).hexdigest() r, s = _ecdsa.sign( - hashed, + hex_digest, str(d), str(k), str(curve.p), diff --git a/fastecdsa/tests/encoding/test_der.py b/fastecdsa/tests/encoding/test_der.py index cc03cbd..2d4f2dc 100644 --- a/fastecdsa/tests/encoding/test_der.py +++ b/fastecdsa/tests/encoding/test_der.py @@ -1,12 +1,13 @@ +from binascii import unhexlify from unittest import TestCase -from fastecdsa._ecdsa import sign +from fastecdsa.curve import secp256k1 +from fastecdsa.ecdsa import sign from fastecdsa.encoding.der import DEREncoder, InvalidDerSignature -from fastecdsa.util import RFC6979 class TestDEREncoder(TestCase): - def test_encode_one_byte_signature(self): + def test_encode_signature(self): self.assertEqual( DEREncoder.encode_signature(r=1, s=2), b"\x30" # SEQUENCE @@ -22,6 +23,40 @@ def test_encode_one_byte_signature(self): # Check that we add a zero byte when the number's highest bit is set self.assertEqual(DEREncoder.encode_signature(r=128, s=128), b"0\x08\x02\x02\x00\x80\x02\x02\x00\x80") + # Check a value on a standard curve like secp256k1 works + # see https://github.com/btccom/secp256k1-go/blob/master/secp256k1/sign_vectors.yaml + secp256k1_vectors = [ + ( + 0x31a84594060e103f5a63eb742bd46cf5f5900d8406e2726dedfc61c7cf43ebad, + unhexlify("9e5755ec2f328cc8635a55415d0e9a09c2b6f2c9b0343c945fbbfe08247a4cbe"), + unhexlify("30440220132382ca59240c2e14ee7ff61d90fc63276325f4cbe8169fc53ade4a407c2fc802204d86fbe3bde69" + "75dd5a91fdc95ad6544dcdf0dab206f02224ce7e2b151bd82ab"), + ), + ( + 0x7177f0d04c79fa0b8c91fe90c1cf1d44772d1fba6e5eb9b281a22cd3aafb51fe, + unhexlify("2d46a712699bae19a634563d74d04cc2da497b841456da270dccb75ac2f7c4e7"), + unhexlify("3045022100d80cf7abc9ab601373780cee3733d2cb5ff69ba1452ec2d2a058adf9645c13be0220011d1213b7d" + "152f72fd8759b45276ba32d9c909602e5ec89550baf3aaa8ed950"), + ), + ( + 0x989e500d6b1397f2c5dcdf43c58ac2f14df753eb6089654e07ff946b3f84f3d5, + unhexlify("c94f4ec84be928017cbbb447d2ab5b5d4d69e5e5fd03da7eae4378a1b1c9c402"), + unhexlify("3045022100d0f5b740cbe3ee5b098d3c5afdefa61bb0797cb4e7b596afbd38174e1c653bb602200329e9f1a09" + "632de477664814791ac31544e04715db68f4b02657ba35863e711"), + ), + ( + 0x39dfc615f2b718397f6903b0c46c47c5687e97d3d2a5e1f2b200f459f7b1219b, + unhexlify("dfeb2092955572ce0695aa038f58df5499949e18f58785553c3e83343cd5eb93"), + unhexlify("30440220692c01edf8aeab271df3ed4e8d57a170f014f8f9d65031aac28b5e1840acfb5602205075f9d1fdbf5" + "079ee052e5f3572d518b3594ef49582899ec44d065f71a55192"), + ), + ] + + for private_key, digest, expected in secp256k1_vectors: + r, s = sign(digest, private_key, curve=secp256k1, prehashed=True) + encoded = DEREncoder.encode_signature(r, s) + self.assertEqual(encoded, expected) + def test_decode_signature(self): with self.assertRaises(InvalidDerSignature): DEREncoder.decode_signature(b"") # length to shot diff --git a/fastecdsa/util.py b/fastecdsa/util.py index 923eef8..9f75b77 100644 --- a/fastecdsa/util.py +++ b/fastecdsa/util.py @@ -1,6 +1,7 @@ from binascii import hexlify import hmac from struct import pack +from typing import Callable class RFC6979: @@ -12,20 +13,22 @@ class RFC6979: key. More info here: http://tools.ietf.org/html/rfc6979. Attributes: - | msg (string): A message being signed. + | msg (bytes): A message being signed. | x (int): An ECDSA private key. | q (int): The order of the generator point of the curve being used to sign the message. | hashfunc (_hashlib.HASH): The hash function used to compress the message. + | prehashed (bool): Whether the signature is on a pre-hashed message. """ - def __init__(self, msg, x, q, hashfunc): + def __init__(self, msg: bytes, x: int, q: int, hashfunc: Callable, prehashed: bool = False): self.x = x self.q = q self.msg = msg_bytes(msg) self.qlen = len(bin(q)) - 2 # -2 for the leading '0b' self.rlen = ((self.qlen + 7) // 8) * 8 self.hashfunc = hashfunc + self.prehashed = prehashed - def _bits2int(self, b): + def _bits2int(self, b: bytes) -> int: """ http://tools.ietf.org/html/rfc6979#section-2.3.2 """ i = int(hexlify(b), 16) blen = len(b) * 8 @@ -35,7 +38,7 @@ def _bits2int(self, b): return i - def _int2octets(self, x): + def _int2octets(self, x: int) -> bytes: """ http://tools.ietf.org/html/rfc6979#section-2.3.3 """ octets = b'' @@ -46,7 +49,7 @@ def _int2octets(self, x): padding = b'\x00' * ((self.rlen // 8) - len(octets)) return padding + octets - def _bits2octets(self, b): + def _bits2octets(self, b: bytes) -> bytes: """ http://tools.ietf.org/html/rfc6979#section-2.3.4 """ z1 = self._bits2int(b) # -2 for the leading '0b' z2 = z1 % self.q @@ -54,9 +57,11 @@ def _bits2octets(self, b): def gen_nonce(self): """ http://tools.ietf.org/html/rfc6979#section-3.2 """ - h1 = self.hashfunc(self.msg) - hash_size = h1.digest_size - h1 = h1.digest() + hash_size = self.hashfunc().digest_size + if self.prehashed: + h1 = self.msg + else: + h1 = self.hashfunc(self.msg).digest() key_and_msg = self._int2octets(self.x) + self._bits2octets(h1) v = b''.join([b'\x01' for _ in range(hash_size)])