Skip to content

Commit

Permalink
Add Schnorrsig half aggregation
Browse files Browse the repository at this point in the history
  • Loading branch information
jonasnick committed May 4, 2021
1 parent f3708a1 commit 31d04c9
Show file tree
Hide file tree
Showing 3 changed files with 204 additions and 0 deletions.
24 changes: 24 additions & 0 deletions include/secp256k1_schnorrsig.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#ifndef SECP256K1_SCHNORRSIG_H
#define SECP256K1_SCHNORRSIG_H

#include <stdint.h>
#include "secp256k1.h"
#include "secp256k1_extrakeys.h"

Expand Down Expand Up @@ -104,6 +105,29 @@ 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);


/**
* TODO: doc, more tests, API tests, separate module? multiexp, z_1 = 1 optimization
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorrsig_halfagg(
const secp256k1_context* ctx,
unsigned char *aggsig,
size_t *aggsig_size,
unsigned char **sig64,
const secp256k1_xonly_pubkey *pk,
unsigned char **msg32,
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 SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorrsig_halfagg_verify(
const secp256k1_context* ctx,
unsigned char *aggsig,
size_t aggsig_size,
const secp256k1_xonly_pubkey *pk,
unsigned char **msg32,
uint32_t n_sigs
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5);

#ifdef __cplusplus
}
#endif
Expand Down
151 changes: 151 additions & 0 deletions src/modules/schnorrsig/main_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -236,4 +236,155 @@ int secp256k1_schnorrsig_verify(const secp256k1_context* ctx, const unsigned cha
secp256k1_fe_equal_var(&rx, &r.x);
}

/* TODO: pk input should be parsed or raw bytes? */
int secp256k1_schnorrsig_halfagg(const secp256k1_context* ctx, unsigned char *aggsig, size_t *aggsig_size, unsigned char **sig64, const secp256k1_xonly_pubkey *pk, unsigned char **msg32, uint32_t n_sigs) {
uint32_t i;
secp256k1_scalar s;
secp256k1_sha256 sha;
unsigned char input_hash[32];

VERIFY_CHECK(ctx != NULL);
ARG_CHECK(aggsig != NULL);
ARG_CHECK(aggsig_size != NULL);
ARG_CHECK(sig64 != NULL);
ARG_CHECK(pk != NULL);
ARG_CHECK(msg32 != NULL);
/* TODO: overflow? */
ARG_CHECK(*aggsig_size >= 32*(1 + n_sigs));

secp256k1_scalar_clear(&s);

/* h = hash_{HalfAggregation}(sig_1[0:32] || pk_1 || m_1 || ... || sig_n[0:32] || pk_n || m_n) */
secp256k1_sha256_initialize_tagged(&sha, (unsigned char*) "HalfAggregation", 15);
for (i = 0; i < n_sigs; i++) {
unsigned char pk_ser[32];
secp256k1_sha256_write(&sha, sig64[i], 32);
secp256k1_xonly_pubkey_serialize(ctx, pk_ser, &pk[i]);
secp256k1_sha256_write(&sha, pk_ser, 32);
secp256k1_sha256_write(&sha, msg32[i], 32);
}
secp256k1_sha256_finalize(&sha, input_hash);

for (i = 0; i < n_sigs; i++) {
/* z_i = int(hash_{HalfAggregationIndex}(h || i)) mod n */
/* s += z_i⋅int(sig[32:64]) */
unsigned char z_bytes[32];
secp256k1_scalar z;
secp256k1_scalar s_i;

secp256k1_sha256_initialize_tagged(&sha, (unsigned char*) "HalfAggregationIndex", 20);
secp256k1_sha256_write(&sha, input_hash, sizeof(input_hash));
/* TODO: fix endianness (or replace by chacha anyway) and add static test vectors */
secp256k1_sha256_write(&sha, (unsigned char *)&n_sigs, sizeof(uint32_t));
secp256k1_sha256_finalize(&sha, z_bytes);
secp256k1_scalar_set_b32(&z, z_bytes, NULL);
secp256k1_scalar_set_b32(&s_i, &sig64[i][32], NULL);
secp256k1_scalar_mul(&s_i, &s_i, &z);
secp256k1_scalar_add(&s, &s, &s_i);
memcpy(&aggsig[32*i], sig64[i], 32);
}
/* Return sig = byte(r_1) || ... || bytes(r_n) || bytes(s) */
secp256k1_scalar_get_b32(&aggsig[32*n_sigs], &s);
*aggsig_size = (n_sigs + 1) * 32;

return 1;
}

int secp256k1_schnorrsig_halfagg_verify(const secp256k1_context* ctx, unsigned char *aggsig, size_t aggsig_size, const secp256k1_xonly_pubkey *pk, unsigned char **msg32, uint32_t n_sigs) {
uint32_t i;
secp256k1_sha256 sha;
unsigned char input_hash[32];
secp256k1_gej rhs;
secp256k1_scalar s;
secp256k1_scalar one;
int overflow;

VERIFY_CHECK(ctx != NULL);
ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx));
ARG_CHECK(aggsig != NULL);
ARG_CHECK(pk != NULL);
ARG_CHECK(msg32 != NULL);

/* TODO: overflow? */
if (aggsig_size != 32*(1+n_sigs)) {
return 0;
}

/* rhs = 0 */
secp256k1_gej_set_infinity(&rhs);

/* h = hash_{HalfAggregation}(sig_1[0:32] || pk_1 || m_1 || ... || sig_n[0:32] || pk_n || m_n) */
secp256k1_sha256_initialize_tagged(&sha, (unsigned char*) "HalfAggregation", 15);
for (i = 0; i < n_sigs; i++) {
unsigned char pk_ser[32];
secp256k1_sha256_write(&sha, &aggsig[i*32], 32);
secp256k1_xonly_pubkey_serialize(ctx, pk_ser, &pk[i]);
secp256k1_sha256_write(&sha, pk_ser, 32);
secp256k1_sha256_write(&sha, msg32[i], 32);
}
secp256k1_sha256_finalize(&sha, input_hash);

for (i = 0; i < n_sigs; i++) {
secp256k1_ge P;
secp256k1_gej Pj;
secp256k1_fe rx;
secp256k1_ge R;
secp256k1_gej Rj;
unsigned char buf[32];
secp256k1_scalar e;
unsigned char z_bytes[32];
secp256k1_scalar z;

/* P_i = lift_x(int(pk)); fail if that fails */
if (!secp256k1_xonly_pubkey_load(ctx, &P, &pk[i])) {
return 0;
}
/* r_i = int(sig[i⋅32:(i+1)⋅32]); fail if r ≥ p */
if (!secp256k1_fe_set_b32(&rx, &aggsig[32*i])) {
return 0;
}
/* R_i = lift_x(r_i); fail if that fails */
if (!secp256k1_ge_set_xo_var(&R, &rx, 0)) {
return 0;
}
/* e_i = int(hash_{BIP0340/challenge}(bytes(r_i) || pk_i || m_i)) mod n */
secp256k1_fe_get_b32(buf, &P.x);
secp256k1_schnorrsig_challenge(&e, &aggsig[32*i], msg32[i], buf);

/* z_i = int(hash_{HalfAggregationIndex}(h || i)) mod n */
secp256k1_sha256_initialize_tagged(&sha, (unsigned char*) "HalfAggregationIndex", 20);
secp256k1_sha256_write(&sha, input_hash, sizeof(input_hash));
/* TODO: fix endianness (or replace by chacha anyway) and add static test vectors */
secp256k1_sha256_write(&sha, (unsigned char *)&n_sigs, sizeof(uint32_t));
secp256k1_sha256_finalize(&sha, z_bytes);
secp256k1_scalar_set_b32(&z, z_bytes, NULL);

/* rhs += z_i⋅(R_1 + e_1⋅P_1) = z_i⋅R_1 + z_i⋅e_1⋅P_1)*/
/* P = e_i⋅R */
secp256k1_gej_set_ge(&Pj, &P);
secp256k1_ecmult(&ctx->ecmult_ctx, &Pj, &Pj, &e, NULL);
/* P = P + R */
secp256k1_gej_set_ge(&Rj, &R);
secp256k1_gej_add_var(&Pj, &Pj, &Rj, NULL);
/* P = z_i⋅P */
secp256k1_ecmult(&ctx->ecmult_ctx, &Pj, &Pj, &z, NULL);
/* rhs += P */
secp256k1_gej_add_var(&rhs, &rhs, &Pj, NULL);
}
/* s = int(sig[n⋅32:(n+1)⋅32]) */
secp256k1_scalar_set_b32(&s, &aggsig[32*n_sigs], &overflow);
if (overflow) {
return 0;
}

secp256k1_scalar_set_int(&one, 1);
/* Fail if s⋅G ≠ rhs */
/* rhs = -rhs */
secp256k1_gej_neg(&rhs, &rhs);
/* rhs = rhs + sG */
secp256k1_ecmult(&ctx->ecmult_ctx, &rhs, &rhs, &one, &s);

return secp256k1_gej_is_infinity(&rhs);
}

#endif
29 changes: 29 additions & 0 deletions src/modules/schnorrsig/tests_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -789,6 +789,34 @@ 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 1
void test_schnorrsig_halfagg(void) {
unsigned char sk[N_SIGS][32];
unsigned char msg[N_SIGS][32];
unsigned char *msg_ptr[N_SIGS];
secp256k1_xonly_pubkey pk[N_SIGS];
unsigned char sig[N_SIGS][64];
unsigned char *sig_ptr[N_SIGS];
int i;

unsigned char aggsig[32 * (1 + N_SIGS)];
size_t aggsig_size = sizeof(aggsig);

for (i = 0; i < N_SIGS; i++) {
secp256k1_keypair keypair;
secp256k1_testrand256(sk[i]);
CHECK(secp256k1_keypair_create(ctx, &keypair, sk[i]));
CHECK(secp256k1_keypair_xonly_pub(ctx, &pk[i], NULL, &keypair));
secp256k1_testrand256(msg[i]);
msg_ptr[i] = msg[i];
CHECK(secp256k1_schnorrsig_sign(ctx, sig[i], msg[i], &keypair, NULL, NULL));
sig_ptr[i] = sig[i];
}
CHECK(secp256k1_schnorrsig_halfagg(ctx, aggsig, &aggsig_size, sig_ptr, pk, msg_ptr, N_SIGS) == 1);
CHECK(secp256k1_schnorrsig_halfagg_verify(ctx, aggsig, aggsig_size, pk, msg_ptr, N_SIGS) == 1);
}
#undef N_SIGS

void run_schnorrsig_tests(void) {
int i;
run_nonce_function_bip340_tests();
Expand All @@ -801,6 +829,7 @@ void run_schnorrsig_tests(void) {
test_schnorrsig_sign_verify();
}
test_schnorrsig_taproot();
test_schnorrsig_halfagg();
}

#endif

0 comments on commit 31d04c9

Please sign in to comment.