ecc: add method "bip340_tagged_hash"
I decided to use the stdlib (hashlib) instead of libsecp for this, as it is simple enough, and the former is faster on my PC. Added a unit test that compares the two.
This commit is contained in:
@@ -32,7 +32,7 @@ from ctypes import (
|
||||
)
|
||||
|
||||
from .util import bfh, assert_bytes, to_bytes, InvalidPassword, profiler, randrange
|
||||
from .crypto import (sha256d, aes_encrypt_with_iv, aes_decrypt_with_iv, hmac_oneshot)
|
||||
from .crypto import (sha256, sha256d, aes_encrypt_with_iv, aes_decrypt_with_iv, hmac_oneshot)
|
||||
from . import constants
|
||||
from .logging import get_logger
|
||||
from .ecc_fast import _libsecp256k1, SECP256K1_EC_UNCOMPRESSED
|
||||
@@ -522,6 +522,12 @@ class ECPrivkey(ECPubkey):
|
||||
return sig
|
||||
|
||||
def schnorr_sign(self, msg32: bytes, *, aux_rand32: bytes = None) -> bytes:
|
||||
"""Creates a BIP-340 schnorr signature for the given message (hash)
|
||||
and using the optional auxiliary random data.
|
||||
|
||||
note: msg32 is supposed to be a 32 byte hash of the message to be signed.
|
||||
The BIP recommends using bip340_tagged_hash for hashing the message.
|
||||
"""
|
||||
assert isinstance(msg32, bytes), type(msg32)
|
||||
assert len(msg32) == 32, len(msg32)
|
||||
if aux_rand32 is None:
|
||||
@@ -589,3 +595,8 @@ class ECPrivkey(ECPubkey):
|
||||
def construct_ecdsa_sig65(sig64: bytes, recid: int, *, is_compressed: bool) -> bytes:
|
||||
comp = 4 if is_compressed else 0
|
||||
return bytes([27 + recid + comp]) + sig64
|
||||
|
||||
|
||||
def bip340_tagged_hash(tag: bytes, msg: bytes) -> bytes:
|
||||
# note: _libsecp256k1.secp256k1_tagged_sha256 benchmarks about 70% slower than this (on my machine)
|
||||
return sha256(sha256(tag) + sha256(tag) + msg)
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
import csv
|
||||
from ctypes import (
|
||||
c_int, c_char_p, c_size_t, c_void_p, create_string_buffer,
|
||||
)
|
||||
import io
|
||||
|
||||
from electrum import ecc
|
||||
from electrum.ecc import ECPubkey, ECPrivkey
|
||||
from electrum.ecc_fast import _libsecp256k1
|
||||
from electrum import crypto
|
||||
from electrum.crypto import sha256
|
||||
|
||||
from . import ElectrumTestCase
|
||||
@@ -82,3 +87,32 @@ class TestSchnorr(ElectrumTestCase):
|
||||
sig = seckey.schnorr_sign(msg32, aux_rand32=None)
|
||||
self.assertTrue(pubkey1.schnorr_verify(sig, msg32))
|
||||
self.assertTrue(pubkey2.schnorr_verify(sig, msg32))
|
||||
|
||||
def test_bip340_tagged_hash(self):
|
||||
try:
|
||||
_libsecp256k1.secp256k1_tagged_sha256.argtypes = [c_void_p, c_char_p, c_char_p, c_size_t, c_char_p, c_size_t]
|
||||
_libsecp256k1.secp256k1_tagged_sha256.restype = c_int
|
||||
except (OSError, AttributeError):
|
||||
raise Exception('libsecp256k1 library too old: missing secp256k1_tagged_sha256 method')
|
||||
|
||||
def bip340_tagged_hash__from_libsecp(tag: bytes, msg: bytes) -> bytes:
|
||||
assert isinstance(tag, bytes), type(tag)
|
||||
assert isinstance(msg, bytes), type(msg)
|
||||
thash = create_string_buffer(32)
|
||||
ret = _libsecp256k1.secp256k1_tagged_sha256(
|
||||
_libsecp256k1.ctx, thash, tag, len(tag), msg, len(msg))
|
||||
assert 1 == ret, ret
|
||||
thash = bytes(thash)
|
||||
return thash
|
||||
|
||||
data = (
|
||||
(b"", b""),
|
||||
(b"", b"hello there"),
|
||||
(b"mytag", b""),
|
||||
(b"mytag", b"hello there"),
|
||||
(bytes(range(256)) * 10, bytes(range(256)) * 50),
|
||||
(bytes(range(256)) * 1000, bytes(range(256)) * 5000),
|
||||
)
|
||||
for tag, msg in data:
|
||||
self.assertEqual(bip340_tagged_hash__from_libsecp(tag, msg),
|
||||
ecc.bip340_tagged_hash(tag, msg))
|
||||
|
||||
Reference in New Issue
Block a user