Skip to content

Commit

Permalink
Implement FROST share refresh
Browse files Browse the repository at this point in the history
  • Loading branch information
wpaulino committed Aug 16, 2024
1 parent cac1662 commit a8fe0b2
Show file tree
Hide file tree
Showing 3 changed files with 228 additions and 22 deletions.
77 changes: 77 additions & 0 deletions examples/frost.c
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,64 @@ int sign(const secp256k1_context* ctx, struct signer_secrets *signer_secrets, st
return secp256k1_frost_partial_sig_agg(ctx, sig64, &signer[signer_id].session, partial_sigs, THRESHOLD);
}

int refresh_shares(const secp256k1_context *ctx, struct signer_secrets *signer_secrets, struct signer *signer) {
int i, j;
secp256k1_frost_share refresh_shares[N_SIGNERS][N_SIGNERS];
secp256k1_pubkey refresh_commitments[N_SIGNERS][THRESHOLD-1];
secp256k1_pubkey commitments_after_refresh[N_SIGNERS][THRESHOLD];
const secp256k1_pubkey *refresh_vss_commitments[N_SIGNERS];
const secp256k1_pubkey *vss_commitments[N_SIGNERS];
const unsigned char *ids[N_SIGNERS];

for (i = 0; i < N_SIGNERS; i++) {
refresh_vss_commitments[i] = refresh_commitments[i];
vss_commitments[i] = signer[i].vss_commitment;
ids[i] = signer[i].id;
}

for (i = 0; i < N_SIGNERS; i++) {
unsigned char refresh_seed[32];
if (!fill_random(refresh_seed, sizeof(refresh_seed))) {
return 0;
}
/* Generate a polynomial share for the participants */
if (!secp256k1_frost_shares_gen_refresh(ctx, refresh_shares[i], refresh_commitments[i], refresh_seed, THRESHOLD, N_SIGNERS, ids)) {
return 0;
}
}

/* Exchange refresh shares and refresh coefficient commitments */
for (i = 0; i < N_SIGNERS; i++) {
secp256k1_frost_share share_after_refresh;
secp256k1_pubkey *vss_commitments_after_refresh[N_SIGNERS];
const secp256k1_frost_share *assigned_shares[N_SIGNERS];

/* Each participant receives a share from each participant corresponding to their index. */
for (j = 0; j < N_SIGNERS; j++) {
vss_commitments_after_refresh[j] = commitments_after_refresh[j];
assigned_shares[j] = &refresh_shares[j][i];
}
/* Each participant aggregates the shares they received. */
if (!secp256k1_frost_refresh_share(ctx, &share_after_refresh, vss_commitments_after_refresh, &signer_secrets[i].agg_share, vss_commitments, assigned_shares, refresh_vss_commitments, THRESHOLD, N_SIGNERS, signer[i].id)) {
return 0;
}
signer_secrets[i].agg_share = share_after_refresh;
/* Each participant generates public verification shares that are
* used for verifying partial signatures. */
if (!secp256k1_frost_compute_pubshare(ctx, &signer[i].pubshare, THRESHOLD, signer[i].id, (const secp256k1_pubkey *const *)vss_commitments_after_refresh, N_SIGNERS)) {
return 0;
}
}

for (i = 0; i < N_SIGNERS; i++) {
for (j = 0; j < THRESHOLD; j++) {
signer[i].vss_commitment[j] = commitments_after_refresh[i][j];
}
}

return 1;
}

int main(void) {
secp256k1_context* ctx;
int i;
Expand Down Expand Up @@ -296,6 +354,25 @@ int main(void) {
return 1;
}
printf("ok\n");
/* Refresh the shares and resign */
printf("Refreshing shares.......");
if (!refresh_shares(ctx, signer_secrets, signers)) {
printf("FAILED\n");
return 1;
}
printf("ok\n");
printf("Signing message.........");
if (!sign(ctx, signer_secrets, signers, msg, sig, &keygen_cache)) {
printf("FAILED\n");
return 1;
}
printf("ok\n");
printf("Verifying signature.....");
if (!secp256k1_schnorrsig_verify(ctx, sig, msg, 32, &pk)) {
printf("FAILED\n");
return 1;
}
printf("ok\n");
secp256k1_context_destroy(ctx);
return 0;
}
23 changes: 23 additions & 0 deletions include/secp256k1_frost.h
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,16 @@ SECP256K1_API int secp256k1_frost_shares_gen(
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);

SECP256K1_API int secp256k1_frost_shares_gen_refresh(
const secp256k1_context *ctx,
secp256k1_frost_share *shares,
secp256k1_pubkey *vss_commitment,
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(7);

/** Aggregates shares
*
* As part of the key generation protocol, each participant receives a share
Expand Down Expand Up @@ -284,6 +294,19 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_frost_share_verify(
const secp256k1_pubkey * const *vss_commitment
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5);

SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_frost_refresh_share(
const secp256k1_context *ctx,
secp256k1_frost_share *share_after_refresh,
secp256k1_pubkey * *vss_commitments_after_refresh,
const secp256k1_frost_share *share,
const secp256k1_pubkey * const *vss_commitments,
const secp256k1_frost_share * const *refresh_shares,
const secp256k1_pubkey * const *refresh_vss_commitments,
size_t n_shares,
size_t threshold,
const unsigned char *id33
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6) SECP256K1_ARG_NONNULL(7) SECP256K1_ARG_NONNULL(10);

/** Computes a public verification share used for verifying partial signatures
*
* Returns: 0 if the arguments are invalid, 1 otherwise
Expand Down
150 changes: 128 additions & 22 deletions src/modules/frost/keygen_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,21 @@ int secp256k1_frost_share_parse(const secp256k1_context* ctx, secp256k1_frost_sh
return 1;
}

static void secp256k1_frost_derive_polygen(unsigned char *polygen32, const unsigned char *seed32, size_t threshold, size_t n_participants, const unsigned char *const *ids33) {
secp256k1_sha256 sha;
size_t i;

secp256k1_sha256_initialize(&sha);
secp256k1_sha256_write(&sha, seed32, 32);
secp256k1_write_be64(&polygen32[0], threshold);
secp256k1_write_be64(&polygen32[8], n_participants);
secp256k1_sha256_write(&sha, polygen32, 16);
for (i = 0; i < n_participants; i++) {
secp256k1_sha256_write(&sha, ids33[i], 33);
}
secp256k1_sha256_finalize(&sha, polygen32);
}

static void secp256k1_frost_derive_coeff(secp256k1_scalar *coeff, const unsigned char *polygen32, size_t i) {
secp256k1_sha256 sha;
unsigned char buf[32];
Expand All @@ -131,13 +146,18 @@ static int secp256k1_frost_vss_gen(const secp256k1_context *ctx, secp256k1_pubke
secp256k1_ge rp;
size_t i;
int ret = 1;
size_t vss_commitment_len = threshold;

if (!pok64) {
vss_commitment_len--;
}

for (i = 0; i < threshold; i++) {
for (i = 0; i < vss_commitment_len; i++) {
secp256k1_scalar coeff_i;

secp256k1_frost_derive_coeff(&coeff_i, polygen32, i);
/* Compute proof-of-knowledge for constant term */
if (i == threshold - 1) {
if (pok64 && i == vss_commitment_len - 1) {
secp256k1_scalar_get_b32(buf, &coeff_i);
ret &= secp256k1_keypair_create(ctx, &keypair, buf);

Expand All @@ -150,30 +170,35 @@ static int secp256k1_frost_vss_gen(const secp256k1_context *ctx, secp256k1_pubke
/* 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);
secp256k1_pubkey_save(&vss_commitment[vss_commitment_len - 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) {
static int secp256k1_frost_share_gen(secp256k1_frost_share *share, const unsigned char *polygen32, size_t threshold, const unsigned char *id33, int refresh) {
secp256k1_scalar idx;
secp256k1_scalar share_i;
size_t i;
int ret = 1;
size_t coeff_count = threshold;

if (refresh) {
coeff_count--;
}

/* 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++) {
for (i = 0; i < coeff_count; 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) {
if (refresh || i < coeff_count - 1) {
secp256k1_scalar_mul(&share_i, &share_i, &idx);
}
}
Expand All @@ -183,7 +208,6 @@ static int secp256k1_frost_share_gen(secp256k1_frost_share *share, const unsigne
}

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;
Expand All @@ -201,21 +225,40 @@ int secp256k1_frost_shares_gen(const secp256k1_context *ctx, secp256k1_frost_sha
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);
secp256k1_frost_derive_polygen(polygen, seed32, threshold, n_participants, ids33);

ret &= secp256k1_frost_vss_gen(ctx, vss_commitment, pok64, polygen, threshold);

for (i = 0; i < n_participants; i++) {
secp256k1_sha256_write(&sha, ids33[i], 33);
ret &= secp256k1_frost_share_gen(&shares[i], polygen, threshold, ids33[i], 0);
}
secp256k1_sha256_finalize(&sha, polygen);

ret &= secp256k1_frost_vss_gen(ctx, vss_commitment, pok64, polygen, threshold);
return ret;
}

int secp256k1_frost_shares_gen_refresh(const secp256k1_context *ctx, secp256k1_frost_share *shares, secp256k1_pubkey *vss_commitment, const unsigned char *seed32, size_t threshold, size_t n_participants, const unsigned char * const* ids33) {
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++) {
ret &= secp256k1_frost_share_gen(&shares[i], polygen, threshold, ids33[i]);
memset(&shares[i], 0, sizeof(shares[i]));
}
ARG_CHECK(vss_commitment != NULL);
ARG_CHECK(seed32 != NULL);
ARG_CHECK(ids33 != NULL);
ARG_CHECK(threshold > 1);
ARG_CHECK(n_participants >= threshold);

secp256k1_frost_derive_polygen(polygen, seed32, threshold, n_participants, ids33);

ret &= secp256k1_frost_vss_gen(ctx, vss_commitment, NULL, polygen, threshold);

for (i = 0; i < n_participants; i++) {
ret &= secp256k1_frost_share_gen(&shares[i], polygen, threshold, ids33[i], 1);
}

return ret;
Expand Down Expand Up @@ -293,11 +336,12 @@ static int secp256k1_frost_interpolate_pubkey_ecmult_callback(secp256k1_scalar *
}

/* See draft-irtf-cfrg-frost-08#appendix-C.2 */
static int secp256k1_frost_vss_verify_internal(const secp256k1_context* ctx, size_t threshold, const unsigned char *id33, const secp256k1_scalar *share, const secp256k1_pubkey * const* vss_commitment) {
static int secp256k1_frost_vss_verify_internal(const secp256k1_context* ctx, size_t threshold, const unsigned char *id33, const secp256k1_scalar *share, const secp256k1_pubkey * const* vss_commitment, int refresh) {
secp256k1_scalar share_neg;
secp256k1_gej tmpj, snj;
secp256k1_ge sng;
secp256k1_frost_verify_share_ecmult_data verify_share_ecmult_data;
size_t vss_commitment_len;

ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));

Expand All @@ -311,9 +355,15 @@ static int secp256k1_frost_vss_verify_internal(const secp256k1_context* ctx, siz
if (!secp256k1_frost_compute_indexhash(&verify_share_ecmult_data.idx, id33)) {
return 0;
}
secp256k1_scalar_set_int(&verify_share_ecmult_data.idxn, 1);
if (refresh) {
verify_share_ecmult_data.idxn = verify_share_ecmult_data.idx;
vss_commitment_len = threshold - 1;
} else {
secp256k1_scalar_set_int(&verify_share_ecmult_data.idxn, 1);
vss_commitment_len = threshold;
}
/* TODO: add scratch */
if (!secp256k1_ecmult_multi_var(&ctx->error_callback, NULL, &tmpj, NULL, secp256k1_frost_verify_share_ecmult_callback, (void *) &verify_share_ecmult_data, threshold)) {
if (!secp256k1_ecmult_multi_var(&ctx->error_callback, NULL, &tmpj, NULL, secp256k1_frost_verify_share_ecmult_callback, (void *) &verify_share_ecmult_data, vss_commitment_len)) {
return 0;
}
secp256k1_scalar_negate(&share_neg, share);
Expand All @@ -337,7 +387,63 @@ int secp256k1_frost_share_verify(const secp256k1_context* ctx, size_t threshold,
return 0;
}

return secp256k1_frost_vss_verify_internal(ctx, threshold, id33, &share_i, vss_commitment);
return secp256k1_frost_vss_verify_internal(ctx, threshold, id33, &share_i, vss_commitment, 0);
}

int secp256k1_frost_refresh_share(const secp256k1_context *ctx, secp256k1_frost_share *share_after_refresh, secp256k1_pubkey * *vss_commitments_after_refresh, const secp256k1_frost_share *share, const secp256k1_pubkey * const *vss_commitments, const secp256k1_frost_share * const *refresh_shares, const secp256k1_pubkey * const *refresh_vss_commitments, size_t threshold, size_t n_participants, const unsigned char *id33) {
secp256k1_scalar acc;
size_t i, j;
int ret = 1;

VERIFY_CHECK(ctx != NULL);
ARG_CHECK(share_after_refresh != NULL);
memset(share_after_refresh, 0, sizeof(secp256k1_frost_share));
ARG_CHECK(vss_commitments_after_refresh != NULL);
ARG_CHECK(share != NULL);
ARG_CHECK(vss_commitments != NULL);
ARG_CHECK(refresh_shares != NULL);
ARG_CHECK(refresh_vss_commitments != NULL);
ARG_CHECK(id33 != NULL);
ARG_CHECK(n_participants > 1);
ARG_CHECK(threshold > 1);

if (threshold > n_participants) {
return 0;
}

if (!secp256k1_frost_share_load(ctx, &acc, share)) {
return 0;
}
for (i = 0; i < n_participants; i++) {
secp256k1_scalar refresh_share_i;
if (!secp256k1_frost_share_load(ctx, &refresh_share_i, refresh_shares[i])) {
return 0;
}

/* Verify each refresh share against its commitments. */
ret &= secp256k1_frost_vss_verify_internal(ctx, threshold, id33, &refresh_share_i, &refresh_vss_commitments[i], 1);

secp256k1_scalar_add(&acc, &acc, &refresh_share_i);
for (j = 0; j < threshold; j++) {
if (j == 0) {
/* The commitments at index 0 remain unchanged. */
vss_commitments_after_refresh[i][j] = vss_commitments[i][j];
} else {
/* The commitments at the other indices need to have the new commitments added. */
secp256k1_pubkey pk;
const secp256k1_pubkey *pubkeys[2];
pubkeys[0] = &vss_commitments[i][j];
pubkeys[1] = &refresh_vss_commitments[i][j-1];
if (!secp256k1_ec_pubkey_combine(ctx, &pk, pubkeys, 2)) {
return 0;
}
vss_commitments_after_refresh[i][j] = pk;
}
}
}
secp256k1_frost_share_save(share_after_refresh, &acc);

return ret;
}

int secp256k1_frost_compute_pubshare(const secp256k1_context* ctx, secp256k1_pubkey *pubshare, size_t threshold, const unsigned char *id33, const secp256k1_pubkey * const* vss_commitments, size_t n_participants) {
Expand Down Expand Up @@ -427,7 +533,7 @@ int secp256k1_frost_share_agg(const secp256k1_context* ctx, secp256k1_frost_shar
return 0;
}
/* Verify share against commitments */
ret &= secp256k1_frost_vss_verify_internal(ctx, threshold, id33, &share_i, &vss_commitments[i]);
ret &= secp256k1_frost_vss_verify_internal(ctx, threshold, id33, &share_i, &vss_commitments[i], 0);
secp256k1_scalar_add(&acc, &acc, &share_i);
}
secp256k1_frost_share_save(agg_share, &acc);
Expand Down

0 comments on commit a8fe0b2

Please sign in to comment.