frost: share generation
This commit adds share generation, as well as share serialization and parsing.
This commit is contained in:
		
							parent
							
								
									2f3fa4cace
								
							
						
					
					
						commit
						2336b02fad
					
				| @ -1,6 +1,8 @@ | |||||||
| #ifndef SECP256K1_FROST_H | #ifndef SECP256K1_FROST_H | ||||||
| #define SECP256K1_FROST_H | #define SECP256K1_FROST_H | ||||||
| 
 | 
 | ||||||
|  | #include "secp256k1_extrakeys.h" | ||||||
|  | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| extern "C" { | extern "C" { | ||||||
| #endif | #endif | ||||||
| @ -15,6 +17,87 @@ extern "C" { | |||||||
|  * (https://crysp.uwaterloo.ca/software/frost/).
 |  * (https://crysp.uwaterloo.ca/software/frost/).
 | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
|  | /** Opaque data structures
 | ||||||
|  |  * | ||||||
|  |  *  The exact representation of data inside is implementation defined and not | ||||||
|  |  *  guaranteed to be portable between different platforms or versions. If you | ||||||
|  |  *  need to convert to a format suitable for storage, transmission, or | ||||||
|  |  *  comparison, use the corresponding serialization and parsing functions. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | /** Opaque data structure that holds a signer's _secret_ share.
 | ||||||
|  |  * | ||||||
|  |  *  Guaranteed to be 36 bytes in size. Serialized and parsed with | ||||||
|  |  *  `frost_share_serialize` and `frost_share_parse`. | ||||||
|  |  */ | ||||||
|  | typedef struct { | ||||||
|  |     unsigned char data[36]; | ||||||
|  | } secp256k1_frost_share; | ||||||
|  | 
 | ||||||
|  | /** Serialize a FROST share
 | ||||||
|  |  * | ||||||
|  |  *  Returns: 1 when the share could be serialized, 0 otherwise | ||||||
|  |  *  Args:    ctx: pointer to a context object | ||||||
|  |  *  Out:   out32: pointer to a 32-byte array to store the serialized share | ||||||
|  |  *  In:    share: pointer to the share | ||||||
|  |  */ | ||||||
|  | SECP256K1_API int secp256k1_frost_share_serialize( | ||||||
|  |     const secp256k1_context *ctx, | ||||||
|  |     unsigned char *out32, | ||||||
|  |     const secp256k1_frost_share *share | ||||||
|  | ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); | ||||||
|  | 
 | ||||||
|  | /** Parse a FROST share.
 | ||||||
|  |  * | ||||||
|  |  *  Returns: 1 when the share could be parsed, 0 otherwise. | ||||||
|  |  *  Args:    ctx: pointer to a context object | ||||||
|  |  *  Out:   share: pointer to a share object | ||||||
|  |  *  In:     in32: pointer to the 32-byte share to be parsed | ||||||
|  |  */ | ||||||
|  | SECP256K1_API int secp256k1_frost_share_parse( | ||||||
|  |     const secp256k1_context *ctx, | ||||||
|  |     secp256k1_frost_share *share, | ||||||
|  |     const unsigned char *in32 | ||||||
|  | ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); | ||||||
|  | 
 | ||||||
|  | /** Creates key shares
 | ||||||
|  |  * | ||||||
|  |  *  To generate a key, each participant generates a share for each other | ||||||
|  |  *  participant. For example, in the case of 2 particpants, Alice and Bob, they | ||||||
|  |  *  each generate 2 shares, distribute 1 share to each other using a secure | ||||||
|  |  *  channel, and keep 1 for themselves. | ||||||
|  |  * | ||||||
|  |  *  Each participant must transmit shares over secure channels to each other | ||||||
|  |  *  participant. | ||||||
|  |  * | ||||||
|  |  *  Each call to this function must have a UNIQUE and uniformly RANDOM seed32 | ||||||
|  |  *  that must that must NOT BE REUSED in subsequent calls to this function and | ||||||
|  |  *  must be KEPT SECRET (even from other participants). | ||||||
|  |  * | ||||||
|  |  *  Returns: 0 if the arguments are invalid, 1 otherwise | ||||||
|  |  *  Args:            ctx: pointer to a context object | ||||||
|  |  *  Out:          shares: pointer to the key shares | ||||||
|  |  *        vss_commitment: pointer to the VSS commitment | ||||||
|  |  *                 pok64: pointer to the proof of knowledge | ||||||
|  |  *   In:          seed32: 32-byte random seed as explained above. Must be | ||||||
|  |  *                        unique to this call to secp256k1_frost_shares_gen | ||||||
|  |  *                        and must be uniformly random. | ||||||
|  |  *             threshold: the minimum number of signers required to produce a | ||||||
|  |  *                        signature | ||||||
|  |  *        n_participants: the total number of participants | ||||||
|  |  *                 ids33: array of 33-byte participant IDs | ||||||
|  |  */ | ||||||
|  | SECP256K1_API int secp256k1_frost_shares_gen( | ||||||
|  |     const secp256k1_context *ctx, | ||||||
|  |     secp256k1_frost_share *shares, | ||||||
|  |     secp256k1_pubkey *vss_commitment, | ||||||
|  |     unsigned char *pok64, | ||||||
|  |     const unsigned char *seed32, | ||||||
|  |     size_t threshold, | ||||||
|  |     size_t n_participants, | ||||||
|  |     const unsigned char * const* ids33 | ||||||
|  | ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(8); | ||||||
|  | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
|  | |||||||
| @ -1,2 +1,4 @@ | |||||||
| include_HEADERS += include/secp256k1_frost.h | include_HEADERS += include/secp256k1_frost.h | ||||||
| noinst_HEADERS += src/modules/frost/main_impl.h | noinst_HEADERS += src/modules/frost/main_impl.h | ||||||
|  | noinst_HEADERS += src/modules/frost/keygen.h | ||||||
|  | noinst_HEADERS += src/modules/frost/keygen_impl.h | ||||||
|  | |||||||
							
								
								
									
										13
									
								
								src/modules/frost/keygen.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								src/modules/frost/keygen.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,13 @@ | |||||||
|  | /**********************************************************************
 | ||||||
|  |  * Copyright (c) 2021-2024 Jesse Posner                               * | ||||||
|  |  * Distributed under the MIT software license, see the accompanying   * | ||||||
|  |  * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
 | ||||||
|  |  **********************************************************************/ | ||||||
|  | 
 | ||||||
|  | #ifndef SECP256K1_MODULE_FROST_KEYGEN_H | ||||||
|  | #define SECP256K1_MODULE_FROST_KEYGEN_H | ||||||
|  | 
 | ||||||
|  | #include "../../../include/secp256k1.h" | ||||||
|  | #include "../../../include/secp256k1_frost.h" | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
							
								
								
									
										191
									
								
								src/modules/frost/keygen_impl.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										191
									
								
								src/modules/frost/keygen_impl.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,191 @@ | |||||||
|  | /**********************************************************************
 | ||||||
|  |  * Copyright (c) 2021-2024 Jesse Posner                               * | ||||||
|  |  * Distributed under the MIT software license, see the accompanying   * | ||||||
|  |  * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
 | ||||||
|  |  **********************************************************************/ | ||||||
|  | 
 | ||||||
|  | #ifndef SECP256K1_MODULE_FROST_KEYGEN_IMPL_H | ||||||
|  | #define SECP256K1_MODULE_FROST_KEYGEN_IMPL_H | ||||||
|  | 
 | ||||||
|  | #include <string.h> | ||||||
|  | 
 | ||||||
|  | #include "../../../include/secp256k1.h" | ||||||
|  | #include "../../../include/secp256k1_extrakeys.h" | ||||||
|  | #include "../../../include/secp256k1_frost.h" | ||||||
|  | 
 | ||||||
|  | #include "keygen.h" | ||||||
|  | #include "../../ecmult.h" | ||||||
|  | #include "../../field.h" | ||||||
|  | #include "../../group.h" | ||||||
|  | #include "../../hash.h" | ||||||
|  | #include "../../scalar.h" | ||||||
|  | 
 | ||||||
|  | /* Computes indexhash = tagged_hash(pk) */ | ||||||
|  | static int secp256k1_frost_compute_indexhash(secp256k1_scalar *indexhash, const unsigned char *id33) { | ||||||
|  |     secp256k1_sha256 sha; | ||||||
|  |     unsigned char buf[32]; | ||||||
|  | 
 | ||||||
|  |     secp256k1_sha256_initialize_tagged(&sha, (unsigned char*)"FROST/index", sizeof("FROST/index") - 1); | ||||||
|  |     secp256k1_sha256_write(&sha, id33, 33); | ||||||
|  |     secp256k1_sha256_finalize(&sha, buf); | ||||||
|  |     secp256k1_scalar_set_b32(indexhash, buf, NULL); | ||||||
|  |     /* The x-coordinate must not be zero (see
 | ||||||
|  |      * draft-irtf-cfrg-frost-08#section-4.2.2) */ | ||||||
|  |     if (secp256k1_scalar_is_zero(indexhash)) { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return 1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static const unsigned char secp256k1_frost_share_magic[4] = { 0xa1, 0x6a, 0x42, 0x03 }; | ||||||
|  | 
 | ||||||
|  | static void secp256k1_frost_share_save(secp256k1_frost_share* share, secp256k1_scalar *s) { | ||||||
|  |     memcpy(&share->data[0], secp256k1_frost_share_magic, 4); | ||||||
|  |     secp256k1_scalar_get_b32(&share->data[4], s); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int secp256k1_frost_share_load(const secp256k1_context* ctx, secp256k1_scalar *s, const secp256k1_frost_share* share) { | ||||||
|  |     int overflow; | ||||||
|  | 
 | ||||||
|  |     ARG_CHECK(secp256k1_memcmp_var(&share->data[0], secp256k1_frost_share_magic, 4) == 0); | ||||||
|  |     secp256k1_scalar_set_b32(s, &share->data[4], &overflow); | ||||||
|  |     /* Parsed shares cannot overflow */ | ||||||
|  |     VERIFY_CHECK(!overflow); | ||||||
|  |     return 1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int secp256k1_frost_share_serialize(const secp256k1_context* ctx, unsigned char *out32, const secp256k1_frost_share* share) { | ||||||
|  |     VERIFY_CHECK(ctx != NULL); | ||||||
|  |     ARG_CHECK(out32 != NULL); | ||||||
|  |     ARG_CHECK(share != NULL); | ||||||
|  |     memcpy(out32, &share->data[4], 32); | ||||||
|  |     return 1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int secp256k1_frost_share_parse(const secp256k1_context* ctx, secp256k1_frost_share* share, const unsigned char *in32) { | ||||||
|  |     secp256k1_scalar tmp; | ||||||
|  |     int overflow; | ||||||
|  |     VERIFY_CHECK(ctx != NULL); | ||||||
|  |     ARG_CHECK(share != NULL); | ||||||
|  |     ARG_CHECK(in32 != NULL); | ||||||
|  | 
 | ||||||
|  |     secp256k1_scalar_set_b32(&tmp, in32, &overflow); | ||||||
|  |     if (overflow) { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |     secp256k1_frost_share_save(share, &tmp); | ||||||
|  |     return 1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void secp256k1_frost_derive_coeff(secp256k1_scalar *coeff, const unsigned char *polygen32, size_t i) { | ||||||
|  |     secp256k1_sha256 sha; | ||||||
|  |     unsigned char buf[32]; | ||||||
|  | 
 | ||||||
|  |     secp256k1_sha256_initialize_tagged(&sha, (unsigned char*)"FROST/coeffgen", sizeof("FROST/coeffgen") - 1); | ||||||
|  |     secp256k1_sha256_write(&sha, polygen32, 32); | ||||||
|  |     secp256k1_write_be64(&buf[0], i); | ||||||
|  |     secp256k1_sha256_write(&sha, buf, 8); | ||||||
|  |     secp256k1_sha256_finalize(&sha, buf); | ||||||
|  |     secp256k1_scalar_set_b32(coeff, buf, NULL); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int secp256k1_frost_vss_gen(const secp256k1_context *ctx, secp256k1_pubkey *vss_commitment, unsigned char *pok64, const unsigned char *polygen32, size_t threshold) { | ||||||
|  |     secp256k1_sha256 sha; | ||||||
|  |     unsigned char buf[32]; | ||||||
|  |     secp256k1_keypair keypair; | ||||||
|  |     secp256k1_gej rj; | ||||||
|  |     secp256k1_ge rp; | ||||||
|  |     size_t i; | ||||||
|  |     int ret = 1; | ||||||
|  | 
 | ||||||
|  |     for (i = 0; i < threshold; i++) { | ||||||
|  |         secp256k1_scalar coeff_i; | ||||||
|  | 
 | ||||||
|  |         secp256k1_frost_derive_coeff(&coeff_i, polygen32, i); | ||||||
|  |         /* Compute proof-of-knowledge for constant term */ | ||||||
|  |         if (i == threshold - 1) { | ||||||
|  |             secp256k1_scalar_get_b32(buf, &coeff_i); | ||||||
|  |             ret &= secp256k1_keypair_create(ctx, &keypair, buf); | ||||||
|  | 
 | ||||||
|  |             secp256k1_sha256_initialize_tagged(&sha, (unsigned char*)"FROST/KeygenPoK", sizeof("FROST/KeygenPoK") - 1); | ||||||
|  |             secp256k1_sha256_finalize(&sha, buf); | ||||||
|  | 
 | ||||||
|  |             ret &= secp256k1_schnorrsig_sign32(ctx, pok64, buf, &keypair, NULL); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         /* Compute commitment to each coefficient */ | ||||||
|  |         secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &rj, &coeff_i); | ||||||
|  |         secp256k1_ge_set_gej(&rp, &rj); | ||||||
|  |         secp256k1_pubkey_save(&vss_commitment[threshold - i - 1], &rp); | ||||||
|  |     } | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int secp256k1_frost_share_gen(secp256k1_frost_share *share, const unsigned char *polygen32, size_t threshold, const unsigned char *id33) { | ||||||
|  |     secp256k1_scalar idx; | ||||||
|  |     secp256k1_scalar share_i; | ||||||
|  |     size_t i; | ||||||
|  |     int ret = 1; | ||||||
|  | 
 | ||||||
|  |     /* Derive share */ | ||||||
|  |     /* See draft-irtf-cfrg-frost-08#appendix-C.1 */ | ||||||
|  |     secp256k1_scalar_set_int(&share_i, 0); | ||||||
|  |     if (!secp256k1_frost_compute_indexhash(&idx, id33)) { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |     for (i = 0; i < threshold; i++) { | ||||||
|  |         secp256k1_scalar coeff_i; | ||||||
|  | 
 | ||||||
|  |         secp256k1_frost_derive_coeff(&coeff_i, polygen32, i); | ||||||
|  |         /* Horner's method to evaluate polynomial to derive shares */ | ||||||
|  |         secp256k1_scalar_add(&share_i, &share_i, &coeff_i); | ||||||
|  |         if (i < threshold - 1) { | ||||||
|  |             secp256k1_scalar_mul(&share_i, &share_i, &idx); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     secp256k1_frost_share_save(share, &share_i); | ||||||
|  | 
 | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int secp256k1_frost_shares_gen(const secp256k1_context *ctx, secp256k1_frost_share *shares, secp256k1_pubkey *vss_commitment, unsigned char *pok64, const unsigned char *seed32, size_t threshold, size_t n_participants, const unsigned char * const* ids33) { | ||||||
|  |     secp256k1_sha256 sha; | ||||||
|  |     unsigned char polygen[32]; | ||||||
|  |     size_t i; | ||||||
|  |     int ret = 1; | ||||||
|  | 
 | ||||||
|  |     VERIFY_CHECK(ctx != NULL); | ||||||
|  |     ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); | ||||||
|  |     ARG_CHECK(shares != NULL); | ||||||
|  |     for (i = 0; i < n_participants; i++) { | ||||||
|  |         memset(&shares[i], 0, sizeof(shares[i])); | ||||||
|  |     } | ||||||
|  |     ARG_CHECK(vss_commitment != NULL); | ||||||
|  |     ARG_CHECK(pok64 != NULL); | ||||||
|  |     ARG_CHECK(seed32 != NULL); | ||||||
|  |     ARG_CHECK(ids33 != NULL); | ||||||
|  |     ARG_CHECK(threshold > 1); | ||||||
|  |     ARG_CHECK(n_participants >= threshold); | ||||||
|  | 
 | ||||||
|  |     /* Commit to all inputs */ | ||||||
|  |     secp256k1_sha256_initialize(&sha); | ||||||
|  |     secp256k1_sha256_write(&sha, seed32, 32); | ||||||
|  |     secp256k1_write_be64(&polygen[0], threshold); | ||||||
|  |     secp256k1_write_be64(&polygen[8], n_participants); | ||||||
|  |     secp256k1_sha256_write(&sha, polygen, 16); | ||||||
|  |     for (i = 0; i < n_participants; i++) { | ||||||
|  |         secp256k1_sha256_write(&sha, ids33[i], 33); | ||||||
|  |     } | ||||||
|  |     secp256k1_sha256_finalize(&sha, polygen); | ||||||
|  | 
 | ||||||
|  |     ret &= secp256k1_frost_vss_gen(ctx, vss_commitment, pok64, polygen, threshold); | ||||||
|  | 
 | ||||||
|  |     for (i = 0; i < n_participants; i++) { | ||||||
|  |         ret &= secp256k1_frost_share_gen(&shares[i], polygen, threshold, ids33[i]); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
| @ -7,4 +7,6 @@ | |||||||
| #ifndef SECP256K1_MODULE_FROST_MAIN | #ifndef SECP256K1_MODULE_FROST_MAIN | ||||||
| #define SECP256K1_MODULE_FROST_MAIN | #define SECP256K1_MODULE_FROST_MAIN | ||||||
| 
 | 
 | ||||||
|  | #include "keygen_impl.h" | ||||||
|  | 
 | ||||||
| #endif | #endif | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user