Skip to content

Commit 67c21be

Browse files
committed
frost: nonce aggregation and adaptor signatures
This commit adds nonce aggregation, as well as adaptor signatures.
1 parent 17c47e9 commit 67c21be

File tree

7 files changed

+568
-1
lines changed

7 files changed

+568
-1
lines changed

include/secp256k1_frost.h

Lines changed: 140 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ extern "C" {
1717
* (https://crysp.uwaterloo.ca/software/frost/).
1818
*
1919
* The module also supports BIP-341 ("Taproot") and BIP-32 ("ordinary") public
20-
* key tweaking.
20+
* key tweaking, and adaptor signatures.
2121
*
2222
* Following the convention used in the MuSig module, the API uses the singular
2323
* term "nonce" to refer to the two "nonces" used by the FROST scheme.
@@ -78,6 +78,16 @@ typedef struct {
7878
unsigned char data[132];
7979
} secp256k1_frost_pubnonce;
8080

81+
/** Opaque data structure that holds a FROST session.
82+
*
83+
* This structure is not required to be kept secret for the signing protocol
84+
* to be secure. Guaranteed to be 133 bytes in size. It can be safely
85+
* copied/moved. No serialization and parsing functions.
86+
*/
87+
typedef struct {
88+
unsigned char data[133];
89+
} secp256k1_frost_session;
90+
8191
/** Parse a signer's public nonce.
8292
*
8393
* Returns: 1 when the nonce could be parsed, 0 otherwise.
@@ -421,6 +431,135 @@ SECP256K1_API int secp256k1_frost_nonce_gen(
421431
const unsigned char *extra_input32
422432
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
423433

434+
/** Takes the public nonces of all signers and computes a session that is
435+
* required for signing and verification of partial signatures. The participant
436+
* IDs can be sorted before combining, but the corresponding pubnonces must be
437+
* resorted as well. All signers must use the same sorting of pubnonces,
438+
* otherwise signing will fail.
439+
*
440+
* Returns: 0 if the arguments are invalid or if some signer sent invalid
441+
* pubnonces, 1 otherwise
442+
* Args: ctx: pointer to a context object
443+
* Out: session: pointer to a struct to store the session
444+
* In: pubnonces: array of pointers to public nonces sent by the signers
445+
* n_pubnonces: number of elements in the pubnonces array. Must be
446+
* greater than 0.
447+
* msg32: the 32-byte message to sign
448+
* agg_pk: the FROST-aggregated public key
449+
* myd_id33: the 33-byte ID of the participant who will use the
450+
* session for signing
451+
* ids33: array of the 33-byte participant IDs of the signers
452+
* tweak_cache: pointer to frost_tweak_cache struct (can be NULL)
453+
* adaptor: optional pointer to an adaptor point encoded as a
454+
* public key if this signing session is part of an
455+
* adaptor signature protocol (can be NULL)
456+
*/
457+
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_frost_nonce_process(
458+
const secp256k1_context *ctx,
459+
secp256k1_frost_session *session,
460+
const secp256k1_frost_pubnonce * const *pubnonces,
461+
size_t n_pubnonces,
462+
const unsigned char *msg32,
463+
const secp256k1_xonly_pubkey *agg_pk,
464+
const unsigned char *my_id33,
465+
const unsigned char * const* ids33,
466+
const secp256k1_frost_tweak_cache *tweak_cache,
467+
const secp256k1_pubkey *adaptor
468+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6) SECP256K1_ARG_NONNULL(7) SECP256K1_ARG_NONNULL(8);
469+
470+
/** Extracts the nonce_parity bit from a session
471+
*
472+
* This is used for adaptor signatures.
473+
*
474+
* Returns: 0 if the arguments are invalid, 1 otherwise
475+
* Args: ctx: pointer to a context object
476+
* Out: nonce_parity: pointer to an integer that indicates the parity
477+
* of the aggregate public nonce. Used for adaptor
478+
* signatures.
479+
* In: session: pointer to the session that was created with
480+
* frost_nonce_process
481+
*/
482+
SECP256K1_API int secp256k1_frost_nonce_parity(
483+
const secp256k1_context *ctx,
484+
int *nonce_parity,
485+
const secp256k1_frost_session *session
486+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
487+
488+
/** Verifies that the adaptor can be extracted by combining the adaptor
489+
* pre-signature and the completed signature.
490+
*
491+
* Returns: 0 if the arguments are invalid or the adaptor signature does not
492+
* verify, 1 otherwise
493+
* Args: ctx: pointer to a context object
494+
* In: pre_sig64: 64-byte pre-signature
495+
* msg32: the 32-byte message being verified
496+
* pubkey: pointer to an x-only public key to verify with
497+
* adaptor: pointer to the adaptor point being verified
498+
* nonce_parity: the output of `frost_nonce_parity` called with the
499+
* session used for producing the pre-signature
500+
*/
501+
SECP256K1_API int secp256k1_frost_verify_adaptor(
502+
const secp256k1_context *ctx,
503+
const unsigned char *pre_sig64,
504+
const unsigned char *msg32,
505+
const secp256k1_xonly_pubkey *pubkey,
506+
const secp256k1_pubkey *adaptor,
507+
int nonce_parity
508+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5);
509+
510+
/** Creates a signature from a pre-signature and an adaptor.
511+
*
512+
* If the sec_adaptor32 argument is incorrect, the output signature will be
513+
* invalid. This function does not verify the signature.
514+
*
515+
* Returns: 0 if the arguments are invalid, or pre_sig64 or sec_adaptor32 contain
516+
* invalid (overflowing) values. 1 otherwise (which does NOT mean the
517+
* signature or the adaptor are valid!)
518+
* Args: ctx: pointer to a context object
519+
* Out: sig64: 64-byte signature. This pointer may point to the same
520+
* memory area as `pre_sig`.
521+
* In: pre_sig64: 64-byte pre-signature
522+
* sec_adaptor32: 32-byte secret adaptor to add to the pre-signature
523+
* nonce_parity: the output of `frost_nonce_parity` called with the
524+
* session used for producing the pre-signature
525+
*/
526+
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_frost_adapt(
527+
const secp256k1_context *ctx,
528+
unsigned char *sig64,
529+
const unsigned char *pre_sig64,
530+
const unsigned char *sec_adaptor32,
531+
int nonce_parity
532+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
533+
534+
/** Extracts a secret adaptor from a FROST pre-signature and corresponding
535+
* signature
536+
*
537+
* This function will not fail unless given grossly invalid data; if it is
538+
* merely given signatures that do not verify, the returned value will be
539+
* nonsense. It is therefore important that all data be verified at earlier
540+
* steps of any protocol that uses this function. In particular, this includes
541+
* verifying all partial signatures that were aggregated into pre_sig64.
542+
*
543+
* Returns: 0 if the arguments are NULL, or sig64 or pre_sig64 contain
544+
* grossly invalid (overflowing) values. 1 otherwise (which does NOT
545+
* mean the signatures or the adaptor are valid!)
546+
* Args: ctx: pointer to a context object
547+
* Out:sec_adaptor32: 32-byte secret adaptor
548+
* In: sig64: complete, valid 64-byte signature
549+
* pre_sig64: the pre-signature corresponding to sig64, i.e., the
550+
* aggregate of partial signatures without the secret
551+
* adaptor
552+
* nonce_parity: the output of `frost_nonce_parity` called with the
553+
* session used for producing sig64
554+
*/
555+
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_frost_extract_adaptor(
556+
const secp256k1_context *ctx,
557+
unsigned char *sec_adaptor32,
558+
const unsigned char *sig64,
559+
const unsigned char *pre_sig64,
560+
int nonce_parity
561+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
562+
424563
#ifdef __cplusplus
425564
}
426565
#endif

src/modules/frost/Makefile.am.include

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ noinst_HEADERS += src/modules/frost/keygen.h
44
noinst_HEADERS += src/modules/frost/keygen_impl.h
55
noinst_HEADERS += src/modules/frost/session.h
66
noinst_HEADERS += src/modules/frost/session_impl.h
7+
noinst_HEADERS += src/modules/frost/adaptor_impl.h

src/modules/frost/adaptor_impl.h

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
/***********************************************************************
2+
* Copyright (c) 2022-2024 Jesse Posner *
3+
* Distributed under the MIT software license, see the accompanying *
4+
* file COPYING or https://www.opensource.org/licenses/mit-license.php.*
5+
***********************************************************************/
6+
7+
#ifndef SECP256K1_MODULE_FROST_ADAPTOR_IMPL_H
8+
#define SECP256K1_MODULE_FROST_ADAPTOR_IMPL_H
9+
10+
#include <string.h>
11+
12+
#include "../../../include/secp256k1.h"
13+
#include "../../../include/secp256k1_frost.h"
14+
15+
#include "session.h"
16+
#include "../../scalar.h"
17+
18+
int secp256k1_frost_nonce_parity(const secp256k1_context* ctx, int *nonce_parity, const secp256k1_frost_session *session) {
19+
secp256k1_frost_session_internal session_i;
20+
VERIFY_CHECK(ctx != NULL);
21+
ARG_CHECK(nonce_parity != NULL);
22+
ARG_CHECK(session != NULL);
23+
24+
if (!secp256k1_frost_session_load(ctx, &session_i, session)) {
25+
return 0;
26+
}
27+
*nonce_parity = session_i.fin_nonce_parity;
28+
return 1;
29+
}
30+
31+
int secp256k1_frost_verify_adaptor(const secp256k1_context* ctx, const unsigned char *pre_sig64, const unsigned char *msg32, const secp256k1_xonly_pubkey *pubkey, const secp256k1_pubkey *adaptor, int nonce_parity) {
32+
secp256k1_scalar s;
33+
secp256k1_scalar e;
34+
secp256k1_gej rj;
35+
secp256k1_ge pk;
36+
secp256k1_gej pkj;
37+
secp256k1_ge r;
38+
unsigned char buf[32];
39+
int overflow;
40+
secp256k1_ge adaptorp;
41+
secp256k1_xonly_pubkey noncepk;
42+
secp256k1_gej fin_nonce_ptj;
43+
44+
VERIFY_CHECK(ctx != NULL);
45+
ARG_CHECK(pre_sig64 != NULL);
46+
ARG_CHECK(msg32 != NULL);
47+
ARG_CHECK(pubkey != NULL);
48+
ARG_CHECK(adaptor != NULL);
49+
ARG_CHECK(nonce_parity == 0 || nonce_parity == 1);
50+
51+
if (!secp256k1_xonly_pubkey_parse(ctx, &noncepk, &pre_sig64[0])) {
52+
return 0;
53+
}
54+
if (!secp256k1_xonly_pubkey_load(ctx, &r, &noncepk)) {
55+
return 0;
56+
}
57+
if (!secp256k1_pubkey_load(ctx, &adaptorp, adaptor)) {
58+
return 0;
59+
}
60+
if (!nonce_parity) {
61+
secp256k1_ge_neg(&adaptorp, &adaptorp);
62+
}
63+
secp256k1_gej_set_ge(&fin_nonce_ptj, &adaptorp);
64+
secp256k1_gej_add_ge_var(&fin_nonce_ptj, &fin_nonce_ptj, &r, NULL);
65+
if (secp256k1_gej_is_infinity(&fin_nonce_ptj)) {
66+
/* unreachable with overwhelming probability */
67+
return 0;
68+
}
69+
70+
secp256k1_scalar_set_b32(&s, &pre_sig64[32], &overflow);
71+
if (overflow) {
72+
return 0;
73+
}
74+
75+
if (!secp256k1_xonly_pubkey_load(ctx, &pk, pubkey)) {
76+
return 0;
77+
}
78+
79+
/* Compute e. */
80+
secp256k1_fe_get_b32(buf, &pk.x);
81+
secp256k1_schnorrsig_challenge(&e, &pre_sig64[0], msg32, 32, buf);
82+
83+
/* Compute rj = s*G + (-e)*pkj */
84+
secp256k1_scalar_negate(&e, &e);
85+
secp256k1_gej_set_ge(&pkj, &pk);
86+
secp256k1_ecmult(&rj, &pkj, &e, &s);
87+
88+
/* secp256k1_ge_set_gej_var(&r, &rj); */
89+
if (secp256k1_gej_is_infinity(&rj)) {
90+
return 0;
91+
}
92+
93+
secp256k1_gej_neg(&rj, &rj);
94+
secp256k1_gej_add_var(&rj, &rj, &fin_nonce_ptj, NULL);
95+
return secp256k1_gej_is_infinity(&rj);
96+
}
97+
98+
int secp256k1_frost_adapt(const secp256k1_context* ctx, unsigned char *sig64, const unsigned char *pre_sig64, const unsigned char *sec_adaptor32, int nonce_parity) {
99+
secp256k1_scalar s;
100+
secp256k1_scalar t;
101+
int overflow;
102+
int ret = 1;
103+
104+
VERIFY_CHECK(ctx != NULL);
105+
ARG_CHECK(sig64 != NULL);
106+
ARG_CHECK(pre_sig64 != NULL);
107+
ARG_CHECK(sec_adaptor32 != NULL);
108+
ARG_CHECK(nonce_parity == 0 || nonce_parity == 1);
109+
110+
secp256k1_scalar_set_b32(&s, &pre_sig64[32], &overflow);
111+
if (overflow) {
112+
return 0;
113+
}
114+
secp256k1_scalar_set_b32(&t, sec_adaptor32, &overflow);
115+
ret &= !overflow;
116+
117+
/* Determine if the secret adaptor should be negated.
118+
*
119+
* The frost_session stores the X-coordinate and the parity of the "final nonce"
120+
* (r + t)*G, where r*G is the aggregate public nonce and t is the secret adaptor.
121+
*
122+
* Since a BIP340 signature requires an x-only public nonce, in the case where
123+
* (r + t)*G has odd Y-coordinate (i.e. nonce_parity == 1), the x-only public nonce
124+
* corresponding to the signature is actually (-r - t)*G. Thus adapting a
125+
* pre-signature requires negating t in this case.
126+
*/
127+
if (nonce_parity) {
128+
secp256k1_scalar_negate(&t, &t);
129+
}
130+
131+
secp256k1_scalar_add(&s, &s, &t);
132+
secp256k1_scalar_get_b32(&sig64[32], &s);
133+
memmove(sig64, pre_sig64, 32);
134+
secp256k1_scalar_clear(&t);
135+
return ret;
136+
}
137+
138+
int secp256k1_frost_extract_adaptor(const secp256k1_context* ctx, unsigned char *sec_adaptor32, const unsigned char *sig64, const unsigned char *pre_sig64, int nonce_parity) {
139+
secp256k1_scalar t;
140+
secp256k1_scalar s;
141+
int overflow;
142+
int ret = 1;
143+
144+
VERIFY_CHECK(ctx != NULL);
145+
ARG_CHECK(sec_adaptor32 != NULL);
146+
ARG_CHECK(sig64 != NULL);
147+
ARG_CHECK(pre_sig64 != NULL);
148+
ARG_CHECK(nonce_parity == 0 || nonce_parity == 1);
149+
150+
secp256k1_scalar_set_b32(&t, &sig64[32], &overflow);
151+
ret &= !overflow;
152+
secp256k1_scalar_negate(&t, &t);
153+
154+
secp256k1_scalar_set_b32(&s, &pre_sig64[32], &overflow);
155+
if (overflow) {
156+
return 0;
157+
}
158+
secp256k1_scalar_add(&t, &t, &s);
159+
160+
if (!nonce_parity) {
161+
secp256k1_scalar_negate(&t, &t);
162+
}
163+
secp256k1_scalar_get_b32(sec_adaptor32, &t);
164+
secp256k1_scalar_clear(&t);
165+
return ret;
166+
}
167+
168+
#endif

src/modules/frost/keygen.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ typedef struct {
1919
int parity_acc;
2020
} secp256k1_tweak_cache_internal;
2121

22+
static int secp256k1_tweak_cache_load(const secp256k1_context* ctx, secp256k1_tweak_cache_internal *cache_i, const secp256k1_frost_tweak_cache *cache);
23+
2224
static int secp256k1_frost_share_load(const secp256k1_context* ctx, secp256k1_scalar *s, const secp256k1_frost_share* share);
2325

26+
static int secp256k1_frost_compute_indexhash(secp256k1_scalar *indexhash, const unsigned char *id33);
27+
2428
#endif

src/modules/frost/main_impl.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,6 @@
99

1010
#include "keygen_impl.h"
1111
#include "session_impl.h"
12+
#include "adaptor_impl.h"
1213

1314
#endif

src/modules/frost/session.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,19 @@
77
#ifndef SECP256K1_MODULE_FROST_SESSION_H
88
#define SECP256K1_MODULE_FROST_SESSION_H
99

10+
#include "../../../include/secp256k1.h"
11+
#include "../../../include/secp256k1_frost.h"
12+
13+
#include "../../scalar.h"
14+
15+
typedef struct {
16+
int fin_nonce_parity;
17+
unsigned char fin_nonce[32];
18+
secp256k1_scalar noncecoef;
19+
secp256k1_scalar challenge;
20+
secp256k1_scalar s_part;
21+
} secp256k1_frost_session_internal;
22+
23+
static int secp256k1_frost_session_load(const secp256k1_context* ctx, secp256k1_frost_session_internal *session_i, const secp256k1_frost_session *session);
24+
1025
#endif

0 commit comments

Comments
 (0)