From e49b9e3dd5db933e5324f80a3add5f880129b2ea Mon Sep 17 00:00:00 2001 From: Benedikt Date: Thu, 27 Jul 2023 13:46:25 +0200 Subject: [PATCH] Incremental Half-Aggregation for Schnorr Signatures. --- .gitignore | 2 + include/secp256k1_schnorrsig.h | 75 +++++++++++ src/libsecp256k1-config.h | 149 ++++++++++++++++++++ src/libsecp256k1-config.h.in | 148 ++++++++++++++++++++ src/modules/schnorrsig/main_impl.h | 202 ++++++++++++++++++++++++++++ src/modules/schnorrsig/tests_impl.h | 161 ++++++++++++++++++++++ src/stamp-h1 | 1 + 7 files changed, 738 insertions(+) create mode 100644 src/libsecp256k1-config.h create mode 100644 src/libsecp256k1-config.h.in create mode 100644 src/stamp-h1 diff --git a/.gitignore b/.gitignore index b3ae618d4..fe87d6312 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,8 @@ schnorr_example *.trs *.sage.py +.vscode/ + Makefile configure .libs/ diff --git a/include/secp256k1_schnorrsig.h b/include/secp256k1_schnorrsig.h index 26358533f..4eed47905 100644 --- a/include/secp256k1_schnorrsig.h +++ b/include/secp256k1_schnorrsig.h @@ -183,6 +183,81 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorrsig_verify( const secp256k1_xonly_pubkey *pubkey ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(5); + +/** Incrementally (Half-)Aggregate a sequence of Schnorr signatures to an existing half-aggregate signature. + * + * Returns 1 on success, 0 on failure. + * Args: ctx: a secp256k1 context object. + * In/Out: aggsig: pointer to the serialized aggregate signature that is input. Will be overwritten by the new serialized aggregate signature. + * aggsig_size: size of the memory allocated in aggsig. Should be large enough to hold the new serialized aggregate signature. + * In: all_pubkeys: Array of x-only public keys, including both the ones for the already aggregated signature + * and the ones for the signatures that should be added. + * Assumed to contain n = n_before + n_new many public keys. + * all_msgs32: Array of 32-byte messages, including both the ones for the already aggregated signature + * and the ones for the signatures that should be added. + * Assumed to contain n = n_before + n_new many messages. + * new_sigs64: Array of 64-byte signatures, containing the new signatures that should be added. + * Assumed to contain n_new many signatures. + * n_before: Number of signatures that are already "contained" in the aggregate signature + * n_new: Number of signatures that should now be added to the aggregate signature + */ +SECP256K1_API int secp256k1_schnorrsig_inc_aggregate( + const secp256k1_context* ctx, + unsigned char* aggsig, + size_t* aggsig_size, + const secp256k1_xonly_pubkey* all_pubkeys, + const unsigned char* all_msgs32, + const unsigned char* new_sigs64, + size_t n_before, + size_t n_new +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6); + + + +/** (Half-)Aggregate a sequence of Schnorr signatures. + * + * Returns 1 on success, 0 on failure. + * Args: ctx: a secp256k1 context object. + * Out: aggsig: pointer to an array of aggsig_size many bytes to store the serialized aggregate signature + * In/Out: aggsig_size: size of the aggsig array that is passed; will be overwritten to be the exact size of aggsig. + * In: pubkeys: Array of x-only public keys. Assumed to contain n many public keys. + * msgs32: Array of 32-byte messages. Assumed to contain n many messages. + * sigs64: Array of 64-byte signatures. Assumed to contain n many signatures. + * n: number of signatures to be aggregated. + */ +SECP256K1_API int secp256k1_schnorrsig_aggregate( + const secp256k1_context* ctx, + unsigned char* aggsig, + size_t* aggsig_size, + const secp256k1_xonly_pubkey* pubkeys, + const unsigned char* msgs32, + const unsigned char* sigs64, + size_t n +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6); + + + +/** Verify a (Half-)aggregate Schnorr signature. + * + * Returns: 1: correct signature + * 0: incorrect signature. + * Args: ctx: a secp256k1 context object. + * In: pubkeys: Array of x-only public keys. Assume to contain n many public keys. + * msgs32: Array of 32-byte messages. Assumed to contain n many messages. + * n: number of signatures to that have been aggregated. + * aggsig: Pointer to an array of aggsig_size many bytes containing the serialized aggregate signatur to be verified + * aggsig_size: Size of the aggregate signature + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorrsig_aggverify( + const secp256k1_context* ctx, + const secp256k1_xonly_pubkey* pubkeys, + const unsigned char* msgs32, + size_t n, + const unsigned char* aggsig, + size_t aggsig_size +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5); + + #ifdef __cplusplus } #endif diff --git a/src/libsecp256k1-config.h b/src/libsecp256k1-config.h new file mode 100644 index 000000000..8d8435522 --- /dev/null +++ b/src/libsecp256k1-config.h @@ -0,0 +1,149 @@ +/* src/libsecp256k1-config.h. Generated from libsecp256k1-config.h.in by configure. */ +/* src/libsecp256k1-config.h.in. Generated from configure.ac by autoheader. */ + +#ifndef LIBSECP256K1_CONFIG_H + +#define LIBSECP256K1_CONFIG_H + +/* Define this symbol to compile out all VERIFY code */ +/* #undef COVERAGE */ + +/* Set ecmult gen precision bits */ +#define ECMULT_GEN_PREC_BITS 4 + +/* Set window size for ecmult precomputation */ +#define ECMULT_WINDOW_SIZE 15 + +/* Define this symbol to enable the Bulletproofs++ module */ +/* #undef ENABLE_MODULE_BPPP */ + +/* Define this symbol to enable the ECDH module */ +/* #undef ENABLE_MODULE_ECDH */ + +/* Define this symbol to enable the ECDSA adaptor module */ +/* #undef ENABLE_MODULE_ECDSA_ADAPTOR */ + +/* Define this symbol to enable the ECDSA sign-to-contract module */ +/* #undef ENABLE_MODULE_ECDSA_S2C */ + +/* Define this symbol to enable the extrakeys module */ +#define ENABLE_MODULE_EXTRAKEYS 1 + +/* Define this symbol to enable the NUMS generator module */ +/* #undef ENABLE_MODULE_GENERATOR */ + +/* Define this symbol to enable the MuSig module */ +/* #undef ENABLE_MODULE_MUSIG */ + +/* Define this symbol to enable the Pedersen / zero knowledge range proof + module */ +/* #undef ENABLE_MODULE_RANGEPROOF */ + +/* Define this symbol to enable the ECDSA pubkey recovery module */ +/* #undef ENABLE_MODULE_RECOVERY */ + +/* Define this symbol to enable the schnorrsig module */ +#define ENABLE_MODULE_SCHNORRSIG 1 + +/* Define this symbol to enable the surjection proof module */ +/* #undef ENABLE_MODULE_SURJECTIONPROOF */ + +/* Define this symbol to enable the key whitelisting module */ +/* #undef ENABLE_MODULE_WHITELIST */ + +/* Define this symbol if __builtin_clzll is available */ +/* #undef HAVE_BUILTIN_CLZLL */ + +/* Define this symbol if __builtin_popcount is available */ +/* #undef HAVE_BUILTIN_POPCOUNT */ + +/* Define to 1 if you have the header file. */ +#define HAVE_DLFCN_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDIO_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define this symbol if valgrind is installed, and it supports the host + platform */ +/* #undef HAVE_VALGRIND */ + +/* Define to the sub-directory where libtool stores uninstalled libraries. */ +#define LT_OBJDIR ".libs/" + +/* Name of package */ +#define PACKAGE "libsecp256k1" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "https://github.com/bitcoin-core/secp256k1/issues" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "libsecp256k1" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "libsecp256k1 0.1.0-pre" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "libsecp256k1" + +/* Define to the home page for this package. */ +#define PACKAGE_URL "https://github.com/bitcoin-core/secp256k1" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "0.1.0-pre" + +/* Define to 1 if all of the C90 standard headers exist (not just the ones + required in a freestanding environment). This macro is provided for + backward compatibility; new code need not use it. */ +#define STDC_HEADERS 1 + +/* Define this symbol to enable x86_64 assembly optimizations */ +/* #undef USE_ASM_X86_64 */ + +/* Define this symbol if an external (non-inline) assembly implementation is + used */ +/* #undef USE_EXTERNAL_ASM */ + +/* Define this symbol if an external implementation of the default callbacks + is used */ +/* #undef USE_EXTERNAL_DEFAULT_CALLBACKS */ + +/* Define this symbol to force the use of the (unsigned) __int128 based wide + multiplication implementation */ +/* #undef USE_FORCE_WIDEMUL_INT128 */ + +/* Define this symbol to force the use of the (u)int64_t based wide + multiplication implementation */ +/* #undef USE_FORCE_WIDEMUL_INT64 */ + +/* Define this symbol to reduce SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS to 16, + disabling parsing and verification */ +/* #undef USE_REDUCED_SURJECTION_PROOF_SIZE */ + +/* Version number of package */ +#define VERSION "0.1.0-pre" + +#endif /*LIBSECP256K1_CONFIG_H*/ diff --git a/src/libsecp256k1-config.h.in b/src/libsecp256k1-config.h.in new file mode 100644 index 000000000..51e7e6b5f --- /dev/null +++ b/src/libsecp256k1-config.h.in @@ -0,0 +1,148 @@ +/* src/libsecp256k1-config.h.in. Generated from configure.ac by autoheader. */ + +#ifndef LIBSECP256K1_CONFIG_H + +#define LIBSECP256K1_CONFIG_H + +/* Define this symbol to compile out all VERIFY code */ +#undef COVERAGE + +/* Set ecmult gen precision bits */ +#undef ECMULT_GEN_PREC_BITS + +/* Set window size for ecmult precomputation */ +#undef ECMULT_WINDOW_SIZE + +/* Define this symbol to enable the Bulletproofs++ module */ +#undef ENABLE_MODULE_BPPP + +/* Define this symbol to enable the ECDH module */ +#undef ENABLE_MODULE_ECDH + +/* Define this symbol to enable the ECDSA adaptor module */ +#undef ENABLE_MODULE_ECDSA_ADAPTOR + +/* Define this symbol to enable the ECDSA sign-to-contract module */ +#undef ENABLE_MODULE_ECDSA_S2C + +/* Define this symbol to enable the extrakeys module */ +#undef ENABLE_MODULE_EXTRAKEYS + +/* Define this symbol to enable the NUMS generator module */ +#undef ENABLE_MODULE_GENERATOR + +/* Define this symbol to enable the MuSig module */ +#undef ENABLE_MODULE_MUSIG + +/* Define this symbol to enable the Pedersen / zero knowledge range proof + module */ +#undef ENABLE_MODULE_RANGEPROOF + +/* Define this symbol to enable the ECDSA pubkey recovery module */ +#undef ENABLE_MODULE_RECOVERY + +/* Define this symbol to enable the schnorrsig module */ +#undef ENABLE_MODULE_SCHNORRSIG + +/* Define this symbol to enable the surjection proof module */ +#undef ENABLE_MODULE_SURJECTIONPROOF + +/* Define this symbol to enable the key whitelisting module */ +#undef ENABLE_MODULE_WHITELIST + +/* Define this symbol if __builtin_clzll is available */ +#undef HAVE_BUILTIN_CLZLL + +/* Define this symbol if __builtin_popcount is available */ +#undef HAVE_BUILTIN_POPCOUNT + +/* Define to 1 if you have the header file. */ +#undef HAVE_DLFCN_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDIO_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define this symbol if valgrind is installed, and it supports the host + platform */ +#undef HAVE_VALGRIND + +/* Define to the sub-directory where libtool stores uninstalled libraries. */ +#undef LT_OBJDIR + +/* Name of package */ +#undef PACKAGE + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the home page for this package. */ +#undef PACKAGE_URL + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* Define to 1 if all of the C90 standard headers exist (not just the ones + required in a freestanding environment). This macro is provided for + backward compatibility; new code need not use it. */ +#undef STDC_HEADERS + +/* Define this symbol to enable x86_64 assembly optimizations */ +#undef USE_ASM_X86_64 + +/* Define this symbol if an external (non-inline) assembly implementation is + used */ +#undef USE_EXTERNAL_ASM + +/* Define this symbol if an external implementation of the default callbacks + is used */ +#undef USE_EXTERNAL_DEFAULT_CALLBACKS + +/* Define this symbol to force the use of the (unsigned) __int128 based wide + multiplication implementation */ +#undef USE_FORCE_WIDEMUL_INT128 + +/* Define this symbol to force the use of the (u)int64_t based wide + multiplication implementation */ +#undef USE_FORCE_WIDEMUL_INT64 + +/* Define this symbol to reduce SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS to 16, + disabling parsing and verification */ +#undef USE_REDUCED_SURJECTION_PROOF_SIZE + +/* Version number of package */ +#undef VERSION + +#endif /*LIBSECP256K1_CONFIG_H*/ diff --git a/src/modules/schnorrsig/main_impl.h b/src/modules/schnorrsig/main_impl.h index 4e7b45a04..9553cdce6 100644 --- a/src/modules/schnorrsig/main_impl.h +++ b/src/modules/schnorrsig/main_impl.h @@ -264,4 +264,206 @@ int secp256k1_schnorrsig_verify(const secp256k1_context* ctx, const unsigned cha secp256k1_fe_equal_var(&rx, &r.x); } + + +/* Initializes SHA256 with fixed midstate. This midstate was computed by applying + * SHA256 to SHA256("HalfAgg/randomizer")||SHA256("HalfAgg/randomizer"). */ +void secp256k1_schnorrsig_sha256_tagged_aggregation(secp256k1_sha256 *sha) { + secp256k1_sha256_initialize(sha); + sha->s[0] = 0xd11f5532ul; + sha->s[1] = 0xfa57f70ful; + sha->s[2] = 0x5db0d728ul; + sha->s[3] = 0xf806ffe1ul; + sha->s[4] = 0x1d4db069ul; + sha->s[5] = 0xb4d587e1ul; + sha->s[6] = 0x50451c2aul; + sha->s[7] = 0x10fb63e9ul; + + sha->bytes = 64; +} + +int secp256k1_schnorrsig_inc_aggregate(const secp256k1_context* ctx, unsigned char* aggsig, size_t* aggsig_size, const secp256k1_xonly_pubkey* all_pubkeys, const unsigned char* all_msgs32, const unsigned char* new_sigs64, size_t n_before, size_t n_new) { + size_t i; + size_t n; + int overflow; + secp256k1_sha256 hash; + secp256k1_scalar s; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(aggsig != NULL); + ARG_CHECK(aggsig_size != NULL); + ARG_CHECK(all_pubkeys != NULL); + ARG_CHECK(all_msgs32 != NULL); + ARG_CHECK(new_sigs64 != NULL); + + /* Check that aggsig_size is large enough, i.e. aggsig_size >= 32*(n+1) */ + n = n_before + n_new; + if ((*aggsig_size >> 5) <= 0 || ((*aggsig_size >> 5)-1) < n) { + return 0; + } + + /* Prepare hash with common prefix. The prefix is the tag and */ + /* r_0 || pk_0 || m_0 || .... || r_{n'-1} || pk_{n'-1} || m_{n'-1} */ + /* where n' = n_before */ + secp256k1_schnorrsig_sha256_tagged_aggregation(&hash); + for (i = 0; i < n_before; ++i) { + /* serialize pk_i */ + unsigned char pk_ser[32]; + if (!secp256k1_xonly_pubkey_serialize(ctx, pk_ser, &all_pubkeys[i])) { + return 0; + } + /* write r_i */ + secp256k1_sha256_write(&hash, &aggsig[i*32], 32); + /* write pk_i */ + secp256k1_sha256_write(&hash, pk_ser, 32); + /* write mes_i*/ + secp256k1_sha256_write(&hash, &all_msgs32[i*32], 32); + } + + /* Compute s = s_old + sum_{i = n_before}^{n} z_i*s_i */ + /* where s_old = 0 if n_before = 0 */ + secp256k1_scalar_set_int(&s, 0); + if (n_before > 0) { + secp256k1_scalar_set_b32(&s, &aggsig[n_before*32], &overflow); + if (overflow) { + return 0; + } + } + for (i = n_before; i < n; ++i) { + unsigned char pk_ser[32]; + unsigned char hashoutput[32]; + secp256k1_sha256 hashcopy; + secp256k1_scalar si; + secp256k1_scalar zi; + + /* Step 0: Serialize pk_i into pk_ser */ + if (!secp256k1_xonly_pubkey_serialize(ctx, pk_ser, &all_pubkeys[i])) { + return 0; + } + + /* Step 1: z_i = TaggedHash(...) */ + /* 1.a) Write into hash r_i, pk_i, mes_i, r_i */ + secp256k1_sha256_write(&hash, &new_sigs64[(i-n_before)*64], 32); + secp256k1_sha256_write(&hash, pk_ser, 32); + secp256k1_sha256_write(&hash, &all_msgs32[i*32], 32); + /* 1.b) Copy the hash */ + hashcopy = hash; + /* 1.c) Finalize the copy to get zi*/ + secp256k1_sha256_finalize(&hashcopy, hashoutput); + secp256k1_scalar_set_b32(&zi, hashoutput, NULL); + + /* Step2: s := s + zi*si */ + secp256k1_scalar_set_b32(&si, &new_sigs64[(i-n_before)*64+32], NULL); + secp256k1_scalar_mul(&si, &si, &zi); + secp256k1_scalar_add(&s, &s, &si); + } + + /* copy new rs into aggsig */ + for (i = n_before; i < n; ++i) { + memcpy(&aggsig[i*32], &new_sigs64[(i-n_before)*64], 32); + } + /* copy new s into aggsig */ + secp256k1_scalar_get_b32(&aggsig[n*32], &s); + *aggsig_size = 32 * (1 + n); + return 1; +} + +int secp256k1_schnorrsig_aggregate(const secp256k1_context* ctx, unsigned char* aggsig, size_t* aggsig_size, const secp256k1_xonly_pubkey* pubkeys, const unsigned char* msgs32, const unsigned char* sigs64, size_t n) { + if (!secp256k1_schnorrsig_inc_aggregate(ctx, aggsig, aggsig_size, pubkeys, msgs32, sigs64, 0, n)) { + return 0; + } + return 1; +} + +int secp256k1_schnorrsig_aggverify(const secp256k1_context* ctx, const secp256k1_xonly_pubkey* pubkeys, const unsigned char* msgs32, size_t n, const unsigned char* aggsig, size_t aggsig_size) { + size_t i; + secp256k1_gej lhs, rhs; + secp256k1_scalar s; + secp256k1_sha256 hash; + int overflow; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(pubkeys != NULL); + ARG_CHECK(msgs32 != NULL); + ARG_CHECK(aggsig != NULL); + + /* Check that aggsig_size is correct, i.e. aggsig_size = 32*(n+1) */ + if ((aggsig_size >> 5) <= 0 || ((aggsig_size >> 5)-1) != n) { + return 0; + } + + /* Compute the rhs: */ + /* Set rhs = 0 */ + /* For each i in 0,.., n-1, do: */ + /* (1) z_i = TaggedHash(...) */ + /* (2) T_i = R_i+e_i*P_i */ + /* (3) rhs = rhs + z_i*T_i */ + secp256k1_gej_set_infinity(&rhs); + secp256k1_schnorrsig_sha256_tagged_aggregation(&hash); + for (i = 0; i < n; ++i) { + secp256k1_fe rx; + secp256k1_ge rp, pp; + secp256k1_scalar ei; + secp256k1_gej ppj, acc; + + unsigned char pk_ser[32]; + unsigned char hashoutput[32]; + secp256k1_sha256 hashcopy; + secp256k1_scalar zi; + + /* Step 0: Serialize pk_i into pk_ser */ + /* We need that in Step 1 and in Step 2 */ + if (!secp256k1_xonly_pubkey_serialize(ctx, pk_ser, &pubkeys[i])) { + return 0; + } + + /* Step 1: z_i = TaggedHash(...) */ + /* 1.a) Write into hash r_i, pk_i, mes_i, r_i */ + secp256k1_sha256_write(&hash, &aggsig[i*32], 32); + secp256k1_sha256_write(&hash, pk_ser, 32); + secp256k1_sha256_write(&hash, &msgs32[i*32], 32); + /* 1.b) Copy the hash */ + hashcopy = hash; + /* 1.c) Finalize the copy to get zi*/ + secp256k1_sha256_finalize(&hashcopy, hashoutput); + secp256k1_scalar_set_b32(&zi, hashoutput, NULL); + + /* Step 2: T_i = R_i+e_i*P_i */ + /* 2.a) R_i = lift_x(int(r_i)); fail if that fails */ + if (!secp256k1_fe_set_b32_limit(&rx, &aggsig[i*32])) { + return 0; + } + if (!secp256k1_ge_set_xo_var(&rp, &rx, 0)) { + return 0; + } + /* 2.b) P_i = lift_x(int(pk_i)); fail if that fails */ + if (!secp256k1_xonly_pubkey_load(ctx, &pp, &pubkeys[i])) { + return 0; + } + /* 2.c) e_i = int(hash_{BIP0340/challenge}(bytes(r_i) || pk_i || m_i)) mod n */ + secp256k1_schnorrsig_challenge(&ei, &aggsig[i*32], &msgs32[i*32], 32, pk_ser); + secp256k1_gej_set_ge(&ppj, &pp); + /* 2.d) acc = R_i + e_i⋅P_i */ + secp256k1_ecmult(&acc, &ppj, &ei, NULL); + secp256k1_gej_add_ge_var(&acc, &acc, &rp, NULL); + + + /* Step 3: rhs = rhs + zi*acc */ + secp256k1_ecmult(&acc, &acc, &zi, NULL); + secp256k1_gej_add_var(&rhs,&rhs,&acc,NULL); + } + + /* Compute the lhs as lhs = s*G */ + secp256k1_scalar_set_b32(&s, &aggsig[n*32], &overflow); + if (overflow) { + return 0; + } + secp256k1_ecmult(&lhs, NULL, &secp256k1_scalar_zero, &s); + + /* Check that lhs == rhs */ + secp256k1_gej_neg(&lhs, &lhs); + secp256k1_gej_add_var(&lhs, &lhs, &rhs, NULL); + return secp256k1_gej_is_infinity(&lhs); +} + #endif diff --git a/src/modules/schnorrsig/tests_impl.h b/src/modules/schnorrsig/tests_impl.h index 90337ff03..22104a1c7 100644 --- a/src/modules/schnorrsig/tests_impl.h +++ b/src/modules/schnorrsig/tests_impl.h @@ -1000,6 +1000,163 @@ static void test_schnorrsig_taproot(void) { CHECK(secp256k1_xonly_pubkey_tweak_add_check(CTX, output_pk_bytes, pk_parity, &internal_pk, tweak) == 1); } + + +/* We test that hash initialized by secp256k1_schnorrsig_sha256_tagged_aggregate has the + * expected state. */ +void test_schnorrsig_sha256_tagged_aggregate(void) { + unsigned char tag[18] = "HalfAgg/randomizer"; + secp256k1_sha256 sha; + secp256k1_sha256 sha_optimized; + + secp256k1_sha256_initialize_tagged(&sha, (unsigned char *) tag, sizeof(tag)); + secp256k1_schnorrsig_sha256_tagged_aggregation(&sha_optimized); + test_sha256_eq(&sha, &sha_optimized); +} + + + +/* In this test we create a bunch of Schnorr signatures, +* aggregate N_INITIAL of them in one shot, and then +* aggregate N_NEW of them incrementally to the already aggregated ones +* the aggregate signature should verify after both steps +*/ +#define N_INITIAL 3 +#define N_NEW 5 +#define N (N_INITIAL+N_NEW) +void test_schnorrsig_aggregate(void) { + int i; + secp256k1_xonly_pubkey pubkeys[N]; + unsigned char msgs32[N*32]; + unsigned char sigs64[N*64]; + unsigned char aggsig[32*(N + 1)]; + size_t aggsig_size = 32*(N + 1); + + /* create N many Schnorr keys and sigs for random messages */ + for (i = 0; i < N; ++i) { + unsigned char sk[32]; + secp256k1_keypair keypair; + secp256k1_testrand256(sk); + secp256k1_testrand256(&msgs32[i*32]); + + CHECK(secp256k1_keypair_create(CTX, &keypair, sk) == 1); + CHECK(secp256k1_keypair_xonly_pub(CTX, &pubkeys[i], NULL, &keypair)); + CHECK(secp256k1_schnorrsig_sign(CTX, &sigs64[i*64], &msgs32[i*32], &keypair, NULL)); + } + + /* aggregate the first N_INITIAL of them */ + CHECK(secp256k1_schnorrsig_aggregate(CTX, aggsig, &aggsig_size, pubkeys, msgs32, sigs64, N_INITIAL)); + /* make sure that the aggregate signature verifies */ + CHECK(secp256k1_schnorrsig_aggverify(CTX, pubkeys, msgs32, N_INITIAL, aggsig, aggsig_size)); + /* aggregate the remaining N_NEW many signatures to the already existing ones */ + aggsig_size = 32*(N + 1); + secp256k1_schnorrsig_inc_aggregate(CTX, aggsig, &aggsig_size, pubkeys, msgs32, &sigs64[N_INITIAL*64], N_INITIAL, N_NEW); + /* make sure that the aggregate signature verifies */ + CHECK(secp256k1_schnorrsig_aggverify(CTX, pubkeys, msgs32, N, aggsig, aggsig_size)); +} + +/* this tests the test vectors from +https://github.com/BlockstreamResearch/cross-input-aggregation/blob/master/hacspec-halfagg/tests/tests.rs#L78 +*/ +void test_schnorrsig_aggregate_spec_vectors(void) { + /* Test vector 0 */ + { + int n, aggsig_size; + n = 0; + const secp256k1_xonly_pubkey pubkeys[0] = {}; + const unsigned char msgs32[0] = {}; + const unsigned char aggsig[32] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + aggsig_size = 32; + CHECK(secp256k1_schnorrsig_aggverify(CTX, pubkeys, msgs32, n, aggsig, aggsig_size)); + } + /* Test vector 1 */ + { + int n, aggsig_size, i; + n = 1; + const unsigned char pubkeys_ser[32] = { + 0x1b, 0x84, 0xc5, 0x56, 0x7b, 0x12, 0x64, 0x40, + 0x99, 0x5d, 0x3e, 0xd5, 0xaa, 0xba, 0x05, 0x65, + 0xd7, 0x1e, 0x18, 0x34, 0x60, 0x48, 0x19, 0xff, + 0x9c, 0x17, 0xf5, 0xe9, 0xd5, 0xdd, 0x07, 0x8f + }; + const unsigned char msgs32[32] = { + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02 + }; + const unsigned char aggsig[32+32] = { + 0xb0, 0x70, 0xaa, 0xfc, 0xea, 0x43, 0x9a, 0x4f, + 0x6f, 0x1b, 0xbf, 0xc2, 0xeb, 0x66, 0xd2, 0x9d, + 0x24, 0xb0, 0xca, 0xb7, 0x4d, 0x6b, 0x74, 0x5c, + 0x3c, 0xfb, 0x00, 0x9c, 0xc8, 0xfe, 0x4a, 0xa8, + 0x10, 0x8f, 0x33, 0x90, 0x76, 0x12, 0xfb, 0x74, + 0x84, 0x19, 0xeb, 0xc4, 0x00, 0x4b, 0x31, 0x69, + 0xe1, 0x6e, 0x35, 0xd5, 0xf1, 0x2b, 0x69, 0x3b, + 0x6b, 0xbc, 0x3d, 0x4a, 0x69, 0x82, 0xf2, 0xf6, + }; + aggsig_size = 32+32; + secp256k1_xonly_pubkey pubkeys[1]; + for (i = 0; i < n; ++i) { + CHECK(secp256k1_xonly_pubkey_parse(CTX, &pubkeys[i], &pubkeys_ser[i*32])); + } + CHECK(secp256k1_schnorrsig_aggverify(CTX, pubkeys, msgs32, n, aggsig, aggsig_size)); + } + /* Test vector 2 */ + { + int n, aggsig_size, i; + n = 2; + const unsigned char pubkeys_ser[2*32] = { + 0x1b, 0x84, 0xc5, 0x56, 0x7b, 0x12, 0x64, 0x40, + 0x99, 0x5d, 0x3e, 0xd5, 0xaa, 0xba, 0x05, 0x65, + 0xd7, 0x1e, 0x18, 0x34, 0x60, 0x48, 0x19, 0xff, + 0x9c, 0x17, 0xf5, 0xe9, 0xd5, 0xdd, 0x07, 0x8f, + + 0x46, 0x27, 0x79, 0xad, 0x4a, 0xad, 0x39, 0x51, + 0x46, 0x14, 0x75, 0x1a, 0x71, 0x08, 0x5f, 0x2f, + 0x10, 0xe1, 0xc7, 0xa5, 0x93, 0xe4, 0xe0, 0x30, + 0xef, 0xb5, 0xb8, 0x72, 0x1c, 0xe5, 0x5b, 0x0b, + }; + const unsigned char msgs32[2*32] = { + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + }; + const unsigned char aggsig[2*32+32] = { + 0xb0, 0x70, 0xaa, 0xfc, 0xea, 0x43, 0x9a, 0x4f, + 0x6f, 0x1b, 0xbf, 0xc2, 0xeb, 0x66, 0xd2, 0x9d, + 0x24, 0xb0, 0xca, 0xb7, 0x4d, 0x6b, 0x74, 0x5c, + 0x3c, 0xfb, 0x00, 0x9c, 0xc8, 0xfe, 0x4a, 0xa8, + 0xa3, 0xaf, 0xbd, 0xb4, 0x5a, 0x6a, 0x34, 0xbf, + 0x7c, 0x8c, 0x00, 0xf1, 0xb6, 0xd7, 0xe7, 0xd3, + 0x75, 0xb5, 0x45, 0x40, 0xf1, 0x37, 0x16, 0xc8, + 0x7b, 0x62, 0xe5, 0x1e, 0x2f, 0x4f, 0x22, 0xff, + 0xc2, 0x11, 0xdb, 0x48, 0x47, 0x9c, 0x2f, 0x54, + 0x6d, 0x52, 0xb0, 0x79, 0x55, 0xe7, 0x64, 0xeb, + 0x6a, 0x14, 0x2d, 0x57, 0x72, 0x45, 0xf4, 0x0a, + 0x44, 0xf5, 0xde, 0xe4, 0x68, 0xda, 0x42, 0x44, + }; + aggsig_size = 2*32+32; + secp256k1_xonly_pubkey pubkeys[2]; + for (i = 0; i < n; ++i) { + CHECK(secp256k1_xonly_pubkey_parse(CTX, &pubkeys[i], &pubkeys_ser[i*32])); + } + CHECK(secp256k1_schnorrsig_aggverify(CTX, pubkeys, msgs32, n, aggsig, aggsig_size)); + } +} + + static void run_schnorrsig_tests(void) { int i; run_nonce_function_bip340_tests(); @@ -1012,6 +1169,10 @@ static void run_schnorrsig_tests(void) { test_schnorrsig_sign_verify(); } test_schnorrsig_taproot(); + + test_schnorrsig_sha256_tagged_aggregate(); + test_schnorrsig_aggregate_spec_vectors(); + test_schnorrsig_aggregate(); } #endif diff --git a/src/stamp-h1 b/src/stamp-h1 new file mode 100644 index 000000000..f863082f5 --- /dev/null +++ b/src/stamp-h1 @@ -0,0 +1 @@ +timestamp for src/libsecp256k1-config.h