Skip to content

Commit 6cb7cac

Browse files
committed
Add Schnorr Signature Half Aggregation
1 parent f3708a1 commit 6cb7cac

File tree

3 files changed

+211
-0
lines changed

3 files changed

+211
-0
lines changed

include/secp256k1_schnorrsig.h

+21
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
#ifndef SECP256K1_SCHNORRSIG_H
22
#define SECP256K1_SCHNORRSIG_H
33

4+
#include <stdint.h>
5+
46
#include "secp256k1.h"
57
#include "secp256k1_extrakeys.h"
68

@@ -104,6 +106,25 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorrsig_verify(
104106
const secp256k1_xonly_pubkey *pubkey
105107
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
106108

109+
SECP256K1_API int secp256k1_schnorrsig_aggregate(
110+
const secp256k1_context* ctx,
111+
unsigned char* aggsig,
112+
size_t* aggsig_size,
113+
unsigned char **sig64,
114+
unsigned char **msg32,
115+
secp256k1_xonly_pubkey *pubkey,
116+
uint32_t n_sigs
117+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6);
118+
119+
SECP256K1_API int secp256k1_schnorrsig_aggverify(
120+
const secp256k1_context* ctx,
121+
unsigned char **msg32,
122+
uint32_t n_msgs,
123+
secp256k1_xonly_pubkey *pubkey,
124+
unsigned char *aggsig,
125+
size_t aggsig_size
126+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5);
127+
107128
#ifdef __cplusplus
108129
}
109130
#endif

src/modules/schnorrsig/main_impl.h

+159
Original file line numberDiff line numberDiff line change
@@ -236,4 +236,163 @@ int secp256k1_schnorrsig_verify(const secp256k1_context* ctx, const unsigned cha
236236
secp256k1_fe_equal_var(&rx, &r.x);
237237
}
238238

239+
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) {
240+
secp256k1_sha256 hash;
241+
uint32_t i;
242+
243+
VERIFY_CHECK((aggsig != NULL) != (sig64 != NULL));
244+
245+
/* TODO: use actual tagged hash */
246+
secp256k1_sha256_initialize(&hash);
247+
/* z_i = int(hash_{HalfAggregation}(r_1 || pk_1 || m_1 || ... || r_n || pk_n || m_n || i)) mod n */
248+
for (i = 0; i < n_sigs; i++) {
249+
unsigned char pk_ser[32];
250+
251+
/* r_i = sig_i[0:32] */
252+
if (sig64 != NULL) {
253+
secp256k1_sha256_write(&hash, sig64[i], 32);
254+
} else {
255+
/* aggsig != NULL */
256+
secp256k1_sha256_write(&hash, &aggsig[i*32], 32);
257+
}
258+
if (!secp256k1_xonly_pubkey_serialize(ctx, pk_ser, &pubkey[i])) {
259+
return 0;
260+
}
261+
secp256k1_sha256_write(&hash, pk_ser, sizeof(pk_ser));
262+
secp256k1_sha256_write(&hash, msg32[i], 32);
263+
}
264+
/* TODO: copy midstate instead of computing intermediate "midhash" */
265+
secp256k1_sha256_finalize(&hash, midhash);
266+
return 1;
267+
}
268+
269+
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) {
270+
uint32_t i;
271+
secp256k1_sha256 hash;
272+
unsigned char midhash[32];
273+
secp256k1_scalar s;
274+
275+
VERIFY_CHECK(ctx != NULL);
276+
ARG_CHECK(aggsig != NULL);
277+
ARG_CHECK(aggsig_size != NULL);
278+
ARG_CHECK(sig64 != NULL);
279+
ARG_CHECK(msg32 != NULL);
280+
ARG_CHECK(pubkey != NULL);
281+
282+
if (*aggsig_size < 32*(1 + n_sigs)) {
283+
return 0;
284+
}
285+
286+
if (!compute_midhash(ctx, midhash, sig64, NULL, msg32, pubkey, n_sigs)) {
287+
return 0;
288+
}
289+
290+
/* s = z_1⋅s_1 + ... + z_n⋅s_n */
291+
secp256k1_scalar_set_int(&s, 0);
292+
for (i = 0; i < n_sigs; i++) {
293+
unsigned char randomizer[32];
294+
secp256k1_scalar zi;
295+
secp256k1_scalar si;
296+
297+
secp256k1_sha256_initialize(&hash);
298+
secp256k1_sha256_write(&hash, midhash, sizeof(midhash));
299+
/* TODO: fix endianness issue */
300+
secp256k1_sha256_write(&hash, (unsigned char *)&i, sizeof(i));
301+
secp256k1_sha256_finalize(&hash, randomizer);
302+
secp256k1_scalar_set_b32(&zi, randomizer, NULL);
303+
304+
secp256k1_scalar_set_b32(&si, &sig64[i][32], NULL);
305+
secp256k1_scalar_mul(&si, &si, &zi);
306+
secp256k1_scalar_add(&s, &s, &si);
307+
308+
memcpy(&aggsig[i*32], sig64[i], 32);
309+
}
310+
311+
/* Return sig = r_1 || ... || r_n || bytes(s) */
312+
secp256k1_scalar_get_b32(&aggsig[n_sigs*32], &s);
313+
*aggsig_size = 32 * (1 + n_sigs);
314+
315+
return 1;
316+
}
317+
318+
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) {
319+
unsigned char midhash[32];
320+
uint32_t i;
321+
secp256k1_gej lhs, rhs;
322+
secp256k1_scalar s;
323+
int overflow;
324+
325+
VERIFY_CHECK(ctx != NULL);
326+
ARG_CHECK(msg32 != NULL);
327+
ARG_CHECK(pubkey != NULL);
328+
ARG_CHECK(aggsig != NULL);
329+
330+
secp256k1_gej_set_infinity(&rhs);
331+
if (aggsig_size != 32*(1 + n_msgs)) {
332+
return 0;
333+
}
334+
335+
if (!compute_midhash(ctx, midhash, NULL, aggsig, msg32, pubkey, n_msgs)) {
336+
return 0;
337+
}
338+
339+
for (i = 0; i < n_msgs; i++) {
340+
secp256k1_sha256 hash;
341+
unsigned char randomizer[32];
342+
secp256k1_scalar zi;
343+
secp256k1_fe rx;
344+
secp256k1_ge rp, pp;
345+
secp256k1_scalar ei;
346+
unsigned char pk_ser[32];
347+
secp256k1_gej ppj, acc;
348+
349+
secp256k1_sha256_initialize(&hash);
350+
secp256k1_sha256_write(&hash, midhash, sizeof(midhash));
351+
/* TODO: fix endianness issue */
352+
secp256k1_sha256_write(&hash, (unsigned char *)&i, sizeof(i));
353+
secp256k1_sha256_finalize(&hash, randomizer);
354+
secp256k1_scalar_set_b32(&zi, randomizer, NULL);
355+
356+
/* R_i = lift_x(int(r_i)); fail if that fails */
357+
if (!secp256k1_fe_set_b32(&rx, &aggsig[i*32])) {
358+
return 0;
359+
}
360+
if (!secp256k1_ge_set_xo_var(&rp, &rx, 0)) {
361+
return 0;
362+
}
363+
364+
/* P_i = lift_x(int(pk_i)); fail if that fails */
365+
if (!secp256k1_xonly_pubkey_load(ctx, &pp, &pubkey[i])) {
366+
return 0;
367+
}
368+
369+
/* e_i = int(hash_{BIP0340/challenge}(bytes(r_i) || pk_i || m_i)) mod n */
370+
/* TODO: compute pubkey serialization again after compute_midhash? */
371+
if (!secp256k1_xonly_pubkey_serialize(ctx, pk_ser, &pubkey[i])) {
372+
return 0;
373+
}
374+
secp256k1_schnorrsig_challenge(&ei, &aggsig[i*32], msg32[i], pk_ser);
375+
secp256k1_gej_set_ge(&ppj, &pp);
376+
/* e_i⋅P_i */
377+
secp256k1_ecmult(&ctx->ecmult_ctx, &acc, &ppj, &ei, NULL);
378+
/* R_i + e_i⋅P_i */
379+
secp256k1_gej_add_ge_var(&acc, &acc, &rp, NULL);
380+
/* z_i⋅(R_i + e_i⋅P_i) */
381+
secp256k1_ecmult(&ctx->ecmult_ctx, &acc, &acc, &zi, NULL);
382+
secp256k1_gej_add_var(&rhs, &rhs, &acc, NULL);
383+
}
384+
/* s = int(sig[n⋅32:(n+1)⋅32]) */
385+
secp256k1_scalar_set_b32(&s, &aggsig[n_msgs*32], &overflow);
386+
if (overflow) {
387+
return 0;
388+
}
389+
/* s⋅G */
390+
secp256k1_ecmult(&ctx->ecmult_ctx, &lhs, NULL, &secp256k1_scalar_zero, &s);
391+
392+
/* lhs ?= rhs */
393+
secp256k1_gej_neg(&lhs, &lhs);
394+
secp256k1_gej_add_var(&lhs, &lhs, &rhs, NULL);
395+
return secp256k1_gej_is_infinity(&lhs);
396+
}
397+
239398
#endif

src/modules/schnorrsig/tests_impl.h

+31
Original file line numberDiff line numberDiff line change
@@ -789,6 +789,36 @@ void test_schnorrsig_taproot(void) {
789789
CHECK(secp256k1_xonly_pubkey_tweak_add_check(ctx, output_pk_bytes, pk_parity, &internal_pk, tweak) == 1);
790790
}
791791

792+
793+
#define N_SIGS 3
794+
void test_schnorrsig_aggregate(void) {
795+
unsigned char aggsig[32*(N_SIGS + 1)];
796+
size_t aggsig_size = sizeof(aggsig);
797+
unsigned char sig64[N_SIGS][64];
798+
unsigned char *sig64_ptr[N_SIGS];
799+
unsigned char msg32[N_SIGS][32];
800+
unsigned char *msg32_ptr[N_SIGS];
801+
secp256k1_xonly_pubkey pubkey[N_SIGS];
802+
int i;
803+
804+
for (i = 0; i < N_SIGS; i++) {
805+
unsigned char sk[32];
806+
secp256k1_keypair keypair;
807+
808+
msg32_ptr[i] = &msg32[i][0];
809+
sig64_ptr[i] = &sig64[i][0];
810+
secp256k1_testrand256(sk);
811+
secp256k1_testrand256(msg32[i]);
812+
813+
CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1);
814+
CHECK(secp256k1_keypair_xonly_pub(ctx, &pubkey[i], NULL, &keypair));
815+
CHECK(secp256k1_schnorrsig_sign(ctx, sig64[i], msg32[i], &keypair, NULL, NULL));
816+
}
817+
818+
CHECK(secp256k1_schnorrsig_aggregate(ctx, aggsig, &aggsig_size, sig64_ptr, msg32_ptr, pubkey, N_SIGS));
819+
CHECK(secp256k1_schnorrsig_aggverify(ctx, msg32_ptr, N_SIGS, pubkey, aggsig, aggsig_size));
820+
}
821+
792822
void run_schnorrsig_tests(void) {
793823
int i;
794824
run_nonce_function_bip340_tests();
@@ -801,6 +831,7 @@ void run_schnorrsig_tests(void) {
801831
test_schnorrsig_sign_verify();
802832
}
803833
test_schnorrsig_taproot();
834+
test_schnorrsig_aggregate();
804835
}
805836

806837
#endif

0 commit comments

Comments
 (0)