2025-05-30 11:59:31 +01:00
cmake_minimum_required ( VERSION 3.22 )
2023-03-08 13:33:16 +00:00
2024-06-26 23:37:36 +01:00
#=============================
# Project / Package metadata
#=============================
2023-03-26 13:00:14 +01:00
project ( libsecp256k1
# The package (a.k.a. release) version is based on semantic versioning 2.0.0 of
# the API. All changes in experimental modules are treated as
# backwards-compatible and therefore at most increase the minor version.
2026-01-26 19:18:39 +00:00
V E R S I O N 0 . 7 . 2
2023-03-26 13:00:14 +01:00
D E S C R I P T I O N " O p t i m i z e d C l i b r a r y f o r E C D S A s i g n a t u r e s a n d s e c r e t / p u b l i c k e y o p e r a t i o n s o n c u r v e s e c p 2 5 6 k 1 . "
H O M E P A G E _ U R L " h t t p s : / / g i t h u b . c o m / b i t c o i n - c o r e / s e c p 2 5 6 k 1 "
L A N G U A G E S C
)
2024-06-26 23:37:36 +01:00
enable_testing ( )
2025-06-19 10:02:23 +02:00
include ( CTestUseLaunchers ) # Allow users to set CTEST_USE_LAUNCHERS in custom `ctest -S` scripts.
2024-06-26 23:37:36 +01:00
list ( APPEND CMAKE_MODULE_PATH ${ PROJECT_SOURCE_DIR } /cmake )
2023-03-08 13:33:16 +00:00
# The library version is based on libtool versioning of the ABI. The set of
# rules for updating the version can be found here:
# https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
# All changes in experimental modules are treated as if they don't affect the
# interface and therefore only increase the revision.
2025-07-21 16:21:31 +02:00
set ( ${ PROJECT_NAME } _LIB_VERSION_CURRENT 6 )
2026-01-26 19:18:39 +00:00
set ( ${ PROJECT_NAME } _LIB_VERSION_REVISION 2 )
2024-11-04 13:45:03 +00:00
set ( ${ PROJECT_NAME } _LIB_VERSION_AGE 0 )
2023-03-08 13:33:16 +00:00
2024-06-26 23:37:36 +01:00
#=============================
# Language setup
#=============================
2023-03-08 13:33:16 +00:00
set ( CMAKE_C_STANDARD 90 )
set ( CMAKE_C_EXTENSIONS OFF )
2024-06-26 23:37:36 +01:00
#=============================
# Configurable options
#=============================
2025-07-27 15:35:58 +01:00
if ( libsecp256k1_IS_TOP_LEVEL )
option ( BUILD_SHARED_LIBS "Build shared libraries." ON )
2023-03-08 13:33:16 +00:00
endif ( )
2023-04-21 12:03:04 +01:00
option ( SECP256K1_INSTALL "Enable installation." ${ PROJECT_IS_TOP_LEVEL } )
2023-04-09 12:51:53 +05:00
2025-07-02 15:18:06 +00:00
option ( SECP256K1_ENABLE_API_VISIBILITY_ATTRIBUTES "Enable visibility attributes in the API." ON )
2024-01-16 14:36:41 +01:00
## Modules
2023-03-08 13:33:16 +00:00
2024-01-16 14:36:41 +01:00
# We declare all options before processing them, to make sure we can express
2024-11-05 16:44:29 +01:00
# dependencies while processing.
2024-01-16 14:36:41 +01:00
option ( SECP256K1_ENABLE_MODULE_ECDH "Enable ECDH module." ON )
2023-03-08 13:33:16 +00:00
option ( SECP256K1_ENABLE_MODULE_RECOVERY "Enable ECDSA pubkey recovery module." OFF )
option ( SECP256K1_ENABLE_MODULE_EXTRAKEYS "Enable extrakeys module." ON )
option ( SECP256K1_ENABLE_MODULE_SCHNORRSIG "Enable schnorrsig module." ON )
2024-01-06 20:17:27 +00:00
option ( SECP256K1_ENABLE_MODULE_MUSIG "Enable musig module." ON )
2026-08-31 00:05:16 +02:00
option ( SECP256K1_ENABLE_MODULE_FROST "Enable FROST module (experimental)." OFF )
chilldkg: Phase 0 - module scaffolding and build wiring
Add an empty, experimental `chilldkg` module as the foundation for a
ChillDKG implementation (distributed key generation for FROST) per the
bip-frost-dkg BIP draft (v0.3.0-dev):
https://github.com/BlockstreamResearch/bip-frost-dkg
The module lives in src/modules/chilldkg/ (separate from the frost
module, per the implementation plan in .idea/docs/
chilldkg-implementation-plan.md: FROST signing (BIP 445) and ChillDKG
are separate BIPs with separate reference repos, test vectors and
review cycles; the dependency between them is one-way bytes).
New files:
- include/secp256k1_chilldkg.h: public header skeleton with the same
"EXTREMELY DANGEROUS / work in progress" warning style as
secp256k1_frost.h, plus a note that the BIP is a draft and tagged
hashes/wire formats may change. No API yet (Phase 3+).
- src/modules/chilldkg/main_impl.h: implementation skeleton including
the public header.
- src/modules/chilldkg/tests_impl.h: trivial scaffolding unit test
(chilldkg_scaffolding_test) registered via the tests_chilldkg[]
CASE1 array used by this repo's unit-test framework.
- src/modules/chilldkg/Makefile.am.include: autotools file list,
mirroring the frost module's.
- src/modules/chilldkg/chilldkg.md: module doc stub (purpose, draft
status, dependency on the schnorrsig and ecdh modules).
Build wiring (mirrors the frost module exactly):
- configure.ac: --enable-module-chilldkg (default no, experimental
gate), dependency errors when schnorrsig or ecdh are explicitly
disabled, AM_CONDITIONAL(ENABLE_MODULE_CHILLDKG), summary line.
- Makefile.am: include src/modules/chilldkg/Makefile.am.include under
ENABLE_MODULE_CHILLDKG.
- src/secp256k1.c: guarded include of modules/chilldkg/main_impl.h
after the frost module.
- src/tests.c: guarded include of tests_impl.h and
MAKE_TEST_MODULE(chilldkg) registration.
- CMakeLists.txt: SECP256K1_ENABLE_MODULE_CHILLDKG option (OFF) +
summary line.
- src/CMakeLists.txt: dependency checks on
SECP256K1_ENABLE_MODULE_SCHNORRSIG and SECP256K1_ENABLE_MODULE_ECDH,
ENABLE_MODULE_CHILLDKG=1 compile definition, public header export.
Verified:
- ./autogen.sh && ./configure --enable-experimental
--enable-module-chilldkg --enable-module-schnorrsig
--enable-module-ecdh && make check: PASS 3/3 (tests, noverify_tests,
exhaustive_tests).
- configure fails with a clear error when schnorrsig or ecdh are
disabled, or when experimental is not enabled.
- CMake build with SECP256K1_ENABLE_MODULE_CHILLDKG=ON: ctest 345/345
passed; dependency errors fire correctly when schnorrsig/ecdh OFF.
2026-08-31 01:37:22 +02:00
option ( SECP256K1_ENABLE_MODULE_CHILLDKG "Enable ChillDKG module (experimental)." OFF )
iceberg: add the Iceberg threshold-MuSig module
Port the experimental Iceberg module from the benchmark-iceberg tree
(github.com/furszy/benchmark-iceberg, sources/secp256k1-kmp/native/
secp256k1) into this repo.
Iceberg is a threshold scheme that lets a group of parties stand in
for a single MuSig2 (BIP 327) participant: the group produces one
ordinary MuSig2 public nonce and one ordinary MuSig2 partial
signature, so cosigners cannot tell a group is involved and need no
changes. Nonces are derived from a caller-chosen per-session label
(sid32) rather than stored, so no signer holds a secret nonce between
rounds; labels are public but must never be reused. A quorum of 2t-1
members (of whom up to t-1 may be corrupt) is needed in each round,
so the threshold is at most half the group rounded up; combined with
the scheme's other constraints the smallest usable group is 2-of-4.
See doc/iceberg.md and the module header for the full usage notes.
Module layout (src/modules/iceberg/, layered bottom-up, each layer
may only use the ones above it -- that ordering is also the
constant-time story):
- scalar_poly.{h,_impl.h}: secret-carrying polynomial arithmetic,
keeping secrets away from inversions (documented in the header).
- rss.{h,_impl.h}: replicated secret sharing evaluation.
- vpss.{h,_impl.h}: verifiable public shares; variable-time by
design, sees only participant indices and published points.
- keygen_impl.h: distributed key generation producing one share per
member.
- session_impl.h: nonce_gen/nonce_agg and partial_sign/
partial_sig_agg producing plain MuSig2 objects.
- tests_impl.h: 28 tests including the shipped vectors.h vector
suite and dealer known-answer tests.
- bench_impl.h: benchmark definitions (wired in a follow-up commit).
Public headers: include/secp256k1_iceberg.h (installed) and
include/secp256k1_iceberg_dealer.h (in-tree only: a trusted dealer is
not part of the shipped API, but tests, benchmarks and the example
need to deal shares).
Content adaptations relative to the source tree (the only changes to
the ported code): three secp256k1_musig_nonce_process call sites in
tests_impl.h gained a NULL adaptor argument, because this repo's
musig is the zkp variant whose public nonce_process takes an optional
adaptor point. All musig internals the module uses (ge_parse_ext,
ge_serialize_ext, keyaggcoef, aggnonce_load, pubnonce_save,
partial_sig_save, nonce_process_internal) are identical in both
trees, as are all core headers the module touches; nothing else
needed adaptation.
Build wiring mirrors the chilldkg module:
- configure.ac: --enable-module-iceberg (default no, experimental
gate), hard dependency on the musig module with a configure error
if musig is explicitly disabled (musig itself pulls in schnorrsig),
AM_CONDITIONAL(ENABLE_MODULE_ICEBERG), summary line.
- Makefile.am: include src/modules/iceberg/Makefile.am.include under
the conditional.
- src/secp256k1.c: guarded include of modules/iceberg/main_impl.h
after the chilldkg block (musig is included earlier, so its
internals are in scope).
- src/tests.c: module test registration via MAKE_TEST_MODULE(iceberg).
- CMakeLists.txt / src/CMakeLists.txt: SECP256K1_ENABLE_MODULE_ICEBERG
option (OFF) with a dependency check on SECP256K1_ENABLE_MODULE_MUSIG
(placed before the musig block so the force-enable takes effect),
ENABLE_MODULE_ICEBERG=1 compile definition, public header export,
summary line.
Verified: ./configure --enable-experimental --enable-module-iceberg
&& make check passes; ./tests --target=iceberg runs the full module
suite (28/28); CMake build + ctest pass; the musig dependency error
fires correctly in both build systems.
2026-08-31 12:24:48 +02:00
option ( SECP256K1_ENABLE_MODULE_ICEBERG "Enable Iceberg threshold-MuSig module (experimental)." OFF )
prefractal: add the nested FROST+MuSig2 module (API, implementation, wiring)
Adds `prefractal`, an experimental module that lets a FROST t-of-n group
occupy ONE participant slot of an ordinary MuSig2 (BIP 327) session. Each
member computes
s_i = k1_i + b_frost*b_musig*k2_i + e*a*lambda_i*g*gacc*d_i
and the group publishes one ordinary MuSig2 public nonce and one ordinary
MuSig2 partial signature, so cosigners need no support for it and cannot tell
a group is involved.
Four public functions, all sessionless (every call takes its session
parameters explicitly, so there are no new opaque types, magics or *_SIZE
constants to keep synchronised):
secp256k1_prefractal_nonce_agg group wire nonce + unscaled aggnonce
secp256k1_prefractal_sign one member's partial signature
secp256k1_prefractal_partial_sig_verify identifiable abort
secp256k1_prefractal_partial_sig_agg sum -> musig partial signature
Three deliberate deviations from BIP 445, all documented in the public header:
1. b_frost does not commit to the message. The target protocols publish the
group's wire nonce before the message exists, so a message-committing
coefficient could not be computed in round one and rebuilt later. The outer
b_musig does commit to the message and multiplies this one, so the product
still binds it. Same trade the iceberg module makes, for the same reason.
The preimage is BIP 445's with the message dropped and the group key
carried in full rather than x-only, since it is used as a full point
downstream.
2. There is NO g_frost factor. Stock FROST normalises its threshold key to
even Y (g_times_gacc_parity = gacc_parity ^ pk_odd, frost/session_impl.h
:664) because it produces a BIP 340 x-only signature. Here the threshold
key is an inner participant of the outer key aggregation and is used as a
full point, so all key-side parity normalisation happens once, at the
aggregate level, off the OUTER keyagg cache. Note this is NOT implied by
the tweak cache being the identity: with an identity cache g_frost is still
-1 for every odd-Y group key, i.e. about half of them. Importing frost's
key-side parity here would yield a signer that works for even-Y groups and
fails for odd-Y ones.
3. The FROST tweak cache must be the identity (tacc == 0, gacc_parity == 0).
Checked in sign and partial_sig_verify, not only in partial_sig_agg, so the
key a member signs under is tied to the cache that was validated; sign and
verify additionally require thresh_pk to equal the cache's own key so the
two arguments cannot disagree.
The verification equation lives in one helper used both by sign's BIP 445
self-check and by partial_sig_verify, so the two cannot drift apart.
Build wiring. Three files order their module blocks differently and the
constraints point in opposite directions:
- src/secp256k1.c: the include goes AFTER frost and musig, because the
module calls their static internals.
- src/CMakeLists.txt: the block goes BEFORE both, because its set() calls
are only observed by blocks that run later.
- configure.ac: the block likewise goes before the musig block, NOT at
iceberg's position further down. configure.ac orders musig and frost ahead
of iceberg, and iceberg's late enable_module_musig=yes is harmless only
because musig defaults to yes. frost defaults to no, so a late
force-enable would leave -DENABLE_MODULE_FROST=1 unemitted while
AM_CONDITIONAL still observed the mutation - a library whose secp256k1.c
never included frost, built alongside frost's own sources.
frost is also the first default-OFF module anything depends on, which breaks
the dependency-guard idiom used everywhere else in both build systems: the
existing "DEFINED X AND NOT X" (CMake) and "x$X = xno" (autotools) tests read
as "the user disabled it explicitly" only for default-ON modules, and are true
by default for a default-OFF one. Since neither build system can distinguish
an explicit disable from the default once both are in the cache, enabling
prefractal simply implies frost; the guard is kept for musig, where it still
means what it says. The CMake block additionally lifts both dependencies into
the parent scope so the top-level configuration summary reports what was
actually built rather than printing "frost OFF" while compiling frost in.
Verified on both build systems:
cmake -B build -DSECP256K1_ENABLE_MODULE_PREFRACTAL=ON -DSECP256K1_BUILD_TESTS=ON
-> musig/frost/prefractal all ON, tests pass, 4 prefractal symbols exported
cmake -B build -DSECP256K1_BUILD_TESTS=ON
-> prefractal OFF, default build unchanged, tests pass
./configure --enable-experimental --enable-module-prefractal && make && make check
-> frost=yes forced on, -DENABLE_MODULE_FROST=1 emitted, 3/3 pass
./configure --enable-module-prefractal
-> correctly refused: "Prefractal module is experimental"
tests_impl.h is a placeholder here so the module links; the real suite lands
next.
2026-09-04 00:44:43 +02:00
option ( SECP256K1_ENABLE_MODULE_PREFRACTAL "Enable Prefractal nested FROST+MuSig2 module (experimental)." OFF )
build: wire the frost_enrollment module into both build systems
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>
2026-09-04 03:53:03 +02:00
option ( SECP256K1_ENABLE_MODULE_FROST_ENROLLMENT "Enable FROST enrollment module (experimental)." OFF )
2024-01-16 14:36:41 +01:00
option ( SECP256K1_ENABLE_MODULE_ELLSWIFT "Enable ElligatorSwift module." ON )
2024-01-25 15:57:16 +01:00
option ( SECP256K1_ENABLE_MODULE_GENERATOR "Enable NUMS generator module." ON )
option ( SECP256K1_ENABLE_MODULE_RANGEPROOF "Enable Range proof module." ON )
option ( SECP256K1_ENABLE_MODULE_SURJECTIONPROOF "Enable Surjection proof module." ON )
option ( SECP256K1_ENABLE_MODULE_WHITELIST "Enable key whitelist module." ON )
option ( SECP256K1_ENABLE_MODULE_ECDSA_ADAPTOR "Enable ecdsa adaptor signatures module." ON )
option ( SECP256K1_ENABLE_MODULE_ECDSA_S2C "Enable ECDSA sign-to-contract module." ON )
option ( SECP256K1_ENABLE_MODULE_BPPP "Enable Bulletproofs++ module." ON )
2026-03-06 12:39:49 +02:00
option ( SECP256K1_ENABLE_MODULE_SCHNORRSIG_HALFAGG "Enable schnorrsig half-aggregation module." ON )
2024-01-25 15:57:16 +01:00
2023-03-08 13:33:16 +00:00
option ( SECP256K1_USE_EXTERNAL_DEFAULT_CALLBACKS "Enable external default callback functions." OFF )
if ( SECP256K1_USE_EXTERNAL_DEFAULT_CALLBACKS )
2023-03-26 13:00:32 +01:00
add_compile_definitions ( USE_EXTERNAL_DEFAULT_CALLBACKS=1 )
2023-03-08 13:33:16 +00:00
endif ( )
2024-05-27 13:26:03 +01:00
set ( SECP256K1_ECMULT_WINDOW_SIZE 15 CACHE STRING "Window size for ecmult precomputation for verification, specified as integer in range [2..24]. The default value is a reasonable setting for desktop machines (currently 15). [default=15]" )
set_property ( CACHE SECP256K1_ECMULT_WINDOW_SIZE PROPERTY STRINGS 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 )
2023-03-08 13:33:16 +00:00
include ( CheckStringOptionValue )
check_string_option_value ( SECP256K1_ECMULT_WINDOW_SIZE )
2023-03-26 13:00:32 +01:00
add_compile_definitions ( ECMULT_WINDOW_SIZE= ${ SECP256K1_ECMULT_WINDOW_SIZE } )
2023-03-08 13:33:16 +00:00
2024-07-01 20:41:36 +01:00
set ( SECP256K1_ECMULT_GEN_KB 86 CACHE STRING "The size of the precomputed table for signing in multiples of 1024 bytes (on typical platforms). Larger values result in possibly better signing or key generation performance at the cost of a larger table. Valid choices are 2, 22, 86. The default value is a reasonable setting for desktop machines (currently 86). [default=86]" )
2024-05-27 13:32:23 +01:00
set_property ( CACHE SECP256K1_ECMULT_GEN_KB PROPERTY STRINGS 2 22 86 )
2021-12-29 15:09:52 -05:00
check_string_option_value ( SECP256K1_ECMULT_GEN_KB )
if ( SECP256K1_ECMULT_GEN_KB EQUAL 2 )
add_compile_definitions ( COMB_BLOCKS=2 )
add_compile_definitions ( COMB_TEETH=5 )
elseif ( SECP256K1_ECMULT_GEN_KB EQUAL 22 )
add_compile_definitions ( COMB_BLOCKS=11 )
add_compile_definitions ( COMB_TEETH=6 )
elseif ( SECP256K1_ECMULT_GEN_KB EQUAL 86 )
add_compile_definitions ( COMB_BLOCKS=43 )
add_compile_definitions ( COMB_TEETH=6 )
2023-03-08 13:33:16 +00:00
endif ( )
set ( SECP256K1_TEST_OVERRIDE_WIDE_MULTIPLY "OFF" CACHE STRING "Test-only override of the (autodetected by the C code) \" widemul\ " setting. Legal values are: \" OFF\ ", \" int128_struct\ ", \" int128\ " or \" int64\ ". [default=OFF]" )
set_property ( CACHE SECP256K1_TEST_OVERRIDE_WIDE_MULTIPLY PROPERTY STRINGS "OFF" "int128_struct" "int128" "int64" )
check_string_option_value ( SECP256K1_TEST_OVERRIDE_WIDE_MULTIPLY )
if ( SECP256K1_TEST_OVERRIDE_WIDE_MULTIPLY )
string ( TOUPPER "${SECP256K1_TEST_OVERRIDE_WIDE_MULTIPLY}" widemul_upper_value )
2023-03-26 13:00:32 +01:00
add_compile_definitions ( USE_FORCE_WIDEMUL_ ${ widemul_upper_value } =1 )
2023-03-08 13:33:16 +00:00
endif ( )
mark_as_advanced ( FORCE SECP256K1_TEST_OVERRIDE_WIDE_MULTIPLY )
2023-11-23 22:51:48 +01:00
set ( SECP256K1_ASM "AUTO" CACHE STRING "Assembly to use: \" AUTO\ ", \" OFF\ ", \" x86_64\ " or \" arm32\ " (experimental). [default=AUTO]" )
2023-05-11 10:03:23 +01:00
set_property ( CACHE SECP256K1_ASM PROPERTY STRINGS "AUTO" "OFF" "x86_64" "arm32" )
2023-03-08 13:33:16 +00:00
check_string_option_value ( SECP256K1_ASM )
2023-05-11 10:03:23 +01:00
if ( SECP256K1_ASM STREQUAL "arm32" )
2023-03-08 13:33:16 +00:00
enable_language ( ASM )
2023-05-12 10:38:50 +01:00
include ( CheckArm32Assembly )
check_arm32_assembly ( )
if ( HAVE_ARM32_ASM )
add_compile_definitions ( USE_EXTERNAL_ASM=1 )
else ( )
2023-11-23 22:51:48 +01:00
message ( FATAL_ERROR "ARM32 assembly requested but not available." )
2023-05-12 10:38:50 +01:00
endif ( )
2023-03-08 13:33:16 +00:00
elseif ( SECP256K1_ASM )
2023-05-12 10:50:18 +01:00
include ( CheckX86_64Assembly )
check_x86_64_assembly ( )
if ( HAVE_X86_64_ASM )
2023-03-08 13:33:16 +00:00
set ( SECP256K1_ASM "x86_64" )
2023-03-26 13:00:32 +01:00
add_compile_definitions ( USE_ASM_X86_64=1 )
2023-03-08 13:33:16 +00:00
elseif ( SECP256K1_ASM STREQUAL "AUTO" )
set ( SECP256K1_ASM "OFF" )
else ( )
2023-11-23 22:51:48 +01:00
message ( FATAL_ERROR "x86_64 assembly requested but not available." )
2023-03-08 13:33:16 +00:00
endif ( )
endif ( )
option ( SECP256K1_EXPERIMENTAL "Allow experimental configuration options." OFF )
if ( NOT SECP256K1_EXPERIMENTAL )
2023-05-11 10:03:23 +01:00
if ( SECP256K1_ASM STREQUAL "arm32" )
2023-11-23 22:51:48 +01:00
message ( FATAL_ERROR "ARM32 assembly is experimental. Use -DSECP256K1_EXPERIMENTAL=ON to allow." )
2023-03-08 13:33:16 +00:00
endif ( )
build: gate the experimental modules on SECP256K1_EXPERIMENTAL in CMake
configure.ac refuses --enable-module-frost, --enable-module-chilldkg and
--enable-module-iceberg outright unless --enable-experimental is also
given. The CMake build had no equivalent, so
-DSECP256K1_ENABLE_MODULE_ICEBERG=ON produced a library with no warning
banner and no acknowledgement that anything experimental was requested.
Of the three, iceberg is the one this matters most for: doc/iceberg.md
tells the reader not to put money behind it, and the CMake path let a
build acquire it without the reader ever passing a flag that says so.
Add the three checks to the existing NOT SECP256K1_EXPERIMENTAL block,
next to the ARM32 assembly check and worded the same way. The options are
declared well above it, so the values are set by the time the block runs.
Verified both directions: the configure fails with "Iceberg module is
experimental. Use -DSECP256K1_EXPERIMENTAL=ON to allow." without the
flag, and succeeds with it.
This deliberately leaves the other zkp experimental modules alone. They
are ON by default in CMake and gating them would change every existing
CMake build; the three added here are OFF by default, so nobody is
relying on the ungated path.
While here, correct doc/iceberg.md, which claimed the module "builds by
default here". SECP_SET_DEFAULT(enable_module_iceberg, no, yes) makes it
off by default and on in dev mode, which is now what the paragraph says,
along with the experimental requirement it did not previously mention.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-01 23:38:05 +02:00
if ( SECP256K1_ENABLE_MODULE_FROST )
message ( FATAL_ERROR "FROST module is experimental. Use -DSECP256K1_EXPERIMENTAL=ON to allow." )
endif ( )
if ( SECP256K1_ENABLE_MODULE_CHILLDKG )
message ( FATAL_ERROR "ChillDKG module is experimental. Use -DSECP256K1_EXPERIMENTAL=ON to allow." )
endif ( )
if ( SECP256K1_ENABLE_MODULE_ICEBERG )
message ( FATAL_ERROR "Iceberg module is experimental. Use -DSECP256K1_EXPERIMENTAL=ON to allow." )
endif ( )
2026-09-04 02:59:16 +02:00
if ( SECP256K1_ENABLE_MODULE_PREFRACTAL )
message ( FATAL_ERROR "Prefractal module is experimental. Use -DSECP256K1_EXPERIMENTAL=ON to allow." )
endif ( )
build: wire the frost_enrollment module into both build systems
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>
2026-09-04 03:53:03 +02:00
if ( SECP256K1_ENABLE_MODULE_FROST_ENROLLMENT )
message ( FATAL_ERROR "FROST enrollment module is experimental. Use -DSECP256K1_EXPERIMENTAL=ON to allow." )
endif ( )
2023-03-08 13:33:16 +00:00
endif ( )
set ( SECP256K1_VALGRIND "AUTO" CACHE STRING "Build with extra checks for running inside Valgrind. [default=AUTO]" )
set_property ( CACHE SECP256K1_VALGRIND PROPERTY STRINGS "AUTO" "OFF" "ON" )
check_string_option_value ( SECP256K1_VALGRIND )
if ( SECP256K1_VALGRIND )
find_package ( Valgrind MODULE )
if ( Valgrind_FOUND )
set ( SECP256K1_VALGRIND ON )
include_directories ( ${ Valgrind_INCLUDE_DIR } )
2023-03-26 13:00:32 +01:00
add_compile_definitions ( VALGRIND )
2023-03-08 13:33:16 +00:00
elseif ( SECP256K1_VALGRIND STREQUAL "AUTO" )
set ( SECP256K1_VALGRIND OFF )
else ( )
message ( FATAL_ERROR "Valgrind support requested but valgrind/memcheck.h header not available." )
endif ( )
endif ( )
option ( SECP256K1_BUILD_BENCHMARK "Build benchmarks." ON )
option ( SECP256K1_BUILD_TESTS "Build tests." ON )
option ( SECP256K1_BUILD_EXHAUSTIVE_TESTS "Build exhaustive tests." ON )
option ( SECP256K1_BUILD_CTIME_TESTS "Build constant-time tests." ${ SECP256K1_VALGRIND } )
option ( SECP256K1_BUILD_EXAMPLES "Build examples." OFF )
# Redefine configuration flags.
# We leave assertions on, because they are only used in the examples, and we want them always on there.
if ( MSVC )
string ( REGEX REPLACE "/DNDEBUG[ \t\r\n]*" "" CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO}" )
string ( REGEX REPLACE "/DNDEBUG[ \t\r\n]*" "" CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}" )
string ( REGEX REPLACE "/DNDEBUG[ \t\r\n]*" "" CMAKE_C_FLAGS_MINSIZEREL "${CMAKE_C_FLAGS_MINSIZEREL}" )
2026-05-29 13:03:42 +02:00
# Match GCC/Clang's size-optimization macro for the inline guard
add_compile_definitions ( $< $<CONFIG:MinSizeRel > :__OPTIMIZE_SIZE__=1> )
2023-03-08 13:33:16 +00:00
else ( )
string ( REGEX REPLACE "-DNDEBUG[ \t\r\n]*" "" CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO}" )
string ( REGEX REPLACE "-DNDEBUG[ \t\r\n]*" "" CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}" )
string ( REGEX REPLACE "-DNDEBUG[ \t\r\n]*" "" CMAKE_C_FLAGS_MINSIZEREL "${CMAKE_C_FLAGS_MINSIZEREL}" )
# Prefer -O2 optimization level. (-O3 is CMake's default for Release for many compilers.)
2024-06-27 11:46:48 -03:00
string ( REGEX REPLACE "-O3( |$)" "-O2\\1" CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}" )
2023-03-08 13:33:16 +00:00
endif ( )
# Define custom "Coverage" build type.
2023-03-14 20:03:42 +00:00
set ( CMAKE_C_FLAGS_COVERAGE "${CMAKE_C_FLAGS_RELWITHDEBINFO} -O0 -DCOVERAGE=1 --coverage" CACHE STRING
2023-03-08 13:33:16 +00:00
" F l a g s u s e d b y t h e C c o m p i l e r d u r i n g \ " C o v e r a g e \ " b u i l d s . "
F O R C E
)
set ( CMAKE_EXE_LINKER_FLAGS_COVERAGE "${CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO} --coverage" CACHE STRING
" F l a g s u s e d f o r l i n k i n g b i n a r i e s d u r i n g \ " C o v e r a g e \ " b u i l d s . "
F O R C E
)
set ( CMAKE_SHARED_LINKER_FLAGS_COVERAGE "${CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO} --coverage" CACHE STRING
" F l a g s u s e d b y t h e s h a r e d l i b r a r i e s l i n k e r d u r i n g \ " C o v e r a g e \ " b u i l d s . "
F O R C E
)
mark_as_advanced (
C M A K E _ C _ F L A G S _ C O V E R A G E
C M A K E _ E X E _ L I N K E R _ F L A G S _ C O V E R A G E
C M A K E _ S H A R E D _ L I N K E R _ F L A G S _ C O V E R A G E
)
2024-06-18 15:44:15 +01:00
if ( PROJECT_IS_TOP_LEVEL )
get_property ( is_multi_config GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG )
set ( default_build_type "RelWithDebInfo" )
if ( is_multi_config )
set ( CMAKE_CONFIGURATION_TYPES "${default_build_type}" "Release" "Debug" "MinSizeRel" "Coverage" CACHE STRING
" S u p p o r t e d c o n f i g u r a t i o n t y p e s . "
2023-03-26 13:15:34 +01:00
F O R C E
)
2024-06-18 15:44:15 +01:00
else ( )
set_property ( CACHE CMAKE_BUILD_TYPE PROPERTY
S T R I N G S " $ { d e f a u l t _ b u i l d _ t y p e } " " R e l e a s e " " D e b u g " " M i n S i z e R e l " " C o v e r a g e "
)
if ( NOT CMAKE_BUILD_TYPE )
message ( STATUS "Setting build type to \" ${ default_build_type } \" as none was specified " )
set ( CMAKE_BUILD_TYPE "${default_build_type}" CACHE STRING
" C h o o s e t h e t y p e o f b u i l d . "
F O R C E
)
endif ( )
2023-03-26 13:15:34 +01:00
endif ( )
2023-03-08 13:33:16 +00:00
endif ( )
2023-03-22 10:35:49 +00:00
include ( TryAppendCFlags )
2023-03-08 13:33:16 +00:00
if ( MSVC )
2024-12-13 09:19:40 +00:00
# For both cl and clang-cl compilers.
2023-05-24 16:57:32 +01:00
try_append_c_flags ( /W3 ) # Production quality warning level.
2024-12-13 09:19:40 +00:00
# Eliminate deprecation warnings for the older, less secure functions.
add_compile_definitions ( _CRT_SECURE_NO_WARNINGS )
else ( )
try_append_c_flags ( -Wall ) # GCC >= 2.95 and probably many other compilers.
endif ( )
if ( CMAKE_C_COMPILER_ID STREQUAL "MSVC" )
# Keep the following commands ordered lexicographically.
2023-03-22 10:37:16 +00:00
try_append_c_flags ( /wd4146 ) # Disable warning C4146 "unary minus operator applied to unsigned type, result still unsigned".
2023-05-24 16:57:32 +01:00
try_append_c_flags ( /wd4244 ) # Disable warning C4244 "'conversion' conversion from 'type1' to 'type2', possible loss of data".
try_append_c_flags ( /wd4267 ) # Disable warning C4267 "'var' : conversion from 'size_t' to 'type', possible loss of data".
2023-03-08 13:33:16 +00:00
else ( )
2023-03-22 10:37:16 +00:00
# Keep the following commands ordered lexicographically.
2023-03-22 10:35:49 +00:00
try_append_c_flags ( -pedantic )
2023-03-22 10:37:16 +00:00
try_append_c_flags ( -Wcast-align ) # GCC >= 2.95.
try_append_c_flags ( -Wcast-align=strict ) # GCC >= 8.0.
try_append_c_flags ( -Wconditional-uninitialized ) # Clang >= 3.0 only.
try_append_c_flags ( -Wextra ) # GCC >= 3.4, this is the newer name of -W, which we don't use because older GCCs will warn about unused functions.
2026-01-29 12:43:40 +00:00
try_append_c_flags ( -Wleading-whitespace=spaces ) # GCC >= 15.0
2023-03-22 10:35:49 +00:00
try_append_c_flags ( -Wnested-externs )
2023-03-22 10:37:16 +00:00
try_append_c_flags ( -Wno-long-long ) # GCC >= 3.0, -Wlong-long is implied by -pedantic.
try_append_c_flags ( -Wno-overlength-strings ) # GCC >= 4.2, -Woverlength-strings is implied by -pedantic.
try_append_c_flags ( -Wno-unused-function ) # GCC >= 3.0, -Wunused-function is implied by -Wall.
try_append_c_flags ( -Wreserved-identifier ) # Clang >= 13.0 only.
2023-03-22 10:35:49 +00:00
try_append_c_flags ( -Wshadow )
try_append_c_flags ( -Wstrict-prototypes )
2026-01-29 12:40:41 +00:00
try_append_c_flags ( -Wtrailing-whitespace=any ) # GCC >= 15.0
2023-03-22 10:35:49 +00:00
try_append_c_flags ( -Wundef )
2023-03-08 13:33:16 +00:00
endif ( )
2024-05-28 09:37:00 +01:00
set ( print_msan_notice )
if ( SECP256K1_BUILD_CTIME_TESTS )
include ( CheckMemorySanitizer )
check_memory_sanitizer ( msan_enabled )
if ( msan_enabled )
try_append_c_flags ( -fno-sanitize-memory-param-retval )
set ( print_msan_notice YES )
endif ( )
unset ( msan_enabled )
endif ( )
2024-06-21 14:45:07 +01:00
set ( SECP256K1_APPEND_CFLAGS "" CACHE STRING "Compiler flags that are appended to the command line after all other flags added by the build system. This variable is intended for debugging and special builds." )
if ( SECP256K1_APPEND_CFLAGS )
# Appending to this low-level rule variable is the only way to
# guarantee that the flags appear at the end of the command line.
string ( APPEND CMAKE_C_COMPILE_OBJECT " ${SECP256K1_APPEND_CFLAGS}" )
endif ( )
2023-04-26 11:10:03 +01:00
2024-09-02 21:41:03 +01:00
set ( SECP256K1_APPEND_LDFLAGS "" CACHE STRING "Linker flags that are appended to the command line after all other flags added by the build system. This variable is intended for debugging and special builds." )
if ( SECP256K1_APPEND_LDFLAGS )
# Appending to this low-level rule variable is the only way to
# guarantee that the flags appear at the end of the command line.
string ( APPEND CMAKE_C_CREATE_SHARED_LIBRARY " ${SECP256K1_APPEND_LDFLAGS}" )
string ( APPEND CMAKE_C_LINK_EXECUTABLE " ${SECP256K1_APPEND_LDFLAGS}" )
endif ( )
2024-06-26 00:29:50 +01:00
if ( NOT CMAKE_RUNTIME_OUTPUT_DIRECTORY )
set ( CMAKE_RUNTIME_OUTPUT_DIRECTORY ${ PROJECT_BINARY_DIR } /bin )
endif ( )
if ( NOT CMAKE_LIBRARY_OUTPUT_DIRECTORY )
set ( CMAKE_LIBRARY_OUTPUT_DIRECTORY ${ PROJECT_BINARY_DIR } /lib )
endif ( )
if ( NOT CMAKE_ARCHIVE_OUTPUT_DIRECTORY )
set ( CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${ PROJECT_BINARY_DIR } /lib )
endif ( )
2023-03-08 13:33:16 +00:00
add_subdirectory ( src )
if ( SECP256K1_BUILD_EXAMPLES )
add_subdirectory ( examples )
endif ( )
message ( "\n" )
message ( "secp256k1 configure summary" )
message ( "===========================" )
message ( "Build artifacts:" )
2023-03-14 21:15:35 +00:00
if ( BUILD_SHARED_LIBS )
set ( library_type "Shared" )
else ( )
set ( library_type "Static" )
endif ( )
message ( " library type ........................ ${library_type}" )
2023-03-08 13:33:16 +00:00
message ( "Optional modules:" )
message ( " ECDH ................................ ${SECP256K1_ENABLE_MODULE_ECDH}" )
message ( " ECDSA pubkey recovery ............... ${SECP256K1_ENABLE_MODULE_RECOVERY}" )
message ( " extrakeys ........................... ${SECP256K1_ENABLE_MODULE_EXTRAKEYS}" )
message ( " schnorrsig .......................... ${SECP256K1_ENABLE_MODULE_SCHNORRSIG}" )
2024-01-06 20:17:27 +00:00
message ( " musig ............................... ${SECP256K1_ENABLE_MODULE_MUSIG}" )
2026-08-31 00:05:16 +02:00
message ( " frost ............................... ${SECP256K1_ENABLE_MODULE_FROST}" )
chilldkg: Phase 0 - module scaffolding and build wiring
Add an empty, experimental `chilldkg` module as the foundation for a
ChillDKG implementation (distributed key generation for FROST) per the
bip-frost-dkg BIP draft (v0.3.0-dev):
https://github.com/BlockstreamResearch/bip-frost-dkg
The module lives in src/modules/chilldkg/ (separate from the frost
module, per the implementation plan in .idea/docs/
chilldkg-implementation-plan.md: FROST signing (BIP 445) and ChillDKG
are separate BIPs with separate reference repos, test vectors and
review cycles; the dependency between them is one-way bytes).
New files:
- include/secp256k1_chilldkg.h: public header skeleton with the same
"EXTREMELY DANGEROUS / work in progress" warning style as
secp256k1_frost.h, plus a note that the BIP is a draft and tagged
hashes/wire formats may change. No API yet (Phase 3+).
- src/modules/chilldkg/main_impl.h: implementation skeleton including
the public header.
- src/modules/chilldkg/tests_impl.h: trivial scaffolding unit test
(chilldkg_scaffolding_test) registered via the tests_chilldkg[]
CASE1 array used by this repo's unit-test framework.
- src/modules/chilldkg/Makefile.am.include: autotools file list,
mirroring the frost module's.
- src/modules/chilldkg/chilldkg.md: module doc stub (purpose, draft
status, dependency on the schnorrsig and ecdh modules).
Build wiring (mirrors the frost module exactly):
- configure.ac: --enable-module-chilldkg (default no, experimental
gate), dependency errors when schnorrsig or ecdh are explicitly
disabled, AM_CONDITIONAL(ENABLE_MODULE_CHILLDKG), summary line.
- Makefile.am: include src/modules/chilldkg/Makefile.am.include under
ENABLE_MODULE_CHILLDKG.
- src/secp256k1.c: guarded include of modules/chilldkg/main_impl.h
after the frost module.
- src/tests.c: guarded include of tests_impl.h and
MAKE_TEST_MODULE(chilldkg) registration.
- CMakeLists.txt: SECP256K1_ENABLE_MODULE_CHILLDKG option (OFF) +
summary line.
- src/CMakeLists.txt: dependency checks on
SECP256K1_ENABLE_MODULE_SCHNORRSIG and SECP256K1_ENABLE_MODULE_ECDH,
ENABLE_MODULE_CHILLDKG=1 compile definition, public header export.
Verified:
- ./autogen.sh && ./configure --enable-experimental
--enable-module-chilldkg --enable-module-schnorrsig
--enable-module-ecdh && make check: PASS 3/3 (tests, noverify_tests,
exhaustive_tests).
- configure fails with a clear error when schnorrsig or ecdh are
disabled, or when experimental is not enabled.
- CMake build with SECP256K1_ENABLE_MODULE_CHILLDKG=ON: ctest 345/345
passed; dependency errors fire correctly when schnorrsig/ecdh OFF.
2026-08-31 01:37:22 +02:00
message ( " chilldkg ............................ ${SECP256K1_ENABLE_MODULE_CHILLDKG}" )
iceberg: add the Iceberg threshold-MuSig module
Port the experimental Iceberg module from the benchmark-iceberg tree
(github.com/furszy/benchmark-iceberg, sources/secp256k1-kmp/native/
secp256k1) into this repo.
Iceberg is a threshold scheme that lets a group of parties stand in
for a single MuSig2 (BIP 327) participant: the group produces one
ordinary MuSig2 public nonce and one ordinary MuSig2 partial
signature, so cosigners cannot tell a group is involved and need no
changes. Nonces are derived from a caller-chosen per-session label
(sid32) rather than stored, so no signer holds a secret nonce between
rounds; labels are public but must never be reused. A quorum of 2t-1
members (of whom up to t-1 may be corrupt) is needed in each round,
so the threshold is at most half the group rounded up; combined with
the scheme's other constraints the smallest usable group is 2-of-4.
See doc/iceberg.md and the module header for the full usage notes.
Module layout (src/modules/iceberg/, layered bottom-up, each layer
may only use the ones above it -- that ordering is also the
constant-time story):
- scalar_poly.{h,_impl.h}: secret-carrying polynomial arithmetic,
keeping secrets away from inversions (documented in the header).
- rss.{h,_impl.h}: replicated secret sharing evaluation.
- vpss.{h,_impl.h}: verifiable public shares; variable-time by
design, sees only participant indices and published points.
- keygen_impl.h: distributed key generation producing one share per
member.
- session_impl.h: nonce_gen/nonce_agg and partial_sign/
partial_sig_agg producing plain MuSig2 objects.
- tests_impl.h: 28 tests including the shipped vectors.h vector
suite and dealer known-answer tests.
- bench_impl.h: benchmark definitions (wired in a follow-up commit).
Public headers: include/secp256k1_iceberg.h (installed) and
include/secp256k1_iceberg_dealer.h (in-tree only: a trusted dealer is
not part of the shipped API, but tests, benchmarks and the example
need to deal shares).
Content adaptations relative to the source tree (the only changes to
the ported code): three secp256k1_musig_nonce_process call sites in
tests_impl.h gained a NULL adaptor argument, because this repo's
musig is the zkp variant whose public nonce_process takes an optional
adaptor point. All musig internals the module uses (ge_parse_ext,
ge_serialize_ext, keyaggcoef, aggnonce_load, pubnonce_save,
partial_sig_save, nonce_process_internal) are identical in both
trees, as are all core headers the module touches; nothing else
needed adaptation.
Build wiring mirrors the chilldkg module:
- configure.ac: --enable-module-iceberg (default no, experimental
gate), hard dependency on the musig module with a configure error
if musig is explicitly disabled (musig itself pulls in schnorrsig),
AM_CONDITIONAL(ENABLE_MODULE_ICEBERG), summary line.
- Makefile.am: include src/modules/iceberg/Makefile.am.include under
the conditional.
- src/secp256k1.c: guarded include of modules/iceberg/main_impl.h
after the chilldkg block (musig is included earlier, so its
internals are in scope).
- src/tests.c: module test registration via MAKE_TEST_MODULE(iceberg).
- CMakeLists.txt / src/CMakeLists.txt: SECP256K1_ENABLE_MODULE_ICEBERG
option (OFF) with a dependency check on SECP256K1_ENABLE_MODULE_MUSIG
(placed before the musig block so the force-enable takes effect),
ENABLE_MODULE_ICEBERG=1 compile definition, public header export,
summary line.
Verified: ./configure --enable-experimental --enable-module-iceberg
&& make check passes; ./tests --target=iceberg runs the full module
suite (28/28); CMake build + ctest pass; the musig dependency error
fires correctly in both build systems.
2026-08-31 12:24:48 +02:00
message ( " iceberg ............................. ${SECP256K1_ENABLE_MODULE_ICEBERG}" )
prefractal: add the nested FROST+MuSig2 module (API, implementation, wiring)
Adds `prefractal`, an experimental module that lets a FROST t-of-n group
occupy ONE participant slot of an ordinary MuSig2 (BIP 327) session. Each
member computes
s_i = k1_i + b_frost*b_musig*k2_i + e*a*lambda_i*g*gacc*d_i
and the group publishes one ordinary MuSig2 public nonce and one ordinary
MuSig2 partial signature, so cosigners need no support for it and cannot tell
a group is involved.
Four public functions, all sessionless (every call takes its session
parameters explicitly, so there are no new opaque types, magics or *_SIZE
constants to keep synchronised):
secp256k1_prefractal_nonce_agg group wire nonce + unscaled aggnonce
secp256k1_prefractal_sign one member's partial signature
secp256k1_prefractal_partial_sig_verify identifiable abort
secp256k1_prefractal_partial_sig_agg sum -> musig partial signature
Three deliberate deviations from BIP 445, all documented in the public header:
1. b_frost does not commit to the message. The target protocols publish the
group's wire nonce before the message exists, so a message-committing
coefficient could not be computed in round one and rebuilt later. The outer
b_musig does commit to the message and multiplies this one, so the product
still binds it. Same trade the iceberg module makes, for the same reason.
The preimage is BIP 445's with the message dropped and the group key
carried in full rather than x-only, since it is used as a full point
downstream.
2. There is NO g_frost factor. Stock FROST normalises its threshold key to
even Y (g_times_gacc_parity = gacc_parity ^ pk_odd, frost/session_impl.h
:664) because it produces a BIP 340 x-only signature. Here the threshold
key is an inner participant of the outer key aggregation and is used as a
full point, so all key-side parity normalisation happens once, at the
aggregate level, off the OUTER keyagg cache. Note this is NOT implied by
the tweak cache being the identity: with an identity cache g_frost is still
-1 for every odd-Y group key, i.e. about half of them. Importing frost's
key-side parity here would yield a signer that works for even-Y groups and
fails for odd-Y ones.
3. The FROST tweak cache must be the identity (tacc == 0, gacc_parity == 0).
Checked in sign and partial_sig_verify, not only in partial_sig_agg, so the
key a member signs under is tied to the cache that was validated; sign and
verify additionally require thresh_pk to equal the cache's own key so the
two arguments cannot disagree.
The verification equation lives in one helper used both by sign's BIP 445
self-check and by partial_sig_verify, so the two cannot drift apart.
Build wiring. Three files order their module blocks differently and the
constraints point in opposite directions:
- src/secp256k1.c: the include goes AFTER frost and musig, because the
module calls their static internals.
- src/CMakeLists.txt: the block goes BEFORE both, because its set() calls
are only observed by blocks that run later.
- configure.ac: the block likewise goes before the musig block, NOT at
iceberg's position further down. configure.ac orders musig and frost ahead
of iceberg, and iceberg's late enable_module_musig=yes is harmless only
because musig defaults to yes. frost defaults to no, so a late
force-enable would leave -DENABLE_MODULE_FROST=1 unemitted while
AM_CONDITIONAL still observed the mutation - a library whose secp256k1.c
never included frost, built alongside frost's own sources.
frost is also the first default-OFF module anything depends on, which breaks
the dependency-guard idiom used everywhere else in both build systems: the
existing "DEFINED X AND NOT X" (CMake) and "x$X = xno" (autotools) tests read
as "the user disabled it explicitly" only for default-ON modules, and are true
by default for a default-OFF one. Since neither build system can distinguish
an explicit disable from the default once both are in the cache, enabling
prefractal simply implies frost; the guard is kept for musig, where it still
means what it says. The CMake block additionally lifts both dependencies into
the parent scope so the top-level configuration summary reports what was
actually built rather than printing "frost OFF" while compiling frost in.
Verified on both build systems:
cmake -B build -DSECP256K1_ENABLE_MODULE_PREFRACTAL=ON -DSECP256K1_BUILD_TESTS=ON
-> musig/frost/prefractal all ON, tests pass, 4 prefractal symbols exported
cmake -B build -DSECP256K1_BUILD_TESTS=ON
-> prefractal OFF, default build unchanged, tests pass
./configure --enable-experimental --enable-module-prefractal && make && make check
-> frost=yes forced on, -DENABLE_MODULE_FROST=1 emitted, 3/3 pass
./configure --enable-module-prefractal
-> correctly refused: "Prefractal module is experimental"
tests_impl.h is a placeholder here so the module links; the real suite lands
next.
2026-09-04 00:44:43 +02:00
message ( " prefractal .......................... ${SECP256K1_ENABLE_MODULE_PREFRACTAL}" )
build: wire the frost_enrollment module into both build systems
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>
2026-09-04 03:53:03 +02:00
message ( " frost-enrollment .................... ${SECP256K1_ENABLE_MODULE_FROST_ENROLLMENT}" )
2022-11-04 16:18:40 -04:00
message ( " ElligatorSwift ...................... ${SECP256K1_ENABLE_MODULE_ELLSWIFT}" )
2024-01-25 15:57:16 +01:00
message ( " generator ........................... ${SECP256K1_ENABLE_MODULE_GENERATOR}" )
message ( " rangeproof .......................... ${SECP256K1_ENABLE_MODULE_RANGEPROOF}" )
message ( " surjectionproof ..................... ${SECP256K1_ENABLE_MODULE_SURJECTIONPROOF}" )
message ( " whitelist ........................... ${SECP256K1_ENABLE_MODULE_WHITELIST}" )
message ( " ecdsa-s2c ........................... ${SECP256K1_ENABLE_MODULE_ECDSA_S2C}" )
message ( " ecdsa-adaptor ....................... ${SECP256K1_ENABLE_MODULE_ECDSA_ADAPTOR}" )
message ( " bppp ................................ ${SECP256K1_ENABLE_MODULE_BPPP}" )
2026-03-06 12:39:49 +02:00
message ( " schnorrsig-halfagg .................. ${SECP256K1_ENABLE_MODULE_SCHNORRSIG_HALFAGG}" )
2023-03-08 13:33:16 +00:00
message ( "Parameters:" )
message ( " ecmult window size .................. ${SECP256K1_ECMULT_WINDOW_SIZE}" )
2021-12-29 15:09:52 -05:00
message ( " ecmult gen table size ............... ${SECP256K1_ECMULT_GEN_KB} KiB" )
2023-03-08 13:33:16 +00:00
message ( "Optional features:" )
2023-11-23 22:51:48 +01:00
message ( " assembly ............................ ${SECP256K1_ASM}" )
2023-03-08 13:33:16 +00:00
message ( " external callbacks .................. ${SECP256K1_USE_EXTERNAL_DEFAULT_CALLBACKS}" )
if ( SECP256K1_TEST_OVERRIDE_WIDE_MULTIPLY )
message ( " wide multiplication (test-only) ..... ${SECP256K1_TEST_OVERRIDE_WIDE_MULTIPLY}" )
endif ( )
message ( "Optional binaries:" )
message ( " benchmark ........................... ${SECP256K1_BUILD_BENCHMARK}" )
message ( " noverify_tests ...................... ${SECP256K1_BUILD_TESTS}" )
set ( tests_status "${SECP256K1_BUILD_TESTS}" )
if ( CMAKE_BUILD_TYPE STREQUAL "Coverage" )
set ( tests_status OFF )
endif ( )
message ( " tests ............................... ${tests_status}" )
message ( " exhaustive tests .................... ${SECP256K1_BUILD_EXHAUSTIVE_TESTS}" )
message ( " ctime_tests ......................... ${SECP256K1_BUILD_CTIME_TESTS}" )
message ( " examples ............................ ${SECP256K1_BUILD_EXAMPLES}" )
message ( "" )
if ( CMAKE_CROSSCOMPILING )
set ( cross_status "TRUE, for ${CMAKE_SYSTEM_NAME}, ${CMAKE_SYSTEM_PROCESSOR}" )
else ( )
set ( cross_status "FALSE" )
endif ( )
message ( "Cross compiling ....................... ${cross_status}" )
2025-07-02 15:18:06 +00:00
message ( "API visibility attributes ............. ${SECP256K1_ENABLE_API_VISIBILITY_ATTRIBUTES}" )
2023-03-08 13:33:16 +00:00
message ( "Valgrind .............................. ${SECP256K1_VALGRIND}" )
get_directory_property ( definitions COMPILE_DEFINITIONS )
string ( REPLACE ";" " " definitions "${definitions}" )
message ( "Preprocessor defined macros ........... ${definitions}" )
2024-05-26 15:53:42 +01:00
message ( "C compiler ............................ ${CMAKE_C_COMPILER_ID} ${CMAKE_C_COMPILER_VERSION}, ${CMAKE_C_COMPILER}" )
2023-03-08 13:33:16 +00:00
message ( "CFLAGS ................................ ${CMAKE_C_FLAGS}" )
get_directory_property ( compile_options COMPILE_OPTIONS )
string ( REPLACE ";" " " compile_options "${compile_options}" )
message ( "Compile options ....................... " ${ compile_options } )
2023-03-26 13:15:34 +01:00
if ( NOT is_multi_config )
2023-03-08 13:33:16 +00:00
message ( "Build type:" )
message ( " - CMAKE_BUILD_TYPE ................... ${CMAKE_BUILD_TYPE}" )
string ( TOUPPER "${CMAKE_BUILD_TYPE}" build_type )
message ( " - CFLAGS ............................. ${CMAKE_C_FLAGS_${build_type}}" )
message ( " - LDFLAGS for executables ............ ${CMAKE_EXE_LINKER_FLAGS_${build_type}}" )
message ( " - LDFLAGS for shared libraries ....... ${CMAKE_SHARED_LINKER_FLAGS_${build_type}}" )
else ( )
2023-03-26 13:15:34 +01:00
message ( "Supported configurations .............. ${CMAKE_CONFIGURATION_TYPES}" )
2023-03-08 13:33:16 +00:00
message ( "RelWithDebInfo configuration:" )
message ( " - CFLAGS ............................. ${CMAKE_C_FLAGS_RELWITHDEBINFO}" )
message ( " - LDFLAGS for executables ............ ${CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO}" )
message ( " - LDFLAGS for shared libraries ....... ${CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO}" )
message ( "Debug configuration:" )
message ( " - CFLAGS ............................. ${CMAKE_C_FLAGS_DEBUG}" )
message ( " - LDFLAGS for executables ............ ${CMAKE_EXE_LINKER_FLAGS_DEBUG}" )
message ( " - LDFLAGS for shared libraries ....... ${CMAKE_SHARED_LINKER_FLAGS_DEBUG}" )
endif ( )
2024-06-21 14:46:42 +01:00
if ( SECP256K1_APPEND_CFLAGS )
message ( "SECP256K1_APPEND_CFLAGS ............... ${SECP256K1_APPEND_CFLAGS}" )
2023-04-26 11:10:03 +01:00
endif ( )
2024-09-02 21:41:03 +01:00
if ( SECP256K1_APPEND_LDFLAGS )
message ( "SECP256K1_APPEND_LDFLAGS .............. ${SECP256K1_APPEND_LDFLAGS}" )
endif ( )
2024-05-28 09:37:00 +01:00
message ( "" )
if ( print_msan_notice )
message (
" N o t e : \ n "
" M e m o r y S a n i t i z e r d e t e c t e d , t r i e d t o a d d - f n o - s a n i t i z e - m e m o r y - p a r a m - r e t v a l t o c o m p i l e o p t i o n s \ n "
" t o a v o i d f a l s e p o s i t i v e s i n c t i m e _ t e s t s . P a s s - D S E C P 2 5 6 K 1 _ B U I L D _ C T I M E _ T E S T S = O F F t o a v o i d t h i s . \ n "
)
endif ( )
2023-03-08 13:33:16 +00:00
if ( SECP256K1_EXPERIMENTAL )
message (
" * * * * * * \ n "
" W A R N I N G : e x p e r i m e n t a l b u i l d \ n "
" E x p e r i m e n t a l f e a t u r e s d o n o t h a v e s t a b l e A P I s o r p r o p e r t i e s , a n d m a y n o t b e s a f e f o r p r o d u c t i o n u s e . \ n "
" * * * * * * \ n "
)
endif ( )