diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 91780179..bd1bb555 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -51,6 +51,7 @@ env: CHILLDKG: 'no' ICEBERG: 'no' PREFRACTAL: 'no' + FROST_ENROLLMENT: 'no' ### test options SECP256K1_TEST_ITERS: 64 BENCH: 'yes' @@ -108,14 +109,14 @@ jobs: matrix: configuration: - env_vars: { WIDEMUL: 'int64', RECOVERY: 'yes' } - - env_vars: { WIDEMUL: 'int64', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', SURJECTIONPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', FROST: 'yes', CHILLDKG: 'yes', ICEBERG: 'yes', PREFRACTAL: 'yes'} + - env_vars: { WIDEMUL: 'int64', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', SURJECTIONPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', FROST: 'yes', CHILLDKG: 'yes', ICEBERG: 'yes', PREFRACTAL: 'yes', FROST_ENROLLMENT: 'yes'} - env_vars: { WIDEMUL: 'int128' } - env_vars: { WIDEMUL: 'int128_struct', ELLSWIFT: 'yes' } - env_vars: { WIDEMUL: 'int128', RECOVERY: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes' } - - env_vars: { WIDEMUL: 'int128', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', SURJECTIONPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', FROST: 'yes', CHILLDKG: 'yes', ICEBERG: 'yes', PREFRACTAL: 'yes'} + - env_vars: { WIDEMUL: 'int128', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', SURJECTIONPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', FROST: 'yes', CHILLDKG: 'yes', ICEBERG: 'yes', PREFRACTAL: 'yes', FROST_ENROLLMENT: 'yes'} - env_vars: { WIDEMUL: 'int128', ASM: 'x86_64', ELLSWIFT: 'yes' } - - env_vars: { RECOVERY: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', SURJECTIONPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', FROST: 'yes', PREFRACTAL: 'yes'} - - env_vars: { CTIMETESTS: 'no', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', SURJECTIONPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', FROST: 'yes', CHILLDKG: 'yes', ICEBERG: 'yes', PREFRACTAL: 'yes', CPPFLAGS: '-DVERIFY' } + - env_vars: { RECOVERY: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', SURJECTIONPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', FROST: 'yes', PREFRACTAL: 'yes', FROST_ENROLLMENT: 'yes'} + - env_vars: { CTIMETESTS: 'no', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', SURJECTIONPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', FROST: 'yes', CHILLDKG: 'yes', ICEBERG: 'yes', PREFRACTAL: 'yes', FROST_ENROLLMENT: 'yes', CPPFLAGS: '-DVERIFY' } - env_vars: { BUILD: 'distcheck', WITH_VALGRIND: 'no', CTIMETESTS: 'no', BENCH: 'no' } - env_vars: { CPPFLAGS: '-DDETERMINISTIC' } - env_vars: { CFLAGS: '-O0', CTIMETESTS: 'no' } @@ -185,6 +186,7 @@ jobs: CHILLDKG: 'yes' ICEBERG: 'yes' PREFRACTAL: 'yes' + FROST_ENROLLMENT: 'yes' CC: ${{ matrix.cc }} steps: @@ -226,6 +228,7 @@ jobs: CHILLDKG: 'yes' ICEBERG: 'yes' PREFRACTAL: 'yes' + FROST_ENROLLMENT: 'yes' CTIMETESTS: 'no' steps: @@ -269,6 +272,7 @@ jobs: CHILLDKG: 'yes' ICEBERG: 'yes' PREFRACTAL: 'yes' + FROST_ENROLLMENT: 'yes' CTIMETESTS: 'no' steps: @@ -303,6 +307,7 @@ jobs: CHILLDKG: 'yes' ICEBERG: 'yes' PREFRACTAL: 'yes' + FROST_ENROLLMENT: 'yes' CTIMETESTS: 'no' CC: ${{ matrix.cc }} @@ -356,6 +361,7 @@ jobs: CHILLDKG: 'yes' ICEBERG: 'yes' PREFRACTAL: 'yes' + FROST_ENROLLMENT: 'yes' CTIMETESTS: 'no' steps: @@ -414,6 +420,7 @@ jobs: CHILLDKG: 'yes' ICEBERG: 'yes' PREFRACTAL: 'yes' + FROST_ENROLLMENT: 'yes' CTIMETESTS: 'no' SECP256K1_TEST_ITERS: 2 @@ -456,6 +463,7 @@ jobs: CHILLDKG: 'yes' ICEBERG: 'yes' PREFRACTAL: 'yes' + FROST_ENROLLMENT: 'yes' CTIMETESTS: 'no' CFLAGS: '-fsanitize=undefined,address -g' UBSAN_OPTIONS: 'print_stacktrace=1:halt_on_error=1' @@ -515,6 +523,7 @@ jobs: CHILLDKG: 'yes' ICEBERG: 'yes' PREFRACTAL: 'yes' + FROST_ENROLLMENT: 'yes' CC: ${{ matrix.cc }} SECP256K1_TEST_ITERS: 32 ASM: 'no' @@ -553,6 +562,7 @@ jobs: CHILLDKG: 'yes' ICEBERG: 'yes' PREFRACTAL: 'yes' + FROST_ENROLLMENT: 'yes' CTIMETESTS: 'no' strategy: @@ -585,15 +595,15 @@ jobs: fail-fast: false matrix: env_vars: - - { WIDEMUL: 'int64', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', SURJECTIONPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', FROST: 'yes', CHILLDKG: 'yes', ICEBERG: 'yes', PREFRACTAL: 'yes' } + - { WIDEMUL: 'int64', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', SURJECTIONPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', FROST: 'yes', CHILLDKG: 'yes', ICEBERG: 'yes', PREFRACTAL: 'yes', FROST_ENROLLMENT: 'yes' } - { WIDEMUL: 'int128_struct', ECMULTGENKB: 2, ECMULTWINDOW: 4 } - - { WIDEMUL: 'int128', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', SURJECTIONPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', FROST: 'yes', CHILLDKG: 'yes', ICEBERG: 'yes', PREFRACTAL: 'yes' } + - { WIDEMUL: 'int128', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', SURJECTIONPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', FROST: 'yes', CHILLDKG: 'yes', ICEBERG: 'yes', PREFRACTAL: 'yes', FROST_ENROLLMENT: 'yes' } - { WIDEMUL: 'int128', RECOVERY: 'yes' } - - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', SURJECTIONPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', FROST: 'yes', CHILLDKG: 'yes', ICEBERG: 'yes', PREFRACTAL: 'yes' } - - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', SURJECTIONPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', FROST: 'yes', CHILLDKG: 'yes', ICEBERG: 'yes', PREFRACTAL: 'yes', CC: 'gcc' } - - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', SURJECTIONPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', FROST: 'yes', CHILLDKG: 'yes', ICEBERG: 'yes', PREFRACTAL: 'yes', WRAPPER_CMD: 'valgrind --error-exitcode=42', SECP256K1_TEST_ITERS: 2 } - - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', SURJECTIONPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', FROST: 'yes', CHILLDKG: 'yes', ICEBERG: 'yes', PREFRACTAL: 'yes', CC: 'gcc', WRAPPER_CMD: 'valgrind --error-exitcode=42', SECP256K1_TEST_ITERS: 2 } - - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', SURJECTIONPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', FROST: 'yes', CHILLDKG: 'yes', ICEBERG: 'yes', PREFRACTAL: 'yes', CPPFLAGS: '-DVERIFY', CTIMETESTS: 'no' } + - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', SURJECTIONPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', FROST: 'yes', CHILLDKG: 'yes', ICEBERG: 'yes', PREFRACTAL: 'yes', FROST_ENROLLMENT: 'yes' } + - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', SURJECTIONPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', FROST: 'yes', CHILLDKG: 'yes', ICEBERG: 'yes', PREFRACTAL: 'yes', FROST_ENROLLMENT: 'yes', CC: 'gcc' } + - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', SURJECTIONPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', FROST: 'yes', CHILLDKG: 'yes', ICEBERG: 'yes', PREFRACTAL: 'yes', FROST_ENROLLMENT: 'yes', WRAPPER_CMD: 'valgrind --error-exitcode=42', SECP256K1_TEST_ITERS: 2 } + - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', SURJECTIONPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', FROST: 'yes', CHILLDKG: 'yes', ICEBERG: 'yes', PREFRACTAL: 'yes', FROST_ENROLLMENT: 'yes', CC: 'gcc', WRAPPER_CMD: 'valgrind --error-exitcode=42', SECP256K1_TEST_ITERS: 2 } + - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', SURJECTIONPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', FROST: 'yes', CHILLDKG: 'yes', ICEBERG: 'yes', PREFRACTAL: 'yes', FROST_ENROLLMENT: 'yes', CPPFLAGS: '-DVERIFY', CTIMETESTS: 'no' } - BUILD: 'distcheck' steps: @@ -773,6 +783,7 @@ jobs: CHILLDKG: 'yes' ICEBERG: 'yes' PREFRACTAL: 'yes' + FROST_ENROLLMENT: 'yes' steps: - *CHECKOUT diff --git a/.gitignore b/.gitignore index 62334853..24404fd9 100644 --- a/.gitignore +++ b/.gitignore @@ -59,6 +59,7 @@ contrib/gh-pr-create.sh frost_example chilldkg_example iceberg_example +frost_enrollment_example ### CMake /CMakeUserPresets.json diff --git a/CMakeLists.txt b/CMakeLists.txt index a7c71e68..85788933 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -55,6 +55,7 @@ option(SECP256K1_ENABLE_MODULE_FROST "Enable FROST module (experimental)." OFF) option(SECP256K1_ENABLE_MODULE_CHILLDKG "Enable ChillDKG module (experimental)." OFF) option(SECP256K1_ENABLE_MODULE_ICEBERG "Enable Iceberg threshold-MuSig module (experimental)." OFF) option(SECP256K1_ENABLE_MODULE_PREFRACTAL "Enable Prefractal nested FROST+MuSig2 module (experimental)." OFF) +option(SECP256K1_ENABLE_MODULE_FROST_ENROLLMENT "Enable FROST enrollment module (experimental)." OFF) option(SECP256K1_ENABLE_MODULE_ELLSWIFT "Enable ElligatorSwift module." ON) option(SECP256K1_ENABLE_MODULE_GENERATOR "Enable NUMS generator module." ON) @@ -142,6 +143,9 @@ if(NOT SECP256K1_EXPERIMENTAL) if(SECP256K1_ENABLE_MODULE_PREFRACTAL) message(FATAL_ERROR "Prefractal module is experimental. Use -DSECP256K1_EXPERIMENTAL=ON to allow.") endif() + if(SECP256K1_ENABLE_MODULE_FROST_ENROLLMENT) + message(FATAL_ERROR "FROST enrollment module is experimental. Use -DSECP256K1_EXPERIMENTAL=ON to allow.") + endif() endif() set(SECP256K1_VALGRIND "AUTO" CACHE STRING "Build with extra checks for running inside Valgrind. [default=AUTO]") @@ -317,6 +321,7 @@ message(" frost ............................... ${SECP256K1_ENABLE_MODULE_FROST message(" chilldkg ............................ ${SECP256K1_ENABLE_MODULE_CHILLDKG}") message(" iceberg ............................. ${SECP256K1_ENABLE_MODULE_ICEBERG}") message(" prefractal .......................... ${SECP256K1_ENABLE_MODULE_PREFRACTAL}") +message(" frost-enrollment .................... ${SECP256K1_ENABLE_MODULE_FROST_ENROLLMENT}") message(" ElligatorSwift ...................... ${SECP256K1_ENABLE_MODULE_ELLSWIFT}") message(" generator ........................... ${SECP256K1_ENABLE_MODULE_GENERATOR}") message(" rangeproof .......................... ${SECP256K1_ENABLE_MODULE_RANGEPROOF}") diff --git a/Makefile.am b/Makefile.am index 9467933a..15bc7644 100644 --- a/Makefile.am +++ b/Makefile.am @@ -320,6 +320,7 @@ EXTRA_DIST = autogen.sh CHANGELOG.md SECURITY.md EXTRA_DIST += doc/release-process.md doc/safegcd_implementation.md EXTRA_DIST += doc/ellswift.md doc/musig.md doc/iceberg.md doc/prefractal.md EXTRA_DIST += src/modules/frost/frost.md src/modules/chilldkg/chilldkg.md +EXTRA_DIST += src/modules/frost_enrollment/frost_enrollment.md EXTRA_DIST += examples/EXAMPLES_COPYING EXTRA_DIST += sage/gen_exhaustive_groups.sage EXTRA_DIST += sage/gen_split_lambda_constants.sage @@ -401,6 +402,10 @@ if ENABLE_MODULE_PREFRACTAL include src/modules/prefractal/Makefile.am.include endif +if ENABLE_MODULE_FROST_ENROLLMENT +include src/modules/frost_enrollment/Makefile.am.include +endif + if ENABLE_MODULE_ICEBERG include src/modules/iceberg/Makefile.am.include endif diff --git a/ci/ci.sh b/ci/ci.sh index b11e220c..40cc1ccf 100755 --- a/ci/ci.sh +++ b/ci/ci.sh @@ -15,7 +15,7 @@ print_environment() { ECMULTWINDOW ECMULTGENKB ASM WIDEMUL WITH_VALGRIND EXTRAFLAGS \ EXPERIMENTAL ECDH RECOVERY EXTRAKEYS SCHNORRSIG MUSIG SCHNORRSIG_HALFAGG ELLSWIFT \ ECDSA_S2C GENERATOR RANGEPROOF SURJECTIONPROOF WHITELIST ECDSAADAPTOR BPPP \ - FROST CHILLDKG ICEBERG PREFRACTAL SECP256K1_TEST_ITERS BENCH SECP256K1_BENCH_ITERS CTIMETESTS SYMBOL_CHECK \ + FROST CHILLDKG ICEBERG PREFRACTAL FROST_ENROLLMENT SECP256K1_TEST_ITERS BENCH SECP256K1_BENCH_ITERS CTIMETESTS SYMBOL_CHECK \ EXAMPLES \ HOST WRAPPER_CMD \ CC CFLAGS CPPFLAGS AR NM \ @@ -73,6 +73,7 @@ fi --enable-module-chilldkg="$CHILLDKG" \ --enable-module-iceberg="$ICEBERG" \ --enable-module-prefractal="$PREFRACTAL" \ + --enable-module-frost-enrollment="$FROST_ENROLLMENT" \ --enable-examples="$EXAMPLES" \ --enable-ctime-tests="$CTIMETESTS" \ --with-valgrind="$WITH_VALGRIND" \ diff --git a/configure.ac b/configure.ac index 683c3e14..81f426d6 100644 --- a/configure.ac +++ b/configure.ac @@ -260,6 +260,11 @@ AC_ARG_ENABLE(module_prefractal, [], [SECP_SET_DEFAULT([enable_module_prefractal], [no], [yes])]) +AC_ARG_ENABLE(module_frost_enrollment, + AS_HELP_STRING([--enable-module-frost-enrollment],[enable FROST enrollment module (experimental)]), + [], + [SECP_SET_DEFAULT([enable_module_frost_enrollment], [no], [yes])]) + # Test-only override of the (autodetected by the C code) "widemul" setting. # Legal values are: # * int64 (for [u]int64_t), @@ -530,6 +535,18 @@ if test x"$enable_module_ellswift" = x"yes"; then SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_ELLSWIFT=1" fi +# Like the prefractal block below, this must stay ahead of the frost block +# further down: the enable_module_frost assignment here is only observed by +# blocks that run after it. +if test x"$enable_module_frost_enrollment" = x"yes"; then + # frost defaults to no, so the "you disabled it explicitly" guard every + # other block uses would reject every frost-enrollment build; enabling + # frost-enrollment simply implies frost. See the prefractal block below for + # the full reasoning. + enable_module_frost=yes + SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_FROST_ENROLLMENT=1" +fi + # This block must stay ahead of the musig and frost blocks below. The # enable_module_* assignments here are only observed by blocks that run after # them, and frost defaults to "no": placing this at the iceberg block's @@ -654,6 +671,9 @@ if test x"$enable_experimental" = x"no"; then if test x"$enable_module_prefractal" = x"yes"; then AC_MSG_ERROR([Prefractal module is experimental. Use --enable-experimental to allow.]) fi + if test x"$enable_module_frost_enrollment" = x"yes"; then + AC_MSG_ERROR([FROST enrollment module 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 @@ -704,6 +724,7 @@ AM_CONDITIONAL([ENABLE_MODULE_FROST], [test x"$enable_module_frost" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_CHILLDKG], [test x"$enable_module_chilldkg" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_ICEBERG], [test x"$enable_module_iceberg" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_PREFRACTAL], [test x"$enable_module_prefractal" = x"yes"]) +AM_CONDITIONAL([ENABLE_MODULE_FROST_ENROLLMENT], [test x"$enable_module_frost_enrollment" = x"yes"]) AM_CONDITIONAL([USE_REDUCED_SURJECTION_PROOF_SIZE], [test x"$use_reduced_surjection_proof_size" = x"yes"]) AM_CONDITIONAL([USE_EXTERNAL_ASM], [test x"$enable_external_asm" = x"yes"]) AM_CONDITIONAL([USE_ASM_ARM], [test x"$set_asm" = x"arm32"]) @@ -749,6 +770,7 @@ echo " module frost = $enable_module_frost" echo " module chilldkg = $enable_module_chilldkg" echo " module iceberg = $enable_module_iceberg" echo " module prefractal = $enable_module_prefractal" +echo " module frost-enrollment = $enable_module_frost_enrollment" echo echo " asm = $set_asm" echo " ecmult window size = $set_ecmult_window" diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 25c428b5..05ee9689 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -73,6 +73,20 @@ if(SECP256K1_ENABLE_MODULE_ELLSWIFT) set_property(TARGET secp256k1 APPEND PROPERTY PUBLIC_HEADER ${PROJECT_SOURCE_DIR}/include/secp256k1_ellswift.h) endif() +# Like the prefractal block below, this must precede the FROST block: the +# set() call here is what forces frost on, and it is only observed by blocks +# that run after it. +if(SECP256K1_ENABLE_MODULE_FROST_ENROLLMENT) + # frost defaults to OFF, so the "DEFINED AND NOT" guard the other blocks use + # would reject every frost-enrollment build; enabling frost-enrollment + # simply implies frost. See the prefractal block below for the full + # reasoning, including why the flag is also lifted into the parent scope. + set(SECP256K1_ENABLE_MODULE_FROST ON) + set(SECP256K1_ENABLE_MODULE_FROST ON PARENT_SCOPE) + add_compile_definitions(ENABLE_MODULE_FROST_ENROLLMENT=1) + set_property(TARGET secp256k1 APPEND PROPERTY PUBLIC_HEADER ${PROJECT_SOURCE_DIR}/include/secp256k1_frost_enrollment.h) +endif() + # Must precede the musig and frost blocks below: the set() calls here are what # force those modules on, and they are only observed by blocks that run after. if(SECP256K1_ENABLE_MODULE_PREFRACTAL) diff --git a/src/modules/frost/session.h b/src/modules/frost/session.h index 01bb1c4f..7c560a3c 100644 --- a/src/modules/frost/session.h +++ b/src/modules/frost/session.h @@ -31,6 +31,13 @@ typedef struct { int g_times_gacc_parity; } secp256k1_frost_session_internal; +/* Sorts n_ids identifiers from ids into out (which must have room for + * n_ids entries and must not alias ids) in ascending order. Used to + * canonicalize a signer set before hashing it, so that parties holding the + * same set in different orders agree on the digest. Requires n_ids <= + * SECP256K1_FROST_MAX_PARTICIPANTS. */ +static void secp256k1_frost_sort_ids(uint32_t *out, const uint32_t *ids, size_t n_ids); + /* Saves the two secret scalars k[0], k[1] into a secnonce. */ static void secp256k1_frost_secnonce_save(secp256k1_frost_secnonce *secnonce, const secp256k1_scalar *k); diff --git a/src/modules/frost_enrollment/Makefile.am.include b/src/modules/frost_enrollment/Makefile.am.include new file mode 100644 index 00000000..f8f13831 --- /dev/null +++ b/src/modules/frost_enrollment/Makefile.am.include @@ -0,0 +1,4 @@ +include_HEADERS += include/secp256k1_frost_enrollment.h +noinst_HEADERS += src/modules/frost_enrollment/main_impl.h +noinst_HEADERS += src/modules/frost_enrollment/enrollment_impl.h +noinst_HEADERS += src/modules/frost_enrollment/tests_impl.h diff --git a/src/modules/frost_enrollment/enrollment_impl.h b/src/modules/frost_enrollment/enrollment_impl.h new file mode 100644 index 00000000..5d082173 --- /dev/null +++ b/src/modules/frost_enrollment/enrollment_impl.h @@ -0,0 +1,105 @@ +/*********************************************************************** + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ + +#ifndef SECP256K1_MODULE_FROST_ENROLLMENT_IMPL_H +#define SECP256K1_MODULE_FROST_ENROLLMENT_IMPL_H + +#include + +#include "../../../include/secp256k1.h" +#include "../../../include/secp256k1_frost_enrollment.h" + +/* This module is compiled into the same translation unit as frost and is + * included after it, so it may use frost's static internals. It adds nothing + * to them: the Lagrange machinery, the identifier conventions and the id + * canonicalization all come from there. */ +#include "../frost/keygen.h" +#include "../frost/session.h" + +#include "../../eckey.h" +#include "../../group.h" +#include "../../hash.h" +#include "../../scalar.h" +#include "../../util.h" + +int secp256k1_frost_enrollment_params_hash(const secp256k1_context *ctx, unsigned char *out32, const secp256k1_pubkey *thresh_pk, const uint32_t *ids, size_t n_ids, uint32_t new_id, size_t n_participants, uint32_t threshold) { + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(out32 != NULL); + memset(out32, 0, 32); + ARG_CHECK(thresh_pk != NULL); + ARG_CHECK(ids != NULL); + (void)n_ids; + (void)new_id; + (void)n_participants; + (void)threshold; + return 0; +} + +int secp256k1_frost_enrollment_shares_gen(const secp256k1_context *ctx, unsigned char *shares32_out, unsigned char *params_hash32_out, unsigned char *session_secrand32, const unsigned char *secshare32, const secp256k1_pubkey *thresh_pk, const uint32_t *ids, size_t n_ids, uint32_t my_id, uint32_t new_id, size_t n_participants, uint32_t threshold) { + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(shares32_out != NULL); + ARG_CHECK(params_hash32_out != NULL); + memset(params_hash32_out, 0, 32); + ARG_CHECK(session_secrand32 != NULL); + ARG_CHECK(secshare32 != NULL); + ARG_CHECK(thresh_pk != NULL); + ARG_CHECK(ids != NULL); + (void)n_ids; + (void)my_id; + (void)new_id; + (void)n_participants; + (void)threshold; + return 0; +} + +int secp256k1_frost_enrollment_share_agg(const secp256k1_context *ctx, unsigned char *sigma32_out, uint32_t *mismatch_id, const unsigned char *all_shares32, const unsigned char *received_params_hashes32, const secp256k1_pubkey *thresh_pk, const uint32_t *ids, size_t n_ids, uint32_t my_id, uint32_t new_id, size_t n_participants, uint32_t threshold) { + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(sigma32_out != NULL); + memset(sigma32_out, 0, 32); + if (mismatch_id != NULL) { + *mismatch_id = UINT32_MAX; + } + ARG_CHECK(all_shares32 != NULL); + ARG_CHECK(received_params_hashes32 != NULL); + ARG_CHECK(thresh_pk != NULL); + ARG_CHECK(ids != NULL); + (void)n_ids; + (void)my_id; + (void)new_id; + (void)n_participants; + (void)threshold; + return 0; +} + +int secp256k1_frost_enrollment_pubshare_derive(const secp256k1_context *ctx, secp256k1_pubkey *new_pubshare_out, const secp256k1_pubkey *pubshares, const uint32_t *ids, size_t n_ids, uint32_t new_id, size_t n_participants, uint32_t threshold) { + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(new_pubshare_out != NULL); + memset(new_pubshare_out, 0, sizeof(*new_pubshare_out)); + ARG_CHECK(pubshares != NULL); + ARG_CHECK(ids != NULL); + (void)n_ids; + (void)new_id; + (void)n_participants; + (void)threshold; + return 0; +} + +int secp256k1_frost_enrollment_secshare_gen(const secp256k1_context *ctx, unsigned char *secshare32_out, const unsigned char *sigmas32, const secp256k1_pubkey *thresh_pk, const uint32_t *ids, size_t n_ids, uint32_t new_id, size_t n_participants, uint32_t threshold, const unsigned char *expected_params_hash32, const secp256k1_pubkey *expected_pubshare) { + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secshare32_out != NULL); + memset(secshare32_out, 0, 32); + ARG_CHECK(sigmas32 != NULL); + ARG_CHECK(thresh_pk != NULL); + ARG_CHECK(ids != NULL); + (void)n_ids; + (void)new_id; + (void)n_participants; + (void)threshold; + (void)expected_params_hash32; + (void)expected_pubshare; + return 0; +} + +#endif diff --git a/src/modules/frost_enrollment/main_impl.h b/src/modules/frost_enrollment/main_impl.h new file mode 100644 index 00000000..023fe67c --- /dev/null +++ b/src/modules/frost_enrollment/main_impl.h @@ -0,0 +1,14 @@ +/*********************************************************************** + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ + +#ifndef SECP256K1_MODULE_FROST_ENROLLMENT_MAIN_H +#define SECP256K1_MODULE_FROST_ENROLLMENT_MAIN_H + +/* One layer only. Enrollment is three rounds of scalar arithmetic over key + * material the frost module already knows how to load and save, so there is + * no keygen, no serialization and no state of its own here. */ +#include "enrollment_impl.h" + +#endif diff --git a/src/modules/frost_enrollment/tests_impl.h b/src/modules/frost_enrollment/tests_impl.h new file mode 100644 index 00000000..bf880ba3 --- /dev/null +++ b/src/modules/frost_enrollment/tests_impl.h @@ -0,0 +1,52 @@ +/*********************************************************************** + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ + +#ifndef SECP256K1_MODULE_FROST_ENROLLMENT_TESTS_IMPL_H +#define SECP256K1_MODULE_FROST_ENROLLMENT_TESTS_IMPL_H + +#include "../../../include/secp256k1_frost_enrollment.h" + +/* Every entry point must reject an empty helper set and leave its output + * zeroed. This holds for the stubs this commit adds and for the finished + * implementation alike, so it doubles as the check that all five symbols are + * reachable from the test binary. */ +static void run_frost_enrollment_rejects_empty_set_test(void) { + secp256k1_pubkey pk; + secp256k1_pubkey pubshare; + unsigned char buf32[32]; + unsigned char secrand32[32]; + unsigned char hash32[32]; + uint32_t ids[1] = { 0 }; + + memset(&pk, 0, sizeof(pk)); + memset(&pubshare, 0xff, sizeof(pubshare)); + memset(secrand32, 0x11, sizeof(secrand32)); + + memset(buf32, 0xff, sizeof(buf32)); + CHECK(secp256k1_frost_enrollment_params_hash(CTX, buf32, &pk, ids, 0, 0, 1, 2) == 0); + CHECK(secp256k1_is_zero_array(buf32, sizeof(buf32))); + + memset(buf32, 0xff, sizeof(buf32)); + memset(hash32, 0xff, sizeof(hash32)); + CHECK(secp256k1_frost_enrollment_shares_gen(CTX, buf32, hash32, secrand32, buf32, &pk, ids, 0, 0, 1, 1, 2) == 0); + CHECK(secp256k1_is_zero_array(hash32, sizeof(hash32))); + + memset(buf32, 0xff, sizeof(buf32)); + CHECK(secp256k1_frost_enrollment_share_agg(CTX, buf32, NULL, buf32, hash32, &pk, ids, 0, 0, 1, 1, 2) == 0); + CHECK(secp256k1_is_zero_array(buf32, sizeof(buf32))); + + CHECK(secp256k1_frost_enrollment_pubshare_derive(CTX, &pubshare, &pk, ids, 0, 0, 1, 2) == 0); + CHECK(secp256k1_is_zero_array((unsigned char *)&pubshare, sizeof(pubshare))); + + memset(buf32, 0xff, sizeof(buf32)); + CHECK(secp256k1_frost_enrollment_secshare_gen(CTX, buf32, buf32, &pk, ids, 0, 0, 1, 2, NULL, NULL) == 0); + CHECK(secp256k1_is_zero_array(buf32, sizeof(buf32))); +} + +static const struct tf_test_entry tests_frost_enrollment[] = { + CASE1(run_frost_enrollment_rejects_empty_set_test), +}; + +#endif /* SECP256K1_MODULE_FROST_ENROLLMENT_TESTS_IMPL_H */ diff --git a/src/secp256k1.c b/src/secp256k1.c index 54b726e4..5ccf1e2c 100644 --- a/src/secp256k1.c +++ b/src/secp256k1.c @@ -965,6 +965,12 @@ static int secp256k1_ge_parse_ext(secp256k1_ge* ge, const unsigned char *in33) { # include "modules/prefractal/main_impl.h" #endif +/* Must follow the frost include above: this module calls frost's static + * internals in place rather than duplicating them. */ +#ifdef ENABLE_MODULE_FROST_ENROLLMENT +# include "modules/frost_enrollment/main_impl.h" +#endif + #ifdef ENABLE_MODULE_ICEBERG # include "modules/iceberg/main_impl.h" #endif diff --git a/src/tests.c b/src/tests.c index 61f94e91..52eb49a2 100644 --- a/src/tests.c +++ b/src/tests.c @@ -7932,6 +7932,10 @@ static void run_ecdsa_wycheproof(void) { # include "modules/prefractal/tests_impl.h" #endif +#ifdef ENABLE_MODULE_FROST_ENROLLMENT +# include "modules/frost_enrollment/tests_impl.h" +#endif + #ifdef ENABLE_MODULE_ICEBERG # include "modules/iceberg/tests_impl.h" #endif @@ -8313,6 +8317,9 @@ static const struct tf_test_module registry_modules[] = { #ifdef ENABLE_MODULE_PREFRACTAL MAKE_TEST_MODULE(prefractal), #endif +#ifdef ENABLE_MODULE_FROST_ENROLLMENT + MAKE_TEST_MODULE(frost_enrollment), +#endif #ifdef ENABLE_MODULE_ICEBERG MAKE_TEST_MODULE(iceberg), #endif