diff --git a/ngu/hash.c b/ngu/hash.c index 6e0659e..829bd75 100644 --- a/ngu/hash.c +++ b/ngu/hash.c @@ -111,6 +111,41 @@ STATIC const mp_obj_type_t modngu_hash_sha512_type = { .locals_dict = (void *)&modngu_hash_sha512_locals_dict, }; +// Tagged sha256 = SHA256(SHA256(tag)||SHA256(tag)||msg) +STATIC mp_obj_t hm_tagged_sha256(size_t n_args, const mp_obj_t *args) { + mp_obj_t tag = args[0]; + mp_obj_t msg = args[1]; + bool is_tag_hashed = false; + if(n_args > 2) { + is_tag_hashed = mp_obj_is_true(args[2]); + } + mp_buffer_info_t t; + mp_buffer_info_t m; + mp_get_buffer_raise(tag, &t, MP_BUFFER_READ); + mp_get_buffer_raise(msg, &m, MP_BUFFER_READ); + + uint8_t s0[32]; + if (is_tag_hashed) { + if (t.len != 32) { + mp_raise_ValueError(MP_ERROR_TEXT("len tag_hash != 32")); + } + memcpy(s0, t.buf, 32); + } else { + sha256_single(t.buf, t.len, s0); + } + int ser_len = 64 + m.len; + uint8_t ser[ser_len]; + memcpy(ser, s0, 32); + memcpy(ser + 32, s0, 32); + memcpy(ser + 64, m.buf, m.len); + + vstr_t res; + vstr_init_len(&res, 32); + sha256_single(ser, ser_len, (uint8_t *)res.buf); + + return mp_obj_new_str_from_vstr(&mp_type_bytes, &res); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(hm_tagged_sha256_obj,2,3, hm_tagged_sha256); // Double sha256 = sha256(sha256('foo').digest()).digest() ... in one step STATIC mp_obj_t hm_double_sha256(mp_obj_t arg) { @@ -258,6 +293,7 @@ STATIC const mp_rom_map_elem_t mp_module_hash_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_ripemd160), MP_ROM_PTR(&hm_single_ripemd160_obj) }, { MP_ROM_QSTR(MP_QSTR_sha256s), MP_ROM_PTR(&hm_single_sha256_obj) }, { MP_ROM_QSTR(MP_QSTR_sha256d), MP_ROM_PTR(&hm_double_sha256_obj) }, + { MP_ROM_QSTR(MP_QSTR_sha256t), MP_ROM_PTR(&hm_tagged_sha256_obj) }, { MP_ROM_QSTR(MP_QSTR_hash160), MP_ROM_PTR(&hm_hash160_obj) }, { MP_ROM_QSTR(MP_QSTR_pbkdf2_sha512), MP_ROM_PTR(&pbkdf2_sha512_obj) }, diff --git a/ngu/k1.c b/ngu/k1.c index dfb7d6d..91c5db7 100644 --- a/ngu/k1.c +++ b/ngu/k1.c @@ -368,27 +368,6 @@ STATIC mp_obj_t s_verify_schnorr(mp_obj_t compact_sig_in, mp_obj_t digest_in, mp } STATIC MP_DEFINE_CONST_FUN_OBJ_3(s_verify_schnorr_obj, s_verify_schnorr); -STATIC mp_obj_t s_tagged_sha256(mp_obj_t tag_in, mp_obj_t msg_in) { -// Compute a tagged hash as defined in BIP-340. -// -// This is useful for creating a message hash and achieving domain separation -// through an application-specific tag. This function returns -// SHA256(SHA256(tag)||SHA256(tag)||msg). - mp_buffer_info_t tag; - mp_get_buffer_raise(tag_in, &tag, MP_BUFFER_READ); - mp_buffer_info_t msg; - mp_get_buffer_raise(msg_in, &msg, MP_BUFFER_READ); - vstr_t rv; - vstr_init_len(&rv, 32); - - int ok = secp256k1_tagged_sha256(lib_ctx, (uint8_t *)rv.buf, tag.buf, tag.len, msg.buf, msg.len); - if (ok != 1) { - mp_raise_ValueError(MP_ERROR_TEXT("secp256k1_tagged_sha256 invalid arguments")); - } - return mp_obj_new_str_from_vstr(&mp_type_bytes, &rv); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_2(s_tagged_sha256_obj, s_tagged_sha256); - STATIC mp_obj_t s_sign_schnorr(mp_obj_t privkey_in, mp_obj_t digest_in, mp_obj_t aux_rand_in) { @@ -679,7 +658,6 @@ STATIC const mp_rom_map_elem_t globals_table[] = { { MP_ROM_QSTR(MP_QSTR_sign), MP_ROM_PTR(&s_sign_obj) }, { MP_ROM_QSTR(MP_QSTR_sign_schnorr), MP_ROM_PTR(&s_sign_schnorr_obj) }, { MP_ROM_QSTR(MP_QSTR_verify_schnorr), MP_ROM_PTR(&s_verify_schnorr_obj) }, - { MP_ROM_QSTR(MP_QSTR_tagged_sha256), MP_ROM_PTR(&s_tagged_sha256_obj) }, { MP_ROM_QSTR(MP_QSTR_ctx_rnd), MP_ROM_PTR(&s_ctx_rnd_obj) }, }; diff --git a/ngu/ngu_tests/test_hash.py b/ngu/ngu_tests/test_hash.py index baa8a1c..9566ab1 100644 --- a/ngu/ngu_tests/test_hash.py +++ b/ngu/ngu_tests/test_hash.py @@ -25,6 +25,20 @@ def expect2(func, msg, dig): expect2(lambda x: sha512(x).digest(), abc, 'ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f') + # test custom implementation of tagged sha256 (with midstate) + for tag, msg in [(b"BIP0XYZ/nonce", b"a" * 92), (b"a", b"b")]: + tag_hash = ngu.hash.sha256s(tag) + assert ngu.hash.sha256s(ngu.hash.sha256s(tag) + ngu.hash.sha256s(tag) + msg) \ + == ngu.hash.sha256t(tag, msg) \ + == ngu.hash.sha256t(tag_hash, msg, True) + + # passing already hashed tag value - len must be 32 bytes + try: + ngu.hash.sha256t(b"a" * 20, b"a" * 96, True) + assert False + except ValueError: + pass + except ImportError: import hashlib from binascii import b2a_hex, a2b_hex diff --git a/ngu/ngu_tests/test_k1.py b/ngu/ngu_tests/test_k1.py index f758d96..af35c47 100644 --- a/ngu/ngu_tests/test_k1.py +++ b/ngu/ngu_tests/test_k1.py @@ -69,10 +69,6 @@ pubkey3 = sig3.verify_recover(md) assert pubkey3 != pubkey -assert ngu.secp256k1.tagged_sha256(b"tag", b"msg") == ngu.hash.sha256s( - ngu.hash.sha256s(b"tag") + ngu.hash.sha256s(b"tag") + b"msg" -) - # keypair tweaking kp = ngu.secp256k1.keypair() tweak32 = ngu.random.bytes(32) @@ -130,7 +126,7 @@ assert xonly_pub_clone.to_bytes() == xonly_pub_bytes # random msg msg = ngu.random.bytes(32) - msg_hash = ngu.secp256k1.tagged_sha256(b"ngu_tests", msg) + msg_hash = ngu.hash.sha256t(b"ngu_tests", msg) aux_rand = ngu.random.bytes(32) sig_kp = ngu.secp256k1.sign_schnorr(kp, msg_hash, aux_rand) sig_raw = ngu.secp256k1.sign_schnorr(kp.privkey(), msg_hash, aux_rand)