Skip to content

Commit

Permalink
Incremental Half-Aggregation for Schnorr Signatures.
Browse files Browse the repository at this point in the history
  • Loading branch information
b-wagn committed Jul 31, 2023
1 parent bfeae12 commit 0495479
Show file tree
Hide file tree
Showing 4 changed files with 424 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ schnorr_example
*.log
*.trs

.vscode/

Makefile
configure
.libs/
Expand Down
75 changes: 75 additions & 0 deletions include/secp256k1_schnorrsig.h
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,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_sigs=n_sigs_before+n_sigs_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_sigs=n_sigs_before+n_sigs_new many messages.
* new_sigs64: Array of 64-byte signatures, containing the new signatures that should be added.
* Assumed to contain n_sigs_new many signatures.
* n_sigs_before: Number of signatures that are already "contained" in the aggregate signature
* n_sigs_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_sigs_before,
size_t n_sigs_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_sigs many public keys.
* msgs32: Array of 32-byte messages. Assumed to contain n_sigs many messages.
* sigs64: Array of 64-byte signatures. Assumed to contain n_sigs many signatures.
* n_sigs: 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_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);



/** Verify a (Half-)aggregate Schnorr signature.
*
* Returns: 1: correct signature
* 2: incorrect signature.
* Args: ctx: a secp256k1 context object.
* In: pubkeys: Array of x-only public keys. Assume to contain n_sigs many public keys.
* msgs32: Array of 32-byte messages. Assumed to contain n_sigs many messages.
* n_sigs: 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_sigs,
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
Expand Down
288 changes: 288 additions & 0 deletions src/modules/schnorrsig/main_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -264,4 +264,292 @@ 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;
}

/**
* Internal helper function for half-aggregation:
* Computes the coefficients zi for i=starting_from to i=n_sigs-1
* used for aggregation for a given sequence of messages, keys and and r's.
* zs should have space for n_sigs-starting_from many scalars.
* Note: We pass pointers to the rs to avoid having to copy them twice
*/
static int compute_aggregation_coefficients(
secp256k1_scalar* zs,
const unsigned char* pubkeys_ser,
const unsigned char** rs,
const unsigned char* msgs32,
size_t starting_from,
size_t n_sigs) {


uint32_t i;
secp256k1_sha256 hash;

if (n_sigs <= starting_from) {
return 0;
}

/* Write common prefix pre into tagged hash */
/* Let n' = starting_from */
/* pre = (r_0 || pk_0 || mes_0 || .... || r_{n'-1} || pk_{n'-1} || mes_{n'-1}) */
secp256k1_schnorrsig_sha256_tagged_aggregation(&hash);
for (i = 0; i < starting_from; ++i) {
/* write r_i */
secp256k1_sha256_write(&hash, rs[i], 32);
/* write pk_i */
secp256k1_sha256_write(&hash, &pubkeys_ser[i*32], 32);
/* write mes_i*/
secp256k1_sha256_write(&hash, &msgs32[i*32], 32);
}

/* zi = Hash( pre || r_{n'} || pk_{n'} || mes_{n'} || ... || r_{i} || pk_{i} || mes_{i} ) */
for (i = starting_from; i < n_sigs; ++i) {
unsigned char hashoutput[32];
secp256k1_sha256 hashcopy;
/* Write into hash r_i, pk_i, mes_i */
secp256k1_sha256_write(&hash, rs[i], 32);
secp256k1_sha256_write(&hash, &pubkeys_ser[i*32], 32);
secp256k1_sha256_write(&hash, &msgs32[i*32], 32);
/* Copy the hash */
memcpy(&hashcopy,&hash,sizeof(hash));
/* Finalize the copy to get zi*/
secp256k1_sha256_finalize(&hashcopy, hashoutput);
secp256k1_scalar_set_b32(&zs[i-starting_from], hashoutput, NULL);
}
return 1;
}


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_sigs_before, size_t n_sigs_new) {
uint32_t i;
uint32_t n_sigs;
int overflow;
secp256k1_scalar s;
unsigned char* pubkeys_ser; /* this will store serialized pubkeys */
secp256k1_scalar* zs; /* this will store the aggregation coefficients */
const unsigned char** rs; /* this will store pointers to the r's in the aggsig */


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_sigs+1) */
n_sigs = n_sigs_before + n_sigs_new;
if ((*aggsig_size >> 5) <= 0 || ((*aggsig_size >> 5)-1) < n_sigs) {
return 0;
}

/* Serialize public keys. */
/* We need that for the aggregation coefficients */
pubkeys_ser = (unsigned char*) checked_malloc(&ctx->error_callback, 32*n_sigs);
for (i = 0; i < n_sigs; ++i) {
if (!secp256k1_xonly_pubkey_serialize(ctx, &pubkeys_ser[i*32], &all_pubkeys[i])) {
free(pubkeys_ser);
return 0;
}
}

/* Compute new aggregation coefficients zi*/
zs = (secp256k1_scalar*) checked_malloc(&ctx->error_callback, n_sigs_new*sizeof(secp256k1_scalar));
rs = (const unsigned char**) checked_malloc(&ctx->error_callback, n_sigs*sizeof(unsigned char*));
for (i = 0; i < n_sigs_before; ++i) {
rs[i] = &aggsig[i*32];
}
for (i = 0; i < n_sigs_new; ++i) {
rs[i+n_sigs_before] = &new_sigs64[i*64];
}
if (!compute_aggregation_coefficients(zs, pubkeys_ser, rs, all_msgs32, n_sigs_before, n_sigs)) {
free(pubkeys_ser);
free(zs);
free(rs);
return 0;
}

/* Compute s = s_old + sum_{i = n_sigs_before}^{n_sigs} z_i*s_i */
if (n_sigs_before == 0) {
secp256k1_scalar_set_int(&s, 0);
} else {
secp256k1_scalar_set_b32(&s, &aggsig[n_sigs_before*32], &overflow);
if (overflow) {
free(pubkeys_ser);
free(zs);
free(rs);
return 0;
}
}
for (i = n_sigs_before; i < n_sigs; ++i) {
secp256k1_scalar si;
int pos;
pos = i-n_sigs_before;
secp256k1_scalar_set_b32(&si, &new_sigs64[pos*64+32], NULL);
secp256k1_scalar_mul(&si, &si, &zs[pos]);
secp256k1_scalar_add(&s, &s, &si);
}

/* copy new rs into aggsig */
for (i = n_sigs_before; i < n_sigs; ++i) {
int pos;
pos = i-n_sigs_before;
memcpy(&aggsig[i*32], &new_sigs64[pos*64], 32);
}

/* copy new s into aggsig */
secp256k1_scalar_get_b32(&aggsig[n_sigs*32], &s);
*aggsig_size = 32 * (1 + n_sigs);

free(pubkeys_ser);
free(zs);
free(rs);

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_sigs) {
if (!secp256k1_schnorrsig_inc_aggregate(ctx, aggsig, aggsig_size, pubkeys, msgs32, sigs64, 0, n_sigs)) {
return 0;
}
return 1;
}

int secp256k1_schnorrsig_aggverify(const secp256k1_context* ctx, const secp256k1_xonly_pubkey* pubkeys, const unsigned char* msgs32, size_t n_sigs, const unsigned char* aggsig, size_t aggsig_size) {
uint32_t i;
secp256k1_gej lhs, rhs;
secp256k1_scalar s;
int overflow, result;
unsigned char* pubkeys_ser; /* this will store serialized pubkeys */
secp256k1_scalar* zs; /* this will store the aggregation coefficients */
const unsigned char** rs; /* this will store pointers to the r's in the aggsig */
secp256k1_ge* ts; /* this will store values R_i + e_i*P_i */

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_sigs+1) */
if ((aggsig_size >> 5) <= 0 || ((aggsig_size >> 5)-1) != n_sigs) {
return 0;
}
/* Serialize public keys. */
/* We need that for the aggregation coefficients and for the Schnorr challenges */
pubkeys_ser = (unsigned char*) checked_malloc(&ctx->error_callback, 32*n_sigs);
for (i = 0; i< n_sigs; ++i) {
if (!secp256k1_xonly_pubkey_serialize(ctx, &pubkeys_ser[i*32], &pubkeys[i])) {
free(pubkeys_ser);
return 0;
}
}

/* Compute aggregation coefficients zi */
zs = (secp256k1_scalar*) checked_malloc(&ctx->error_callback,n_sigs*sizeof(secp256k1_scalar));
rs = (const unsigned char**) checked_malloc(&ctx->error_callback,n_sigs*sizeof(unsigned char*));
for (i = 0; i< n_sigs; ++i) {
rs[i] = &aggsig[i*32];
}
if (!compute_aggregation_coefficients(zs,pubkeys_ser,rs,msgs32,0,n_sigs)) {
free(pubkeys_ser);
free(zs);
free(rs);
return 0;
}

/* For each i in 0,.., n_sigs-1, we compute T_i = R_i+e_i*P_i */
ts = (secp256k1_ge*) checked_malloc(&ctx->error_callback, n_sigs*sizeof(secp256k1_ge));

for (i = 0; i < n_sigs; ++i) {
secp256k1_fe rx;
secp256k1_ge rp, pp;
secp256k1_scalar ei;
secp256k1_gej ppj, acc;

/* R_i = lift_x(int(r_i)); fail if that fails */
if (!secp256k1_fe_set_b32(&rx, &aggsig[i*32])) {
free(pubkeys_ser);
free(zs);
free(rs);
free(ts);
return 0;
}
if (!secp256k1_ge_set_xo_var(&rp, &rx, 0)) {
free(pubkeys_ser);
free(zs);
free(rs);
free(ts);
return 0;
}
/* P_i = lift_x(int(pk_i)); fail if that fails */
if (!secp256k1_xonly_pubkey_load(ctx, &pp, &pubkeys[i])) {
free(pubkeys_ser);
free(zs);
free(rs);
free(ts);
return 0;
}
/* 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, &pubkeys_ser[i*32]);
secp256k1_gej_set_ge(&ppj, &pp);
/* e_i⋅P_i */
secp256k1_ecmult(&acc, &ppj, &ei, NULL);
/* R_i + e_i⋅P_i */
secp256k1_gej_add_ge_var(&acc, &acc, &rp, NULL);
/* Write into T_i */
secp256k1_ge_set_gej_var(&ts[i], &acc);
}

/* Compute the rhs as rhs = sum_{i} z_i*T_i */
/* Later, this should be a multi-exp */
secp256k1_gej_set_infinity(&rhs);
for (i = 0; i < n_sigs; ++i) {
secp256k1_gej acc;
secp256k1_gej_set_ge(&acc, &ts[i]);
secp256k1_ecmult(&acc, &acc, &zs[i], NULL);
secp256k1_gej_add_var(&rhs, &rhs, &acc, NULL);
}

/* Compute the lhs as lhs = s*G */
secp256k1_scalar_set_b32(&s, &aggsig[n_sigs*32], &overflow);
if (overflow) {
free(pubkeys_ser);
free(zs);
free(rs);
free(ts);
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);
result = secp256k1_gej_is_infinity(&lhs);

free(pubkeys_ser);
free(zs);
free(rs);
free(ts);

return result;
}

#endif

0 comments on commit 0495479

Please sign in to comment.