diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 25712e00..3f7527aa 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -48,6 +48,7 @@ env: BPPP: 'no' SCHNORRSIG_HALFAGG: 'no' FROST: 'no' + CHILLDKG: 'no' ### test options SECP256K1_TEST_ITERS: 64 BENCH: 'yes' @@ -105,14 +106,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'} + - 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'} - 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'} + - 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'} - 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'} - - 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', CPPFLAGS: '-DVERIFY' } + - 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', CPPFLAGS: '-DVERIFY' } - env_vars: { BUILD: 'distcheck', WITH_VALGRIND: 'no', CTIMETESTS: 'no', BENCH: 'no' } - env_vars: { CPPFLAGS: '-DDETERMINISTIC' } - env_vars: { CFLAGS: '-O0', CTIMETESTS: 'no' } @@ -179,6 +180,7 @@ jobs: BPPP: 'yes' SCHNORRSIG_HALFAGG: 'yes' FROST: 'yes' + CHILLDKG: 'yes' CC: ${{ matrix.cc }} steps: @@ -217,6 +219,7 @@ jobs: BPPP: 'yes' SCHNORRSIG_HALFAGG: 'yes' FROST: 'yes' + CHILLDKG: 'yes' CTIMETESTS: 'no' steps: @@ -257,6 +260,7 @@ jobs: BPPP: 'yes' SCHNORRSIG_HALFAGG: 'yes' FROST: 'yes' + CHILLDKG: 'yes' CTIMETESTS: 'no' steps: @@ -288,6 +292,7 @@ jobs: BPPP: 'yes' SCHNORRSIG_HALFAGG: 'yes' FROST: 'yes' + CHILLDKG: 'yes' CTIMETESTS: 'no' CC: ${{ matrix.cc }} @@ -338,6 +343,7 @@ jobs: BPPP: 'yes' SCHNORRSIG_HALFAGG: 'yes' FROST: 'yes' + CHILLDKG: 'yes' CTIMETESTS: 'no' steps: @@ -393,6 +399,7 @@ jobs: BPPP: 'yes' SCHNORRSIG_HALFAGG: 'yes' FROST: 'yes' + CHILLDKG: 'yes' CTIMETESTS: 'no' SECP256K1_TEST_ITERS: 2 @@ -432,6 +439,7 @@ jobs: BPPP: 'yes' SCHNORRSIG_HALFAGG: 'yes' FROST: 'yes' + CHILLDKG: 'yes' CTIMETESTS: 'no' CFLAGS: '-fsanitize=undefined,address -g' UBSAN_OPTIONS: 'print_stacktrace=1:halt_on_error=1' @@ -488,6 +496,7 @@ jobs: BPPP: 'yes' SCHNORRSIG_HALFAGG: 'yes' FROST: 'yes' + CHILLDKG: 'yes' CC: ${{ matrix.cc }} SECP256K1_TEST_ITERS: 32 ASM: 'no' @@ -523,6 +532,7 @@ jobs: BPPP: 'yes' SCHNORRSIG_HALFAGG: 'yes' FROST: 'yes' + CHILLDKG: 'yes' CTIMETESTS: 'no' strategy: @@ -555,15 +565,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' } + - { 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' } - { 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' } + - { 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' } - { 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' } - - { 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', 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', 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', 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', 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' } + - { 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', 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', 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', 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', CPPFLAGS: '-DVERIFY', CTIMETESTS: 'no' } - BUILD: 'distcheck' steps: @@ -740,6 +750,7 @@ jobs: BPPP: 'yes' SCHNORRSIG_HALFAGG: 'yes' FROST: 'yes' + CHILLDKG: 'yes' steps: - *CHECKOUT diff --git a/ci/ci.sh b/ci/ci.sh index b3bf27e0..5dbb9604 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 SECP256K1_TEST_ITERS BENCH SECP256K1_BENCH_ITERS CTIMETESTS SYMBOL_CHECK \ + FROST CHILLDKG SECP256K1_TEST_ITERS BENCH SECP256K1_BENCH_ITERS CTIMETESTS SYMBOL_CHECK \ EXAMPLES \ HOST WRAPPER_CMD \ CC CFLAGS CPPFLAGS AR NM \ @@ -70,6 +70,7 @@ fi --enable-module-musig="$MUSIG" \ --enable-module-schnorrsig-halfagg="$SCHNORRSIG_HALFAGG" \ --enable-module-frost="$FROST" \ + --enable-module-chilldkg="$CHILLDKG" \ --enable-examples="$EXAMPLES" \ --enable-ctime-tests="$CTIMETESTS" \ --with-valgrind="$WITH_VALGRIND" \ diff --git a/src/ctime_tests.c b/src/ctime_tests.c index e5e0308e..a47b0ffa 100644 --- a/src/ctime_tests.c +++ b/src/ctime_tests.c @@ -57,6 +57,10 @@ #include "../include/secp256k1_frost.h" #endif +#ifdef ENABLE_MODULE_CHILLDKG +#include "../include/secp256k1_chilldkg.h" +#endif + static void run_tests(secp256k1_context *ctx, unsigned char *key); int main(void) { @@ -455,6 +459,134 @@ static void run_tests(secp256k1_context *ctx, unsigned char *key) { } } #endif + +#ifdef ENABLE_MODULE_CHILLDKG + { + /* Full ChillDKG session with n = 2, t = 2 (pmsg1: 227 bytes, cmsg1: + * 357 bytes, cert: 128 bytes, recovery data: 394 bytes). */ + unsigned char hostseckeys[2][32]; + unsigned char hostpubkeys[2 * 33]; + unsigned char dkg_random[2][32]; + unsigned char aux_rands[2][32]; + secp256k1_chilldkg_participant_state1 state1[2]; + secp256k1_chilldkg_participant_state2 state2[2]; + secp256k1_chilldkg_coordinator_state cstate; + unsigned char pmsgs1[2][227]; + const unsigned char *pmsgs1_ptrs[2]; + unsigned char cmsg1[357]; + unsigned char pmsgs2[2][64]; + const unsigned char *pmsgs2_ptrs[2]; + unsigned char cmsg2[128]; + unsigned char secshare32[32]; + unsigned char thresh_pk33[33]; + unsigned char pubshares33[2 * 33]; + unsigned char recovery[394]; + unsigned char rec_secshare32[32]; + unsigned char rec_thresh_pk33[33]; + unsigned char rec_pubshares33[33 * SECP256K1_CHILLDKG_MAX_PARTICIPANTS]; + unsigned char rec_hostpubkeys33[33 * SECP256K1_CHILLDKG_MAX_PARTICIPANTS]; + unsigned char ack_sig[64]; + size_t rec_n; + uint32_t rec_t; + uint32_t fault_index; + secp256k1_chilldkg_fault fault; + + pmsgs1_ptrs[0] = pmsgs1[0]; + pmsgs1_ptrs[1] = pmsgs1[1]; + pmsgs2_ptrs[0] = pmsgs2[0]; + pmsgs2_ptrs[1] = pmsgs2[1]; + + /* All public inputs are derived from defined memory. key is reused as + * the base of the (secret) host keys, session randomness and + * auxiliary randomness. */ + SECP256K1_CHECKMEM_DEFINE(key, 32); + for (i = 0; i < 2; i++) { + memcpy(hostseckeys[i], key, 32); + hostseckeys[i][0] = hostseckeys[i][0] + 4 + i; + memcpy(dkg_random[i], key, 32); + dkg_random[i][0] = dkg_random[i][0] + 6 + i; + memcpy(aux_rands[i], key, 32); + aux_rands[i][0] = aux_rands[i][0] + 8 + i; + } + + /* Test chilldkg_hostpubkey_gen. The host secret keys are secret; the + * host public keys are public. */ + for (i = 0; i < 2; i++) { + SECP256K1_CHECKMEM_UNDEFINE(hostseckeys[i], 32); + ret = secp256k1_chilldkg_hostpubkey_gen(ctx, &hostpubkeys[33 * i], hostseckeys[i]); + SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret)); + CHECK(ret == 1); + SECP256K1_CHECKMEM_DEFINE(&hostpubkeys[33 * i], 33); + } + + /* Test chilldkg_participant_step1. The host secret key and the + * session randomness are secret; pmsg1 and the state1 object (which + * contains no secrets) are public. */ + for (i = 0; i < 2; i++) { + SECP256K1_CHECKMEM_UNDEFINE(hostseckeys[i], 32); + SECP256K1_CHECKMEM_UNDEFINE(dkg_random[i], 32); + ret = secp256k1_chilldkg_participant_step1(ctx, &state1[i], pmsgs1[i], hostseckeys[i], hostpubkeys, 2, 2, dkg_random[i]); + SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret)); + CHECK(ret == 1); + SECP256K1_CHECKMEM_DEFINE(pmsgs1[i], sizeof(pmsgs1[i])); + SECP256K1_CHECKMEM_DEFINE(&state1[i], sizeof(state1[i])); + } + + /* The coordinator's steps use only public inputs. */ + fault = secp256k1_chilldkg_coordinator_step1(ctx, &cstate, cmsg1, &fault_index, pmsgs1_ptrs, hostpubkeys, 2, 2); + SECP256K1_CHECKMEM_DEFINE(&fault, sizeof(fault)); + CHECK(fault == SECP256K1_CHILLDKG_OK); + + /* Test chilldkg_participant_step2. The host secret key and the aux + * randomness are secret; the CertEq signature is public. The state2 + * object contains the secret share and stays secret. */ + for (i = 0; i < 2; i++) { + SECP256K1_CHECKMEM_UNDEFINE(hostseckeys[i], 32); + SECP256K1_CHECKMEM_UNDEFINE(aux_rands[i], 32); + fault = secp256k1_chilldkg_participant_step2(ctx, &state2[i], pmsgs2[i], &fault_index, NULL, &state1[i], hostseckeys[i], cmsg1, aux_rands[i]); + SECP256K1_CHECKMEM_DEFINE(&fault, sizeof(fault)); + CHECK(fault == SECP256K1_CHILLDKG_OK); + SECP256K1_CHECKMEM_DEFINE(pmsgs2[i], sizeof(pmsgs2[i])); + } + + fault = secp256k1_chilldkg_coordinator_finalize(ctx, cmsg2, thresh_pk33, pubshares33, recovery, &fault_index, &cstate, pmsgs2_ptrs); + SECP256K1_CHECKMEM_DEFINE(&fault, sizeof(fault)); + CHECK(fault == SECP256K1_CHILLDKG_OK); + + /* Test chilldkg_participant_finalize. The state2 input and the + * secshare output are secret; the threshold public key, the public + * shares and the recovery data are public. */ + fault = secp256k1_chilldkg_participant_finalize(ctx, secshare32, thresh_pk33, pubshares33, recovery, &fault_index, &state2[0], cmsg2); + SECP256K1_CHECKMEM_DEFINE(&fault, sizeof(fault)); + CHECK(fault == SECP256K1_CHILLDKG_OK); + SECP256K1_CHECKMEM_DEFINE(thresh_pk33, sizeof(thresh_pk33)); + SECP256K1_CHECKMEM_DEFINE(pubshares33, sizeof(pubshares33)); + SECP256K1_CHECKMEM_DEFINE(recovery, sizeof(recovery)); + + /* Test chilldkg_participant_recover. The host secret key and the + * recovered secret share are secret; the recovery data and the + * remaining outputs are public. */ + SECP256K1_CHECKMEM_UNDEFINE(hostseckeys[0], 32); + fault = secp256k1_chilldkg_participant_recover(ctx, rec_secshare32, rec_thresh_pk33, rec_pubshares33, rec_hostpubkeys33, &rec_n, &rec_t, &fault_index, hostseckeys[0], recovery, sizeof(recovery)); + SECP256K1_CHECKMEM_DEFINE(&fault, sizeof(fault)); + CHECK(fault == SECP256K1_CHILLDKG_OK); + SECP256K1_CHECKMEM_DEFINE(rec_thresh_pk33, sizeof(rec_thresh_pk33)); + SECP256K1_CHECKMEM_DEFINE(rec_pubshares33, 2 * 33); + SECP256K1_CHECKMEM_DEFINE(rec_hostpubkeys33, 2 * 33); + SECP256K1_CHECKMEM_DEFINE(&rec_n, sizeof(rec_n)); + SECP256K1_CHECKMEM_DEFINE(&rec_t, sizeof(rec_t)); + CHECK(rec_n == 2 && rec_t == 2); + + /* Test chilldkg_recovery_ack_sign. The host secret key and the aux + * randomness are secret; the acknowledgment signature is public. */ + SECP256K1_CHECKMEM_UNDEFINE(hostseckeys[0], 32); + SECP256K1_CHECKMEM_UNDEFINE(aux_rands[0], 32); + ret = secp256k1_chilldkg_recovery_ack_sign(ctx, ack_sig, hostseckeys[0], hostpubkeys, 2, 2, recovery, sizeof(recovery), aux_rands[0]); + SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret)); + CHECK(ret == 1); + SECP256K1_CHECKMEM_DEFINE(ack_sig, sizeof(ack_sig)); + } +#endif } #if defined(__GNUC__) diff --git a/src/modules/chilldkg/encpedpop_impl.h b/src/modules/chilldkg/encpedpop_impl.h index 46ca623a..7e6f02e4 100644 --- a/src/modules/chilldkg/encpedpop_impl.h +++ b/src/modules/chilldkg/encpedpop_impl.h @@ -212,6 +212,10 @@ static int secp256k1_chilldkg_encpedpop_participant_step1(const secp256k1_contex secp256k1_ge pubnonce_ge; secp256k1_ecmult_gen_gej(&ctx->ecmult_gen_ctx, &pubnoncej, &secnonce); secp256k1_ge_set_gej(&pubnonce_ge, &pubnoncej); + /* The pubnonce is public (it is part of the participant message); + * declassify it so that the variable-time serialization is allowed + * to branch on it. */ + secp256k1_declassify(ctx, &pubnonce_ge, sizeof(pubnonce_ge)); secp256k1_chilldkg_point_save(pubnonce33, &pubnonce_ge); secp256k1_gej_clear(&pubnoncej); diff --git a/src/modules/chilldkg/main_impl.h b/src/modules/chilldkg/main_impl.h index af7a7af4..310ce4c2 100644 --- a/src/modules/chilldkg/main_impl.h +++ b/src/modules/chilldkg/main_impl.h @@ -262,6 +262,9 @@ int secp256k1_chilldkg_hostpubkey_gen(const secp256k1_context *ctx, unsigned cha /* pubkey_gen_plain in the reference (IndividualPubkey of BIP 327). */ secp256k1_ecmult_gen_gej(&ctx->ecmult_gen_ctx, &pubkeyj, &seckey); secp256k1_ge_set_gej(&pubkey, &pubkeyj); + /* The host public key is not secret; declassify it so that the + * variable-time serialization is allowed to branch on it. */ + secp256k1_declassify(ctx, &pubkey, sizeof(pubkey)); secp256k1_chilldkg_point_save(hostpubkey33, &pubkey); secp256k1_gej_clear(&pubkeyj); } @@ -348,12 +351,18 @@ int secp256k1_chilldkg_participant_step1(const secp256k1_context *ctx, secp256k1 } /* HostSeckeyError if there is no match; RandomnessError if the * randomness is all zeroes (guards against a malfunctioning random - * number generator). */ - if (found && !secp256k1_is_zero_array(random32, 32)) { - /* In EncPedPop, both the seed and the deckey are the host secret - * key, and the enckeys are the host public keys (see - * chilldkg_ref/chilldkg.py participant_step1). */ - ret = secp256k1_chilldkg_encpedpop_participant_step1(ctx, &enc_state, pmsg1, hostseckey32, hostseckey32, hostpubkeys33, threshold, participant_id, random32, n_participants); + * number generator). Whether the randomness is all zeroes does not + * leak anything about its value otherwise (if it is, the session + * fails), so the check can be declassified. */ + { + int random_is_zero = secp256k1_is_zero_array(random32, 32); + secp256k1_declassify(ctx, &random_is_zero, sizeof(random_is_zero)); + if (found && !random_is_zero) { + /* In EncPedPop, both the seed and the deckey are the host + * secret key, and the enckeys are the host public keys (see + * chilldkg_ref/chilldkg.py participant_step1). */ + ret = secp256k1_chilldkg_encpedpop_participant_step1(ctx, &enc_state, pmsg1, hostseckey32, hostseckey32, hostpubkeys33, threshold, participant_id, random32, n_participants); + } } } diff --git a/src/modules/chilldkg/simplpedpop_impl.h b/src/modules/chilldkg/simplpedpop_impl.h index 15e9bd38..4c717455 100644 --- a/src/modules/chilldkg/simplpedpop_impl.h +++ b/src/modules/chilldkg/simplpedpop_impl.h @@ -411,9 +411,17 @@ static secp256k1_chilldkg_fault secp256k1_chilldkg_simplpedpop_participant_inves for (i = 0; i < n; i++) { secp256k1_scalar_add(&secshare_sum, &secshare_sum, &partial_secshares[i]); } - if (!secp256k1_scalar_eq(&secshare_sum, &inv_data->secshare)) { - secp256k1_scalar_clear(&secshare_sum); - return SECP256K1_CHILLDKG_FAULTY_COORDINATOR; + /* We can declassify the result of the comparison because branching on it + * only leaks whether the sum of the partial secshares equals the + * secshare, which is exactly what the resulting public fault code + * reveals. */ + { + int sums_match = secp256k1_scalar_eq(&secshare_sum, &inv_data->secshare); + secp256k1_declassify(ctx, &sums_match, sizeof(sums_match)); + if (!sums_match) { + secp256k1_scalar_clear(&secshare_sum); + return SECP256K1_CHILLDKG_FAULTY_COORDINATOR; + } } secp256k1_scalar_clear(&secshare_sum); diff --git a/src/modules/chilldkg/util_impl.h b/src/modules/chilldkg/util_impl.h index 70267a92..f10512da 100644 --- a/src/modules/chilldkg/util_impl.h +++ b/src/modules/chilldkg/util_impl.h @@ -203,6 +203,7 @@ static int secp256k1_chilldkg_schnorrsig_sign(const secp256k1_context *ctx, unsi secp256k1_ecmult_gen_ge(&ctx->ecmult_gen_ctx, &pk, &sk); /* The public key is not secret, so variable-time normalization and * branching on its y parity are fine. */ + secp256k1_declassify(ctx, &pk, sizeof(pk)); secp256k1_fe_normalize_var(&pk.x); secp256k1_fe_normalize_var(&pk.y); if (secp256k1_fe_is_odd(&pk.y)) { @@ -242,6 +243,10 @@ static int secp256k1_chilldkg_schnorrsig_sign(const secp256k1_context *ctx, unsi secp256k1_memclear_explicit(seckey, sizeof(seckey)); secp256k1_memclear_explicit(nonce32, sizeof(nonce32)); + /* Branching on the return value only leaks whether the derived nonce is + * zero, which happens with negligible probability (the case of an invalid + * secret key is declassified above). */ + secp256k1_declassify(ctx, &ret, sizeof(ret)); return ret; } diff --git a/src/modules/chilldkg/vss_impl.h b/src/modules/chilldkg/vss_impl.h index 94cb9a0e..9d793a8d 100644 --- a/src/modules/chilldkg/vss_impl.h +++ b/src/modules/chilldkg/vss_impl.h @@ -75,6 +75,10 @@ static void secp256k1_chilldkg_vss_commit(const secp256k1_context *ctx, secp256k * in the point at infinity, matching the reference. */ secp256k1_ecmult_gen_gej(&ctx->ecmult_gen_ctx, &comj, &coeffs[j]); secp256k1_ge_set_gej(&coms[j], &comj); + /* The commitment is public (it is part of the participant message); + * declassify it so that the variable-time serialization is allowed + * to branch on it. */ + secp256k1_declassify(ctx, &coms[j], sizeof(coms[j])); secp256k1_gej_clear(&comj); } } @@ -121,6 +125,11 @@ static int secp256k1_chilldkg_vss_verify_secshare(const secp256k1_context *ctx, VERIFY_CHECK(ctx != NULL); secp256k1_ecmult_gen_ge(&ctx->ecmult_gen_ctx, &actual, secshare); + /* We can declassify because in honest runs actual = secshare*G is the + * (public) pubshare itself, and because the verification result is always + * public: it determines a public fault code. The discrete log of actual + * (the secret share) is not revealed. */ + secp256k1_declassify(ctx, &actual, sizeof(actual)); if (secp256k1_ge_is_infinity(&actual) || secp256k1_ge_is_infinity(pubshare)) { return secp256k1_ge_is_infinity(&actual) && secp256k1_ge_is_infinity(pubshare); }