From 6cb7cac9ff909b037a1a6382c14d40ab5369f9b2 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Wed, 23 Jun 2021 12:23:56 +0000 Subject: [PATCH] Add Schnorr Signature Half Aggregation --- include/secp256k1_schnorrsig.h | 21 ++++ src/modules/schnorrsig/main_impl.h | 159 ++++++++++++++++++++++++++++ src/modules/schnorrsig/tests_impl.h | 31 ++++++ 3 files changed, 211 insertions(+) diff --git a/include/secp256k1_schnorrsig.h b/include/secp256k1_schnorrsig.h index 0150cd339..1242a957f 100644 --- a/include/secp256k1_schnorrsig.h +++ b/include/secp256k1_schnorrsig.h @@ -1,6 +1,8 @@ #ifndef SECP256K1_SCHNORRSIG_H #define SECP256K1_SCHNORRSIG_H +#include + #include "secp256k1.h" #include "secp256k1_extrakeys.h" @@ -104,6 +106,25 @@ 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(3) SECP256K1_ARG_NONNULL(4); +SECP256K1_API int secp256k1_schnorrsig_aggregate( + const secp256k1_context* ctx, + unsigned char* aggsig, + size_t* aggsig_size, + unsigned char **sig64, + unsigned char **msg32, + secp256k1_xonly_pubkey *pubkey, + uint32_t n_sigs +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6); + +SECP256K1_API int secp256k1_schnorrsig_aggverify( + const secp256k1_context* ctx, + unsigned char **msg32, + uint32_t n_msgs, + secp256k1_xonly_pubkey *pubkey, + unsigned char *aggsig, + size_t aggsig_size +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); + #ifdef __cplusplus } #endif diff --git a/src/modules/schnorrsig/main_impl.h b/src/modules/schnorrsig/main_impl.h index 22e1b33a5..d6cfdb6fd 100644 --- a/src/modules/schnorrsig/main_impl.h +++ b/src/modules/schnorrsig/main_impl.h @@ -236,4 +236,163 @@ int secp256k1_schnorrsig_verify(const secp256k1_context* ctx, const unsigned cha secp256k1_fe_equal_var(&rx, &r.x); } +static int compute_midhash(const secp256k1_context* ctx, unsigned char *midhash, unsigned char **sig64, unsigned char *aggsig, unsigned char **msg32, secp256k1_xonly_pubkey *pubkey, size_t n_sigs) { + secp256k1_sha256 hash; + uint32_t i; + + VERIFY_CHECK((aggsig != NULL) != (sig64 != NULL)); + + /* TODO: use actual tagged hash */ + secp256k1_sha256_initialize(&hash); + /* z_i = int(hash_{HalfAggregation}(r_1 || pk_1 || m_1 || ... || r_n || pk_n || m_n || i)) mod n */ + for (i = 0; i < n_sigs; i++) { + unsigned char pk_ser[32]; + + /* r_i = sig_i[0:32] */ + if (sig64 != NULL) { + secp256k1_sha256_write(&hash, sig64[i], 32); + } else { + /* aggsig != NULL */ + secp256k1_sha256_write(&hash, &aggsig[i*32], 32); + } + if (!secp256k1_xonly_pubkey_serialize(ctx, pk_ser, &pubkey[i])) { + return 0; + } + secp256k1_sha256_write(&hash, pk_ser, sizeof(pk_ser)); + secp256k1_sha256_write(&hash, msg32[i], 32); + } + /* TODO: copy midstate instead of computing intermediate "midhash" */ + secp256k1_sha256_finalize(&hash, midhash); + return 1; +} + +int secp256k1_schnorrsig_aggregate(const secp256k1_context* ctx, unsigned char* aggsig, size_t* aggsig_size, unsigned char **sig64, unsigned char **msg32, secp256k1_xonly_pubkey *pubkey, uint32_t n_sigs) { + uint32_t i; + secp256k1_sha256 hash; + unsigned char midhash[32]; + secp256k1_scalar s; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(aggsig != NULL); + ARG_CHECK(aggsig_size != NULL); + ARG_CHECK(sig64 != NULL); + ARG_CHECK(msg32 != NULL); + ARG_CHECK(pubkey != NULL); + + if (*aggsig_size < 32*(1 + n_sigs)) { + return 0; + } + + if (!compute_midhash(ctx, midhash, sig64, NULL, msg32, pubkey, n_sigs)) { + return 0; + } + + /* s = z_1⋅s_1 + ... + z_n⋅s_n */ + secp256k1_scalar_set_int(&s, 0); + for (i = 0; i < n_sigs; i++) { + unsigned char randomizer[32]; + secp256k1_scalar zi; + secp256k1_scalar si; + + secp256k1_sha256_initialize(&hash); + secp256k1_sha256_write(&hash, midhash, sizeof(midhash)); + /* TODO: fix endianness issue */ + secp256k1_sha256_write(&hash, (unsigned char *)&i, sizeof(i)); + secp256k1_sha256_finalize(&hash, randomizer); + secp256k1_scalar_set_b32(&zi, randomizer, NULL); + + secp256k1_scalar_set_b32(&si, &sig64[i][32], NULL); + secp256k1_scalar_mul(&si, &si, &zi); + secp256k1_scalar_add(&s, &s, &si); + + memcpy(&aggsig[i*32], sig64[i], 32); + } + + /* Return sig = r_1 || ... || r_n || bytes(s) */ + secp256k1_scalar_get_b32(&aggsig[n_sigs*32], &s); + *aggsig_size = 32 * (1 + n_sigs); + + return 1; +} + +int secp256k1_schnorrsig_aggverify(const secp256k1_context* ctx, unsigned char **msg32, uint32_t n_msgs, secp256k1_xonly_pubkey *pubkey, unsigned char *aggsig, size_t aggsig_size) { + unsigned char midhash[32]; + uint32_t i; + secp256k1_gej lhs, rhs; + secp256k1_scalar s; + int overflow; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(msg32 != NULL); + ARG_CHECK(pubkey != NULL); + ARG_CHECK(aggsig != NULL); + + secp256k1_gej_set_infinity(&rhs); + if (aggsig_size != 32*(1 + n_msgs)) { + return 0; + } + + if (!compute_midhash(ctx, midhash, NULL, aggsig, msg32, pubkey, n_msgs)) { + return 0; + } + + for (i = 0; i < n_msgs; i++) { + secp256k1_sha256 hash; + unsigned char randomizer[32]; + secp256k1_scalar zi; + secp256k1_fe rx; + secp256k1_ge rp, pp; + secp256k1_scalar ei; + unsigned char pk_ser[32]; + secp256k1_gej ppj, acc; + + secp256k1_sha256_initialize(&hash); + secp256k1_sha256_write(&hash, midhash, sizeof(midhash)); + /* TODO: fix endianness issue */ + secp256k1_sha256_write(&hash, (unsigned char *)&i, sizeof(i)); + secp256k1_sha256_finalize(&hash, randomizer); + secp256k1_scalar_set_b32(&zi, randomizer, NULL); + + /* R_i = lift_x(int(r_i)); fail if that fails */ + if (!secp256k1_fe_set_b32(&rx, &aggsig[i*32])) { + return 0; + } + if (!secp256k1_ge_set_xo_var(&rp, &rx, 0)) { + return 0; + } + + /* P_i = lift_x(int(pk_i)); fail if that fails */ + if (!secp256k1_xonly_pubkey_load(ctx, &pp, &pubkey[i])) { + return 0; + } + + /* e_i = int(hash_{BIP0340/challenge}(bytes(r_i) || pk_i || m_i)) mod n */ + /* TODO: compute pubkey serialization again after compute_midhash? */ + if (!secp256k1_xonly_pubkey_serialize(ctx, pk_ser, &pubkey[i])) { + return 0; + } + secp256k1_schnorrsig_challenge(&ei, &aggsig[i*32], msg32[i], pk_ser); + secp256k1_gej_set_ge(&ppj, &pp); + /* e_i⋅P_i */ + secp256k1_ecmult(&ctx->ecmult_ctx, &acc, &ppj, &ei, NULL); + /* R_i + e_i⋅P_i */ + secp256k1_gej_add_ge_var(&acc, &acc, &rp, NULL); + /* z_i⋅(R_i + e_i⋅P_i) */ + secp256k1_ecmult(&ctx->ecmult_ctx, &acc, &acc, &zi, NULL); + secp256k1_gej_add_var(&rhs, &rhs, &acc, NULL); + } + /* s = int(sig[n⋅32:(n+1)⋅32]) */ + secp256k1_scalar_set_b32(&s, &aggsig[n_msgs*32], &overflow); + if (overflow) { + return 0; + } + /* s⋅G */ + secp256k1_ecmult(&ctx->ecmult_ctx, &lhs, NULL, &secp256k1_scalar_zero, &s); + + /* 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 338462fc9..a07485fae 100644 --- a/src/modules/schnorrsig/tests_impl.h +++ b/src/modules/schnorrsig/tests_impl.h @@ -789,6 +789,36 @@ void test_schnorrsig_taproot(void) { CHECK(secp256k1_xonly_pubkey_tweak_add_check(ctx, output_pk_bytes, pk_parity, &internal_pk, tweak) == 1); } + +#define N_SIGS 3 +void test_schnorrsig_aggregate(void) { + unsigned char aggsig[32*(N_SIGS + 1)]; + size_t aggsig_size = sizeof(aggsig); + unsigned char sig64[N_SIGS][64]; + unsigned char *sig64_ptr[N_SIGS]; + unsigned char msg32[N_SIGS][32]; + unsigned char *msg32_ptr[N_SIGS]; + secp256k1_xonly_pubkey pubkey[N_SIGS]; + int i; + + for (i = 0; i < N_SIGS; i++) { + unsigned char sk[32]; + secp256k1_keypair keypair; + + msg32_ptr[i] = &msg32[i][0]; + sig64_ptr[i] = &sig64[i][0]; + secp256k1_testrand256(sk); + secp256k1_testrand256(msg32[i]); + + CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1); + CHECK(secp256k1_keypair_xonly_pub(ctx, &pubkey[i], NULL, &keypair)); + CHECK(secp256k1_schnorrsig_sign(ctx, sig64[i], msg32[i], &keypair, NULL, NULL)); + } + + CHECK(secp256k1_schnorrsig_aggregate(ctx, aggsig, &aggsig_size, sig64_ptr, msg32_ptr, pubkey, N_SIGS)); + CHECK(secp256k1_schnorrsig_aggverify(ctx, msg32_ptr, N_SIGS, pubkey, aggsig, aggsig_size)); +} + void run_schnorrsig_tests(void) { int i; run_nonce_function_bip340_tests(); @@ -801,6 +831,7 @@ void run_schnorrsig_tests(void) { test_schnorrsig_sign_verify(); } test_schnorrsig_taproot(); + test_schnorrsig_aggregate(); } #endif