Browse Source

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.
master
SomberNight 2 years ago
parent
commit
52f1a2ce25
No known key found for this signature in database
GPG Key ID: B33B5F232C6271E9
  1. 13
      electrum/ecc.py
  2. 34
      tests/test_ecc.py

13
electrum/ecc.py

@ -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)

34
tests/test_ecc.py

@ -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))

Loading…
Cancel
Save