From bc0036297b906bc53ce3886ea146c752e47e1ce9 Mon Sep 17 00:00:00 2001 From: SomberNight Date: Thu, 28 Jun 2018 11:42:47 +0200 Subject: [PATCH] fast hmac on python 3.7+ --- lib/bitcoin.py | 10 +++++----- lib/crypto.py | 9 +++++++++ lib/ecc.py | 6 +++--- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/lib/bitcoin.py b/lib/bitcoin.py index ad205efb8..1fe405fe2 100644 --- a/lib/bitcoin.py +++ b/lib/bitcoin.py @@ -31,7 +31,7 @@ from . import version from . import segwit_addr from . import constants from . import ecc -from .crypto import Hash, sha256, hash_160 +from .crypto import Hash, sha256, hash_160, hmac_oneshot ################################## transactions @@ -149,7 +149,7 @@ def add_number_to_script(i: int) -> bytes: hash_encode = lambda x: bh2u(x[::-1]) hash_decode = lambda x: bfh(x)[::-1] -hmac_sha_512 = lambda x, y: hmac.new(x, y, hashlib.sha512).digest() +hmac_sha_512 = lambda x, y: hmac_oneshot(x, y, hashlib.sha512) def is_new_seed(x, prefix=version.SEED_PREFIX): @@ -565,7 +565,7 @@ def _CKD_priv(k, c, s, is_prime): raise BitcoinException('Impossible xprv (not within curve order)') from e cK = keypair.get_public_key_bytes(compressed=True) data = bytes([0]) + k + s if is_prime else cK + s - I = hmac.new(c, data, hashlib.sha512).digest() + I = hmac_oneshot(c, data, hashlib.sha512) I_left = ecc.string_to_number(I[0:32]) k_n = (I_left + ecc.string_to_number(k)) % ecc.CURVE_ORDER if I_left >= ecc.CURVE_ORDER or k_n == 0: @@ -589,7 +589,7 @@ def CKD_pub(cK, c, n): # helper function, callable with arbitrary string. # note: 's' does not need to fit into 32 bits here! (c.f. trustedcoin billing) def _CKD_pub(cK, c, s): - I = hmac.new(c, cK + s, hashlib.sha512).digest() + I = hmac_oneshot(c, cK + s, hashlib.sha512) pubkey = ecc.ECPrivkey(I[0:32]) + ecc.ECPubkey(cK) if pubkey.is_at_infinity(): raise ecc.InvalidECPointException() @@ -683,7 +683,7 @@ def xpub_from_xprv(xprv): def bip32_root(seed, xtype): - I = hmac.new(b"Bitcoin seed", seed, hashlib.sha512).digest() + I = hmac_oneshot(b"Bitcoin seed", seed, hashlib.sha512) master_k = I[0:32] master_c = I[32:] # create xprv first, as that will check if master_k is within curve order diff --git a/lib/crypto.py b/lib/crypto.py index 7020649a5..de8b6b5d7 100644 --- a/lib/crypto.py +++ b/lib/crypto.py @@ -26,6 +26,7 @@ import base64 import os import hashlib +import hmac import pyaes @@ -140,3 +141,11 @@ def hash_160(x: bytes) -> bytes: from . import ripemd md = ripemd.new(sha256(x)) return md.digest() + + +def hmac_oneshot(key: bytes, msg: bytes, digest) -> bytes: + if hasattr(hmac, 'digest'): + # requires python 3.7+; faster + return hmac.digest(key, msg, digest) + else: + return hmac.new(key, msg, digest).digest() diff --git a/lib/ecc.py b/lib/ecc.py index 585883fc2..0fd0f248b 100644 --- a/lib/ecc.py +++ b/lib/ecc.py @@ -36,7 +36,7 @@ from ecdsa.ellipticcurve import Point from ecdsa.util import string_to_number, number_to_string from .util import bfh, bh2u, assert_bytes, print_error, to_bytes, InvalidPassword, profiler -from .crypto import (Hash, aes_encrypt_with_iv, aes_decrypt_with_iv) +from .crypto import (Hash, aes_encrypt_with_iv, aes_decrypt_with_iv, hmac_oneshot) from .ecc_fast import do_monkey_patching_of_python_ecdsa_internals_with_libsecp256k1 @@ -285,7 +285,7 @@ class ECPubkey(object): ciphertext = aes_encrypt_with_iv(key_e, iv, message) ephemeral_pubkey = ephemeral.get_public_key_bytes(compressed=True) encrypted = magic + ephemeral_pubkey + ciphertext - mac = hmac.new(key_m, encrypted, hashlib.sha256).digest() + mac = hmac_oneshot(key_m, encrypted, hashlib.sha256) return base64.b64encode(encrypted + mac) @@ -424,7 +424,7 @@ class ECPrivkey(ECPubkey): ecdh_key = (ephemeral_pubkey * self.secret_scalar).get_public_key_bytes(compressed=True) key = hashlib.sha512(ecdh_key).digest() iv, key_e, key_m = key[0:16], key[16:32], key[32:] - if mac != hmac.new(key_m, encrypted[:-32], hashlib.sha256).digest(): + if mac != hmac_oneshot(key_m, encrypted[:-32], hashlib.sha256): raise InvalidPassword() return aes_decrypt_with_iv(key_e, iv, ciphertext)