11 Commits

Author SHA1 Message Date
Jesse Posner
536b0458ad frost trusted dealer: use set_int instead of clear 2024-05-15 12:53:45 -07:00
Jesse Posner
d3ef472559 frost trusted dealer: improve doc based upon PR feedback 2024-05-15 12:52:48 -07:00
Jesse Posner
e94367c83e frost trusted dealer: Add documentation file
This commit adds a documentation file with instructions for how to use
the module.
2024-05-14 15:29:07 -07:00
Jesse Posner
1b9567289b frost trusted dealer: add tests
Add api tests, nonce tests, tweak tests, sha256 tag tests, and constant
time tests.
2024-05-14 15:29:03 -07:00
Jesse Posner
fb34b29d7f frost trusted dealer: add example file
This commit adds an example file to demonstrate how to use the module.
2024-05-14 15:28:12 -07:00
Jesse Posner
9170dd337c frost trusted dealer: signature generation and aggregation
This commit adds signature generation and aggregation, as well as
partial signature serialization and parsing.
2024-05-14 15:28:12 -07:00
Jesse Posner
ea059393f0 frost trusted dealer: nonce aggregation and adaptor signatures
This commit adds nonce aggregation, as well as adaptor signatures.
2024-05-14 15:28:12 -07:00
Jesse Posner
5368c81a3c frost trusted dealer: key tweaking
This commits add BIP-341 ("Taproot") and BIP-32 ("ordinary") public key
tweaking.
2024-05-14 15:28:00 -07:00
Jesse Posner
9b852191de frost trusted dealer: nonce generation
This commits adds nonce generation, as well as serialization and
parsing.
2024-05-14 15:26:35 -07:00
Jesse Posner
8969cee21c frost trusted dealer: share generation
This commit adds trusted share generation, as well as share
serialization and parsing.
2024-05-14 14:32:10 -07:00
Jesse Posner
6f47d2eb22 frost trusted dealer: initialize project
This commit adds the foundational configuration and building scripts
and an initial structure for the project.
2024-05-14 14:32:05 -07:00
20 changed files with 1096 additions and 605 deletions

View File

@@ -41,6 +41,7 @@ env:
ECDSAADAPTOR: 'no'
BPPP: 'no'
SCHNORRSIG_HALFAGG: 'no'
FROST: 'no'
### test options
SECP256K1_TEST_ITERS:
BENCH: 'yes'
@@ -79,14 +80,14 @@ jobs:
matrix:
configuration:
- env_vars: { WIDEMUL: 'int64', RECOVERY: 'yes' }
- env_vars: { WIDEMUL: 'int64', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', MUSIG: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes'}
- env_vars: { WIDEMUL: 'int64', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', MUSIG: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', FROST: 'yes' }
- env_vars: { WIDEMUL: 'int128' }
- env_vars: { WIDEMUL: 'int128_struct', ELLSWIFT: 'yes' }
- env_vars: { WIDEMUL: 'int128', RECOVERY: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes' }
- env_vars: { WIDEMUL: 'int128', ECDH: 'yes', SCHNORRSIG: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', MUSIG: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes'}
- env_vars: { WIDEMUL: 'int128', ECDH: 'yes', SCHNORRSIG: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', MUSIG: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', FROST: 'yes' }
- env_vars: { WIDEMUL: 'int128', ASM: 'x86_64', ELLSWIFT: 'yes' }
- env_vars: { RECOVERY: 'yes', SCHNORRSIG: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', MUSIG: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes'}
- env_vars: { CTIMETESTS: 'no', RECOVERY: 'yes', ECDH: 'yes', SCHNORRSIG: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', MUSIG: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', CPPFLAGS: '-DVERIFY' }
- env_vars: { RECOVERY: 'yes', SCHNORRSIG: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', MUSIG: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', FROST: 'yes' }
- env_vars: { CTIMETESTS: 'no', RECOVERY: 'yes', ECDH: 'yes', SCHNORRSIG: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', MUSIG: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', FROST: 'yes', CPPFLAGS: '-DVERIFY' }
- env_vars: { BUILD: 'distcheck', WITH_VALGRIND: 'no', CTIMETESTS: 'no', BENCH: 'no' }
- env_vars: { CPPFLAGS: '-DDETERMINISTIC' }
- env_vars: { CFLAGS: '-O0', CTIMETESTS: 'no' }
@@ -158,6 +159,7 @@ jobs:
ECDSAADAPTOR: 'yes'
BPPP: 'yes'
SCHNORRSIG_HALFAGG: 'yes'
FROST: 'yes'
CC: ${{ matrix.cc }}
steps:
@@ -211,6 +213,7 @@ jobs:
ECDSAADAPTOR: 'yes'
BPPP: 'yes'
SCHNORRSIG_HALFAGG: 'yes'
FROST: 'yes'
CTIMETESTS: 'no'
steps:
@@ -271,6 +274,7 @@ jobs:
ECDSAADAPTOR: 'yes'
BPPP: 'yes'
SCHNORRSIG_HALFAGG: 'yes'
FROST: 'yes'
CTIMETESTS: 'no'
steps:
@@ -325,6 +329,7 @@ jobs:
ECDSAADAPTOR: 'yes'
BPPP: 'yes'
SCHNORRSIG_HALFAGG: 'yes'
FROST: 'yes'
CTIMETESTS: 'no'
strategy:
@@ -389,6 +394,7 @@ jobs:
ECDSAADAPTOR: 'yes'
BPPP: 'yes'
SCHNORRSIG_HALFAGG: 'yes'
FROST: 'yes'
CTIMETESTS: 'no'
steps:
@@ -450,6 +456,7 @@ jobs:
ECDSAADAPTOR: 'yes'
BPPP: 'yes'
SCHNORRSIG_HALFAGG: 'yes'
FROST: 'yes'
CTIMETESTS: 'no'
SECP256K1_TEST_ITERS: 2
@@ -510,6 +517,7 @@ jobs:
ECDSAADAPTOR: 'yes'
BPPP: 'yes'
SCHNORRSIG_HALFAGG: 'yes'
FROST: 'yes'
CTIMETESTS: 'no'
CFLAGS: '-fsanitize=undefined,address -g'
UBSAN_OPTIONS: 'print_stacktrace=1:halt_on_error=1'
@@ -576,6 +584,7 @@ jobs:
ECDSAADAPTOR: 'yes'
BPPP: 'yes'
SCHNORRSIG_HALFAGG: 'yes'
FROST: 'yes'
CTIMETESTS: 'yes'
CC: 'clang'
SECP256K1_TEST_ITERS: 32
@@ -632,6 +641,7 @@ jobs:
ECDSAADAPTOR: 'yes'
BPPP: 'yes'
SCHNORRSIG_HALFAGG: 'yes'
FROST: 'yes'
CTIMETESTS: 'no'
strategy:
@@ -688,15 +698,15 @@ jobs:
fail-fast: false
matrix:
env_vars:
- { WIDEMUL: 'int64', RECOVERY: 'yes', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', MUSIG: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes' }
- { WIDEMUL: 'int64', RECOVERY: 'yes', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', MUSIG: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', FROST: 'yes' }
- { WIDEMUL: 'int128_struct', ECMULTGENPRECISION: 2, ECMULTWINDOW: 4 }
- { WIDEMUL: 'int128', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', MUSIG: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes' }
- { WIDEMUL: 'int128', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', MUSIG: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', FROST: 'yes' }
- { WIDEMUL: 'int128', RECOVERY: 'yes' }
- { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', MUSIG: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes' }
- { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', MUSIG: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', CC: 'gcc' }
- { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', MUSIG: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', WRAPPER_CMD: 'valgrind --error-exitcode=42', SECP256K1_TEST_ITERS: 2 }
- { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', MUSIG: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', CC: 'gcc', WRAPPER_CMD: 'valgrind --error-exitcode=42', SECP256K1_TEST_ITERS: 2 }
- { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', MUSIG: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', CPPFLAGS: '-DVERIFY', CTIMETESTS: 'no' }
- { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', MUSIG: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', FROST: 'yes' }
- { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', MUSIG: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', FROST: 'yes', CC: 'gcc' }
- { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', MUSIG: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', FROST: 'yes', WRAPPER_CMD: 'valgrind --error-exitcode=42', SECP256K1_TEST_ITERS: 2 }
- { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', MUSIG: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', FROST: 'yes', CC: 'gcc', WRAPPER_CMD: 'valgrind --error-exitcode=42', SECP256K1_TEST_ITERS: 2 }
- { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', MUSIG: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', FROST: 'yes', CPPFLAGS: '-DVERIFY', CTIMETESTS: 'no' }
- BUILD: 'distcheck'
steps:
@@ -816,6 +826,7 @@ jobs:
ECDSAADAPTOR: 'yes'
BPPP: 'yes'
SCHNORRSIG_HALFAGG: 'yes'
FROST: 'yes'
steps:
- name: Checkout

View File

@@ -12,7 +12,7 @@ Added features:
* Experimental module for Confidential Assets (Pedersen commitments, range proofs, and [surjection proofs](src/modules/surjection/surjection.md)).
* Experimental module for Bulletproofs++ range proofs.
* Experimental module for [address whitelisting](src/modules/whitelist/whitelist.md).
* Experimental module for [FROST](src/modules/frost/frost.md).
* Experimental module for FROST.
Experimental features are made available for testing and review by the community. The APIs of these features should not be considered stable.

View File

@@ -14,7 +14,7 @@ print_environment() {
for var in WERROR_CFLAGS MAKEFLAGS BUILD \
ECMULTWINDOW ECMULTGENPRECISION ASM WIDEMUL WITH_VALGRIND EXTRAFLAGS \
EXPERIMENTAL ECDH RECOVERY SCHNORRSIG SCHNORRSIG_HALFAGG ELLSWIFT \
ECDSA_S2C GENERATOR RANGEPROOF WHITELIST MUSIG ECDSAADAPTOR BPPP \
ECDSA_S2C GENERATOR RANGEPROOF WHITELIST MUSIG ECDSAADAPTOR BPPP FROST \
SECP256K1_TEST_ITERS BENCH SECP256K1_BENCH_ITERS CTIMETESTS\
EXAMPLES \
HOST WRAPPER_CMD \
@@ -83,6 +83,7 @@ esac
--enable-module-schnorrsig="$SCHNORRSIG" --enable-module-musig="$MUSIG" --enable-module-ecdsa-adaptor="$ECDSAADAPTOR" \
--enable-module-schnorrsig="$SCHNORRSIG" \
--enable-module-schnorrsig-halfagg="$SCHNORRSIG_HALFAGG" \
--enable-module-frost="$FROST" \
--enable-examples="$EXAMPLES" \
--enable-ctime-tests="$CTIMETESTS" \
--with-valgrind="$WITH_VALGRIND" \

View File

@@ -236,15 +236,15 @@ AC_ARG_ENABLE(module_ecdsa-adaptor,
[],
[SECP_SET_DEFAULT([enable_module_ecdsa_adaptor], [no], [yes])])
AC_ARG_ENABLE(module_frost,
AS_HELP_STRING([--enable-module-frost],[enable FROST module [default=no]]),
[],
[SECP_SET_DEFAULT([enable_module_frost], [no], [yes])])
AC_ARG_ENABLE(external_default_callbacks,
AS_HELP_STRING([--enable-external-default-callbacks],[enable external default callback functions [default=no]]), [],
[SECP_SET_DEFAULT([enable_external_default_callbacks], [no], [no])])
AC_ARG_ENABLE(module_frost,
AS_HELP_STRING([--enable-module-frost],[enable FROST module (experimental)]),
[],
[SECP_SET_DEFAULT([enable_module_frost], [no], [yes])])
# Test-only override of the (autodetected by the C code) "widemul" setting.
# Legal values are:
# * int64 (for [u]int64_t),
@@ -475,6 +475,14 @@ if test x"$enable_module_ecdsa_adaptor" = x"yes"; then
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_ECDSA_ADAPTOR=1"
fi
if test x"$enable_module_frost" = x"yes"; then
if test x"$enable_module_schnorrsig" = x"no"; then
AC_MSG_ERROR([Module dependency error: You have disabled the schnorrsig module explicitly, but it is required by the musig module.])
fi
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_FROST=1"
enable_module_schnorrsig=yes
fi
if test x"$enable_module_musig" = x"yes"; then
if test x"$enable_module_schnorrsig" = x"no"; then
AC_MSG_ERROR([Module dependency error: You have disabled the schnorrsig module explicitly, but it is required by the musig module.])
@@ -535,14 +543,6 @@ if test x"$enable_module_ecdh" = x"yes"; then
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_ECDH=1"
fi
if test x"$enable_module_frost" = x"yes"; then
if test x"$enable_module_schnorrsig" = x"no"; then
AC_MSG_ERROR([Module dependency error: You have disabled the schnorrsig module explicitly, but it is required by the frost module.])
fi
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_FROST=1"
enable_module_schnorrsig=yes
fi
if test x"$enable_external_default_callbacks" = x"yes"; then
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DUSE_EXTERNAL_DEFAULT_CALLBACKS=1"
fi
@@ -592,12 +592,12 @@ else
if test x"$enable_module_generator" = x"yes"; then
AC_MSG_ERROR([NUMS generator module is experimental. Use --enable-experimental to allow.])
fi
if test x"$set_asm" = x"arm32"; then
AC_MSG_ERROR([ARM32 assembly is experimental. Use --enable-experimental to allow.])
fi
if test x"$enable_module_frost" = x"yes"; then
AC_MSG_ERROR([FROST module is experimental. Use --enable-experimental to allow.])
fi
if test x"$set_asm" = x"arm32"; then
AC_MSG_ERROR([ARM32 assembly is experimental. Use --enable-experimental to allow.])
fi
fi
###
@@ -666,9 +666,9 @@ echo " module whitelist = $enable_module_whitelist"
echo " module musig = $enable_module_musig"
echo " module ecdsa-s2c = $enable_module_ecdsa_s2c"
echo " module ecdsa-adaptor = $enable_module_ecdsa_adaptor"
echo " module frost = $enable_module_frost"
echo " module bppp = $enable_module_bppp"
echo " module schnorrsig-halfagg = $enable_module_schnorrsig_halfagg"
echo " module frost = $enable_module_frost"
echo
echo " asm = $set_asm"
echo " ecmult window size = $set_ecmult_window"

View File

@@ -25,10 +25,8 @@
#define THRESHOLD 3
struct signer_secrets {
secp256k1_keypair keypair;
secp256k1_frost_share agg_share;
secp256k1_frost_share share;
secp256k1_frost_secnonce secnonce;
unsigned char seed[32];
};
struct signer {
@@ -36,86 +34,26 @@ struct signer {
secp256k1_frost_pubnonce pubnonce;
secp256k1_frost_session session;
secp256k1_frost_partial_sig partial_sig;
secp256k1_pubkey vss_commitment[THRESHOLD];
unsigned char vss_hash[32];
unsigned char pok[64];
unsigned char id[33];
};
/* Create a key pair and store it in seckey and pubkey */
int create_keypair_and_seed(const secp256k1_context* ctx, struct signer_secrets *signer_secrets, struct signer *signer) {
unsigned char seckey[32];
secp256k1_pubkey pubkey_tmp;
size_t size = 33;
while (1) {
if (!fill_random(seckey, sizeof(seckey))) {
printf("Failed to generate randomness\n");
return 1;
}
if (secp256k1_keypair_create(ctx, &signer_secrets->keypair, seckey)) {
break;
}
}
if (!secp256k1_keypair_pub(ctx, &pubkey_tmp, &signer_secrets->keypair)) {
return 0;
}
if (!secp256k1_ec_pubkey_serialize(ctx, signer->id, &size, &pubkey_tmp, SECP256K1_EC_COMPRESSED)) {
return 0;
}
if (!fill_random(signer_secrets->seed, sizeof(signer_secrets->seed))) {
return 0;
}
return 1;
}
/* Create shares and coefficient commitments */
int create_shares(const secp256k1_context* ctx, struct signer_secrets *signer_secrets, struct signer *signer, secp256k1_xonly_pubkey *agg_pk) {
int i, j;
secp256k1_frost_share shares[N_SIGNERS][N_SIGNERS];
const secp256k1_pubkey *vss_commitments[N_SIGNERS];
const unsigned char *ids[N_SIGNERS];
int create_shares(const secp256k1_context* ctx, struct signer_secrets *signer_secrets, struct signer *signers, secp256k1_xonly_pubkey *pk) {
int i;
secp256k1_frost_share shares[N_SIGNERS];
secp256k1_pubkey pubshares[N_SIGNERS];
unsigned char seed[32];
for (i = 0; i < N_SIGNERS; i++) {
vss_commitments[i] = signer[i].vss_commitment;
ids[i] = signer[i].id;
if (!fill_random(seed, sizeof(seed))) {
return 0;
}
if (!secp256k1_frost_shares_trusted_gen(ctx, shares, pubshares, pk, seed, THRESHOLD, N_SIGNERS)) {
return 0;
}
for (i = 0; i < N_SIGNERS; i++) {
/* Generate a polynomial share for the participants */
if (!secp256k1_frost_shares_gen(ctx, shares[i], signer[i].vss_commitment, signer[i].pok, signer_secrets[i].seed, THRESHOLD, N_SIGNERS, ids)) {
return 0;
}
}
/* KeyGen communication round 1: exchange shares and coefficient
* commitments */
for (i = 0; i < N_SIGNERS; i++) {
const secp256k1_frost_share *assigned_shares[N_SIGNERS];
/* Each participant receives a share from each participant (including
* themselves) corresponding to their index. */
for (j = 0; j < N_SIGNERS; j++) {
assigned_shares[j] = &shares[j][i];
}
/* Each participant aggregates the shares they received. */
if (!secp256k1_frost_share_agg(ctx, &signer_secrets[i].agg_share, agg_pk, assigned_shares, vss_commitments, N_SIGNERS, THRESHOLD, signer[i].id)) {
return 0;
}
for (j = 0; j < N_SIGNERS; j++) {
/* Each participant verifies their shares. share_agg calls this
* internally, so it is only neccessary to call this function if
* share_agg returns an error, to determine which participant(s)
* submitted faulty data. */
if (!secp256k1_frost_share_verify(ctx, THRESHOLD, signer[i].id, assigned_shares[j], &vss_commitments[j])) {
return 0;
}
/* Each participant generates public verification shares that are
* used for verifying partial signatures. */
if (!secp256k1_frost_compute_pubshare(ctx, &signer[j].pubshare, THRESHOLD, signer[j].id, vss_commitments, N_SIGNERS)) {
return 0;
}
}
signer_secrets[i].share = shares[i];
signers[i].pubshare = pubshares[i];
}
return 1;
@@ -123,12 +61,12 @@ int create_shares(const secp256k1_context* ctx, struct signer_secrets *signer_se
/* Tweak the pubkey corresponding to the provided tweak cache, update the cache
* and return the tweaked aggregate pk. */
int tweak(const secp256k1_context* ctx, secp256k1_xonly_pubkey *agg_pk, secp256k1_frost_tweak_cache *cache) {
int tweak(const secp256k1_context* ctx, secp256k1_xonly_pubkey *pk, secp256k1_frost_tweak_cache *cache) {
secp256k1_pubkey output_pk;
unsigned char ordinary_tweak[32] = "this could be a BIP32 tweak....";
unsigned char xonly_tweak[32] = "this could be a taproot tweak..";
if (!secp256k1_frost_pubkey_tweak(ctx, cache, agg_pk)) {
if (!secp256k1_frost_pubkey_tweak(ctx, cache, pk)) {
return 0;
}
@@ -154,7 +92,7 @@ int tweak(const secp256k1_context* ctx, secp256k1_xonly_pubkey *agg_pk, secp256k
* the Schnorr signature against it. For this purpose we can ignore the
* `pk_parity` output argument; we would need it if we would have to open
* the taproot commitment. */
if (!secp256k1_xonly_pubkey_from_pubkey(ctx, agg_pk, NULL, &output_pk)) {
if (!secp256k1_xonly_pubkey_from_pubkey(ctx, pk, NULL, &output_pk)) {
return 0;
}
return 1;
@@ -162,17 +100,18 @@ int tweak(const secp256k1_context* ctx, secp256k1_xonly_pubkey *agg_pk, secp256k
/* Sign a message hash with the given threshold and aggregate shares and store
* the result in sig */
int sign(const secp256k1_context* ctx, struct signer_secrets *signer_secrets, struct signer *signer, const unsigned char* msg32, secp256k1_xonly_pubkey *agg_pk, unsigned char *sig64, const secp256k1_frost_tweak_cache *cache) {
int sign(const secp256k1_context* ctx, struct signer_secrets *signer_secrets, struct signer *signer, const unsigned char* msg32, secp256k1_xonly_pubkey *pk, unsigned char *sig64, const secp256k1_frost_tweak_cache *cache) {
int i;
int signer_id = 0;
size_t signer_id = 0;
int signers[THRESHOLD];
int is_signer[N_SIGNERS];
const secp256k1_frost_pubnonce *pubnonces[THRESHOLD];
const unsigned char *ids[THRESHOLD];
size_t ids[THRESHOLD];
const secp256k1_frost_partial_sig *partial_sigs[THRESHOLD];
for (i = 0; i < N_SIGNERS; i++) {
unsigned char session_id[32];
/* Create random session ID. It is absolutely necessary that the session ID
* is unique for every call of secp256k1_frost_nonce_gen. Otherwise
* it's trivial for an attacker to extract the secret key! */
@@ -181,14 +120,14 @@ int sign(const secp256k1_context* ctx, struct signer_secrets *signer_secrets, st
}
/* Initialize session and create secret nonce for signing and public
* nonce to send to the other signers. */
if (!secp256k1_frost_nonce_gen(ctx, &signer_secrets[i].secnonce, &signer[i].pubnonce, session_id, &signer_secrets[i].agg_share, msg32, agg_pk, NULL)) {
if (!secp256k1_frost_nonce_gen(ctx, &signer_secrets[i].secnonce, &signer[i].pubnonce, session_id, &signer_secrets[i].share, msg32, pk, NULL)) {
return 0;
}
is_signer[i] = 0; /* Initialize is_signer */
}
/* Select a random subset of signers */
for (i = 0; i < THRESHOLD; i++) {
unsigned int subset_seed;
size_t subset_seed;
while (1) {
if (!fill_random((unsigned char*)&subset_seed, sizeof(subset_seed))) {
@@ -205,19 +144,19 @@ int sign(const secp256k1_context* ctx, struct signer_secrets *signer_secrets, st
/* Mark signer as assigned */
pubnonces[i] = &signer[signer_id].pubnonce;
/* pubkeys[i] = &signer[signer_id].pubkey; */
ids[i] = signer[signer_id].id;
ids[i] = signer_id + 1;
}
/* Signing communication round 1: Exchange nonces */
for (i = 0; i < THRESHOLD; i++) {
signer_id = signers[i];
if (!secp256k1_frost_nonce_process(ctx, &signer[signer_id].session, pubnonces, THRESHOLD, msg32, agg_pk, signer[signer_id].id, ids, cache, NULL)) {
if (!secp256k1_frost_nonce_process(ctx, &signer[signer_id].session, pubnonces, THRESHOLD, msg32, pk, signer_id + 1, ids, cache, NULL)) {
return 0;
}
/* partial_sign will clear the secnonce by setting it to 0. That's because
* you must _never_ reuse the secnonce (or use the same session_id to
* create a secnonce). If you do, you effectively reuse the nonce and
* leak the secret key. */
if (!secp256k1_frost_partial_sign(ctx, &signer[signer_id].partial_sig, &signer_secrets[signer_id].secnonce, &signer_secrets[signer_id].agg_share, &signer[signer_id].session, cache)) {
if (!secp256k1_frost_partial_sign(ctx, &signer[signer_id].partial_sig, &signer_secrets[signer_id].secnonce, &signer_secrets[signer_id].share, &signer[signer_id].session, cache)) {
return 0;
}
partial_sigs[i] = &signer[signer_id].partial_sig;
@@ -246,45 +185,36 @@ int sign(const secp256k1_context* ctx, struct signer_secrets *signer_secrets, st
int main(void) {
secp256k1_context* ctx;
int i;
struct signer_secrets signer_secrets[N_SIGNERS];
struct signer signers[N_SIGNERS];
secp256k1_xonly_pubkey agg_pk;
secp256k1_xonly_pubkey pk;
secp256k1_frost_tweak_cache cache;
unsigned char msg[32] = "this_could_be_the_hash_of_a_msg!";
unsigned char sig[64];
/* Create a context for signing and verification */
ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
printf("Creating key pairs......");
for (i = 0; i < N_SIGNERS; i++) {
if (!create_keypair_and_seed(ctx, &signer_secrets[i], &signers[i])) {
printf("FAILED\n");
return 1;
}
}
printf("ok\n");
printf("Creating shares.........");
if (!create_shares(ctx, signer_secrets, signers, &agg_pk)) {
if (!create_shares(ctx, signer_secrets, signers, &pk)) {
printf("FAILED\n");
return 1;
}
printf("ok\n");
printf("Tweaking................");
/* Optionally tweak the aggregate key */
if (!tweak(ctx, &agg_pk, &cache)) {
/* Optionally tweak the key */
if (!tweak(ctx, &pk, &cache)) {
printf("FAILED\n");
return 1;
}
printf("ok\n");
printf("Signing message.........");
if (!sign(ctx, signer_secrets, signers, msg, &agg_pk, sig, &cache)) {
if (!sign(ctx, signer_secrets, signers, msg, &pk, sig, &cache)) {
printf("FAILED\n");
return 1;
}
printf("ok\n");
printf("Verifying signature.....");
if (!secp256k1_schnorrsig_verify(ctx, sig, msg, 32, &agg_pk)) {
if (!secp256k1_schnorrsig_verify(ctx, sig, msg, 32, &pk)) {
printf("FAILED\n");
return 1;
}

View File

@@ -9,13 +9,14 @@ extern "C" {
#include <stdint.h>
/** This code is currently a work in progress. It's not secure nor stable.
* IT IS EXTREMELY DANGEROUS AND RECKLESS TO USE THIS MODULE IN PRODUCTION!
*
/** This code is currently a work in progress. It's not secure nor stable. IT
* IS EXTREMELY DANGEROUS AND RECKLESS TO USE THIS MODULE IN PRODUCTION!
* This module implements a variant of Flexible Round-Optimized Schnorr
* Threshold Signatures (FROST) by Chelsea Komlo and Ian Goldberg
* (https://crysp.uwaterloo.ca/software/frost/). Signatures are compatible with
* BIP-340 ("Schnorr").
* BIP-340 ("Schnorr"). There's an example C source file in the module's
* directory (examples/frost.c) that demonstrates how it can be used.
*
* The module also supports BIP-341 ("Taproot") and BIP-32 ("ordinary") public
* key tweaking, and adaptor signatures.
@@ -182,14 +183,12 @@ SECP256K1_API int secp256k1_frost_share_parse(
/** 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
* To generate a key, a trusted dealer generates a share for each other
* participant.
*
* The trusted dealer must transmit shares over secure channels to
* participants.
*
* 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).
@@ -197,109 +196,25 @@ SECP256K1_API int secp256k1_frost_share_parse(
* 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.
* pubshares: pointer to the public verification shares
* pk: pointer to the x-only public key
* In: seed32: a 32-byte random seed as explained above. Must be
* unique to this call to
* secp256k1_frost_shares_trusted_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(
SECP256K1_API int secp256k1_frost_shares_trusted_gen(
const secp256k1_context *ctx,
secp256k1_frost_share *shares,
secp256k1_pubkey *vss_commitment,
unsigned char *pok64,
secp256k1_pubkey *pubshares,
secp256k1_xonly_pubkey *pk,
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);
/** Aggregates shares
*
* As part of the key generation protocol, each participant receives a share
* from each participant, including a share they "receive" from themselves.
* This function verifies those shares against their VSS commitments,
* aggregates the shares, and then aggregates the commitments to each
* participant's first polynomial coefficient to derive the aggregate public
* key.
*
* If this function returns an error, `secp256k1_frost_share_verify` can be
* called on each share to determine which participants submitted faulty
* shares.
*
* Returns: 0 if the arguments are invalid, 1 otherwise (which does NOT mean
* the resulting signature verifies).
* Args: ctx: pointer to a context object
* Out: agg_share: the aggregated share
* agg_pk: the aggregated x-only public key
* In: shares: all key generation shares for the partcipant's index
* vss_commitments: coefficient commitments of all participants ordered by
* the x-only pubkeys of the participants
* n_shares: the total number of shares
* threshold: the minimum number of shares required to produce a
* signature
* id33: the 33-byte ID of the participant whose shares are being
* aggregated
*/
SECP256K1_API int secp256k1_frost_share_agg(
const secp256k1_context *ctx,
secp256k1_frost_share *agg_share,
secp256k1_xonly_pubkey *agg_pk,
const secp256k1_frost_share * const *shares,
const secp256k1_pubkey * const *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(8);
/** Verifies a share received during a key generation session
*
* The signature is verified against the VSS commitment received with the
* share. This is only useful for purposes of determining which share(s) are
* invalid if share_agg returns an error.
*
* Returns: 0 if the arguments are invalid or the share does not verify, 1
* otherwise
* Args ctx: pointer to a context object
* In: threshold: the minimum number of signers required to produce a
* signature
* id33: the 33-byte participant ID of the share recipient
* share: pointer to a key generation share
* vss_commitment: the VSS commitment associated with the share
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_frost_share_verify(
const secp256k1_context *ctx,
size_t threshold,
const unsigned char *id33,
const secp256k1_frost_share *share,
const secp256k1_pubkey * const *vss_commitment
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5);
/** Computes a public verification share used for verifying partial signatures
*
* Returns: 0 if the arguments are invalid, 1 otherwise
* Args: ctx: pointer to a context object
* In: pubshare: pointer to a struct to store the public verification
* share
* threshold: the minimum number of signers required to produce a
* signature
* id33: the 33-byte participant ID of the participant whose
* partial signature will be verified with the pubshare
* vss_commitments: coefficient commitments of all participants
* n_participants: the total number of participants
*/
SECP256K1_API 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
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5);
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5);
/** Obtain the aggregate public key from a FROST x-only aggregate public key.
*
@@ -311,7 +226,7 @@ SECP256K1_API int secp256k1_frost_compute_pubshare(
* Args: ctx: pointer to a context object
* Out: ec_agg_pk: the FROST-aggregated public key.
* In: xonly_agg_pk: the aggregated x-only public key that is the output of
* `secp256k1_frost_share_agg`
* `secp256k1_frost_shares_gen`
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_frost_pubkey_get(
const secp256k1_context *ctx,
@@ -326,7 +241,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_frost_pubkey_get(
* Out: tweak_cache: pointer to a frost_tweak_cache struct that is required
* for key tweaking
* In: agg_pk: the aggregated x-only public key that is the output of
* `secp256k1_frost_share_agg`
* `secp256k1_frost_shares_gen`
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_frost_pubkey_tweak(
const secp256k1_context *ctx,
@@ -342,7 +257,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_frost_pubkey_tweak(
* the following pseudocode buf and buf2 have identical contents (absent
* earlier failures).
*
* secp256k1_frost_share_agg(..., xonly_agg_pk, ...)
* secp256k1_frost_shares_gen(..., xonly_agg_pk, ...)
* secp256k1_frost_pubkey_tweak(..., tweak_cache, xonly_agg_pk)
* secp256k1_frost_pubkey_ec_tweak_add(..., output_pk, tweak_cache, tweak32)
* secp256k1_ec_pubkey_serialize(..., buf, output_pk)
@@ -385,7 +300,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_frost_pubkey_ec_tweak_a
* the following pseudocode xonly_pubkey_tweak_add_check (absent earlier
* failures) returns 1.
*
* secp256k1_frost_share_agg(..., agg_pk, ...)
* secp256k1_frost_shares_gen(..., agg_pk, ...)
* secp256k1_frost_pubkey_tweak(..., tweak_cache, agg_pk)
* secp256k1_frost_pubkey_xonly_tweak_add(..., output_pk, tweak_cache, tweak32)
* secp256k1_xonly_pubkey_serialize(..., buf, output_pk)
@@ -486,9 +401,9 @@ SECP256K1_API int secp256k1_frost_nonce_gen(
* 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
* my_id: the ID of the participant who will use the session for
* signing
* ids: array of the 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
@@ -501,11 +416,11 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_frost_nonce_process(
size_t n_pubnonces,
const unsigned char *msg32,
const secp256k1_xonly_pubkey *agg_pk,
const unsigned char *my_id33,
const unsigned char * const* ids33,
size_t my_id,
const size_t *ids,
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);
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6) SECP256K1_ARG_NONNULL(8);
/** Produces a partial signature
*

View File

@@ -47,6 +47,10 @@
#include "../include/secp256k1_musig.h"
#endif
#ifdef ENABLE_MODULE_FROST
#include "include/secp256k1_frost.h"
#endif
static void run_tests(secp256k1_context *ctx, unsigned char *key);
int main(void) {
@@ -349,4 +353,79 @@ static void run_tests(secp256k1_context *ctx, unsigned char *key) {
CHECK(ret == 1);
}
#endif
#ifdef ENABLE_MODULE_FROST
{
secp256k1_xonly_pubkey pk;
unsigned char session_id[32];
secp256k1_frost_secnonce secnonce[2];
secp256k1_frost_pubnonce pubnonce[2];
const secp256k1_frost_pubnonce *pubnonce_ptr[2];
secp256k1_frost_tweak_cache cache;
secp256k1_frost_session session;
secp256k1_frost_partial_sig partial_sig;
const secp256k1_frost_partial_sig *partial_sig_ptr[1];
unsigned char extra_input[32];
unsigned char sec_adaptor[32];
secp256k1_pubkey adaptor;
unsigned char pre_sig[64];
int nonce_parity;
secp256k1_frost_share shares[2];
secp256k1_pubkey pubshares[2];
size_t ids[2];
pubnonce_ptr[0] = &pubnonce[0];
pubnonce_ptr[1] = &pubnonce[1];
SECP256K1_CHECKMEM_DEFINE(key, 32);
memcpy(extra_input, key, sizeof(extra_input));
extra_input[0] = extra_input[0] + 1;
memcpy(sec_adaptor, key, sizeof(sec_adaptor));
sec_adaptor[0] = extra_input[0] + 2;
memcpy(session_id, key, sizeof(session_id));
session_id[0] = session_id[0] + 3;
partial_sig_ptr[0] = &partial_sig;
ids[0] = 1;
ids[1] = 2;
/* shares_gen */
SECP256K1_CHECKMEM_UNDEFINE(key, 32);
ret = secp256k1_frost_shares_trusted_gen(ctx, shares, pubshares, &pk, key, 2, 2);
SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
CHECK(ret == 1);
SECP256K1_CHECKMEM_UNDEFINE(&shares[0], sizeof(shares[0]));
SECP256K1_CHECKMEM_UNDEFINE(&shares[1], sizeof(shares[1]));
/* nonce_gen */
SECP256K1_CHECKMEM_UNDEFINE(session_id, sizeof(session_id));
CHECK(secp256k1_ec_pubkey_create(ctx, &adaptor, sec_adaptor));
SECP256K1_CHECKMEM_UNDEFINE(extra_input, sizeof(extra_input));
SECP256K1_CHECKMEM_UNDEFINE(sec_adaptor, sizeof(sec_adaptor));
CHECK(secp256k1_frost_pubkey_tweak(ctx, &cache, &pk) == 1);
ret = secp256k1_frost_nonce_gen(ctx, &secnonce[0], &pubnonce[0], session_id, &shares[0], msg, &pk, extra_input);
SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
CHECK(ret == 1);
ret = secp256k1_frost_nonce_gen(ctx, &secnonce[1], &pubnonce[1], session_id, &shares[1], msg, &pk, extra_input);
SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
CHECK(ret == 1);
/* partial_sign */
CHECK(secp256k1_frost_nonce_process(ctx, &session, pubnonce_ptr, 2, msg, &pk, 1, ids, &cache, &adaptor) == 1);
ret = secp256k1_keypair_create(ctx, &keypair, key);
SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
CHECK(ret == 1);
ret = secp256k1_frost_partial_sign(ctx, &partial_sig, &secnonce[0], &shares[0], &session, &cache);
SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
CHECK(ret == 1);
/* adapt */
SECP256K1_CHECKMEM_DEFINE(&partial_sig, sizeof(partial_sig));
CHECK(secp256k1_frost_partial_sig_agg(ctx, pre_sig, &session, partial_sig_ptr, 1));
SECP256K1_CHECKMEM_DEFINE(pre_sig, sizeof(pre_sig));
CHECK(secp256k1_frost_nonce_parity(ctx, &nonce_parity, &session));
ret = secp256k1_frost_adapt(ctx, sig, pre_sig, sec_adaptor, nonce_parity);
SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
CHECK(ret == 1);
/* extract_adaptor */
ret = secp256k1_frost_extract_adaptor(ctx, sec_adaptor, sig, pre_sig, nonce_parity);
SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
CHECK(ret == 1);
}
#endif
}

View File

@@ -5,3 +5,4 @@ noinst_HEADERS += src/modules/frost/keygen_impl.h
noinst_HEADERS += src/modules/frost/session.h
noinst_HEADERS += src/modules/frost/session_impl.h
noinst_HEADERS += src/modules/frost/adaptor_impl.h
noinst_HEADERS += src/modules/frost/tests_impl.h

View File

@@ -1,5 +1,5 @@
/***********************************************************************
* Copyright (c) 2022-2024 Jesse Posner *
* Copyright (c) 2022-2023 Jesse Posner *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or https://www.opensource.org/licenses/mit-license.php.*
***********************************************************************/

View File

@@ -0,0 +1,78 @@
Notes on the frost module API
===========================
The following sections contain additional notes on the API of the frost module
(`include/secp256k1_frost.h`). A usage example can be found in
`examples/frost.c`.
# API misuse
Users of the frost module must take great care to make sure of the following:
1. The dealer establishes a secure communications channel with each participant
and uses that channel to transmit shares during key generation.
2. A unique set of coefficients per key generation session is generated in
`secp256k1_frost_share_gen`. See the corresponding comment in
`include/secp256k1_frost.h` for how to ensure that.
3. The `pubnonces` provided to `secp256k1_frost_nonce_process` are sorted by
the corresponding lexicographic ordering of the x-only pubkey of each
participant, and the `pubkeys` provided to `secp256k1_frost_nonce_process`
are sorted lexicographically.
4. A unique nonce per signing session is generated in
`secp256k1_frost_nonce_gen`. See the corresponding comment in
`include/secp256k1_frost.h` for how to ensure that.
5. The `secp256k1_frost_secnonce` structure is never copied or serialized. See
also the comment on `secp256k1_frost_secnonce` in
`include/secp256k1_frost.h`.
6. Opaque data structures are never written to or read from directly. Instead,
only the provided accessor functions are used.
7. If adaptor signatures are used, all partial signatures are verified.
# Key Generation
1. A trusted dealer generates shares with `secp256k1_frost_shares_trusted_gen`
and distributes a share and the public key to each participant using a
secure channel.
# Tweaking
A (Taproot) tweak can be added to the resulting public key with
`secp256k1_xonly_pubkey_tweak_add`, after converting it to an xonly pubkey if
necessary with `secp256k1_xonly_pubkey_from_pubkey`.
An ordinary tweak can be added to the resulting public key with
`secp256k1_ec_pubkey_tweak_add`, after converting it to an ordinary pubkey if
necessary with `secp256k1_frost_pubkey_get`.
Tweaks can also be chained together by tweaking an already tweaked key.
# Signing
1. Optionally add a tweak by calling `secp256k1_frost_pubkey_tweak` and then
`secp256k1_frost_pubkey_xonly_tweak_add` for a Taproot tweak and
`secp256k1_frost_pubkey_ec_tweak_add` for an ordinary tweak.
2. Generate a pair of secret and public nonce with `secp256k1_frost_nonce_gen`
and send the public nonce to the other signers.
3. Process the aggregate nonce with `secp256k1_frost_nonce_process`.
4. Create a partial signature with `secp256k1_frost_partial_sign`.
5. Verify the partial signatures (optional in some scenarios) with
`secp256k1_frost_partial_sig_verify`.
6. Someone (not necessarily the signer) obtains all partial signatures and
aggregates them into the final Schnorr signature using
`secp256k1_frost_partial_sig_agg`.
The aggregate signature can be verified with `secp256k1_schnorrsig_verify`.
Note that steps 1 to 3 can happen before the message to be signed is known to
the signers. Therefore, the communication round to exchange nonces can be
viewed as a pre-processing step that is run whenever convenient to the signers.
This disables some of the defense-in-depth measures that may protect against
API misuse in some cases. Similarly, the API supports an alternative protocol
flow where generating the key (see Key Generation above) is allowed to happen
after exchanging nonces (step 2).
# Verification
A participant who wants to verify the partial signatures, but does not sign
itself may do so using the above instructions except that the verifier skips
steps 2 and 4.

View File

@@ -1,5 +1,5 @@
/**********************************************************************
* Copyright (c) 2021-2024 Jesse Posner *
* Copyright (c) 2021-2023 Jesse Posner *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/

View File

@@ -1,5 +1,5 @@
/**********************************************************************
* Copyright (c) 2021-2024 Jesse Posner *
* Copyright (c) 2021-2023 Jesse Posner *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
@@ -53,24 +53,6 @@ static int secp256k1_tweak_cache_load(const secp256k1_context* ctx, secp256k1_tw
return 1;
}
/* 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) {
@@ -81,6 +63,8 @@ static void secp256k1_frost_share_save(secp256k1_frost_share* share, secp256k1_s
static int secp256k1_frost_share_load(const secp256k1_context* ctx, secp256k1_scalar *s, const secp256k1_frost_share* share) {
int overflow;
/* The magic is non-secret so it can be declassified to allow branching. */
secp256k1_declassify(ctx, &share->data[0], 4);
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 */
@@ -111,82 +95,14 @@ int secp256k1_frost_share_parse(const secp256k1_context* ctx, secp256k1_frost_sh
return 1;
}
static void secp256k1_frost_derive_coeff(secp256k1_scalar *coeff, const unsigned char *polygen32, size_t i) {
int secp256k1_frost_shares_trusted_gen(const secp256k1_context *ctx, secp256k1_frost_share *shares, secp256k1_pubkey *pubshares, secp256k1_xonly_pubkey *pk, const unsigned char *seed32, size_t threshold, size_t n_participants) {
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;
size_t i, j;
int ret = 1;
int pk_parity = 0;
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
@@ -194,254 +110,69 @@ int secp256k1_frost_shares_gen(const secp256k1_context *ctx, secp256k1_frost_sha
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(pubshares != NULL);
ARG_CHECK(pk != 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);
/* Commit to threshold, n_participants, and seed */
secp256k1_sha256_initialize_tagged(&sha, (unsigned char*)"FROST/trusted-shares-polygen", sizeof("FROST/trusted-shares-polygen") - 1);
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);
/* Derive shares */
/* See draft-irtf-cfrg-frost-08#appendix-C.1 */
for (i = 0; i < n_participants; i++) {
ret &= secp256k1_frost_share_gen(&shares[i], polygen, threshold, ids33[i]);
}
secp256k1_scalar share_i, idx;
return ret;
}
secp256k1_scalar_set_int(&share_i, 0);
typedef struct {
const secp256k1_context *ctx;
secp256k1_scalar idx;
secp256k1_scalar idxn;
const secp256k1_pubkey * const* vss_commitment;
} secp256k1_frost_verify_share_ecmult_data;
for (j = 0; j < threshold; j++) {
unsigned char buf[32];
secp256k1_scalar coeff_i;
typedef struct {
const secp256k1_context *ctx;
secp256k1_scalar idx;
secp256k1_scalar idxn;
const secp256k1_pubkey * const* vss_commitments;
size_t threshold;
} secp256k1_frost_compute_pubshare_ecmult_data;
secp256k1_sha256_initialize_tagged(&sha, (unsigned char*)"FROST/trusted-shares-coeffgen", sizeof("FROST/trusted-shares-coeffgen") - 1);
secp256k1_sha256_write(&sha, polygen, 32);
secp256k1_write_be64(&buf[0], j);
secp256k1_sha256_write(&sha, buf, 8);
secp256k1_sha256_finalize(&sha, buf);
secp256k1_scalar_set_b32(&coeff_i, buf, NULL);
typedef struct {
const secp256k1_context *ctx;
const secp256k1_pubkey * const* pks;
size_t threshold;
} secp256k1_frost_pubkey_combine_ecmult_data;
/* Horner's method to evaluate polynomial to derive shares */
secp256k1_scalar_add(&share_i, &share_i, &coeff_i);
if (j < threshold - 1) {
secp256k1_scalar_set_int(&idx, i + 1);
secp256k1_scalar_mul(&share_i, &share_i, &idx);
}
static int secp256k1_frost_verify_share_ecmult_callback(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *data) {
secp256k1_frost_verify_share_ecmult_data *ctx = (secp256k1_frost_verify_share_ecmult_data *) data;
if (!secp256k1_pubkey_load(ctx->ctx, pt, *(ctx->vss_commitment)+idx)) {
return 0;
}
*sc = ctx->idxn;
secp256k1_scalar_mul(&ctx->idxn, &ctx->idxn, &ctx->idx);
return 1;
}
static int secp256k1_frost_compute_pubshare_ecmult_callback(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *data) {
secp256k1_frost_compute_pubshare_ecmult_data *ctx = (secp256k1_frost_compute_pubshare_ecmult_data *) data;
if (!secp256k1_pubkey_load(ctx->ctx, pt, &ctx->vss_commitments[idx/ctx->threshold][idx % ctx->threshold])) {
return 0;
}
if (idx != 0 && idx % ctx->threshold == 0) {
secp256k1_scalar_set_int(&ctx->idxn, 1);
}
*sc = ctx->idxn;
secp256k1_scalar_mul(&ctx->idxn, &ctx->idxn, &ctx->idx);
return 1;
}
static int secp256k1_frost_pubkey_combine_callback(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *data) {
secp256k1_frost_pubkey_combine_ecmult_data *ctx = (secp256k1_frost_pubkey_combine_ecmult_data *) data;
secp256k1_scalar_set_int(sc, 1);
/* the public key is the first index of each set of coefficients */
return secp256k1_pubkey_load(ctx->ctx, pt, &ctx->pks[idx][0]);
}
/* 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) {
secp256k1_scalar share_neg;
secp256k1_gej tmpj, snj;
secp256k1_ge sng;
secp256k1_frost_verify_share_ecmult_data verify_share_ecmult_data;
ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
/* Use an EC multi-multiplication to verify the following equation:
* 0 = - share_i*G + idx^0*vss_commitment[0]
* + ...
* + idx^(threshold - 1)*vss_commitment[threshold - 1]*/
verify_share_ecmult_data.ctx = ctx;
verify_share_ecmult_data.vss_commitment = vss_commitment;
/* Evaluate the public polynomial at the idx */
if (!secp256k1_frost_compute_indexhash(&verify_share_ecmult_data.idx, id33)) {
return 0;
}
secp256k1_scalar_set_int(&verify_share_ecmult_data.idxn, 1);
/* 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)) {
return 0;
}
secp256k1_scalar_negate(&share_neg, share);
secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &snj, &share_neg);
secp256k1_ge_set_gej(&sng, &snj);
secp256k1_gej_add_ge(&tmpj, &tmpj, &sng);
return secp256k1_gej_is_infinity(&tmpj);
}
/* See draft-irtf-cfrg-frost-08#appendix-C.2 */
int secp256k1_frost_share_verify(const secp256k1_context* ctx, size_t threshold, const unsigned char *id33, const secp256k1_frost_share *share, const secp256k1_pubkey * const* vss_commitment) {
secp256k1_scalar share_i;
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(id33 != NULL);
ARG_CHECK(share != NULL);
ARG_CHECK(vss_commitment != NULL);
ARG_CHECK(threshold > 1);
if (!secp256k1_frost_share_load(ctx, &share_i, share)) {
return 0;
}
return secp256k1_frost_vss_verify_internal(ctx, threshold, id33, &share_i, vss_commitment);
}
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) {
secp256k1_gej pkj;
secp256k1_ge pkp, tmp;
secp256k1_frost_compute_pubshare_ecmult_data compute_pubshare_ecmult_data;
secp256k1_frost_pubkey_combine_ecmult_data pubkey_combine_ecmult_data;
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(pubshare != NULL);
memset(pubshare, 0, sizeof(*pubshare));
ARG_CHECK(id33 != NULL);
ARG_CHECK(vss_commitments != NULL);
ARG_CHECK(n_participants > 1);
ARG_CHECK(threshold > 1);
if (threshold > n_participants) {
return 0;
}
/* Use an EC multi-multiplication to compute the following equation:
* agg_share_i*G = (
* idx^0*vss_commitment[0][0] + ...
* + idx^(t - 1)*vss_commitment[0][t - 1]
* ) + ...
* + (
* idx^0*vss_commitment[n - 1][0] + ...
* + idx^(t - 1)*vss_commitment[n - 1][t - 1]
* )*/
compute_pubshare_ecmult_data.ctx = ctx;
compute_pubshare_ecmult_data.vss_commitments = vss_commitments;
compute_pubshare_ecmult_data.threshold = threshold;
/* Evaluate the public polynomial at the idx */
if (!secp256k1_frost_compute_indexhash(&compute_pubshare_ecmult_data.idx, id33)) {
return 0;
}
secp256k1_scalar_set_int(&compute_pubshare_ecmult_data.idxn, 1);
/* TODO: add scratch */
if (!secp256k1_ecmult_multi_var(&ctx->error_callback, NULL, &pkj, NULL, secp256k1_frost_compute_pubshare_ecmult_callback, (void *) &compute_pubshare_ecmult_data, n_participants*threshold)) {
return 0;
}
secp256k1_ge_set_gej(&tmp, &pkj);
/* Combine pubkeys */
pubkey_combine_ecmult_data.ctx = ctx;
pubkey_combine_ecmult_data.pks = vss_commitments;
pubkey_combine_ecmult_data.threshold = threshold;
/* TODO: add scratch */
if (!secp256k1_ecmult_multi_var(&ctx->error_callback, NULL, &pkj, NULL, secp256k1_frost_pubkey_combine_callback, (void *) &pubkey_combine_ecmult_data, n_participants)) {
return 0;
}
secp256k1_ge_set_gej(&pkp, &pkj);
secp256k1_fe_normalize_var(&pkp.y);
if (secp256k1_fe_is_odd(&pkp.y)) {
secp256k1_ge_neg(&tmp, &tmp);
}
secp256k1_pubkey_save(pubshare, &tmp);
return 1;
}
int secp256k1_frost_share_agg(const secp256k1_context* ctx, secp256k1_frost_share *agg_share, secp256k1_xonly_pubkey *agg_pk, const secp256k1_frost_share * const* shares, const secp256k1_pubkey * const* vss_commitments, size_t n_shares, size_t threshold, const unsigned char *id33) {
secp256k1_frost_pubkey_combine_ecmult_data pubkey_combine_ecmult_data;
secp256k1_gej pkj;
secp256k1_ge pkp;
int pk_parity;
secp256k1_scalar acc;
size_t i;
int ret = 1;
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(agg_share != NULL);
memset(agg_share, 0, sizeof(*agg_share));
ARG_CHECK(agg_pk != NULL);
memset(agg_pk, 0, sizeof(*agg_pk));
ARG_CHECK(shares != NULL);
ARG_CHECK(vss_commitments != NULL);
ARG_CHECK(id33 != NULL);
ARG_CHECK(n_shares > 1);
ARG_CHECK(threshold > 1);
if (threshold > n_shares) {
return 0;
}
secp256k1_scalar_clear(&acc);
for (i = 0; i < n_shares; i++) {
secp256k1_scalar share_i;
if (!secp256k1_frost_share_load(ctx, &share_i, shares[i])) {
return 0;
/* Compute x-only public key for constant term */
if (i == 0 && j == threshold - 1) {
/* Compute commitment to constant term */
secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &rj, &coeff_i);
secp256k1_ge_set_gej(&rp, &rj);
/* The commitment is non-secret so it can be declassified to
* allow branching. */
secp256k1_declassify(ctx, &rp, sizeof(rp));
secp256k1_fe_normalize_var(&rp.y);
pk_parity = secp256k1_extrakeys_ge_even_y(&rp);
secp256k1_xonly_pubkey_save(pk, &rp);
}
}
/* Verify share against commitments */
ret &= secp256k1_frost_vss_verify_internal(ctx, threshold, id33, &share_i, &vss_commitments[i]);
secp256k1_scalar_add(&acc, &acc, &share_i);
if (pk_parity == 1) {
secp256k1_scalar_negate(&share_i, &share_i);
}
secp256k1_frost_share_save(&shares[i], &share_i);
/* Compute pubshare */
secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &rj, &share_i);
secp256k1_ge_set_gej(&rp, &rj);
secp256k1_pubkey_save(&pubshares[i], &rp);
}
/* Combine pubkeys */
pubkey_combine_ecmult_data.ctx = ctx;
pubkey_combine_ecmult_data.pks = vss_commitments;
pubkey_combine_ecmult_data.threshold = threshold;
/* TODO: add scratch */
if (!secp256k1_ecmult_multi_var(&ctx->error_callback, NULL, &pkj, NULL, secp256k1_frost_pubkey_combine_callback, (void *) &pubkey_combine_ecmult_data, n_shares)) {
return 0;
}
secp256k1_ge_set_gej(&pkp, &pkj);
secp256k1_fe_normalize_var(&pkp.y);
pk_parity = secp256k1_extrakeys_ge_even_y(&pkp);
secp256k1_xonly_pubkey_save(agg_pk, &pkp);
/* Invert the aggregate share if the combined pubkey has an odd Y coordinate. */
if (pk_parity == 1) {
secp256k1_scalar_negate(&acc, &acc);
}
secp256k1_frost_share_save(agg_share, &acc);
return ret;
}

View File

@@ -1,5 +1,5 @@
/**********************************************************************
* Copyright (c) 2021-2024 Jesse Posner *
* Copyright (c) 2021-2023 Jesse Posner *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/

View File

@@ -1,5 +1,5 @@
/**********************************************************************
* Copyright (c) 2021-2024 Jesse Posner *
* Copyright (c) 2021-2023 Jesse Posner *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/

View File

@@ -1,5 +1,5 @@
/**********************************************************************
* Copyright (c) 2021-2024 Jesse Posner *
* Copyright (c) 2021-2023 Jesse Posner *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
@@ -313,7 +313,7 @@ int secp256k1_frost_nonce_gen(const secp256k1_context* ctx, secp256k1_frost_secn
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) {
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;
@@ -334,19 +334,16 @@ static int secp256k1_frost_sum_nonces(const secp256k1_context* ctx, secp256k1_ge
/* 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) {
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 size_t *ids) {
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_set_int(&idx, ids[i]);
secp256k1_scalar_get_b32(buf, &idx);
secp256k1_sha256_write(&sha, buf, 32);
if (!secp256k1_frost_pubnonce_serialize(ctx, buf, pubnonces[i])) {
@@ -360,7 +357,7 @@ static int secp256k1_frost_compute_noncehash(const secp256k1_context* ctx, unsig
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) {
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 size_t *ids) {
unsigned char noncehash[32];
secp256k1_ge fin_nonce_pt;
secp256k1_gej fin_nonce_ptj;
@@ -368,7 +365,7 @@ static int secp256k1_frost_nonce_process_internal(const secp256k1_context* ctx,
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)) {
if (!secp256k1_frost_compute_noncehash(ctx, noncehash, msg, pubnonces, n_pubnonces, pk32, ids)) {
return 0;
}
/* fin_nonce = aggnonce[0] + b*aggnonce[1] */
@@ -389,7 +386,7 @@ static int secp256k1_frost_nonce_process_internal(const secp256k1_context* ctx,
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) {
static int secp256k1_frost_lagrange_coefficient(secp256k1_scalar *r, const size_t *ids, size_t n_participants, size_t my_id) {
size_t i;
secp256k1_scalar num;
secp256k1_scalar den;
@@ -397,15 +394,11 @@ static int secp256k1_frost_lagrange_coefficient(secp256k1_scalar *r, const unsig
secp256k1_scalar_set_int(&num, 1);
secp256k1_scalar_set_int(&den, 1);
if (!secp256k1_frost_compute_indexhash(&party_idx, my_id33)) {
return 0;
}
secp256k1_scalar_set_int(&party_idx, my_id);
for (i = 0; i < n_participants; i++) {
secp256k1_scalar mul;
if (!secp256k1_frost_compute_indexhash(&mul, ids33[i])) {
return 0;
}
secp256k1_scalar_set_int(&mul, ids[i]);
if (secp256k1_scalar_eq(&mul, &party_idx)) {
continue;
}
@@ -422,7 +415,7 @@ static int secp256k1_frost_lagrange_coefficient(secp256k1_scalar *r, const unsig
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) {
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, size_t my_id, const size_t *ids, 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];
@@ -435,10 +428,12 @@ int secp256k1_frost_nonce_process(const secp256k1_context* ctx, secp256k1_frost_
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(ids != NULL);
ARG_CHECK(n_pubnonces > 1);
ARG_CHECK(my_id != 0);
for (i = 0; i < n_pubnonces; i++) {
ARG_CHECK(ids[i] != 0);
}
if (!secp256k1_xonly_pubkey_serialize(ctx, pk32, pk)) {
return 0;
@@ -453,6 +448,7 @@ int secp256k1_frost_nonce_process(const secp256k1_context* ctx, secp256k1_frost_
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
@@ -476,7 +472,7 @@ int secp256k1_frost_nonce_process(const secp256k1_context* ctx, secp256k1_frost_
}
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)) {
if (!secp256k1_frost_nonce_process_internal(ctx, &session_i.fin_nonce_parity, fin_nonce, &session_i.noncecoef, aggnonce_ptj, msg32, pubnonces, n_pubnonces, pk32, ids)) {
return 0;
}
@@ -500,7 +496,7 @@ int secp256k1_frost_nonce_process(const secp256k1_context* ctx, secp256k1_frost_
}
/* Update the challenge by multiplying the Lagrange coefficient to prepare
* for signing. */
if (!secp256k1_frost_lagrange_coefficient(&l, ids33, n_pubnonces, my_id33)) {
if (!secp256k1_frost_lagrange_coefficient(&l, ids, n_pubnonces, my_id)) {
return 0;
}
secp256k1_scalar_mul(&session_i.challenge, &session_i.challenge, &l);

View File

@@ -0,0 +1,738 @@
/***********************************************************************
* Copyright (c) 2022, 2023 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_TESTS_IMPL_H
#define SECP256K1_MODULE_FROST_TESTS_IMPL_H
#include <stdlib.h>
#include <string.h>
#include "../../../include/secp256k1.h"
#include "../../../include/secp256k1_extrakeys.h"
#include "../../../include/secp256k1_frost.h"
#include "session.h"
#include "keygen.h"
#include "../../scalar.h"
#include "../../scratch.h"
#include "../../field.h"
#include "../../group.h"
#include "../../hash.h"
#include "../../util.h"
/* Simple (non-adaptor, non-tweaked) 3-of-5 FROST aggregate, sign, verify
* test. */
void frost_simple_test(void) {
secp256k1_frost_pubnonce pubnonce[5];
const secp256k1_frost_pubnonce *pubnonce_ptr[5];
unsigned char msg[32];
secp256k1_xonly_pubkey pk;
unsigned char seed[32];
secp256k1_frost_share shares[5];
secp256k1_frost_secnonce secnonce[5];
secp256k1_pubkey pubshares[5];
secp256k1_frost_partial_sig partial_sig[5];
const secp256k1_frost_partial_sig *partial_sig_ptr[5];
unsigned char final_sig[64];
secp256k1_frost_session session;
int i;
size_t ids[5];
secp256k1_testrand256(seed);
for (i = 0; i < 5; i++) {
pubnonce_ptr[i] = &pubnonce[i];
partial_sig_ptr[i] = &partial_sig[i];
ids[i] = i + 1;
}
CHECK(secp256k1_frost_shares_trusted_gen(CTX, shares, pubshares, &pk, seed, 3, 5) == 1);
secp256k1_testrand256(msg);
for (i = 0; i < 3; i++) {
unsigned char session_id[32];
secp256k1_testrand256(session_id);
CHECK(secp256k1_frost_nonce_gen(CTX, &secnonce[i], &pubnonce[i], session_id, &shares[i], NULL, NULL, NULL) == 1);
}
for (i = 0; i < 3; i++) {
CHECK(secp256k1_frost_nonce_process(CTX, &session, pubnonce_ptr, 3, msg, &pk, ids[i], ids, NULL, NULL) == 1);
CHECK(secp256k1_frost_partial_sign(CTX, &partial_sig[i], &secnonce[i], &shares[i], &session, NULL) == 1);
CHECK(secp256k1_frost_partial_sig_verify(CTX, &partial_sig[i], &pubnonce[i], &pubshares[i], &session, NULL) == 1);
}
CHECK(secp256k1_frost_partial_sig_agg(CTX, final_sig, &session, partial_sig_ptr, 3) == 1);
CHECK(secp256k1_schnorrsig_verify(CTX, final_sig, msg, sizeof(msg), &pk) == 1);
}
void frost_pubnonce_summing_to_inf(secp256k1_frost_pubnonce *pubnonce) {
secp256k1_ge ge[2];
int i;
secp256k1_gej summed_nonces[2];
const secp256k1_frost_pubnonce *pubnonce_ptr[2];
ge[0] = secp256k1_ge_const_g;
ge[1] = secp256k1_ge_const_g;
for (i = 0; i < 2; i++) {
secp256k1_frost_pubnonce_save(&pubnonce[i], ge);
pubnonce_ptr[i] = &pubnonce[i];
secp256k1_ge_neg(&ge[0], &ge[0]);
secp256k1_ge_neg(&ge[1], &ge[1]);
}
secp256k1_frost_sum_nonces(CTX, summed_nonces, pubnonce_ptr, 2);
CHECK(secp256k1_gej_is_infinity(&summed_nonces[0]));
CHECK(secp256k1_gej_is_infinity(&summed_nonces[1]));
}
int frost_memcmp_and_randomize(unsigned char *value, const unsigned char *expected, size_t len) {
int ret;
size_t i;
ret = secp256k1_memcmp_var(value, expected, len);
for (i = 0; i < len; i++) {
value[i] = secp256k1_testrand_bits(8);
}
return ret;
}
void frost_api_tests(void) {
secp256k1_frost_partial_sig partial_sig[5];
const secp256k1_frost_partial_sig *partial_sig_ptr[5];
secp256k1_frost_partial_sig invalid_partial_sig;
const secp256k1_frost_partial_sig *invalid_partial_sig_ptr[5];
unsigned char final_sig[64];
unsigned char pre_sig[64];
unsigned char buf[32];
/* unsigned char sk[5][32]; */
unsigned char max64[64];
unsigned char zeros68[68] = { 0 };
unsigned char session_id[5][32];
unsigned char seed[32];
secp256k1_frost_secnonce secnonce[5];
secp256k1_frost_secnonce secnonce_tmp;
secp256k1_frost_secnonce invalid_secnonce;
secp256k1_frost_pubnonce pubnonce[5];
const secp256k1_frost_pubnonce *pubnonce_ptr[5];
unsigned char pubnonce_ser[66];
secp256k1_frost_pubnonce inf_pubnonce[5];
secp256k1_frost_pubnonce invalid_pubnonce;
const secp256k1_frost_pubnonce *invalid_pubnonce_ptr[5];
unsigned char msg[32];
secp256k1_xonly_pubkey pk;
secp256k1_pubkey full_pk;
secp256k1_frost_tweak_cache tweak_cache;
secp256k1_frost_tweak_cache invalid_tweak_cache;
secp256k1_frost_session session[5];
secp256k1_frost_session invalid_session;
secp256k1_xonly_pubkey invalid_pk;
unsigned char tweak[32];
int nonce_parity;
unsigned char sec_adaptor[32];
unsigned char sec_adaptor1[32];
secp256k1_pubkey adaptor;
secp256k1_pubkey invalid_vss_pk;
secp256k1_frost_share invalid_share;
secp256k1_frost_share shares[5];
secp256k1_pubkey pubshares[5];
int i;
size_t ids[5];
size_t invalid_ids[5];
/** setup **/
memset(max64, 0xff, sizeof(max64));
memset(&invalid_share, 0xff, sizeof(invalid_share));
/* Simulate structs being uninitialized by setting it to 0s. We don't want
* to produce undefined behavior by actually providing uninitialized
* structs. */
memset(&invalid_pk, 0, sizeof(invalid_pk));
memset(&invalid_secnonce, 0, sizeof(invalid_secnonce));
memset(&invalid_partial_sig, 0, sizeof(invalid_partial_sig));
memset(&invalid_pubnonce, 0, sizeof(invalid_pubnonce));
memset(&invalid_vss_pk, 0, sizeof(invalid_vss_pk));
memset(&invalid_tweak_cache, 0, sizeof(invalid_tweak_cache));
memset(&invalid_session, 0, sizeof(invalid_session));
frost_pubnonce_summing_to_inf(inf_pubnonce);
secp256k1_testrand256(sec_adaptor);
secp256k1_testrand256(msg);
secp256k1_testrand256(tweak);
secp256k1_testrand256(seed);
CHECK(secp256k1_ec_pubkey_create(CTX, &adaptor, sec_adaptor) == 1);
for (i = 0; i < 5; i++) {
pubnonce_ptr[i] = &pubnonce[i];
partial_sig_ptr[i] = &partial_sig[i];
invalid_partial_sig_ptr[i] = &partial_sig[i];
ids[i] = i + 1;
invalid_ids[i] = i + 1;
secp256k1_testrand256(session_id[i]);
}
invalid_pubnonce_ptr[0] = &invalid_pubnonce;
invalid_partial_sig_ptr[0] = &invalid_partial_sig;
invalid_ids[2] = 0;
/** main test body **/
/** Key generation **/
CHECK(secp256k1_frost_shares_trusted_gen(CTX, shares, pubshares, &pk, seed, 3, 5) == 1);
CHECK_ILLEGAL(CTX, secp256k1_frost_shares_trusted_gen(CTX, NULL, pubshares, &pk, seed, 3, 5));
CHECK_ILLEGAL(CTX, secp256k1_frost_shares_trusted_gen(CTX, shares, NULL, &pk, seed, 3, 5));
for (i = 0; i < 5; i++) {
CHECK(frost_memcmp_and_randomize(shares[i].data, zeros68, sizeof(shares[i].data)) == 0);
}
CHECK_ILLEGAL(CTX, secp256k1_frost_shares_trusted_gen(CTX, shares, pubshares, NULL, seed, 3, 5));
for (i = 0; i < 5; i++) {
CHECK(frost_memcmp_and_randomize(shares[i].data, zeros68, sizeof(shares[i].data)) == 0);
}
CHECK_ILLEGAL(CTX, secp256k1_frost_shares_trusted_gen(CTX, shares, pubshares, &pk, NULL, 3, 5));
for (i = 0; i < 5; i++) {
CHECK(frost_memcmp_and_randomize(shares[i].data, zeros68, sizeof(shares[i].data)) == 0);
}
CHECK_ILLEGAL(CTX, secp256k1_frost_shares_trusted_gen(CTX, shares, pubshares, &pk, seed, 0, 5));
for (i = 0; i < 5; i++) {
CHECK(frost_memcmp_and_randomize(shares[i].data, zeros68, sizeof(shares[i].data)) == 0);
}
CHECK_ILLEGAL(CTX, secp256k1_frost_shares_trusted_gen(CTX, shares, pubshares, &pk, seed, 3, 0));
CHECK_ILLEGAL(CTX, secp256k1_frost_shares_trusted_gen(CTX, shares, pubshares, &pk, seed, 3, 2));
CHECK(secp256k1_frost_shares_trusted_gen(CTX, shares, pubshares, &pk, seed, 3, 5) == 1);
/* pubkey_get */
CHECK(secp256k1_frost_pubkey_get(CTX, &full_pk, &pk) == 1);
CHECK_ILLEGAL(CTX, secp256k1_frost_pubkey_get(CTX, NULL, &pk));
CHECK_ILLEGAL(CTX, secp256k1_frost_pubkey_get(CTX, &full_pk, NULL));
CHECK(secp256k1_memcmp_var(&full_pk, zeros68, sizeof(full_pk)) == 0);
/** Tweaking **/
/* pubkey_tweak */
CHECK(secp256k1_frost_pubkey_tweak(CTX, &tweak_cache, &pk) == 1);
CHECK_ILLEGAL(CTX, secp256k1_frost_pubkey_tweak(CTX, NULL, &pk));
CHECK_ILLEGAL(CTX, secp256k1_frost_pubkey_tweak(CTX, &tweak_cache, NULL));
CHECK_ILLEGAL(CTX, secp256k1_frost_pubkey_tweak(CTX, &tweak_cache, &invalid_pk));
CHECK(secp256k1_frost_pubkey_tweak(CTX, &tweak_cache, &pk) == 1);
/* tweak_add */
{
int (*tweak_func[2]) (const secp256k1_context* ctx, secp256k1_pubkey *output_pubkey, secp256k1_frost_tweak_cache *tweak_cache, const unsigned char *tweak32);
tweak_func[0] = secp256k1_frost_pubkey_ec_tweak_add;
tweak_func[1] = secp256k1_frost_pubkey_xonly_tweak_add;
CHECK(secp256k1_frost_pubkey_tweak(CTX, &tweak_cache, &pk) == 1);
for (i = 0; i < 2; i++) {
secp256k1_pubkey tmp_output_pk;
secp256k1_frost_tweak_cache tmp_tweak_cache = tweak_cache;
CHECK((*tweak_func[i])(CTX, &tmp_output_pk, &tmp_tweak_cache, tweak) == 1);
/* Reset tweak_cache */
tmp_tweak_cache = tweak_cache;
CHECK((*tweak_func[i])(CTX, &tmp_output_pk, &tmp_tweak_cache, tweak) == 1);
tmp_tweak_cache = tweak_cache;
CHECK((*tweak_func[i])(CTX, NULL, &tmp_tweak_cache, tweak) == 1);
tmp_tweak_cache = tweak_cache;
CHECK_ILLEGAL(CTX, (*tweak_func[i])(CTX, &tmp_output_pk, NULL, tweak));
CHECK(frost_memcmp_and_randomize(tmp_output_pk.data, zeros68, sizeof(tmp_output_pk.data)) == 0);
tmp_tweak_cache = tweak_cache;
CHECK_ILLEGAL(CTX, (*tweak_func[i])(CTX, &tmp_output_pk, &tmp_tweak_cache, NULL));
CHECK(frost_memcmp_and_randomize(tmp_output_pk.data, zeros68, sizeof(tmp_output_pk.data)) == 0);
tmp_tweak_cache = tweak_cache;
CHECK((*tweak_func[i])(CTX, &tmp_output_pk, &tmp_tweak_cache, max64) == 0);
CHECK(frost_memcmp_and_randomize(tmp_output_pk.data, zeros68, sizeof(tmp_output_pk.data)) == 0);
tmp_tweak_cache = tweak_cache;
/* Uninitialized tweak_cache */
CHECK_ILLEGAL(CTX, (*tweak_func[i])(CTX, &tmp_output_pk, &invalid_tweak_cache, tweak));
CHECK(frost_memcmp_and_randomize(tmp_output_pk.data, zeros68, sizeof(tmp_output_pk.data)) == 0);
}
}
/** Session creation **/
CHECK(secp256k1_frost_nonce_gen(CTX, &secnonce[0], &pubnonce[0], session_id[0], &shares[0], msg, &pk, max64) == 1);
CHECK_ILLEGAL(STATIC_CTX, secp256k1_frost_nonce_gen(STATIC_CTX, &secnonce[0], &pubnonce[0], session_id[0], &shares[0], msg, &pk, max64));
CHECK_ILLEGAL(CTX, secp256k1_frost_nonce_gen(CTX, NULL, &pubnonce[0], session_id[0], &shares[0], msg, &pk, max64));
CHECK_ILLEGAL(CTX, secp256k1_frost_nonce_gen(CTX, &secnonce[0], NULL, session_id[0], &shares[0], msg, &pk, max64));
CHECK_ILLEGAL(CTX, secp256k1_frost_nonce_gen(CTX, &secnonce[0], &pubnonce[0], NULL, &shares[0], msg, &pk, max64));
CHECK(frost_memcmp_and_randomize(secnonce[0].data, zeros68, sizeof(secnonce[0].data)) == 0);
/* no seckey and session_id is 0 */
CHECK(secp256k1_frost_nonce_gen(CTX, &secnonce[0], &pubnonce[0], zeros68, NULL, msg, &pk, max64) == 0);
CHECK(frost_memcmp_and_randomize(secnonce[0].data, zeros68, sizeof(secnonce[0].data)) == 0);
/* session_id 0 is fine when a seckey is provided */
CHECK(secp256k1_frost_nonce_gen(CTX, &secnonce[0], &pubnonce[0], zeros68, &shares[0], msg, &pk, max64) == 1);
CHECK(secp256k1_frost_nonce_gen(CTX, &secnonce[0], &pubnonce[0], session_id[0], NULL, msg, &pk, max64) == 1);
/* invalid share */
CHECK_ILLEGAL(CTX, secp256k1_frost_nonce_gen(CTX, &secnonce[0], &pubnonce[0], session_id[0], &invalid_share, msg, &pk, max64));
CHECK(frost_memcmp_and_randomize(secnonce[0].data, zeros68, sizeof(secnonce[0].data)) == 0);
CHECK(secp256k1_frost_nonce_gen(CTX, &secnonce[0], &pubnonce[0], session_id[0], &shares[0], NULL, &pk, max64) == 1);
CHECK(secp256k1_frost_nonce_gen(CTX, &secnonce[0], &pubnonce[0], session_id[0], &shares[0], msg, NULL, max64) == 1);
CHECK_ILLEGAL(CTX, secp256k1_frost_nonce_gen(CTX, &secnonce[0], &pubnonce[0], session_id[0], &shares[0], msg, &invalid_pk, max64));
CHECK(frost_memcmp_and_randomize(secnonce[0].data, zeros68, sizeof(secnonce[0].data)) == 0);
CHECK(secp256k1_frost_nonce_gen(CTX, &secnonce[0], &pubnonce[0], session_id[0], &shares[0], msg, &pk, NULL) == 1);
/* Every in-argument except session_id can be NULL */
CHECK(secp256k1_frost_nonce_gen(CTX, &secnonce[0], &pubnonce[0], session_id[0], NULL, NULL, NULL, NULL) == 1);
CHECK(secp256k1_frost_nonce_gen(CTX, &secnonce[1], &pubnonce[1], session_id[1], &shares[1], NULL, NULL, NULL) == 1);
CHECK(secp256k1_frost_nonce_gen(CTX, &secnonce[2], &pubnonce[2], session_id[2], &shares[2], NULL, NULL, NULL) == 1);
/** Serialize and parse public nonces **/
CHECK_ILLEGAL(CTX, secp256k1_frost_pubnonce_serialize(CTX, NULL, &pubnonce[0]));
CHECK_ILLEGAL(CTX, secp256k1_frost_pubnonce_serialize(CTX, pubnonce_ser, NULL));
CHECK(frost_memcmp_and_randomize(pubnonce_ser, zeros68, sizeof(pubnonce_ser)) == 0);
CHECK_ILLEGAL(CTX, secp256k1_frost_pubnonce_serialize(CTX, pubnonce_ser, &invalid_pubnonce));
CHECK(frost_memcmp_and_randomize(pubnonce_ser, zeros68, sizeof(pubnonce_ser)) == 0);
CHECK(secp256k1_frost_pubnonce_serialize(CTX, pubnonce_ser, &pubnonce[0]) == 1);
CHECK(secp256k1_frost_pubnonce_parse(CTX, &pubnonce[0], pubnonce_ser) == 1);
CHECK_ILLEGAL(CTX, secp256k1_frost_pubnonce_parse(CTX, NULL, pubnonce_ser));
CHECK_ILLEGAL(CTX, secp256k1_frost_pubnonce_parse(CTX, &pubnonce[0], NULL));
CHECK(secp256k1_frost_pubnonce_parse(CTX, &pubnonce[0], zeros68) == 0);
CHECK(secp256k1_frost_pubnonce_parse(CTX, &pubnonce[0], pubnonce_ser) == 1);
{
/* Check that serialize and parse results in the same value */
secp256k1_frost_pubnonce tmp;
CHECK(secp256k1_frost_pubnonce_serialize(CTX, pubnonce_ser, &pubnonce[0]) == 1);
CHECK(secp256k1_frost_pubnonce_parse(CTX, &tmp, pubnonce_ser) == 1);
CHECK(secp256k1_memcmp_var(&tmp, &pubnonce[0], sizeof(tmp)) == 0);
}
/** Process nonces **/
CHECK(secp256k1_frost_nonce_process(CTX, &session[0], pubnonce_ptr, 3, msg, &pk, ids[0], ids, &tweak_cache, &adaptor) == 1);
CHECK_ILLEGAL(CTX, secp256k1_frost_nonce_process(CTX, NULL, pubnonce_ptr, 3, msg, &pk, ids[0], ids, &tweak_cache, &adaptor));
CHECK_ILLEGAL(CTX, secp256k1_frost_nonce_process(CTX, &session[0], NULL, 3, msg, &pk, ids[0], ids, &tweak_cache, &adaptor));
CHECK_ILLEGAL(CTX, secp256k1_frost_nonce_process(CTX, &session[0], pubnonce_ptr, 0, msg, &pk, ids[0], ids, &tweak_cache, &adaptor));
CHECK_ILLEGAL(CTX, secp256k1_frost_nonce_process(CTX, &session[0], invalid_pubnonce_ptr, 3, msg, &pk, ids[0], ids, &tweak_cache, &adaptor));
CHECK_ILLEGAL(CTX, secp256k1_frost_nonce_process(CTX, &session[0], pubnonce_ptr, 3, NULL, &pk, ids[0], ids, &tweak_cache, &adaptor));
CHECK_ILLEGAL(CTX, secp256k1_frost_nonce_process(CTX, &session[0], pubnonce_ptr, 3, msg, NULL, ids[0], ids, &tweak_cache, &adaptor));
CHECK_ILLEGAL(CTX, secp256k1_frost_nonce_process(CTX, &session[0], pubnonce_ptr, 3, msg, &pk, 0, ids, &tweak_cache, &adaptor));
CHECK_ILLEGAL(CTX, secp256k1_frost_nonce_process(CTX, &session[0], pubnonce_ptr, 3, msg, &pk, ids[0], invalid_ids, &tweak_cache, &adaptor));
CHECK_ILLEGAL(CTX, secp256k1_frost_nonce_process(CTX, &session[0], pubnonce_ptr, 3, msg, &pk, ids[0], NULL, &tweak_cache, &adaptor));
CHECK(secp256k1_frost_nonce_process(CTX, &session[0], pubnonce_ptr, 3, msg, &pk, ids[0], ids, NULL, &adaptor) == 1);
CHECK_ILLEGAL(CTX, secp256k1_frost_nonce_process(CTX, &session[0], pubnonce_ptr, 3, msg, &pk, ids[0], ids, &invalid_tweak_cache, &adaptor));
CHECK(secp256k1_frost_nonce_process(CTX, &session[0], pubnonce_ptr, 3, msg, &pk, ids[0], ids, &tweak_cache, NULL) == 1);
CHECK_ILLEGAL(CTX, secp256k1_frost_nonce_process(CTX, &session[0], pubnonce_ptr, 3, msg, &pk, ids[0], ids, &tweak_cache, (secp256k1_pubkey *)&invalid_pk));
CHECK(secp256k1_frost_nonce_process(CTX, &session[0], pubnonce_ptr, 3, msg, &pk, ids[0], ids, &tweak_cache, &adaptor) == 1);
CHECK(secp256k1_frost_nonce_process(CTX, &session[1], pubnonce_ptr, 3, msg, &pk, ids[1], ids, &tweak_cache, &adaptor) == 1);
CHECK(secp256k1_frost_nonce_process(CTX, &session[2], pubnonce_ptr, 3, msg, &pk, ids[2], ids, &tweak_cache, &adaptor) == 1);
memcpy(&secnonce_tmp, &secnonce[0], sizeof(secnonce_tmp));
CHECK(secp256k1_frost_partial_sign(CTX, &partial_sig[0], &secnonce_tmp, &shares[0], &session[0], &tweak_cache) == 1);
/* The secnonce is set to 0 and subsequent signing attempts fail */
CHECK(secp256k1_memcmp_var(&secnonce_tmp, zeros68, sizeof(secnonce_tmp)) == 0);
CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sign(CTX, &partial_sig[0], &secnonce_tmp, &shares[0], &session[0], &tweak_cache));
memcpy(&secnonce_tmp, &secnonce[0], sizeof(secnonce_tmp));
CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sign(CTX, NULL, &secnonce_tmp, &shares[0], &session[0], &tweak_cache));
memcpy(&secnonce_tmp, &secnonce[0], sizeof(secnonce_tmp));
CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sign(CTX, &partial_sig[0], NULL, &shares[0], &session[0], &tweak_cache));
CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sign(CTX, &partial_sig[0], &invalid_secnonce, &shares[0], &session[0], &tweak_cache));
CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sign(CTX, &partial_sig[0], &secnonce_tmp, NULL, &session[0], &tweak_cache));
memcpy(&secnonce_tmp, &secnonce[0], sizeof(secnonce_tmp));
CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sign(CTX, &partial_sig[0], &secnonce_tmp, &invalid_share, &session[0], &tweak_cache));
memcpy(&secnonce_tmp, &secnonce[0], sizeof(secnonce_tmp));
CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sign(CTX, &partial_sig[0], &secnonce_tmp, &shares[0], NULL, &tweak_cache));
memcpy(&secnonce_tmp, &secnonce[0], sizeof(secnonce_tmp));
CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sign(CTX, &partial_sig[0], &secnonce_tmp, &shares[0], &invalid_session, &tweak_cache));
memcpy(&secnonce_tmp, &secnonce[0], sizeof(secnonce_tmp));
CHECK(secp256k1_frost_partial_sign(CTX, &partial_sig[0], &secnonce_tmp, &shares[0], &session[0], NULL) == 1);
memcpy(&secnonce_tmp, &secnonce[0], sizeof(secnonce_tmp));
CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sign(CTX, &partial_sig[0], &secnonce_tmp, &shares[0], &session[0], &invalid_tweak_cache));
memcpy(&secnonce_tmp, &secnonce[0], sizeof(secnonce_tmp));
CHECK(secp256k1_frost_partial_sign(CTX, &partial_sig[0], &secnonce[0], &shares[0], &session[0], &tweak_cache) == 1);
CHECK(secp256k1_frost_partial_sign(CTX, &partial_sig[1], &secnonce[1], &shares[1], &session[1], &tweak_cache) == 1);
CHECK(secp256k1_frost_partial_sign(CTX, &partial_sig[2], &secnonce[2], &shares[2], &session[2], &tweak_cache) == 1);
CHECK(secp256k1_frost_partial_sig_serialize(CTX, buf, &partial_sig[0]) == 1);
CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sig_serialize(CTX, NULL, &partial_sig[0]));
CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sig_serialize(CTX, buf, NULL));
CHECK(secp256k1_frost_partial_sig_parse(CTX, &partial_sig[0], buf) == 1);
CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sig_parse(CTX, NULL, buf));
CHECK(secp256k1_frost_partial_sig_parse(CTX, &partial_sig[0], max64) == 0);
CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sig_parse(CTX, &partial_sig[0], NULL));
{
/* Check that serialize and parse results in the same value */
secp256k1_frost_partial_sig tmp;
CHECK(secp256k1_frost_partial_sig_serialize(CTX, buf, &partial_sig[0]) == 1);
CHECK(secp256k1_frost_partial_sig_parse(CTX, &tmp, buf) == 1);
CHECK(secp256k1_memcmp_var(&tmp, &partial_sig[0], sizeof(tmp)) == 0);
}
/** Partial signature verification */
CHECK(secp256k1_frost_partial_sig_verify(CTX, &partial_sig[0], &pubnonce[0], &pubshares[0], &session[0], &tweak_cache) == 1);
CHECK(secp256k1_frost_partial_sig_verify(CTX, &partial_sig[1], &pubnonce[0], &pubshares[0], &session[0], &tweak_cache) == 0);
CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sig_verify(CTX, NULL, &pubnonce[0], &pubshares[0], &session[0], &tweak_cache));
CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sig_verify(CTX, &invalid_partial_sig, &pubnonce[0], &pubshares[0], &session[0], &tweak_cache));
CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sig_verify(CTX, &partial_sig[0], NULL, &pubshares[0], &session[0], &tweak_cache));
CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sig_verify(CTX, &partial_sig[0], &invalid_pubnonce, &pubshares[0], &session[0], &tweak_cache));
CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sig_verify(CTX, &partial_sig[0], &pubnonce[0], NULL, &session[0], &tweak_cache));
CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sig_verify(CTX, &partial_sig[0], &pubnonce[0], &invalid_vss_pk, &session[0], &tweak_cache));
CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sig_verify(CTX, &partial_sig[0], &pubnonce[0], &pubshares[0], NULL, &tweak_cache));
CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sig_verify(CTX, &partial_sig[0], &pubnonce[0], &pubshares[0], &invalid_session, &tweak_cache));
CHECK(secp256k1_frost_partial_sig_verify(CTX, &partial_sig[0], &pubnonce[0], &pubshares[0], &session[0], NULL) == 1);
CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sig_verify(CTX, &partial_sig[0], &pubnonce[0], &pubshares[0], &session[0], &invalid_tweak_cache));
CHECK(secp256k1_frost_partial_sig_verify(CTX, &partial_sig[0], &pubnonce[0], &pubshares[0], &session[0], &tweak_cache) == 1);
CHECK(secp256k1_frost_partial_sig_verify(CTX, &partial_sig[1], &pubnonce[1], &pubshares[1], &session[1], &tweak_cache) == 1);
CHECK(secp256k1_frost_partial_sig_verify(CTX, &partial_sig[2], &pubnonce[2], &pubshares[2], &session[2], &tweak_cache) == 1);
/** Signature aggregation and verification */
CHECK(secp256k1_frost_partial_sig_agg(CTX, pre_sig, &session[0], partial_sig_ptr, 3) == 1);
CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sig_agg(CTX, NULL, &session[0], partial_sig_ptr, 3));
CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sig_agg(CTX, pre_sig, NULL, partial_sig_ptr, 3));
CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sig_agg(CTX, pre_sig, &invalid_session, partial_sig_ptr, 3));
CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sig_agg(CTX, pre_sig, &session[0], NULL, 3));
CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sig_agg(CTX, pre_sig, &session[0], invalid_partial_sig_ptr, 3));
CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sig_agg(CTX, pre_sig, &session[0], partial_sig_ptr, 0));
CHECK(secp256k1_frost_partial_sig_agg(CTX, pre_sig, &session[0], partial_sig_ptr, 1) == 1);
CHECK(secp256k1_frost_partial_sig_agg(CTX, pre_sig, &session[1], partial_sig_ptr, 2) == 1);
CHECK(secp256k1_frost_partial_sig_agg(CTX, pre_sig, &session[2], partial_sig_ptr, 3) == 1);
/** Adaptor signature verification */
CHECK(secp256k1_frost_nonce_parity(CTX, &nonce_parity, &session[0]) == 1);
CHECK_ILLEGAL(CTX, secp256k1_frost_nonce_parity(CTX, NULL, &session[0]));
CHECK_ILLEGAL(CTX, secp256k1_frost_nonce_parity(CTX, &nonce_parity, NULL));
CHECK_ILLEGAL(CTX, secp256k1_frost_nonce_parity(CTX, &nonce_parity, &invalid_session));
CHECK(secp256k1_frost_adapt(CTX, final_sig, pre_sig, sec_adaptor, nonce_parity) == 1);
CHECK_ILLEGAL(CTX, secp256k1_frost_adapt(CTX, NULL, pre_sig, sec_adaptor, 0));
CHECK_ILLEGAL(CTX, secp256k1_frost_adapt(CTX, final_sig, NULL, sec_adaptor, 0));
CHECK(secp256k1_frost_adapt(CTX, final_sig, max64, sec_adaptor, 0) == 0);
CHECK_ILLEGAL(CTX, secp256k1_frost_adapt(CTX, final_sig, pre_sig, NULL, 0));
CHECK(secp256k1_frost_adapt(CTX, final_sig, pre_sig, max64, 0) == 0);
CHECK_ILLEGAL(CTX, secp256k1_frost_adapt(CTX, final_sig, pre_sig, sec_adaptor, 2));
/* sig and pre_sig argument point to the same location */
memcpy(final_sig, pre_sig, sizeof(final_sig));
CHECK(secp256k1_frost_adapt(CTX, final_sig, final_sig, sec_adaptor, nonce_parity) == 1);
CHECK(secp256k1_schnorrsig_verify(CTX, final_sig, msg, sizeof(msg), &pk) == 1);
CHECK(secp256k1_frost_adapt(CTX, final_sig, pre_sig, sec_adaptor, nonce_parity) == 1);
CHECK(secp256k1_schnorrsig_verify(CTX, final_sig, msg, sizeof(msg), &pk) == 1);
/** Secret adaptor can be extracted from signature */
CHECK(secp256k1_frost_extract_adaptor(CTX, sec_adaptor1, final_sig, pre_sig, nonce_parity) == 1);
CHECK(secp256k1_memcmp_var(sec_adaptor, sec_adaptor1, 32) == 0);
/* wrong nonce parity */
CHECK(secp256k1_frost_extract_adaptor(CTX, sec_adaptor1, final_sig, pre_sig, !nonce_parity) == 1);
CHECK(secp256k1_memcmp_var(sec_adaptor, sec_adaptor1, 32) != 0);
CHECK_ILLEGAL(CTX, secp256k1_frost_extract_adaptor(CTX, NULL, final_sig, pre_sig, 0));
CHECK_ILLEGAL(CTX, secp256k1_frost_extract_adaptor(CTX, sec_adaptor1, NULL, pre_sig, 0));
CHECK(secp256k1_frost_extract_adaptor(CTX, sec_adaptor1, max64, pre_sig, 0) == 0);
CHECK_ILLEGAL(CTX, secp256k1_frost_extract_adaptor(CTX, sec_adaptor1, final_sig, NULL, 0));
CHECK(secp256k1_frost_extract_adaptor(CTX, sec_adaptor1, final_sig, max64, 0) == 0);
CHECK_ILLEGAL(CTX, secp256k1_frost_extract_adaptor(CTX, sec_adaptor1, final_sig, pre_sig, 2));
}
void frost_nonce_bitflip(unsigned char **args, size_t n_flip, size_t n_bytes) {
secp256k1_scalar k1[2], k2[2];
secp256k1_nonce_function_frost(k1, args[0], args[1], args[2], args[3], args[4]);
secp256k1_testrand_flip(args[n_flip], n_bytes);
secp256k1_nonce_function_frost(k2, args[0], args[1], args[2], args[3], args[4]);
CHECK(secp256k1_scalar_eq(&k1[0], &k2[0]) == 0);
CHECK(secp256k1_scalar_eq(&k1[1], &k2[1]) == 0);
}
void frost_nonce_test(void) {
unsigned char *args[5];
unsigned char session_id[32];
unsigned char sk[32];
unsigned char msg[32];
unsigned char agg_pk[32];
unsigned char extra_input[32];
int i, j;
secp256k1_scalar k[5][2];
secp256k1_testrand_bytes_test(session_id, sizeof(session_id));
secp256k1_testrand_bytes_test(sk, sizeof(sk));
secp256k1_testrand_bytes_test(msg, sizeof(msg));
secp256k1_testrand_bytes_test(agg_pk, sizeof(agg_pk));
secp256k1_testrand_bytes_test(extra_input, sizeof(extra_input));
/* Check that a bitflip in an argument results in different nonces. */
args[0] = session_id;
args[1] = msg;
args[2] = sk;
args[3] = agg_pk;
args[4] = extra_input;
for (i = 0; i < COUNT; i++) {
frost_nonce_bitflip(args, 0, sizeof(session_id));
frost_nonce_bitflip(args, 1, sizeof(msg));
frost_nonce_bitflip(args, 2, sizeof(sk));
frost_nonce_bitflip(args, 3, sizeof(agg_pk));
frost_nonce_bitflip(args, 4, sizeof(extra_input));
}
/* Check that if any argument is NULL, a different nonce is produced than if
* any other argument is NULL. */
memcpy(msg, session_id, sizeof(msg));
memcpy(sk, session_id, sizeof(sk));
memcpy(agg_pk, session_id, sizeof(agg_pk));
memcpy(extra_input, session_id, sizeof(extra_input));
secp256k1_nonce_function_frost(k[0], args[0], args[1], args[2], args[3], args[4]);
secp256k1_nonce_function_frost(k[1], args[0], NULL, args[2], args[3], args[4]);
secp256k1_nonce_function_frost(k[2], args[0], args[1], NULL, args[3], args[4]);
secp256k1_nonce_function_frost(k[3], args[0], args[1], args[2], NULL, args[4]);
secp256k1_nonce_function_frost(k[4], args[0], args[1], args[2], args[3], NULL);
for (i = 0; i < 4; i++) {
for (j = i+1; j < 5; j++) {
CHECK(secp256k1_scalar_eq(&k[i][0], &k[j][0]) == 0);
CHECK(secp256k1_scalar_eq(&k[i][1], &k[j][1]) == 0);
}
}
}
/* Attempts to create a signature for the aggregate public key using given secret
* keys and tweak_cache. */
void frost_tweak_test_helper(const secp256k1_xonly_pubkey* pk, const secp256k1_frost_share *sr0, const secp256k1_frost_share *sr1, const secp256k1_frost_share *sr2, secp256k1_frost_tweak_cache *tweak_cache, const size_t *ids, const secp256k1_pubkey *sr_pk0, const secp256k1_pubkey *sr_pk1, const secp256k1_pubkey *sr_pk2) {
unsigned char session_id[3][32];
unsigned char msg[32];
secp256k1_frost_secnonce secnonce[3];
secp256k1_frost_pubnonce pubnonce[3];
const secp256k1_frost_pubnonce *pubnonce_ptr[3];
secp256k1_frost_session session[5];
secp256k1_frost_partial_sig partial_sig[3];
const secp256k1_frost_partial_sig *partial_sig_ptr[3];
unsigned char final_sig[64];
int i;
for (i = 0; i < 3; i++) {
pubnonce_ptr[i] = &pubnonce[i];
partial_sig_ptr[i] = &partial_sig[i];
secp256k1_testrand256(session_id[i]);
}
secp256k1_testrand256(msg);
CHECK(secp256k1_frost_nonce_gen(CTX, &secnonce[0], &pubnonce[0], session_id[0], sr0, NULL, NULL, NULL) == 1);
CHECK(secp256k1_frost_nonce_gen(CTX, &secnonce[1], &pubnonce[1], session_id[1], sr1, NULL, NULL, NULL) == 1);
CHECK(secp256k1_frost_nonce_gen(CTX, &secnonce[2], &pubnonce[2], session_id[2], sr2, NULL, NULL, NULL) == 1);
CHECK(secp256k1_frost_nonce_process(CTX, &session[0], pubnonce_ptr, 3, msg, pk, ids[0], ids, tweak_cache, NULL) == 1);
CHECK(secp256k1_frost_nonce_process(CTX, &session[1], pubnonce_ptr, 3, msg, pk, ids[1], ids, tweak_cache, NULL) == 1);
CHECK(secp256k1_frost_nonce_process(CTX, &session[2], pubnonce_ptr, 3, msg, pk, ids[2], ids, tweak_cache, NULL) == 1);
CHECK(secp256k1_frost_partial_sign(CTX, &partial_sig[0], &secnonce[0], sr0, &session[0], tweak_cache) == 1);
CHECK(secp256k1_frost_partial_sign(CTX, &partial_sig[1], &secnonce[1], sr1, &session[1], tweak_cache) == 1);
CHECK(secp256k1_frost_partial_sign(CTX, &partial_sig[2], &secnonce[2], sr2, &session[2], tweak_cache) == 1);
CHECK(secp256k1_frost_partial_sig_verify(CTX, &partial_sig[0], &pubnonce[0], sr_pk0, &session[0], tweak_cache) == 1);
CHECK(secp256k1_frost_partial_sig_verify(CTX, &partial_sig[1], &pubnonce[1], sr_pk1, &session[1], tweak_cache) == 1);
CHECK(secp256k1_frost_partial_sig_verify(CTX, &partial_sig[2], &pubnonce[2], sr_pk2, &session[2], tweak_cache) == 1);
CHECK(secp256k1_frost_partial_sig_agg(CTX, final_sig, &session[0], partial_sig_ptr, 3) == 1);
CHECK(secp256k1_schnorrsig_verify(CTX, final_sig, msg, sizeof(msg), pk) == 1);
}
/* Create aggregate public key P[0], tweak multiple times (using xonly and
* ordinary tweaking) and test signing. */
void frost_tweak_test(void) {
secp256k1_pubkey pubshares[5];
secp256k1_frost_tweak_cache tweak_cache;
enum { N_TWEAKS = 8 };
secp256k1_pubkey P[N_TWEAKS + 1];
secp256k1_xonly_pubkey P_xonly[N_TWEAKS + 1];
unsigned char seed[32];
secp256k1_frost_share shares[5];
int i;
size_t ids[5];
secp256k1_testrand256(seed);
/* Key Setup */
for (i = 0; i < 5; i++) {
ids[i] = i + 1;
}
CHECK(secp256k1_frost_shares_trusted_gen(CTX, shares, pubshares, &P_xonly[0], seed, 3, 5) == 1);
frost_tweak_test_helper(&P_xonly[0], &shares[0], &shares[1], &shares[2], NULL, ids, &pubshares[0], &pubshares[1], &pubshares[2]);
CHECK(secp256k1_frost_pubkey_get(CTX, &P[0], &P_xonly[0]));
CHECK(secp256k1_frost_pubkey_tweak(CTX, &tweak_cache, &P_xonly[0]) == 1);
/* Compute Pi = f(Pj) + tweaki*G where where j = i-1 and try signing for */
/* that key. If xonly is set to true, the function f is normalizes the input */
/* point to have an even X-coordinate ("xonly-tweaking"). */
/* Otherwise, the function f is the identity function. */
for (i = 1; i <= N_TWEAKS; i++) {
unsigned char tweak[32];
int P_parity;
int xonly = secp256k1_testrand_bits(1);
secp256k1_testrand256(tweak);
if (xonly) {
CHECK(secp256k1_frost_pubkey_xonly_tweak_add(CTX, &P[i], &tweak_cache, tweak) == 1);
} else {
CHECK(secp256k1_frost_pubkey_ec_tweak_add(CTX, &P[i], &tweak_cache, tweak) == 1);
}
CHECK(secp256k1_xonly_pubkey_from_pubkey(CTX, &P_xonly[i], &P_parity, &P[i]));
/* Check that frost_pubkey_tweak_add produces same result as */
/* xonly_pubkey_tweak_add or ec_pubkey_tweak_add. */
if (xonly) {
unsigned char P_serialized[32];
CHECK(secp256k1_xonly_pubkey_serialize(CTX, P_serialized, &P_xonly[i]));
CHECK(secp256k1_xonly_pubkey_tweak_add_check(CTX, P_serialized, P_parity, &P_xonly[i-1], tweak) == 1);
} else {
secp256k1_pubkey tmp_key = P[i-1];
CHECK(secp256k1_ec_pubkey_tweak_add(CTX, &tmp_key, tweak));
CHECK(secp256k1_memcmp_var(&tmp_key, &P[i], sizeof(tmp_key)) == 0);
}
/* Test signing for P[i] */
frost_tweak_test_helper(&P_xonly[i], &shares[0], &shares[1], &shares[2], &tweak_cache, ids, &pubshares[0], &pubshares[1], &pubshares[2]);
}
}
/* Performs a FROST DKG */
void frost_dkg_test_helper(secp256k1_frost_share *shares, secp256k1_xonly_pubkey *pk) {
unsigned char seed[32];
secp256k1_pubkey pubshares[5];
secp256k1_testrand256(seed);
CHECK(secp256k1_frost_shares_trusted_gen(CTX, shares, pubshares, pk, seed, 3, 5) == 1);
}
/* Signs a message with a FROST keypair */
int frost_sign_test_helper(unsigned char *final_sig, const secp256k1_frost_share *share, const secp256k1_xonly_pubkey *pk, const unsigned char *msg, const secp256k1_pubkey *adaptor) {
unsigned char session_id[3][32];
secp256k1_frost_secnonce secnonce[3];
secp256k1_frost_pubnonce pubnonce[3];
const secp256k1_frost_pubnonce *pubnonce_ptr[3];
secp256k1_frost_partial_sig partial_sig[5];
const secp256k1_frost_partial_sig *partial_sig_ptr[5];
secp256k1_frost_session session;
int i;
int nonce_parity;
secp256k1_frost_session_internal session_i;
size_t ids[5];
for (i = 0; i < 3; i++) {
pubnonce_ptr[i] = &pubnonce[i];
partial_sig_ptr[i] = &partial_sig[i];
ids[i] = i + 1;
}
for (i = 0; i < 3; i++) {
secp256k1_testrand256(session_id[i]);
CHECK(secp256k1_frost_nonce_gen(CTX, &secnonce[i], &pubnonce[i], session_id[i], &share[i], NULL, NULL, NULL) == 1);
}
for (i = 0; i < 3; i++) {
CHECK(secp256k1_frost_nonce_process(CTX, &session, pubnonce_ptr, 3, msg, pk, i + 1, ids, NULL, adaptor) == 1);
CHECK(secp256k1_frost_partial_sign(CTX, &partial_sig[i], &secnonce[i], &share[i], &session, NULL) == 1);
}
CHECK(secp256k1_frost_partial_sig_agg(CTX, final_sig, &session, partial_sig_ptr, 3) == 1);
CHECK(secp256k1_frost_nonce_parity(CTX, &nonce_parity, &session));
secp256k1_frost_session_load(CTX, &session_i, &session);
return nonce_parity;
}
void frost_rand_scalar(secp256k1_scalar *scalar) {
unsigned char buf32[32];
secp256k1_testrand256(buf32);
secp256k1_scalar_set_b32(scalar, buf32, NULL);
}
void frost_multi_hop_lock_tests(void) {
secp256k1_frost_share share_a[5];
secp256k1_frost_share share_b[5];
secp256k1_xonly_pubkey agg_pk_a;
secp256k1_xonly_pubkey agg_pk_b;
unsigned char asig_ab[64];
unsigned char asig_bc[64];
unsigned char pop[32];
secp256k1_pubkey pubkey_pop;
unsigned char tx_ab[32];
unsigned char tx_bc[32];
unsigned char buf[32];
secp256k1_scalar t1, t2, tp;
secp256k1_pubkey l, r;
secp256k1_ge l_ge, r_ge;
secp256k1_scalar deckey;
unsigned char sig_ab[64];
unsigned char sig_bc[64];
int nonce_parity_ab;
int nonce_parity_bc;
/* Alice DKG */
frost_dkg_test_helper(share_a, &agg_pk_a);
/* Bob DKG */
frost_dkg_test_helper(share_b, &agg_pk_b);
/* Carol setup */
/* Proof of payment */
secp256k1_testrand256(pop);
CHECK(secp256k1_ec_pubkey_create(CTX, &pubkey_pop, pop));
/* Alice setup */
secp256k1_testrand256(tx_ab);
frost_rand_scalar(&t1);
frost_rand_scalar(&t2);
secp256k1_scalar_add(&tp, &t1, &t2);
/* Left lock */
secp256k1_pubkey_load(CTX, &l_ge, &pubkey_pop);
CHECK(secp256k1_eckey_pubkey_tweak_add(&l_ge, &t1));
secp256k1_pubkey_save(&l, &l_ge);
/* Right lock */
secp256k1_pubkey_load(CTX, &r_ge, &pubkey_pop);
CHECK(secp256k1_eckey_pubkey_tweak_add(&r_ge, &tp));
secp256k1_pubkey_save(&r, &r_ge);
/* Encrypt Alice's signature with the left lock as the encryption key */
nonce_parity_ab = frost_sign_test_helper(asig_ab, share_a, &agg_pk_a, tx_ab, &l);
/* Bob setup */
CHECK(secp256k1_frost_verify_adaptor(CTX, asig_ab, tx_ab, &agg_pk_a, &l, nonce_parity_ab) == 1);
secp256k1_testrand256(tx_bc);
/* Encrypt Bob's signature with the right lock as the encryption key */
nonce_parity_bc = frost_sign_test_helper(asig_bc, share_b, &agg_pk_b, tx_bc, &r);
/* Carol decrypt */
CHECK(secp256k1_frost_verify_adaptor(CTX, asig_bc, tx_bc, &agg_pk_b, &r, nonce_parity_bc) == 1);
secp256k1_scalar_set_b32(&deckey, pop, NULL);
secp256k1_scalar_add(&deckey, &deckey, &tp);
secp256k1_scalar_get_b32(buf, &deckey);
CHECK(secp256k1_frost_adapt(CTX, sig_bc, asig_bc, buf, nonce_parity_bc));
CHECK(secp256k1_schnorrsig_verify(CTX, sig_bc, tx_bc, sizeof(tx_bc), &agg_pk_b) == 1);
/* Bob recover and decrypt */
CHECK(secp256k1_frost_extract_adaptor(CTX, buf, sig_bc, asig_bc, nonce_parity_bc));
secp256k1_scalar_set_b32(&deckey, buf, NULL);
secp256k1_scalar_negate(&t2, &t2);
secp256k1_scalar_add(&deckey, &deckey, &t2);
secp256k1_scalar_get_b32(buf, &deckey);
CHECK(secp256k1_frost_adapt(CTX, sig_ab, asig_ab, buf, nonce_parity_ab));
CHECK(secp256k1_schnorrsig_verify(CTX, sig_ab, tx_ab, sizeof(tx_ab), &agg_pk_a) == 1);
/* Alice recover and derive proof of payment */
CHECK(secp256k1_frost_extract_adaptor(CTX, buf, sig_ab, asig_ab, nonce_parity_ab));
secp256k1_scalar_set_b32(&deckey, buf, NULL);
secp256k1_scalar_negate(&t1, &t1);
secp256k1_scalar_add(&deckey, &deckey, &t1);
secp256k1_scalar_get_b32(buf, &deckey);
CHECK(secp256k1_memcmp_var(buf, pop, 32) == 0);
}
void run_frost_tests(void) {
int i;
for (i = 0; i < COUNT; i++) {
frost_simple_test();
}
frost_api_tests();
frost_nonce_test();
for (i = 0; i < COUNT; i++) {
/* Run multiple times to ensure that pk and nonce have different y
* parities */
frost_tweak_test();
}
for (i = 0; i < COUNT; i++) {
frost_multi_hop_lock_tests();
}
}
#endif

View File

@@ -276,6 +276,7 @@ static void secp256k1_pedersen_commitment_save(secp256k1_pedersen_commitment* co
int secp256k1_pedersen_commitment_parse(const secp256k1_context* ctx, secp256k1_pedersen_commitment* commit, const unsigned char *input) {
secp256k1_fe x;
secp256k1_ge ge;
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(commit != NULL);
@@ -284,20 +285,28 @@ int secp256k1_pedersen_commitment_parse(const secp256k1_context* ctx, secp256k1_
if ((input[0] & 0xFE) != 8 ||
!secp256k1_fe_set_b32_limit(&x, &input[1]) ||
!secp256k1_ge_x_on_curve_var(&x)) {
!secp256k1_ge_set_xquad(&ge, &x)) {
return 0;
}
memcpy(commit->data, input, 33);
if (input[0] & 1) {
secp256k1_ge_neg(&ge, &ge);
}
secp256k1_pedersen_commitment_save(commit, &ge);
return 1;
}
int secp256k1_pedersen_commitment_serialize(const secp256k1_context* ctx, unsigned char *output, const secp256k1_pedersen_commitment* commit) {
secp256k1_ge ge;
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(output != NULL);
ARG_CHECK(commit != NULL);
memcpy(output, commit->data, 33);
secp256k1_pedersen_commitment_load(&ge, commit);
output[0] = 9 ^ secp256k1_fe_is_square_var(&ge.y);
secp256k1_fe_normalize_var(&ge.x);
secp256k1_fe_get_b32(&output[1], &ge.x);
return 1;
}

View File

@@ -264,13 +264,7 @@ static void test_pedersen(void) {
}
CHECK(secp256k1_pedersen_blind_sum(CTX, &blinds[(total - 1) * 32], bptr, total - 1, inputs));
for (i = 0; i < total; i++) {
unsigned char result[33];
secp256k1_pedersen_commitment parse;
CHECK(secp256k1_pedersen_commit(CTX, &commits[i], &blinds[i * 32], values[i], secp256k1_generator_h));
CHECK(secp256k1_pedersen_commitment_serialize(CTX, result, &commits[i]));
CHECK(secp256k1_pedersen_commitment_parse(CTX, &parse, result));
CHECK(secp256k1_memcmp_var(&commits[i], &parse, 33) == 0);
}
CHECK(secp256k1_pedersen_verify_tally(CTX, cptr, inputs, &cptr[inputs], outputs));
CHECK(secp256k1_pedersen_verify_tally(CTX, &cptr[inputs], outputs, cptr, inputs));

View File

@@ -889,6 +889,10 @@ static int secp256k1_ge_parse_ext(secp256k1_ge* ge, const unsigned char *in33) {
# include "modules/ecdsa_adaptor/main_impl.h"
#endif
#ifdef ENABLE_MODULE_FROST
# include "modules/frost/main_impl.h"
#endif
#ifdef ENABLE_MODULE_MUSIG
# include "modules/musig/main_impl.h"
#endif
@@ -908,7 +912,3 @@ static int secp256k1_ge_parse_ext(secp256k1_ge* ge, const unsigned char *in33) {
#ifdef ENABLE_MODULE_SURJECTIONPROOF
# include "modules/surjection/main_impl.h"
#endif
#ifdef ENABLE_MODULE_FROST
# include "modules/frost/main_impl.h"
#endif

View File

@@ -7502,6 +7502,10 @@ static void run_ecdsa_wycheproof(void) {
# include "modules/ecdsa_adaptor/tests_impl.h"
#endif
#ifdef ENABLE_MODULE_FROST
# include "modules/frost/tests_impl.h"
#endif
static void run_secp256k1_memczero_test(void) {
unsigned char buf1[6] = {1, 2, 3, 4, 5, 6};
unsigned char buf2[sizeof(buf1)];
@@ -7892,6 +7896,10 @@ int main(int argc, char **argv) {
run_ecdsa_adaptor_tests();
#endif
#ifdef ENABLE_MODULE_FROST
run_frost_tests();
#endif
/* util tests */
run_secp256k1_memczero_test();
run_secp256k1_byteorder_tests();