chilldkg: CI wiring, ctime_tests coverage, declassify fixes

CI:
- ci/ci.sh: new CHILLDKG environment variable, passed to configure as
  --enable-module-chilldkg (mirroring FROST).
- .github/workflows/ci.yml: default CHILLDKG: 'no' and CHILLDKG: 'yes'
  in every job that enables FROST, except the x86_64 matrix entry that
  deliberately builds without the ecdh module (chilldkg requires
  schnorrsig + ecdh; the configure-time dependency error would fire
  there). YAML validity and per-job dependency presence checked
  programmatically.

ctime_tests:
- src/ctime_tests.c: run a full ChillDKG session (n = 2, t = 2) through
  the public API under the memory checker: hostpubkey_gen, params_hash,
  participant_step1, coordinator_step1, participant_step2,
  coordinator_finalize, participant_finalize, participant_recover and
  recovery_ack_sign. Host secret keys, session randomness, aux
  randomness and the resulting secret shares are undefined (secret);
  all protocol messages, the certificate, threshold public key, public
  shares, recovery data, ack signature and the secret-free state1
  objects are defined (public). state2 stays secret (contains the
  secret share).

Constant-time fixes found by running the new block under
MemorySanitizer (valgrind unavailable locally; MSan build via clang +
CMake). All are missing declassifications of secret-derived but public
(or public-outcome) values, following the frost module's
secp256k1_declassify pattern with justification comments; no real
constant-time bugs were found:
- hostpubkey_gen: declassify the computed host public key before
  serialization (public output).
- participant_step1: declassify the zero-randomness check result (only
  reveals "the RNG returned 32 zero bytes", which aborts the session).
- encpedpop participant_step1: declassify the pubnonce point before
  serialization (public, part of pmsg1).
- chilldkg_schnorrsig_sign: declassify the signer public key before
  normalization/parity branch, and declassify the return value (a
  failure only reveals a zero derived nonce, negligible probability).
- vss_commit: declassify the VSS commitments before serialization
  (public, part of pmsg1).
- vss_verify_secshare: declassify secshare*G before the infinity/eq
  checks (equals the public pubshare in honest runs; the discrete log
  is not revealed).
- simplpedpop_participant_investigate (proactive audit; not reached by
  ctime_tests): declassify the secshare-sum comparison result (the
  public fault code reveals it anyway).

Verified: MSan ctime_tests exits 0; autotools make check 10/10 (the
local tree is configured without --enable-ctime-tests because neither
valgrind nor an MSan-instrumented gcc build is available; CI runs
ctime_tests under valgrind as before); CMake ctest 428/428;
./tests --target=chilldkg and ./chilldkg_example pass.
This commit is contained in:
Kgothatso Ngako
2026-08-31 10:25:14 +02:00
parent d1c817b528
commit 3b68633f59
8 changed files with 199 additions and 20 deletions

View File

@@ -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

View File

@@ -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" \

View File

@@ -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__)

View File

@@ -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);

View File

@@ -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);
}
}
}

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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);
}