Second of six commits adding the frost_enrollment module. This one is
scaffolding only: the five entry points are stubs that validate their
pointer arguments, zero their outputs and return 0. What is being
verified here is that the module configures, compiles, links, exports
its symbols and registers its test module in both build systems -- so
that the next commit changes nothing but arithmetic.
Ordering is the one thing in this commit that can go silently wrong, and
it goes wrong in opposite directions in the two build systems:
- configure.ac executes its `if` blocks in file order, and
enable_module_frost defaults to no (configure.ac:243). A block placed
after the frost block at :601 that sets enable_module_frost=yes flips
the variable too late: AM_CONDITIONAL goes true, so the header is
installed and the Makefile fragment is pulled in, but
-DENABLE_MODULE_FROST=1 is never appended, so src/secp256k1.c never
includes frost's implementation and every secp256k1_frost_* symbol
fails to link. The new block therefore goes ahead of both the frost
block and prefractal's, which documents the same trap.
- src/CMakeLists.txt processes dependents FIRST, so the same block goes
above the FROST block there, beside prefractal's.
Verified rather than assumed: configuring with ONLY
--enable-module-frost-enrollment emits -DENABLE_MODULE_FROST=1
alongside -DENABLE_MODULE_FROST_ENROLLMENT=1, and the CMake summary
prints "frost ON" for the same configuration -- the latter is what the
PARENT_SCOPE lift buys, since the summary runs after
add_subdirectory(src) and would otherwise report a module it is
compiling in as OFF.
The dependency guard is prefractal's implies-frost idiom, copied
verbatim along with its reasoning. frost is default-OFF, so the
`test x"$enable_module_frost" = x"no"` / `DEFINED X AND NOT X` guard
every other module uses -- which reads as "the user disabled it
explicitly" for a default-ON dependency -- is true by default here and
cannot tell an explicit --disable-module-frost from the default once
both are in the cache. Enabling frost-enrollment simply implies frost,
with no error.
The one frost-module change in the whole series is in this commit:
src/modules/frost/session.h gains a declaration for
secp256k1_frost_sort_ids, which is defined at session_impl.h:517 and
declared nowhere. The params hash needs it to canonicalize identifier
order. Prefractal reaches frost's statics through translation-unit
ordering alone; rather than inherit reuse-by-link-order, this declares
the function where keygen.h:48 already declares derive_pubshare_at, so
the reuse goes through an interface. No behavior change: it is a
declaration for an existing static definition in the same TU.
CI wiring is two files, and skipping either half fails quietly:
- ci/ci.sh gets FROST_ENROLLMENT in the reproduction header's variable
list and --enable-module-frost-enrollment="$FROST_ENROLLMENT" after
the prefractal line.
- .github/workflows/ci.yml gets FROST_ENROLLMENT at every PREFRACTAL
site: the global default, 11 inline matrix entries and 10 job-level
env blocks. Without the default, ci.sh runs under set -eux with an
empty $FROST_ENROLLMENT, passes --enable-module-frost-enrollment="",
`test x"" = x"yes"` is false, and the module is off in all of CI while
ci.sh visibly has the plumbing.
Verified programmatically over the parsed workflow: across the 106
effective job contexts, PREFRACTAL and FROST_ENROLLMENT now agree in
every single one (45 set to yes, no mismatches), no context sets
FROST_ENROLLMENT without FROST or without EXPERIMENTAL, and no context
leaves it undefined. ci.sh passes sh -n.
The stub test is not a placeholder that has to be deleted later: every
entry point must reject an empty helper set and leave its output zeroed,
which is true of the stubs and stays true of the finished
implementation, so it doubles as the check that all five symbols are
reachable from the test binary.
Verification. Autotools: ./autogen.sh, then a frost-enrollment-only
configure and a full configure with frost, chilldkg, iceberg, prefractal
and frost-enrollment all on -- both build with zero warnings under the
project's -Werror-grade flag set, ./tests and ./exhaustive_tests exit 0,
and `./tests -l` lists the frost_enrollment module. CMake: configure with
-DSECP256K1_EXPERIMENTAL=ON -DSECP256K1_ENABLE_MODULE_FROST_ENROLLMENT=ON
builds clean and ctest passes 391 tests. nm shows the five new symbols
exported from libsecp256k1.so; tools/symbol-check.py could not be run
here because python3-lief is not installed in this environment, but all
five carry the required secp256k1_ prefix. make dist succeeds and the
tarball carries src/modules/frost_enrollment/frost_enrollment.md
alongside the other module documents.
One unrelated observation from this build: a stale
src/ctime_tests-ctime_tests.o left over from an earlier configure with a
different module set will fail to link, because automake does not track
CPPFLAGS changes across reconfigures. make clean between configurations
with different module sets, not a fault in this change.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Port src/bench_iceberg.c, the standalone benchmark binary the
module's bench_impl.h is written for (this repo's bench harness has
no per-module include pattern for it, so the source tree's own wiring
is mirrored instead): noinst_PROGRAMS under USE_BENCHMARK +
ENABLE_MODULE_ICEBERG in Makefile.am, a bench_iceberg target in
src/CMakeLists.txt, and a .gitignore entry. The benchmark covers the
group configurations 2-of-3, 3-of-5, 4-of-7, 5-of-9 and 5-of-10.
Verified: ./bench_iceberg builds and runs under both build systems.
Port examples/iceberg.c from the source tree: a full Iceberg session
demonstrating the call order from the module docs -- distributed key
generation, pubshare_gen/pubkey_agg to obtain the group public key,
nonce_gen/nonce_agg into an ordinary MuSig2 public nonce, and
partial_sign/partial_sig_agg into an ordinary MuSig2 partial
signature.
One content adaptation: the secp256k1_musig_nonce_process call gains
a NULL adaptor argument, matching this repo's zkp musig variant.
Wired like the chilldkg example: autotools noinst_PROGRAMS +
TESTS entry under ENABLE_MODULE_ICEBERG (the example runs as part of
make check), CMake example target in examples/CMakeLists.txt, and
iceberg_example added to .gitignore.
Verified: ./iceberg_example runs to completion (exit 0) under both
build systems.
Final phase of the ChillDKG module: upstream test vectors, a
DKG->FROST integration test, boundary tests, full module
documentation and a runnable example.
Test vectors:
- tools/test_vectors_chilldkg_generate.py converts all 10 upstream
bip-frost-dkg JSON vector files into src/modules/chilldkg/vectors.h
(modeled on tools/test_vectors_frost_generate.py; takes the vectors
directory as an argument; upstream pinned to commit
a91896883f85b159415ecf298d5e844879af112d, recorded in the generated
header with the exact regeneration invocation; regeneration is
reproducible byte-for-byte).
- tests_impl.h vector runners execute 191 of 241 upstream cases
through the public API: hostpubkey_gen, params_hash,
participant_step1/step2/finalize/investigate,
coordinator_step1/finalize/investigate, recover. Happy paths are
byte-exact (pmsg1/cmsg1/pmsg2/cmsg2/dkg_output/recovery/cinv);
error cases assert both the fault enum and fault_index against
expectedError.participantId. The 50 skipped cases are
wrong-length/wrong-count inputs not expressible with the
fixed-size C API; each skip is documented in vectors.h.
Boundary/robustness tests: t=1, t=n, n=2, a full n=128/t=2 session
end-to-end with per-participant secshare*G == pubshare checks and a
recovery roundtrip, and a state1 memcpy roundtrip (step2 from a copied
state object).
DKG->FROST integration test (guarded by ENABLE_MODULE_FROST): a full
ChillDKG session (n=3, t=2) feeds (secshare, thresh_pk, pubshares)
directly into the frost module. ChillDKG's thresh_pk is already
TapTweak'ed, so frost_tweak_cache_init is called with no further
tweaks (frost's tweaked x-only key asserted equal to the x-only part
of the ChillDKG thresh_pk); signers 0 and 2 run nonce_gen, nonce_agg,
session_init with the shared x = id+1 convention, frost_sign,
partial_sig_verify and partial_sig_agg; the aggregate signature
verifies as a plain BIP-340 signature against the threshold key.
Example: examples/chilldkg.c runs a full 2-of-3 DKG session (host key
generation, params hash, participant/coordinator steps, finalize, and
a recovery roundtrip via participant_recover) with fixed-size buffers
and secret erasure. Wired into Makefile.am and
examples/CMakeLists.txt exactly like frost_example (runs as a TEST);
chilldkg_example binary added to .gitignore.
Docs: src/modules/chilldkg/chilldkg.md now documents the protocol
summary, message-flow table with exact byte sizes, blame taxonomy,
recovery workflow, security notes (host key reuse/retention, fresh
randomness per session, state secrecy, recovery-data sensitivity) and
the pinned reference commit; src/modules/frost/frost.md points at the
new module as the intended DKG.
Bug fix found by the vector runner (recover tcId 9): the internal
recover() passed a possibly-NULL fault_index from coordinator_recover
to certeq_verify, which dereferences it on failure; now uses a local.
Verified: make check 10/10 (3 test suites + 7 examples incl.
chilldkg_example, exit 0 when run); CMake ctest 428/428 with chilldkg
+ frost, and a no-frost build confirms the ENABLE_MODULE_FROST guard;
make distdir includes vectors.h, the example and the generator.
The module is feature-complete against bip-frost-dkg v0.3.0-dev at
a91896883f85b159415ecf298d5e844879af112d. The BIP is still a draft;
tagged hashes and wire formats may change upstream.
CI / x86_64: Linux (Debian stable) (clang, map[env_vars:map[CFLAGS:-O1 ECDH:yes ELLSWIFT:yes EXTRAKEYS:yes MUSIG:yes RECOVERY:yes SCHNORRSIG:yes]]) (push) Has been cancelled
CI / x86_64: Linux (Debian stable) (clang, map[env_vars:map[ELLSWIFT:yes EXTRAKEYS:yes MUSIG:yes RECOVERY:yes SCHNORRSIG:yes WIDEMUL:int128]]) (push) Has been cancelled
CI / x86_64: Linux (Debian stable) (clang-snapshot, map[env_vars:map[BENCH:no BUILD:distcheck CTIMETESTS:no WITH_VALGRIND:no]]) (push) Has been cancelled
CI / x86_64: Linux (Debian stable) (clang-snapshot, map[env_vars:map[CFLAGS:-O1 ECDH:yes ELLSWIFT:yes EXTRAKEYS:yes MUSIG:yes RECOVERY:yes SCHNORRSIG:yes]]) (push) Has been cancelled
CI / x86_64: Linux (Debian stable) (clang-snapshot, map[env_vars:map[ELLSWIFT:yes EXTRAKEYS:yes MUSIG:yes RECOVERY:yes SCHNORRSIG:yes WIDEMUL:int128]]) (push) Has been cancelled
CI / x86_64: Linux (Debian stable) (gcc, map[env_vars:map[CFLAGS:-O1 ECDH:yes ELLSWIFT:yes EXTRAKEYS:yes MUSIG:yes RECOVERY:yes SCHNORRSIG:yes]]) (push) Has been cancelled
CI / x86_64: Linux (Debian stable) (gcc, map[env_vars:map[ELLSWIFT:yes EXTRAKEYS:yes MUSIG:yes RECOVERY:yes SCHNORRSIG:yes WIDEMUL:int128]]) (push) Has been cancelled
CI / x86_64: Linux (Debian stable) (gcc-snapshot, map[env_vars:map[BENCH:no BUILD:distcheck CTIMETESTS:no WITH_VALGRIND:no]]) (push) Has been cancelled
CI / x86_64: Linux (Debian stable) (gcc-snapshot, map[env_vars:map[CFLAGS:-O1 ECDH:yes ELLSWIFT:yes EXTRAKEYS:yes MUSIG:yes RECOVERY:yes SCHNORRSIG:yes]]) (push) Has been cancelled
CI / x86_64: Linux (Debian stable) (gcc-snapshot, map[env_vars:map[ELLSWIFT:yes EXTRAKEYS:yes MUSIG:yes RECOVERY:yes SCHNORRSIG:yes WIDEMUL:int128]]) (push) Has been cancelled
CI / i686: Linux (Debian stable) (clang --target=i686-pc-linux-gnu -isystem /usr/i686-linux-gnu/include, map[env_vars:map[]]) (push) Has been cancelled
CI / MSan (clang, map[env_vars:map[CFLAGS:-fsanitize=memory -fsanitize-recover=memory -fsanitize-memory-param-retval -g CTIMETESTS:no]]) (push) Has been cancelled
CI / MSan (clang, map[env_vars:map[CFLAGS:-fsanitize=memory -fsanitize-recover=memory -g -O3 CTIMETESTS:yes ECMULTGENKB:2 ECMULTWINDOW:2]]) (push) Has been cancelled
CI / MSan (clang-snapshot, map[env_vars:map[CFLAGS:-fsanitize=memory -fsanitize-recover=memory -fsanitize-memory-param-retval -g CTIMETESTS:no]]) (push) Has been cancelled
CI / MSan (clang-snapshot, map[env_vars:map[CFLAGS:-fsanitize=memory -fsanitize-recover=memory -g -O3 CTIMETESTS:yes ECMULTGENKB:2 ECMULTWINDOW:2]]) (push) Has been cancelled
This change improves separation from CMake build directories, which
typically use the "build" prefix.
Additionally, corresponding `.gitignore` entries have been refactored.
This should hopefully be useful as orientation for users implementing
the key exchange part of BIP324. Conceptually the example is not very
different to the ECDH one, so a lot of code/comments are just copied
(e.g. context creation, secret key generation, shared secret comparison,
console output, cleanup with secret key clearing).
0f088ec112 Rename CTIMETEST -> CTIMETESTS (Pieter Wuille)
74b026f05d Add runtime checking for DECLASSIFY flag (Pieter Wuille)
5e2e6fcfc0 Run ctime test in Linux MSan CI job (Pieter Wuille)
18974061a3 Make ctime tests building configurable (Pieter Wuille)
5048be17e9 Rename valgrind_ctime_test -> ctime_tests (Pieter Wuille)
6eed6c18de Update error messages to suggest msan as well (Pieter Wuille)
8e11f89a68 Add support for msan integration to checkmem.h (Pieter Wuille)
8dc64079eb Add compile-time error to valgrind_ctime_test (Pieter Wuille)
0db05a770e Abstract interactions with valgrind behind new checkmem.h (Pieter Wuille)
4f1a54e41d Move valgrind CPPFLAGS into SECP_CONFIG_DEFINES (Pieter Wuille)
Pull request description:
This introduces an abstraction layer `src/checkmem.h`, which defines macros for interacting with memory checking tools. Depending on the environment, they're mapped to MemorySanitizer builtins, Valgrind integration macros, or nothing at all.
This means that msan builds immediately benefit from existing undefined memory checks in the tests. It also means those builds result in a `ctime_tests` (new name for `valgrind_ctime_test`) binary that can usefully test constant-timeness (not inside Valgrind, and with the downside that it's not running against a production library build, but it's faster and available on more platforms).
Such an msan-ctime test is added to the Linux x86_64 msan CI job, as an example. More CI cases could be added (e.g. for MacOs or ARM Linux) later.
ACKs for top commit:
real-or-random:
ACK 0f088ec112
hebasto:
ACK 0f088ec112, I have reviewed the code and it looks OK. Able to build `ctime_tests` using MSan.
Tree-SHA512: f4ffcc0c2ea794894662d9797b3a349770a4b361996f967f33d7d14b332171de5d525f50bcebaeaf7d0624957083380962079c75e490d1b7d71f8f9eb6211590
This uses AM_PROG_AR to discover ar, which is the recommended way to do
so. Among other advantages, it honors the AR environment variable (as
set from the outside). The macro has been around since automake 1.11.2
(Dec 2011).
This commit also removes code that discovers ranlib and strip. ranlib
has been obsolete for decades (ar does its task now automatically), and
anyway LT_INIT takes care of discovering it. The code we used to set
STRIP was last mentioned in the automake 1.5 manual. Since automake 1.6
(Mar 2002), strip is discovered automatically when necessary (look for
the *private* macro AM_PROG_INSTALL_STRIP in the automake manual).
- Improve Makefile.am for both prebuilt tables files
- On the way, tidy EXTRA_DIST: Move the header files to noinst_HEADERS,
where they conceptually belong, and add missing SECURITY.md to EXTRA_DIST
This header contains a static array that replaces the ecmult_context pre_g and pre_g_128 tables.
The gen_ecmult_static_pre_g program generates this header file.