frost: nonce aggregation and adaptor signatures
This commit adds nonce aggregation, as well as adaptor signatures.
This commit is contained in:
		
							parent
							
								
									17c47e9708
								
							
						
					
					
						commit
						67c21beadd
					
				| @ -17,7 +17,7 @@ extern "C" { | |||||||
|  * (https://crysp.uwaterloo.ca/software/frost/).
 |  * (https://crysp.uwaterloo.ca/software/frost/).
 | ||||||
|  * |  * | ||||||
|  * The module also supports BIP-341 ("Taproot") and BIP-32 ("ordinary") public |  * The module also supports BIP-341 ("Taproot") and BIP-32 ("ordinary") public | ||||||
|  * key tweaking. |  * key tweaking, and adaptor signatures. | ||||||
|  * |  * | ||||||
|  * Following the convention used in the MuSig module, the API uses the singular |  * Following the convention used in the MuSig module, the API uses the singular | ||||||
|  * term "nonce" to refer to the two "nonces" used by the FROST scheme. |  * term "nonce" to refer to the two "nonces" used by the FROST scheme. | ||||||
| @ -78,6 +78,16 @@ typedef struct { | |||||||
|     unsigned char data[132]; |     unsigned char data[132]; | ||||||
| } secp256k1_frost_pubnonce; | } secp256k1_frost_pubnonce; | ||||||
| 
 | 
 | ||||||
|  | /** Opaque data structure that holds a FROST session.
 | ||||||
|  |  * | ||||||
|  |  *  This structure is not required to be kept secret for the signing protocol | ||||||
|  |  *  to be secure. Guaranteed to be 133 bytes in size. It can be safely | ||||||
|  |  *  copied/moved. No serialization and parsing functions. | ||||||
|  |  */ | ||||||
|  | typedef struct { | ||||||
|  |     unsigned char data[133]; | ||||||
|  | } secp256k1_frost_session; | ||||||
|  | 
 | ||||||
| /** Parse a signer's public nonce.
 | /** Parse a signer's public nonce.
 | ||||||
|  * |  * | ||||||
|  *  Returns: 1 when the nonce could be parsed, 0 otherwise. |  *  Returns: 1 when the nonce could be parsed, 0 otherwise. | ||||||
| @ -421,6 +431,135 @@ SECP256K1_API int secp256k1_frost_nonce_gen( | |||||||
|     const unsigned char *extra_input32 |     const unsigned char *extra_input32 | ||||||
| ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); | ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); | ||||||
| 
 | 
 | ||||||
|  | /** Takes the public nonces of all signers and computes a session that is
 | ||||||
|  |  *  required for signing and verification of partial signatures. The participant | ||||||
|  |  *  IDs can be sorted before combining, but the corresponding pubnonces must be | ||||||
|  |  *  resorted as well. All signers must use the same sorting of pubnonces, | ||||||
|  |  *  otherwise signing will fail. | ||||||
|  |  * | ||||||
|  |  *  Returns: 0 if the arguments are invalid or if some signer sent invalid | ||||||
|  |  *           pubnonces, 1 otherwise | ||||||
|  |  *  Args:          ctx: pointer to a context object | ||||||
|  |  *  Out:       session: pointer to a struct to store the session | ||||||
|  |  *  In:      pubnonces: array of pointers to public nonces sent by the signers | ||||||
|  |  *         n_pubnonces: number of elements in the pubnonces array. Must be | ||||||
|  |  *                      greater than 0. | ||||||
|  |  *               msg32: the 32-byte message to sign | ||||||
|  |  *              agg_pk: the FROST-aggregated public key | ||||||
|  |  *            myd_id33: the 33-byte ID of the participant who will use the | ||||||
|  |  *                      session for signing | ||||||
|  |  *               ids33: array of the 33-byte participant IDs of the signers | ||||||
|  |  *         tweak_cache: pointer to frost_tweak_cache struct (can be NULL) | ||||||
|  |  *             adaptor: optional pointer to an adaptor point encoded as a | ||||||
|  |  *                      public key if this signing session is part of an | ||||||
|  |  *                      adaptor signature protocol (can be NULL) | ||||||
|  |  */ | ||||||
|  | SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_frost_nonce_process( | ||||||
|  |     const secp256k1_context *ctx, | ||||||
|  |     secp256k1_frost_session *session, | ||||||
|  |     const secp256k1_frost_pubnonce * const *pubnonces, | ||||||
|  |     size_t n_pubnonces, | ||||||
|  |     const unsigned char *msg32, | ||||||
|  |     const secp256k1_xonly_pubkey *agg_pk, | ||||||
|  |     const unsigned char *my_id33, | ||||||
|  |     const unsigned char * const* ids33, | ||||||
|  |     const secp256k1_frost_tweak_cache *tweak_cache, | ||||||
|  |     const secp256k1_pubkey *adaptor | ||||||
|  | ) 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); | ||||||
|  | 
 | ||||||
|  | /** Extracts the nonce_parity bit from a session
 | ||||||
|  |  * | ||||||
|  |  *  This is used for adaptor signatures. | ||||||
|  |  * | ||||||
|  |  *  Returns: 0 if the arguments are invalid, 1 otherwise | ||||||
|  |  *  Args:         ctx: pointer to a context object | ||||||
|  |  *  Out: nonce_parity: pointer to an integer that indicates the parity | ||||||
|  |  *                     of the aggregate public nonce. Used for adaptor | ||||||
|  |  *                     signatures. | ||||||
|  |  *  In:       session: pointer to the session that was created with | ||||||
|  |  *                     frost_nonce_process | ||||||
|  |  */ | ||||||
|  | SECP256K1_API int secp256k1_frost_nonce_parity( | ||||||
|  |     const secp256k1_context *ctx, | ||||||
|  |     int *nonce_parity, | ||||||
|  |     const secp256k1_frost_session *session | ||||||
|  | ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); | ||||||
|  | 
 | ||||||
|  | /** Verifies that the adaptor can be extracted by combining the adaptor
 | ||||||
|  |  *  pre-signature and the completed signature. | ||||||
|  |  * | ||||||
|  |  *  Returns: 0 if the arguments are invalid or the adaptor signature does not | ||||||
|  |  *           verify, 1 otherwise | ||||||
|  |  *  Args:         ctx: pointer to a context object | ||||||
|  |  *  In:     pre_sig64: 64-byte pre-signature | ||||||
|  |  *              msg32: the 32-byte message being verified | ||||||
|  |  *             pubkey: pointer to an x-only public key to verify with | ||||||
|  |  *            adaptor: pointer to the adaptor point being verified | ||||||
|  |  *       nonce_parity: the output of `frost_nonce_parity` called with the | ||||||
|  |  *                     session used for producing the pre-signature | ||||||
|  |  */ | ||||||
|  | SECP256K1_API 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 | ||||||
|  | ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); | ||||||
|  | 
 | ||||||
|  | /** Creates a signature from a pre-signature and an adaptor.
 | ||||||
|  |  * | ||||||
|  |  *  If the sec_adaptor32 argument is incorrect, the output signature will be | ||||||
|  |  *  invalid. This function does not verify the signature. | ||||||
|  |  * | ||||||
|  |  *  Returns: 0 if the arguments are invalid, or pre_sig64 or sec_adaptor32 contain | ||||||
|  |  *           invalid (overflowing) values. 1 otherwise (which does NOT mean the | ||||||
|  |  *           signature or the adaptor are valid!) | ||||||
|  |  *  Args:         ctx: pointer to a context object | ||||||
|  |  *  Out:        sig64: 64-byte signature. This pointer may point to the same | ||||||
|  |  *                     memory area as `pre_sig`. | ||||||
|  |  *  In:     pre_sig64: 64-byte pre-signature | ||||||
|  |  *      sec_adaptor32: 32-byte secret adaptor to add to the pre-signature | ||||||
|  |  *       nonce_parity: the output of `frost_nonce_parity` called with the | ||||||
|  |  *                     session used for producing the pre-signature | ||||||
|  |  */ | ||||||
|  | SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_frost_adapt( | ||||||
|  |     const secp256k1_context *ctx, | ||||||
|  |     unsigned char *sig64, | ||||||
|  |     const unsigned char *pre_sig64, | ||||||
|  |     const unsigned char *sec_adaptor32, | ||||||
|  |     int nonce_parity | ||||||
|  | ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); | ||||||
|  | 
 | ||||||
|  | /** Extracts a secret adaptor from a FROST pre-signature and corresponding
 | ||||||
|  |  *  signature | ||||||
|  |  * | ||||||
|  |  *  This function will not fail unless given grossly invalid data; if it is | ||||||
|  |  *  merely given signatures that do not verify, the returned value will be | ||||||
|  |  *  nonsense. It is therefore important that all data be verified at earlier | ||||||
|  |  *  steps of any protocol that uses this function. In particular, this includes | ||||||
|  |  *  verifying all partial signatures that were aggregated into pre_sig64. | ||||||
|  |  * | ||||||
|  |  *  Returns: 0 if the arguments are NULL, or sig64 or pre_sig64 contain | ||||||
|  |  *           grossly invalid (overflowing) values. 1 otherwise (which does NOT | ||||||
|  |  *           mean the signatures or the adaptor are valid!) | ||||||
|  |  *  Args:         ctx: pointer to a context object | ||||||
|  |  *  Out:sec_adaptor32: 32-byte secret adaptor | ||||||
|  |  *  In:         sig64: complete, valid 64-byte signature | ||||||
|  |  *          pre_sig64: the pre-signature corresponding to sig64, i.e., the | ||||||
|  |  *                     aggregate of partial signatures without the secret | ||||||
|  |  *                     adaptor | ||||||
|  |  *       nonce_parity: the output of `frost_nonce_parity` called with the | ||||||
|  |  *                     session used for producing sig64 | ||||||
|  |  */ | ||||||
|  | SECP256K1_API SECP256K1_WARN_UNUSED_RESULT 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 | ||||||
|  | ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); | ||||||
|  | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
|  | |||||||
| @ -4,3 +4,4 @@ noinst_HEADERS += src/modules/frost/keygen.h | |||||||
| noinst_HEADERS += src/modules/frost/keygen_impl.h | noinst_HEADERS += src/modules/frost/keygen_impl.h | ||||||
| noinst_HEADERS += src/modules/frost/session.h | noinst_HEADERS += src/modules/frost/session.h | ||||||
| noinst_HEADERS += src/modules/frost/session_impl.h | noinst_HEADERS += src/modules/frost/session_impl.h | ||||||
|  | noinst_HEADERS += src/modules/frost/adaptor_impl.h | ||||||
|  | |||||||
							
								
								
									
										168
									
								
								src/modules/frost/adaptor_impl.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										168
									
								
								src/modules/frost/adaptor_impl.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,168 @@ | |||||||
|  | /***********************************************************************
 | ||||||
|  |  * Copyright (c) 2022-2024 Jesse Posner                                * | ||||||
|  |  * Distributed under the MIT software license, see the accompanying    * | ||||||
|  |  * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
 | ||||||
|  |  ***********************************************************************/ | ||||||
|  | 
 | ||||||
|  | #ifndef SECP256K1_MODULE_FROST_ADAPTOR_IMPL_H | ||||||
|  | #define SECP256K1_MODULE_FROST_ADAPTOR_IMPL_H | ||||||
|  | 
 | ||||||
|  | #include <string.h> | ||||||
|  | 
 | ||||||
|  | #include "../../../include/secp256k1.h" | ||||||
|  | #include "../../../include/secp256k1_frost.h" | ||||||
|  | 
 | ||||||
|  | #include "session.h" | ||||||
|  | #include "../../scalar.h" | ||||||
|  | 
 | ||||||
|  | int secp256k1_frost_nonce_parity(const secp256k1_context* ctx, int *nonce_parity, const secp256k1_frost_session *session) { | ||||||
|  |     secp256k1_frost_session_internal session_i; | ||||||
|  |     VERIFY_CHECK(ctx != NULL); | ||||||
|  |     ARG_CHECK(nonce_parity != NULL); | ||||||
|  |     ARG_CHECK(session != NULL); | ||||||
|  | 
 | ||||||
|  |     if (!secp256k1_frost_session_load(ctx, &session_i, session)) { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |     *nonce_parity = session_i.fin_nonce_parity; | ||||||
|  |     return 1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 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) { | ||||||
|  |     secp256k1_scalar s; | ||||||
|  |     secp256k1_scalar e; | ||||||
|  |     secp256k1_gej rj; | ||||||
|  |     secp256k1_ge pk; | ||||||
|  |     secp256k1_gej pkj; | ||||||
|  |     secp256k1_ge r; | ||||||
|  |     unsigned char buf[32]; | ||||||
|  |     int overflow; | ||||||
|  |     secp256k1_ge adaptorp; | ||||||
|  |     secp256k1_xonly_pubkey noncepk; | ||||||
|  |     secp256k1_gej fin_nonce_ptj; | ||||||
|  | 
 | ||||||
|  |     VERIFY_CHECK(ctx != NULL); | ||||||
|  |     ARG_CHECK(pre_sig64 != NULL); | ||||||
|  |     ARG_CHECK(msg32 != NULL); | ||||||
|  |     ARG_CHECK(pubkey != NULL); | ||||||
|  |     ARG_CHECK(adaptor != NULL); | ||||||
|  |     ARG_CHECK(nonce_parity == 0 || nonce_parity == 1); | ||||||
|  | 
 | ||||||
|  |     if (!secp256k1_xonly_pubkey_parse(ctx, &noncepk, &pre_sig64[0])) { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |     if (!secp256k1_xonly_pubkey_load(ctx, &r, &noncepk)) { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |     if (!secp256k1_pubkey_load(ctx, &adaptorp, adaptor)) { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |     if (!nonce_parity) { | ||||||
|  |         secp256k1_ge_neg(&adaptorp, &adaptorp); | ||||||
|  |     } | ||||||
|  |     secp256k1_gej_set_ge(&fin_nonce_ptj, &adaptorp); | ||||||
|  |     secp256k1_gej_add_ge_var(&fin_nonce_ptj, &fin_nonce_ptj, &r, NULL); | ||||||
|  |     if (secp256k1_gej_is_infinity(&fin_nonce_ptj)) { | ||||||
|  |         /* unreachable with overwhelming probability */ | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     secp256k1_scalar_set_b32(&s, &pre_sig64[32], &overflow); | ||||||
|  |     if (overflow) { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (!secp256k1_xonly_pubkey_load(ctx, &pk, pubkey)) { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /* Compute e. */ | ||||||
|  |     secp256k1_fe_get_b32(buf, &pk.x); | ||||||
|  |     secp256k1_schnorrsig_challenge(&e, &pre_sig64[0], msg32, 32, buf); | ||||||
|  | 
 | ||||||
|  |     /* Compute rj =  s*G + (-e)*pkj */ | ||||||
|  |     secp256k1_scalar_negate(&e, &e); | ||||||
|  |     secp256k1_gej_set_ge(&pkj, &pk); | ||||||
|  |     secp256k1_ecmult(&rj, &pkj, &e, &s); | ||||||
|  | 
 | ||||||
|  |     /* secp256k1_ge_set_gej_var(&r, &rj); */ | ||||||
|  |     if (secp256k1_gej_is_infinity(&rj)) { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     secp256k1_gej_neg(&rj, &rj); | ||||||
|  |     secp256k1_gej_add_var(&rj, &rj, &fin_nonce_ptj, NULL); | ||||||
|  |     return secp256k1_gej_is_infinity(&rj); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int secp256k1_frost_adapt(const secp256k1_context* ctx, unsigned char *sig64, const unsigned char *pre_sig64, const unsigned char *sec_adaptor32, int nonce_parity) { | ||||||
|  |     secp256k1_scalar s; | ||||||
|  |     secp256k1_scalar t; | ||||||
|  |     int overflow; | ||||||
|  |     int ret = 1; | ||||||
|  | 
 | ||||||
|  |     VERIFY_CHECK(ctx != NULL); | ||||||
|  |     ARG_CHECK(sig64 != NULL); | ||||||
|  |     ARG_CHECK(pre_sig64 != NULL); | ||||||
|  |     ARG_CHECK(sec_adaptor32 != NULL); | ||||||
|  |     ARG_CHECK(nonce_parity == 0 || nonce_parity == 1); | ||||||
|  | 
 | ||||||
|  |     secp256k1_scalar_set_b32(&s, &pre_sig64[32], &overflow); | ||||||
|  |     if (overflow) { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |     secp256k1_scalar_set_b32(&t, sec_adaptor32, &overflow); | ||||||
|  |     ret &= !overflow; | ||||||
|  | 
 | ||||||
|  |     /* Determine if the secret adaptor should be negated.
 | ||||||
|  |      * | ||||||
|  |      * The frost_session stores the X-coordinate and the parity of the "final nonce" | ||||||
|  |      * (r + t)*G, where r*G is the aggregate public nonce and t is the secret adaptor. | ||||||
|  |      * | ||||||
|  |      * Since a BIP340 signature requires an x-only public nonce, in the case where | ||||||
|  |      * (r + t)*G has odd Y-coordinate (i.e. nonce_parity == 1), the x-only public nonce | ||||||
|  |      * corresponding to the signature is actually (-r - t)*G. Thus adapting a | ||||||
|  |      * pre-signature requires negating t in this case. | ||||||
|  |      */ | ||||||
|  |     if (nonce_parity) { | ||||||
|  |         secp256k1_scalar_negate(&t, &t); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     secp256k1_scalar_add(&s, &s, &t); | ||||||
|  |     secp256k1_scalar_get_b32(&sig64[32], &s); | ||||||
|  |     memmove(sig64, pre_sig64, 32); | ||||||
|  |     secp256k1_scalar_clear(&t); | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 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) { | ||||||
|  |     secp256k1_scalar t; | ||||||
|  |     secp256k1_scalar s; | ||||||
|  |     int overflow; | ||||||
|  |     int ret = 1; | ||||||
|  | 
 | ||||||
|  |     VERIFY_CHECK(ctx != NULL); | ||||||
|  |     ARG_CHECK(sec_adaptor32 != NULL); | ||||||
|  |     ARG_CHECK(sig64 != NULL); | ||||||
|  |     ARG_CHECK(pre_sig64 != NULL); | ||||||
|  |     ARG_CHECK(nonce_parity == 0 || nonce_parity == 1); | ||||||
|  | 
 | ||||||
|  |     secp256k1_scalar_set_b32(&t, &sig64[32], &overflow); | ||||||
|  |     ret &= !overflow; | ||||||
|  |     secp256k1_scalar_negate(&t, &t); | ||||||
|  | 
 | ||||||
|  |     secp256k1_scalar_set_b32(&s, &pre_sig64[32], &overflow); | ||||||
|  |     if (overflow) { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |     secp256k1_scalar_add(&t, &t, &s); | ||||||
|  | 
 | ||||||
|  |     if (!nonce_parity) { | ||||||
|  |         secp256k1_scalar_negate(&t, &t); | ||||||
|  |     } | ||||||
|  |     secp256k1_scalar_get_b32(sec_adaptor32, &t); | ||||||
|  |     secp256k1_scalar_clear(&t); | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
| @ -19,6 +19,10 @@ typedef struct { | |||||||
|     int parity_acc; |     int parity_acc; | ||||||
| } secp256k1_tweak_cache_internal; | } secp256k1_tweak_cache_internal; | ||||||
| 
 | 
 | ||||||
|  | static int secp256k1_tweak_cache_load(const secp256k1_context* ctx, secp256k1_tweak_cache_internal *cache_i, const secp256k1_frost_tweak_cache *cache); | ||||||
|  | 
 | ||||||
| static int secp256k1_frost_share_load(const secp256k1_context* ctx, secp256k1_scalar *s, const secp256k1_frost_share* share); | static int secp256k1_frost_share_load(const secp256k1_context* ctx, secp256k1_scalar *s, const secp256k1_frost_share* share); | ||||||
| 
 | 
 | ||||||
|  | static int secp256k1_frost_compute_indexhash(secp256k1_scalar *indexhash, const unsigned char *id33); | ||||||
|  | 
 | ||||||
| #endif | #endif | ||||||
|  | |||||||
| @ -9,5 +9,6 @@ | |||||||
| 
 | 
 | ||||||
| #include "keygen_impl.h" | #include "keygen_impl.h" | ||||||
| #include "session_impl.h" | #include "session_impl.h" | ||||||
|  | #include "adaptor_impl.h" | ||||||
| 
 | 
 | ||||||
| #endif | #endif | ||||||
|  | |||||||
| @ -7,4 +7,19 @@ | |||||||
| #ifndef SECP256K1_MODULE_FROST_SESSION_H | #ifndef SECP256K1_MODULE_FROST_SESSION_H | ||||||
| #define SECP256K1_MODULE_FROST_SESSION_H | #define SECP256K1_MODULE_FROST_SESSION_H | ||||||
| 
 | 
 | ||||||
|  | #include "../../../include/secp256k1.h" | ||||||
|  | #include "../../../include/secp256k1_frost.h" | ||||||
|  | 
 | ||||||
|  | #include "../../scalar.h" | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     int fin_nonce_parity; | ||||||
|  |     unsigned char fin_nonce[32]; | ||||||
|  |     secp256k1_scalar noncecoef; | ||||||
|  |     secp256k1_scalar challenge; | ||||||
|  |     secp256k1_scalar s_part; | ||||||
|  | } secp256k1_frost_session_internal; | ||||||
|  | 
 | ||||||
|  | static int secp256k1_frost_session_load(const secp256k1_context* ctx, secp256k1_frost_session_internal *session_i, const secp256k1_frost_session *session); | ||||||
|  | 
 | ||||||
| #endif | #endif | ||||||
|  | |||||||
| @ -74,6 +74,49 @@ static int secp256k1_frost_pubnonce_load(const secp256k1_context* ctx, secp256k1 | |||||||
|     return 1; |     return 1; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static const unsigned char secp256k1_frost_session_cache_magic[4] = { 0x5c, 0x11, 0xa8, 0x3 }; | ||||||
|  | 
 | ||||||
|  | /* A session consists of
 | ||||||
|  |  * - 4 byte session cache magic | ||||||
|  |  * - 1 byte the parity of the final nonce | ||||||
|  |  * - 32 byte serialized x-only final nonce | ||||||
|  |  * - 32 byte nonce coefficient b | ||||||
|  |  * - 32 byte signature challenge hash e | ||||||
|  |  * - 32 byte scalar s that is added to the partial signatures of the signers | ||||||
|  |  */ | ||||||
|  | static void secp256k1_frost_session_save(secp256k1_frost_session *session, const secp256k1_frost_session_internal *session_i) { | ||||||
|  |     unsigned char *ptr = session->data; | ||||||
|  | 
 | ||||||
|  |     memcpy(ptr, secp256k1_frost_session_cache_magic, 4); | ||||||
|  |     ptr += 4; | ||||||
|  |     *ptr = session_i->fin_nonce_parity; | ||||||
|  |     ptr += 1; | ||||||
|  |     memcpy(ptr, session_i->fin_nonce, 32); | ||||||
|  |     ptr += 32; | ||||||
|  |     secp256k1_scalar_get_b32(ptr, &session_i->noncecoef); | ||||||
|  |     ptr += 32; | ||||||
|  |     secp256k1_scalar_get_b32(ptr, &session_i->challenge); | ||||||
|  |     ptr += 32; | ||||||
|  |     secp256k1_scalar_get_b32(ptr, &session_i->s_part); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int secp256k1_frost_session_load(const secp256k1_context* ctx, secp256k1_frost_session_internal *session_i, const secp256k1_frost_session *session) { | ||||||
|  |     const unsigned char *ptr = session->data; | ||||||
|  | 
 | ||||||
|  |     ARG_CHECK(secp256k1_memcmp_var(ptr, secp256k1_frost_session_cache_magic, 4) == 0); | ||||||
|  |     ptr += 4; | ||||||
|  |     session_i->fin_nonce_parity = *ptr; | ||||||
|  |     ptr += 1; | ||||||
|  |     memcpy(session_i->fin_nonce, ptr, 32); | ||||||
|  |     ptr += 32; | ||||||
|  |     secp256k1_scalar_set_b32(&session_i->noncecoef, ptr, NULL); | ||||||
|  |     ptr += 32; | ||||||
|  |     secp256k1_scalar_set_b32(&session_i->challenge, ptr, NULL); | ||||||
|  |     ptr += 32; | ||||||
|  |     secp256k1_scalar_set_b32(&session_i->s_part, ptr, NULL); | ||||||
|  |     return 1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| int secp256k1_frost_pubnonce_serialize(const secp256k1_context* ctx, unsigned char *out66, const secp256k1_frost_pubnonce* nonce) { | int secp256k1_frost_pubnonce_serialize(const secp256k1_context* ctx, unsigned char *out66, const secp256k1_frost_pubnonce* nonce) { | ||||||
|     secp256k1_ge ge[2]; |     secp256k1_ge ge[2]; | ||||||
|     int i; |     int i; | ||||||
| @ -230,4 +273,200 @@ int secp256k1_frost_nonce_gen(const secp256k1_context* ctx, secp256k1_frost_secn | |||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static int secp256k1_frost_sum_nonces(const secp256k1_context* ctx, secp256k1_gej *summed_nonces, const secp256k1_frost_pubnonce * const *pubnonces, size_t n_pubnonces) { | ||||||
|  |     size_t i; | ||||||
|  |     int j; | ||||||
|  | 
 | ||||||
|  |     secp256k1_gej_set_infinity(&summed_nonces[0]); | ||||||
|  |     secp256k1_gej_set_infinity(&summed_nonces[1]); | ||||||
|  | 
 | ||||||
|  |     for (i = 0; i < n_pubnonces; i++) { | ||||||
|  |         secp256k1_ge nonce_pt[2]; | ||||||
|  |         if (!secp256k1_frost_pubnonce_load(ctx, nonce_pt, pubnonces[i])) { | ||||||
|  |             return 0; | ||||||
|  |         } | ||||||
|  |         for (j = 0; j < 2; j++) { | ||||||
|  |             secp256k1_gej_add_ge_var(&summed_nonces[j], &summed_nonces[j], &nonce_pt[j], NULL); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return 1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* TODO: consider updating to frost-08 to address maleability at the cost of performance */ | ||||||
|  | /* See https://github.com/cfrg/draft-irtf-cfrg-frost/pull/217 */ | ||||||
|  | static int secp256k1_frost_compute_noncehash(const secp256k1_context* ctx, unsigned char *noncehash, const unsigned char *msg, const secp256k1_frost_pubnonce * const *pubnonces, size_t n_pubnonces, const unsigned char *pk32, const unsigned char * const *ids33) { | ||||||
|  |     unsigned char buf[66]; | ||||||
|  |     secp256k1_sha256 sha; | ||||||
|  |     size_t i; | ||||||
|  | 
 | ||||||
|  |     secp256k1_sha256_initialize_tagged(&sha, (unsigned char*)"FROST/noncecoef", sizeof("FROST/noncecoef") - 1); | ||||||
|  |     /* TODO: sort by index */ | ||||||
|  |     for (i = 0; i < n_pubnonces; i++) { | ||||||
|  |         secp256k1_scalar idx; | ||||||
|  | 
 | ||||||
|  |         if (!secp256k1_frost_compute_indexhash(&idx, ids33[i])) { | ||||||
|  |             return 0; | ||||||
|  |         } | ||||||
|  |         secp256k1_scalar_get_b32(buf, &idx); | ||||||
|  |         secp256k1_sha256_write(&sha, buf, 32); | ||||||
|  |         if (!secp256k1_frost_pubnonce_serialize(ctx, buf, pubnonces[i])) { | ||||||
|  |             return 0; | ||||||
|  |         } | ||||||
|  |         secp256k1_sha256_write(&sha, buf, sizeof(buf)); | ||||||
|  |     } | ||||||
|  |     secp256k1_sha256_write(&sha, pk32, 32); | ||||||
|  |     secp256k1_sha256_write(&sha, msg, 32); | ||||||
|  |     secp256k1_sha256_finalize(&sha, noncehash); | ||||||
|  |     return 1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int secp256k1_frost_nonce_process_internal(const secp256k1_context* ctx, int *fin_nonce_parity, unsigned char *fin_nonce, secp256k1_scalar *b, secp256k1_gej *aggnoncej, const unsigned char *msg, const secp256k1_frost_pubnonce * const *pubnonces, size_t n_pubnonces, const unsigned char *pk32, const unsigned char * const *ids33) { | ||||||
|  |     unsigned char noncehash[32]; | ||||||
|  |     secp256k1_ge fin_nonce_pt; | ||||||
|  |     secp256k1_gej fin_nonce_ptj; | ||||||
|  |     secp256k1_ge aggnonce[2]; | ||||||
|  | 
 | ||||||
|  |     secp256k1_ge_set_gej(&aggnonce[0], &aggnoncej[0]); | ||||||
|  |     secp256k1_ge_set_gej(&aggnonce[1], &aggnoncej[1]); | ||||||
|  |     if (!secp256k1_frost_compute_noncehash(ctx, noncehash, msg, pubnonces, n_pubnonces, pk32, ids33)) { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |     /* fin_nonce = aggnonce[0] + b*aggnonce[1] */ | ||||||
|  |     secp256k1_scalar_set_b32(b, noncehash, NULL); | ||||||
|  |     secp256k1_ecmult(&fin_nonce_ptj, &aggnoncej[1], b, NULL); | ||||||
|  |     secp256k1_gej_add_ge_var(&fin_nonce_ptj, &fin_nonce_ptj, &aggnonce[0], NULL); | ||||||
|  |     secp256k1_ge_set_gej(&fin_nonce_pt, &fin_nonce_ptj); | ||||||
|  | 
 | ||||||
|  |     if (secp256k1_ge_is_infinity(&fin_nonce_pt)) { | ||||||
|  |         /* unreachable with overwhelming probability */ | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |     secp256k1_fe_normalize_var(&fin_nonce_pt.x); | ||||||
|  |     secp256k1_fe_get_b32(fin_nonce, &fin_nonce_pt.x); | ||||||
|  | 
 | ||||||
|  |     secp256k1_fe_normalize_var(&fin_nonce_pt.y); | ||||||
|  |     *fin_nonce_parity = secp256k1_fe_is_odd(&fin_nonce_pt.y); | ||||||
|  |     return 1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int secp256k1_frost_lagrange_coefficient(secp256k1_scalar *r, const unsigned char * const *ids33, size_t n_participants, const unsigned char *my_id33) { | ||||||
|  |     size_t i; | ||||||
|  |     secp256k1_scalar num; | ||||||
|  |     secp256k1_scalar den; | ||||||
|  |     secp256k1_scalar party_idx; | ||||||
|  | 
 | ||||||
|  |     secp256k1_scalar_set_int(&num, 1); | ||||||
|  |     secp256k1_scalar_set_int(&den, 1); | ||||||
|  |     if (!secp256k1_frost_compute_indexhash(&party_idx, my_id33)) { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |     for (i = 0; i < n_participants; i++) { | ||||||
|  |         secp256k1_scalar mul; | ||||||
|  | 
 | ||||||
|  |         if (!secp256k1_frost_compute_indexhash(&mul, ids33[i])) { | ||||||
|  |             return 0; | ||||||
|  |         } | ||||||
|  |         if (secp256k1_scalar_eq(&mul, &party_idx)) { | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         secp256k1_scalar_negate(&mul, &mul); | ||||||
|  |         secp256k1_scalar_mul(&num, &num, &mul); | ||||||
|  |         secp256k1_scalar_add(&mul, &mul, &party_idx); | ||||||
|  |         secp256k1_scalar_mul(&den, &den, &mul); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     secp256k1_scalar_inverse_var(&den, &den); | ||||||
|  |     secp256k1_scalar_mul(r, &num, &den); | ||||||
|  | 
 | ||||||
|  |     return 1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int secp256k1_frost_nonce_process(const secp256k1_context* ctx, secp256k1_frost_session *session, const secp256k1_frost_pubnonce * const* pubnonces, size_t n_pubnonces, const unsigned char *msg32, const secp256k1_xonly_pubkey *pk, const unsigned char *my_id33, const unsigned char * const *ids33, const secp256k1_frost_tweak_cache *tweak_cache, const secp256k1_pubkey *adaptor) { | ||||||
|  |     secp256k1_ge aggnonce_pt[2]; | ||||||
|  |     secp256k1_gej aggnonce_ptj[2]; | ||||||
|  |     unsigned char fin_nonce[32]; | ||||||
|  |     secp256k1_frost_session_internal session_i = { 0 }; | ||||||
|  |     unsigned char pk32[32]; | ||||||
|  |     size_t i; | ||||||
|  |     secp256k1_scalar l; | ||||||
|  | 
 | ||||||
|  |     VERIFY_CHECK(ctx != NULL); | ||||||
|  |     ARG_CHECK(session != NULL); | ||||||
|  |     ARG_CHECK(msg32 != NULL); | ||||||
|  |     ARG_CHECK(pubnonces != NULL); | ||||||
|  |     ARG_CHECK(ids33 != NULL); | ||||||
|  |     ARG_CHECK(my_id33 != NULL); | ||||||
|  |     ARG_CHECK(pk != NULL); | ||||||
|  |     ARG_CHECK(n_pubnonces > 1); | ||||||
|  | 
 | ||||||
|  |     if (!secp256k1_xonly_pubkey_serialize(ctx, pk32, pk)) { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (!secp256k1_frost_sum_nonces(ctx, aggnonce_ptj, pubnonces, n_pubnonces)) { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |     for (i = 0; i < 2; i++) { | ||||||
|  |         if (secp256k1_gej_is_infinity(&aggnonce_ptj[i])) { | ||||||
|  |             /* There must be at least one dishonest signer. If we would return 0
 | ||||||
|  |                here, we will never be able to determine who it is. Therefore, we | ||||||
|  |                should continue such that the culprit is revealed when collecting | ||||||
|  |                and verifying partial signatures. | ||||||
|  |                However, dealing with the point at infinity (loading, | ||||||
|  |                de-/serializing) would require a lot of extra code complexity. | ||||||
|  |                Instead, we set the aggregate nonce to some arbitrary point (the | ||||||
|  |                generator). This is secure, because it only restricts the | ||||||
|  |                abilities of the attacker: an attacker that forces the sum of | ||||||
|  |                nonces to be infinity by sending some maliciously generated nonce | ||||||
|  |                pairs can be turned into an attacker that forces the sum to be | ||||||
|  |                the generator (by simply adding the generator to one of the | ||||||
|  |                malicious nonces), and this does not change the winning condition | ||||||
|  |                of the EUF-CMA game. */ | ||||||
|  |             aggnonce_pt[i] = secp256k1_ge_const_g; | ||||||
|  |         } else { | ||||||
|  |             secp256k1_ge_set_gej(&aggnonce_pt[i], &aggnonce_ptj[i]); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     /* Add public adaptor to nonce */ | ||||||
|  |     if (adaptor != NULL) { | ||||||
|  |         secp256k1_ge adaptorp; | ||||||
|  |         if (!secp256k1_pubkey_load(ctx, &adaptorp, adaptor)) { | ||||||
|  |             return 0; | ||||||
|  |         } | ||||||
|  |         secp256k1_gej_add_ge_var(&aggnonce_ptj[0], &aggnonce_ptj[0], &adaptorp, NULL); | ||||||
|  |     } | ||||||
|  |     if (!secp256k1_frost_nonce_process_internal(ctx, &session_i.fin_nonce_parity, fin_nonce, &session_i.noncecoef, aggnonce_ptj, msg32, pubnonces, n_pubnonces, pk32, ids33)) { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     secp256k1_schnorrsig_challenge(&session_i.challenge, fin_nonce, msg32, 32, pk32); | ||||||
|  | 
 | ||||||
|  |     /* If there is a tweak then set `challenge` times `tweak` to the `s`-part.*/ | ||||||
|  |     secp256k1_scalar_set_int(&session_i.s_part, 0); | ||||||
|  |     if (tweak_cache != NULL) { | ||||||
|  |         secp256k1_tweak_cache_internal cache_i; | ||||||
|  |         if (!secp256k1_tweak_cache_load(ctx, &cache_i, tweak_cache)) { | ||||||
|  |             return 0; | ||||||
|  |         } | ||||||
|  |         if (!secp256k1_scalar_is_zero(&cache_i.tweak)) { | ||||||
|  |             secp256k1_scalar e_tmp; | ||||||
|  |             secp256k1_scalar_mul(&e_tmp, &session_i.challenge, &cache_i.tweak); | ||||||
|  |             if (secp256k1_fe_is_odd(&cache_i.pk.y)) { | ||||||
|  |                 secp256k1_scalar_negate(&e_tmp, &e_tmp); | ||||||
|  |             } | ||||||
|  |             secp256k1_scalar_add(&session_i.s_part, &session_i.s_part, &e_tmp); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     /* Update the challenge by multiplying the Lagrange coefficient to prepare
 | ||||||
|  |      * for signing. */ | ||||||
|  |     if (!secp256k1_frost_lagrange_coefficient(&l, ids33, n_pubnonces, my_id33)) { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |     secp256k1_scalar_mul(&session_i.challenge, &session_i.challenge, &l); | ||||||
|  |     memcpy(session_i.fin_nonce, fin_nonce, sizeof(session_i.fin_nonce)); | ||||||
|  |     secp256k1_frost_session_save(session, &session_i); | ||||||
|  |     return 1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| #endif | #endif | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user