Skip to content

Commit 808dc8a

Browse files
committed
schnorr_adaptor: add adaptor signature APIs
This commit adds the Schnorr adaptor signature APIs: - adaptor_presign Creates a pre-signature for a given message and adaptor point. - adaptor_extract Extracts the adaptor point from a pre-signature. - adaptor_adapt Adapts the pre-signature to produce a BIP-340 Schnorr signature. - adaptor_extract_sec Extracts the secret adaptor (discrete logarithm of adaptor point) from a pre-signature and the corresponding BIP-340 signature.
1 parent 6d51696 commit 808dc8a

File tree

2 files changed

+371
-0
lines changed

2 files changed

+371
-0
lines changed

include/secp256k1_schnorr_adaptor.h

+103
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,109 @@ typedef int (*secp256k1_nonce_function_hardened_schnorr_adaptor)(
105105
*/
106106
SECP256K1_API const secp256k1_nonce_function_hardened_schnorr_adaptor secp256k1_nonce_function_schnorr_adaptor;
107107

108+
/** Creates a pre-signature for a given message and adaptor point.
109+
*
110+
* The pre-signature can be converted into a valid BIP-340 Schnorr signature
111+
* (using `schnorr_adaptor_adapt`) by combining it with the discrete logarithm
112+
* of the adaptor point.
113+
*
114+
* This function only signs 32-byte messages. If you have messages of a
115+
* different size (or the same size but without a context-specific tag
116+
* prefix), it is recommended to create a 32-byte message hash with
117+
* secp256k1_tagged_sha256 and then sign the hash. Tagged hashing allows
118+
* providing an context-specific tag for domain separation. This prevents
119+
* signatures from being valid in multiple contexts by accident.
120+
*
121+
* Returns 1 on success, 0 on failure.
122+
* Args: ctx: pointer to a context object (not secp256k1_context_static).
123+
* Out: pre_sig65: pointer to a 65-byte array to store the pre-signature.
124+
* In: msg32: the 32-byte message being signed.
125+
* keypair: pointer to an initialized keypair.
126+
* adaptor: pointer to an adaptor point encoded as a public key.
127+
* aux_rand32: pointer to arbitrary data used by the nonce generation
128+
* function (can be NULL). If it is non-NULL and
129+
* secp256k1_nonce_function_schnorr_adaptor is used, then
130+
* aux_rand32 must be a pointer to 32-byte auxiliary randomness
131+
* as per BIP-340.
132+
*/
133+
SECP256K1_API int secp256k1_schnorr_adaptor_presign(
134+
const secp256k1_context *ctx,
135+
unsigned char *pre_sig65,
136+
const unsigned char *msg32,
137+
const secp256k1_keypair *keypair,
138+
const secp256k1_pubkey *adaptor,
139+
const unsigned char *aux_rand32
140+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5);
141+
142+
/** Extracts the adaptor point from a pre-signature.
143+
*
144+
* This function assumes that pre_sig65 was created using the corresponding
145+
* msg32, pubkey, and a valid adaptor point, which it will extract. If these
146+
* inputs are not related (e.g., if pre_sig65 was generated with a different
147+
* key or message), the extracted adaptor point will be incorrect. However,
148+
* the function will still return 1 to indicate a successful extraction.
149+
*
150+
* Returns 1 on success, 0 on failure.
151+
* Args: ctx: pointer to a context object.
152+
* Out: adaptor: pointer to store the adaptor point.
153+
* In: pre_sig65: pointer to a 65-byte pre-signature.
154+
* msg32: the 32-byte message associated with presig_65
155+
* pubkey: pointer to the x-only public key associated with pre_sig65
156+
*/
157+
SECP256K1_API int secp256k1_schnorr_adaptor_extract(
158+
const secp256k1_context *ctx,
159+
secp256k1_pubkey *adaptor,
160+
const unsigned char *pre_sig65,
161+
const unsigned char *msg32,
162+
const secp256k1_xonly_pubkey *pubkey
163+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5);
164+
165+
/** Adapts the pre-signature to produce a BIP-340 Schnorr signature.
166+
*
167+
* The output BIP-340 signature is not verified by this function.
168+
* To verify it, use `secp256k1_schnorrsig_verify`.
169+
*
170+
* If the pre_sig65 and sec_adaptor32 values are not related, the
171+
* output signature will be invalid. In this case, the function will
172+
* still return 1 to indicate successful execution.
173+
*
174+
* Returns 1 on success, 0 on failure.
175+
* Args: ctx: pointer to a context object.
176+
* Out: sig64: pointer to a 64-byte array to store the adapted
177+
* pre-signature. This pointer may point to the same
178+
* memory area as `pre_sig65`.
179+
* In: pre_sig65: pointer to a 65-byte pre-signature.
180+
* sec_adaptor32: pointer to a 32-byte secret adaptor associated with pre_sig65
181+
*/
182+
SECP256K1_API int secp256k1_schnorr_adaptor_adapt(
183+
const secp256k1_context *ctx,
184+
unsigned char *sig64,
185+
const unsigned char *pre_sig65,
186+
const unsigned char *sec_adaptor32
187+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
188+
189+
/** Extracts the secret adaptor (discrete logarithm of the adaptor point)
190+
* from a pre-signature and the corresponding BIP-340 signature.
191+
*
192+
* This function assumes that the sig64 was created by adapting pre_sig65.
193+
* If these inputs are not related, the extracted secret adaptor will be
194+
* incorrect. However, the function will still return 1 to indicate successful
195+
* extraction.
196+
*
197+
* Returns 1 on success, 0 on failure.
198+
* Args: ctx: pointer to a context object.
199+
* Out: sec_adaptor32: pointer to a 32-byte array to store the secret adaptor.
200+
* In: pre_sig65: pointer to a 65-byte pre-signature.
201+
* sig64: pointer to a valid 64-byte BIP-340 Schnorr signature
202+
* associated with pre_sig65.
203+
*/
204+
SECP256K1_API int secp256k1_schnorr_adaptor_extract_sec(
205+
const secp256k1_context *ctx,
206+
unsigned char *sec_adaptor32,
207+
const unsigned char *pre_sig65,
208+
const unsigned char *sig64
209+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
210+
108211
#ifdef __cplusplus
109212
}
110213
#endif

src/modules/schnorr_adaptor/main_impl.h

+268
Original file line numberDiff line numberDiff line change
@@ -99,4 +99,272 @@ static int nonce_function_schnorr_adaptor(unsigned char *nonce32, const unsigned
9999

100100
const secp256k1_nonce_function_hardened_schnorr_adaptor secp256k1_nonce_function_schnorr_adaptor = nonce_function_schnorr_adaptor;
101101

102+
static int secp256k1_schnorr_adaptor_presign_internal(const secp256k1_context *ctx, unsigned char *pre_sig65, const unsigned char *msg32, const secp256k1_keypair *keypair, const secp256k1_pubkey *adaptor, secp256k1_nonce_function_hardened_schnorr_adaptor noncefp, void *ndata) {
103+
secp256k1_scalar sk;
104+
secp256k1_scalar e;
105+
secp256k1_scalar k;
106+
secp256k1_gej rj, rpj;
107+
secp256k1_ge r, rp;
108+
secp256k1_ge pk;
109+
secp256k1_ge adaptor_ge;
110+
unsigned char nonce32[32] = { 0 };
111+
unsigned char pk_buf[32];
112+
unsigned char seckey[32];
113+
unsigned char adaptor_buff[33];
114+
size_t cmprssd_len = 33; /* for serializing `adaptor_ge` and `pre_sig65` */
115+
int serialize_ret = 0;
116+
int ret = 1;
117+
118+
VERIFY_CHECK(ctx != NULL);
119+
ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
120+
ARG_CHECK(pre_sig65 != NULL);
121+
ARG_CHECK(msg32 != NULL);
122+
ARG_CHECK(keypair != NULL);
123+
ARG_CHECK(adaptor != NULL);
124+
125+
if (noncefp == NULL) {
126+
noncefp = secp256k1_nonce_function_schnorr_adaptor;
127+
}
128+
129+
/* T := adaptor_ge */
130+
if(!secp256k1_pubkey_load(ctx, &adaptor_ge, adaptor)){
131+
return 0;
132+
}
133+
134+
ret &= secp256k1_keypair_load(ctx, &sk, &pk, keypair);
135+
/* Because we are signing for a x-only pubkey, the secret key is negated
136+
* before signing if the point corresponding to the secret key does not
137+
* have an even Y. */
138+
if (secp256k1_fe_is_odd(&pk.y)) {
139+
secp256k1_scalar_negate(&sk, &sk);
140+
}
141+
142+
/* Generate the nonce k */
143+
secp256k1_scalar_get_b32(seckey, &sk);
144+
secp256k1_fe_get_b32(pk_buf, &pk.x);
145+
serialize_ret = secp256k1_eckey_pubkey_serialize(&adaptor_ge, adaptor_buff, &cmprssd_len, 1);
146+
VERIFY_CHECK(serialize_ret);
147+
ret &= !!noncefp(nonce32, msg32, seckey, adaptor_buff, pk_buf, schnorr_adaptor_algo, sizeof(schnorr_adaptor_algo), ndata);
148+
secp256k1_scalar_set_b32(&k, nonce32, NULL);
149+
ret &= !secp256k1_scalar_is_zero(&k);
150+
secp256k1_scalar_cmov(&k, &secp256k1_scalar_one, !ret);
151+
152+
/* R = k*G */
153+
secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &rj, &k);
154+
secp256k1_ge_set_gej(&r, &rj);
155+
156+
/* We declassify the non-secret values R and T to allow using them
157+
* as branch points. */
158+
secp256k1_declassify(ctx, &rj, sizeof(rj));
159+
secp256k1_declassify(ctx, &adaptor_ge, sizeof(adaptor_ge));
160+
/* R' = R + T */
161+
secp256k1_gej_add_ge_var(&rpj, &rj, &adaptor_ge, NULL);
162+
secp256k1_ge_set_gej(&rp, &rpj);
163+
164+
/* We declassify R' (non-secret value) to branch on it */
165+
secp256k1_declassify(ctx, &rp, sizeof(rp));
166+
secp256k1_fe_normalize_var(&rp.y);
167+
168+
/* Determine if the secret nonce should be negated.
169+
*
170+
* pre_sig65[0:33] contains the compressed 33-byte encoding of the public
171+
* nonce R' = k*G + T, where k is the secret nonce and T is the adaptor point.
172+
*
173+
* Since a BIP340 signature requires an x-only public nonce, in the case where
174+
* R' = k*G + T has odd Y-coordinate, the x-only public nonce corresponding to
175+
* the signature is actually -k*G - T. Therefore, we negate k to ensure that the
176+
* adapted pre-signature will result in a valid BIP340 signature, with an even R'.y
177+
*
178+
* pre_sig65[33:65] = k + e * d if R'.y is even
179+
* = -k + e * d if R'.y is odd
180+
*/
181+
if (secp256k1_fe_is_odd(&rp.y)) {
182+
secp256k1_scalar_negate(&k, &k);
183+
}
184+
serialize_ret = secp256k1_eckey_pubkey_serialize(&rp, pre_sig65, &cmprssd_len, 1);
185+
/* R' is not the point at infinity with overwhelming probability */
186+
VERIFY_CHECK(serialize_ret);
187+
(void) serialize_ret;
188+
189+
secp256k1_schnorrsig_challenge(&e, &pre_sig65[1], msg32, 32, pk_buf);
190+
secp256k1_scalar_mul(&e, &e, &sk);
191+
secp256k1_scalar_add(&e, &e, &k);
192+
secp256k1_scalar_get_b32(&pre_sig65[33], &e);
193+
194+
secp256k1_memczero(pre_sig65, 65, !ret);
195+
secp256k1_scalar_clear(&k);
196+
secp256k1_scalar_clear(&sk);
197+
memset(seckey, 0, sizeof(seckey));
198+
199+
return ret;
200+
}
201+
202+
int secp256k1_schnorr_adaptor_presign(const secp256k1_context *ctx, unsigned char *pre_sig65, const unsigned char *msg32, const secp256k1_keypair *keypair, const secp256k1_pubkey *adaptor, const unsigned char *aux_rand32) {
203+
/* We cast away const from the passed aux_rand32 argument since we know the default nonce function does not modify it. */
204+
return secp256k1_schnorr_adaptor_presign_internal(ctx, pre_sig65, msg32, keypair, adaptor, secp256k1_nonce_function_schnorr_adaptor, (unsigned char*)aux_rand32);
205+
}
206+
207+
208+
int secp256k1_schnorr_adaptor_extract(const secp256k1_context *ctx, secp256k1_pubkey *adaptor, const unsigned char *pre_sig65, const unsigned char *msg32, const secp256k1_xonly_pubkey *pubkey) {
209+
secp256k1_scalar s;
210+
secp256k1_scalar e;
211+
secp256k1_ge pk;
212+
secp256k1_gej pkj;
213+
secp256k1_ge adaptor_ge;
214+
secp256k1_gej adaptor_gej;
215+
secp256k1_gej rj;
216+
secp256k1_ge rp;
217+
unsigned char buf[32];
218+
int overflow;
219+
220+
VERIFY_CHECK(ctx != NULL);
221+
ARG_CHECK(adaptor != NULL);
222+
ARG_CHECK(pre_sig65 != NULL);
223+
ARG_CHECK(msg32 != NULL);
224+
ARG_CHECK(pubkey != NULL);
225+
226+
/* R' := pre_sig65[0:33] */
227+
if (!secp256k1_eckey_pubkey_parse(&rp, &pre_sig65[0], 33)) {
228+
return 0;
229+
}
230+
/* s := pre_sig65[33:65] */
231+
secp256k1_scalar_set_b32(&s, &pre_sig65[33], &overflow);
232+
if (overflow) {
233+
return 0;
234+
}
235+
236+
if (!secp256k1_xonly_pubkey_load(ctx, &pk, pubkey)) {
237+
return 0;
238+
}
239+
240+
/* Compute e */
241+
secp256k1_fe_get_b32(buf, &pk.x);
242+
secp256k1_schnorrsig_challenge(&e, &pre_sig65[1], msg32, 32, buf);
243+
244+
/* Compute R = s*G + (-e)*P */
245+
secp256k1_scalar_negate(&e, &e);
246+
secp256k1_gej_set_ge(&pkj, &pk);
247+
secp256k1_ecmult(&rj, &pkj, &e, &s);
248+
if (secp256k1_gej_is_infinity(&rj)) {
249+
return 0;
250+
}
251+
252+
/* Determine if R needs to be negated
253+
*
254+
* `adaptor_presign` negates the secret nonce k when R’.y is odd, during
255+
* the computation of the s value (i.e., presig[33:65]). Therefore, we need
256+
* to negate R = k*G (if R'.y is odd) before subtracting it from R' = R + T.
257+
*
258+
* T = R' - R if R'.y is even
259+
* = R' + R if R'.y is odd
260+
*/
261+
secp256k1_fe_normalize_var(&rp.y);
262+
if (!secp256k1_fe_is_odd(&rp.y)) {
263+
secp256k1_gej_neg(&rj, &rj);
264+
}
265+
secp256k1_gej_add_ge_var(&adaptor_gej, &rj, &rp, NULL);
266+
secp256k1_ge_set_gej(&adaptor_ge, &adaptor_gej);
267+
if (secp256k1_ge_is_infinity(&adaptor_ge)) {
268+
return 0;
269+
}
270+
secp256k1_pubkey_save(adaptor, &adaptor_ge);
271+
272+
return 1;
273+
}
274+
275+
int secp256k1_schnorr_adaptor_adapt(const secp256k1_context *ctx, unsigned char *sig64, const unsigned char *pre_sig65, const unsigned char *sec_adaptor32) {
276+
secp256k1_scalar s;
277+
secp256k1_scalar t;
278+
int overflow;
279+
int ret = 1;
280+
281+
VERIFY_CHECK(ctx != NULL);
282+
ARG_CHECK(sig64 != NULL);
283+
ARG_CHECK(pre_sig65 != NULL);
284+
ARG_CHECK(sec_adaptor32 != NULL);
285+
286+
if (pre_sig65[0] != SECP256K1_TAG_PUBKEY_EVEN && pre_sig65[0] != SECP256K1_TAG_PUBKEY_ODD) {
287+
return 0;
288+
}
289+
secp256k1_scalar_set_b32(&s, &pre_sig65[33], &overflow);
290+
if (overflow) {
291+
return 0;
292+
}
293+
secp256k1_scalar_set_b32(&t, sec_adaptor32, &overflow);
294+
ret &= !overflow;
295+
296+
/* Determine if the secret adaptor should be negated.
297+
*
298+
* pre_sig65[0:33] contains the compressed 33-byte encoding of the public
299+
* nonce R' = (k + t)*G, where r is the secret nonce generated by
300+
* `adaptor_presign` and t is the secret adaptor.
301+
*
302+
* Since a BIP340 signature requires an x-only public nonce, in the case where
303+
* (k + t)*G has odd Y-coordinate, the x-only public nonce corresponding to the
304+
* signature is actually (-k - t)*G. Thus adapting a pre-signature requires
305+
* negating t in this case.
306+
*
307+
* sig64[32:64] = s + t if R'.y is even
308+
* = s - t if R'.y is odd
309+
*/
310+
if (pre_sig65[0] == SECP256K1_TAG_PUBKEY_ODD) {
311+
secp256k1_scalar_negate(&t, &t);
312+
}
313+
secp256k1_scalar_add(&s, &s, &t);
314+
secp256k1_scalar_get_b32(&sig64[32], &s);
315+
memmove(sig64, &pre_sig65[1], 32);
316+
317+
secp256k1_memczero(sig64, 64, !ret);
318+
secp256k1_scalar_clear(&t);
319+
return ret;
320+
}
321+
322+
int secp256k1_schnorr_adaptor_extract_sec(const secp256k1_context *ctx, unsigned char *sec_adaptor32, const unsigned char *pre_sig65, const unsigned char *sig64) {
323+
secp256k1_scalar t;
324+
secp256k1_scalar s;
325+
int overflow;
326+
int ret = 1;
327+
328+
VERIFY_CHECK(ctx != NULL);
329+
ARG_CHECK(sec_adaptor32 != NULL);
330+
ARG_CHECK(pre_sig65 != NULL);
331+
ARG_CHECK(sig64 != NULL);
332+
333+
if (pre_sig65[0] != SECP256K1_TAG_PUBKEY_EVEN && pre_sig65[0] != SECP256K1_TAG_PUBKEY_ODD) {
334+
return 0;
335+
}
336+
secp256k1_scalar_set_b32(&s, &pre_sig65[33], &overflow);
337+
if (overflow) {
338+
return 0;
339+
}
340+
secp256k1_scalar_set_b32(&t, &sig64[32], &overflow);
341+
ret &= !overflow;
342+
343+
/*TODO: should we parse presig[0:33] & sig[0:32], to make sure the presig &
344+
* has valid public nonce point?
345+
*
346+
* But we don't care about their validity here right? Then why do we ARG_CHECK
347+
* presig[0] parity byte?
348+
*
349+
* Here, the inputs are invalid but the output is valid :/ */
350+
351+
secp256k1_scalar_negate(&s, &s);
352+
secp256k1_scalar_add(&t, &t, &s);
353+
/* `adaptor_adapt` negates the secret adaptor t when R’.y is odd, during
354+
* the computation of the BIP340 signature. Therefore, we need negate
355+
* (sig[32:64] - pre_sig65[33:65]) in this case.
356+
*
357+
* t = (sig[32:64] - pre_sig65[33:65]) if R'.y is even
358+
* = -(sig[32:64] - pre_sig65[33:65]) if R'.y is odd
359+
*/
360+
if (pre_sig65[0] == SECP256K1_TAG_PUBKEY_ODD) {
361+
secp256k1_scalar_negate(&t, &t);
362+
}
363+
secp256k1_scalar_get_b32(sec_adaptor32, &t);
364+
365+
secp256k1_memczero(sec_adaptor32, 32, !ret);
366+
secp256k1_scalar_clear(&t);
367+
return ret;
368+
}
369+
102370
#endif

0 commit comments

Comments
 (0)