Compare commits

..

15 Commits

Author SHA1 Message Date
kngako
69d5e6bdc1 Update README
Some checks failed
CI / Valgrind (memcheck) (map[env_vars:map[ASM:no CC:clang ECMULTGENPRECISION:2 ECMULTWINDOW:2]]) (push) Has been skipped
CI / Valgrind (memcheck) (map[env_vars:map[ASM:no CC:i686-linux-gnu-gcc ECMULTGENPRECISION:2 ECMULTWINDOW:2 HOST:i686-linux-gnu]]) (push) Has been skipped
CI / UBSan, ASan, LSan (map[env_vars:map[ASM:auto CC:clang]]) (push) Has been skipped
CI / UBSan, ASan, LSan (map[env_vars:map[ASM:auto CC:i686-linux-gnu-gcc HOST:i686-linux-gnu]]) (push) Has been skipped
CI / UBSan, ASan, LSan (map[env_vars:map[ASM:no CC:clang ECMULTGENPRECISION:2 ECMULTWINDOW:2]]) (push) Has been skipped
CI / UBSan, ASan, LSan (map[env_vars:map[ASM:no CC:i686-linux-gnu-gcc ECMULTGENPRECISION:2 ECMULTWINDOW:2 HOST:i686-linux-gnu]]) (push) Has been skipped
CI / MSan (map[env_vars:map[CFLAGS:-fsanitize=memory -fsanitize-recover=memory -g -O3 ECMULTGENPRECISION:2 ECMULTWINDOW:2]]) (push) Has been skipped
CI / MSan (map[env_vars:map[CFLAGS:-fsanitize=memory -fsanitize-recover=memory -g]]) (push) Has been skipped
CI / ${{ matrix.configuration.job_name }} (map[env_vars:map[HOST:i686-w64-mingw32] job_name:i686 (mingw32-w64): Windows (Debian stable, Wine)]) (push) Has been skipped
CI / ${{ matrix.configuration.job_name }} (map[env_vars:map[HOST:x86_64-w64-mingw32] job_name:x86_64 (mingw32-w64): Windows (Debian stable, Wine)]) (push) Has been skipped
CI / C++ -fpermissive (entire project) (push) Has been skipped
CI / C++ (public headers) (push) Has been skipped
CI / SageMath prover (push) Failing after 2m13s
CI / release (push) Failing after 8m4s
CI / x86_64: macOS Monterey (map[BPPP:yes CC:gcc ECDH:yes ECDSAADAPTOR:yes ECDSA_S2C:yes ELLSWIFT:yes EXPERIMENTAL:yes FROST:yes GENERATOR:yes MUSIG:yes RANGEPROOF:yes RECOVERY:yes SCHNORRSIG:yes SCHNORRSIG_HALFAGG:yes SECP256K1_TEST_ITERS:2 WHITELIST:yes W… (push) Has been cancelled
CI / x86_64: macOS Monterey (map[BPPP:yes CC:gcc ECDH:yes ECDSAADAPTOR:yes ECDSA_S2C:yes ELLSWIFT:yes EXPERIMENTAL:yes FROST:yes GENERATOR:yes MUSIG:yes RANGEPROOF:yes RECOVERY:yes SCHNORRSIG:yes SCHNORRSIG_HALFAGG:yes WHITELIST:yes WIDEMUL:int128]) (push) Has been cancelled
CI / x86_64: macOS Monterey (map[BPPP:yes CPPFLAGS:-DVERIFY CTIMETESTS:no ECDH:yes ECDSAADAPTOR:yes ECDSA_S2C:yes ELLSWIFT:yes EXPERIMENTAL:yes FROST:yes GENERATOR:yes MUSIG:yes RANGEPROOF:yes RECOVERY:yes SCHNORRSIG:yes SCHNORRSIG_HALFAGG:yes WHITELIST:yes… (push) Has been cancelled
CI / x86_64: macOS Monterey (map[BPPP:yes ECDH:yes ECDSAADAPTOR:yes ECDSA_S2C:yes ELLSWIFT:yes EXPERIMENTAL:yes FROST:yes GENERATOR:yes MUSIG:yes RANGEPROOF:yes RECOVERY:yes SCHNORRSIG:yes SCHNORRSIG_HALFAGG:yes SECP256K1_TEST_ITERS:2 WHITELIST:yes WIDEMUL:… (push) Has been cancelled
CI / x86_64: macOS Monterey (map[BPPP:yes ECDH:yes ECDSAADAPTOR:yes ECDSA_S2C:yes ELLSWIFT:yes EXPERIMENTAL:yes FROST:yes GENERATOR:yes MUSIG:yes RANGEPROOF:yes RECOVERY:yes SCHNORRSIG:yes SCHNORRSIG_HALFAGG:yes WHITELIST:yes WIDEMUL:int128]) (push) Has been cancelled
CI / x86_64: macOS Monterey (map[BPPP:yes ECDH:yes ECDSAADAPTOR:yes ECDSA_S2C:yes ELLSWIFT:yes EXPERIMENTAL:yes FROST:yes GENERATOR:yes MUSIG:yes RANGEPROOF:yes RECOVERY:yes SCHNORRSIG:yes SCHNORRSIG_HALFAGG:yes WHITELIST:yes WIDEMUL:int64]) (push) Has been cancelled
CI / x86_64: macOS Monterey (map[BPPP:yes ECDH:yes ECDSAADAPTOR:yes ECDSA_S2C:yes ELLSWIFT:yes EXPERIMENTAL:yes FROST:yes GENERATOR:yes MUSIG:yes RANGEPROOF:yes SCHNORRSIG:yes SCHNORRSIG_HALFAGG:yes WHITELIST:yes WIDEMUL:int128]) (push) Has been cancelled
CI / x86_64: macOS Monterey (map[BUILD:distcheck]) (push) Has been cancelled
CI / x86_64: macOS Monterey (map[ECMULTGENPRECISION:2 ECMULTWINDOW:4 WIDEMUL:int128_struct]) (push) Has been cancelled
CI / x86_64: macOS Monterey (map[RECOVERY:yes WIDEMUL:int128]) (push) Has been cancelled
CI / ${{ matrix.configuration.job_name }} (map[cmake_options:-A Win32 job_name:x86 (MSVC): Windows (VS 2022)]) (push) Has been cancelled
CI / ${{ matrix.configuration.job_name }} (map[cmake_options:-A x64 -DBUILD_SHARED_LIBS=OFF job_name:x64 (MSVC): Windows (VS 2022, static)]) (push) Has been cancelled
CI / ${{ matrix.configuration.job_name }} (map[cmake_options:-A x64 -DBUILD_SHARED_LIBS=ON job_name:x64 (MSVC): Windows (VS 2022, shared)]) (push) Has been cancelled
CI / ${{ matrix.configuration.job_name }} (map[cmake_options:-A x64 -DSECP256K1_TEST_OVERRIDE_WIDE_MULTIPLY=int128_struct cpp_flags:/DSECP256K1_MSVC_MULH_TEST_OVERRIDE job_name:x64 (MSVC): Windows (VS 2022, int128_struct with __(u)mulh)]) (push) Has been cancelled
CI / ${{ matrix.configuration.job_name }} (map[cmake_options:-A x64 -DSECP256K1_TEST_OVERRIDE_WIDE_MULTIPLY=int128_struct job_name:x64 (MSVC): Windows (VS 2022, int128_struct)]) (push) Has been cancelled
CI / x64 (MSVC): C++ (public headers) (push) Has been cancelled
2024-08-23 02:09:22 +02:00
Kgothatso
df0bdeb096 Add xconfigure to gitignore
Some checks failed
CI / Valgrind (memcheck) (map[env_vars:map[ASM:no CC:clang ECMULTGENPRECISION:2 ECMULTWINDOW:2]]) (push) Has been skipped
CI / Valgrind (memcheck) (map[env_vars:map[ASM:no CC:i686-linux-gnu-gcc ECMULTGENPRECISION:2 ECMULTWINDOW:2 HOST:i686-linux-gnu]]) (push) Has been skipped
CI / UBSan, ASan, LSan (map[env_vars:map[ASM:auto CC:clang]]) (push) Has been skipped
CI / UBSan, ASan, LSan (map[env_vars:map[ASM:auto CC:i686-linux-gnu-gcc HOST:i686-linux-gnu]]) (push) Has been skipped
CI / UBSan, ASan, LSan (map[env_vars:map[ASM:no CC:clang ECMULTGENPRECISION:2 ECMULTWINDOW:2]]) (push) Has been skipped
CI / UBSan, ASan, LSan (map[env_vars:map[ASM:no CC:i686-linux-gnu-gcc ECMULTGENPRECISION:2 ECMULTWINDOW:2 HOST:i686-linux-gnu]]) (push) Has been skipped
CI / MSan (map[env_vars:map[CFLAGS:-fsanitize=memory -fsanitize-recover=memory -g -O3 ECMULTGENPRECISION:2 ECMULTWINDOW:2]]) (push) Has been skipped
CI / MSan (map[env_vars:map[CFLAGS:-fsanitize=memory -fsanitize-recover=memory -g]]) (push) Has been skipped
CI / ${{ matrix.configuration.job_name }} (map[env_vars:map[HOST:i686-w64-mingw32] job_name:i686 (mingw32-w64): Windows (Debian stable, Wine)]) (push) Has been skipped
CI / ${{ matrix.configuration.job_name }} (map[env_vars:map[HOST:x86_64-w64-mingw32] job_name:x86_64 (mingw32-w64): Windows (Debian stable, Wine)]) (push) Has been skipped
CI / C++ -fpermissive (entire project) (push) Has been skipped
CI / C++ (public headers) (push) Has been skipped
CI / SageMath prover (push) Has started running
CI / release (push) Failing after 22m11s
CI / x86_64: macOS Monterey (map[BPPP:yes CC:gcc ECDH:yes ECDSAADAPTOR:yes ECDSA_S2C:yes ELLSWIFT:yes EXPERIMENTAL:yes FROST:yes GENERATOR:yes MUSIG:yes RANGEPROOF:yes RECOVERY:yes SCHNORRSIG:yes SCHNORRSIG_HALFAGG:yes SECP256K1_TEST_ITERS:2 WHITELIST:yes W… (push) Has been cancelled
CI / x86_64: macOS Monterey (map[BPPP:yes CC:gcc ECDH:yes ECDSAADAPTOR:yes ECDSA_S2C:yes ELLSWIFT:yes EXPERIMENTAL:yes FROST:yes GENERATOR:yes MUSIG:yes RANGEPROOF:yes RECOVERY:yes SCHNORRSIG:yes SCHNORRSIG_HALFAGG:yes WHITELIST:yes WIDEMUL:int128]) (push) Has been cancelled
CI / x86_64: macOS Monterey (map[BPPP:yes CPPFLAGS:-DVERIFY CTIMETESTS:no ECDH:yes ECDSAADAPTOR:yes ECDSA_S2C:yes ELLSWIFT:yes EXPERIMENTAL:yes FROST:yes GENERATOR:yes MUSIG:yes RANGEPROOF:yes RECOVERY:yes SCHNORRSIG:yes SCHNORRSIG_HALFAGG:yes WHITELIST:yes… (push) Has been cancelled
CI / x86_64: macOS Monterey (map[BPPP:yes ECDH:yes ECDSAADAPTOR:yes ECDSA_S2C:yes ELLSWIFT:yes EXPERIMENTAL:yes FROST:yes GENERATOR:yes MUSIG:yes RANGEPROOF:yes RECOVERY:yes SCHNORRSIG:yes SCHNORRSIG_HALFAGG:yes SECP256K1_TEST_ITERS:2 WHITELIST:yes WIDEMUL:… (push) Has been cancelled
CI / x86_64: macOS Monterey (map[BPPP:yes ECDH:yes ECDSAADAPTOR:yes ECDSA_S2C:yes ELLSWIFT:yes EXPERIMENTAL:yes FROST:yes GENERATOR:yes MUSIG:yes RANGEPROOF:yes RECOVERY:yes SCHNORRSIG:yes SCHNORRSIG_HALFAGG:yes WHITELIST:yes WIDEMUL:int128]) (push) Has been cancelled
CI / x86_64: macOS Monterey (map[BPPP:yes ECDH:yes ECDSAADAPTOR:yes ECDSA_S2C:yes ELLSWIFT:yes EXPERIMENTAL:yes FROST:yes GENERATOR:yes MUSIG:yes RANGEPROOF:yes RECOVERY:yes SCHNORRSIG:yes SCHNORRSIG_HALFAGG:yes WHITELIST:yes WIDEMUL:int64]) (push) Has been cancelled
CI / x86_64: macOS Monterey (map[BPPP:yes ECDH:yes ECDSAADAPTOR:yes ECDSA_S2C:yes ELLSWIFT:yes EXPERIMENTAL:yes FROST:yes GENERATOR:yes MUSIG:yes RANGEPROOF:yes SCHNORRSIG:yes SCHNORRSIG_HALFAGG:yes WHITELIST:yes WIDEMUL:int128]) (push) Has been cancelled
CI / x86_64: macOS Monterey (map[BUILD:distcheck]) (push) Has been cancelled
CI / x86_64: macOS Monterey (map[ECMULTGENPRECISION:2 ECMULTWINDOW:4 WIDEMUL:int128_struct]) (push) Has been cancelled
CI / x86_64: macOS Monterey (map[RECOVERY:yes WIDEMUL:int128]) (push) Has been cancelled
CI / ${{ matrix.configuration.job_name }} (map[cmake_options:-A Win32 job_name:x86 (MSVC): Windows (VS 2022)]) (push) Has been cancelled
CI / ${{ matrix.configuration.job_name }} (map[cmake_options:-A x64 -DBUILD_SHARED_LIBS=OFF job_name:x64 (MSVC): Windows (VS 2022, static)]) (push) Has been cancelled
CI / ${{ matrix.configuration.job_name }} (map[cmake_options:-A x64 -DBUILD_SHARED_LIBS=ON job_name:x64 (MSVC): Windows (VS 2022, shared)]) (push) Has been cancelled
CI / ${{ matrix.configuration.job_name }} (map[cmake_options:-A x64 -DSECP256K1_TEST_OVERRIDE_WIDE_MULTIPLY=int128_struct cpp_flags:/DSECP256K1_MSVC_MULH_TEST_OVERRIDE job_name:x64 (MSVC): Windows (VS 2022, int128_struct with __(u)mulh)]) (push) Has been cancelled
CI / ${{ matrix.configuration.job_name }} (map[cmake_options:-A x64 -DSECP256K1_TEST_OVERRIDE_WIDE_MULTIPLY=int128_struct job_name:x64 (MSVC): Windows (VS 2022, int128_struct)]) (push) Has been cancelled
CI / x64 (MSVC): C++ (public headers) (push) Has been cancelled
2024-08-03 19:55:35 +02:00
Jesse Posner
3891388905
frost: add documentation file
This commit adds a documentation file with detailed instructions for how
to use the module properly.
2024-07-23 14:33:22 -07:00
Jesse Posner
36ff5b02da
frost: add tests
Add api tests, nonce tests, tweak tests, sha256 tag tests, and constant
time tests.
2024-07-23 14:33:09 -07:00
Jesse Posner
c2d48a7039
frost: add example file
This commit adds an example file to demonstrate how to use the module.
2024-07-16 23:36:51 -07:00
Jesse Posner
ef15156ffb
frost: signature generation and aggregation
This commit adds signature generation and aggregation, as well as
partial signature serialization and parsing.
2024-07-16 23:36:51 -07:00
Jesse Posner
67c21beadd
frost: nonce aggregation and adaptor signatures
This commit adds nonce aggregation, as well as adaptor signatures.
2024-07-16 23:36:47 -07:00
Jesse Posner
17c47e9708
frost: key tweaking
This commits add BIP-341 ("Taproot") and BIP-32 ("ordinary") public key
tweaking.
2024-07-16 22:13:19 -07:00
Jesse Posner
f606507120
frost: nonce generation
This commits adds nonce generation, as well as serialization and
parsing.
2024-07-16 22:13:19 -07:00
Jesse Posner
197fb7efb9
frost: share aggregation
This commit adds share aggregation and verification, as well as
computation of public verification shares.
2024-07-16 22:13:15 -07:00
Jesse Posner
2336b02fad
frost: share generation
This commit adds share generation, as well as share serialization and
parsing.
2024-07-16 22:01:45 -07:00
Jesse Posner
2f3fa4cace
frost: initialize project
This commit adds the foundational configuration and building scripts
and an initial structure for the project.
2024-07-16 22:01:40 -07:00
Jonas Nick
168377204d
Merge elementsproject/secp256k1-zkp#294: generator: massively speed up serialization
6361266013ad14428c89334013c74f8dec6f8e9d generator: speed up parsing (Andrew Poelstra)
5e7c2c178dc22779ad0f23d39aea39fba0746687 generator: massively speed up serialization (Andrew Poelstra)

Pull request description:

ACKs for top commit:
  real-or-random:
    utACK 6361266013ad14428c89334013c74f8dec6f8e9d
  jonasnick:
    ACK 6361266013ad14428c89334013c74f8dec6f8e9d

Tree-SHA512: 9f35467ac9d39d23b68a3f830c920f61ae39d99974d6a864df4a3c19860dc8fc447609d0480e45234c66250878d34da03bfcf0056eaa83d3c78babb254962bf4
2024-05-22 11:55:07 +00:00
Andrew Poelstra
6361266013
generator: speed up parsing
Similar to speeding up serialization; in our parsing logic we did a
bunch of expensive stuff then expensively inverted it. Drop everything
except the essential checks and then memcpy.
2024-05-21 13:32:12 +00:00
Andrew Poelstra
5e7c2c178d
generator: massively speed up serialization
`secp256k1_pedersen_commit_serialize` would call `_load` (which does a
sqrt to fully decompress the key, then a conditional negation based on
the flag), then check the Jacobian symbol of the resulting y-coordinate,
then re-serialize based on this.

Instead, don't do any of this stuff. Copy the flag directly out of the
internal representation and copy the x-coordinate directly out of the
internal representation.

Checked that none of the other _serialize methods in the modules do
this.

Fixes #293
2024-05-20 12:40:02 +00:00
19 changed files with 1060 additions and 366 deletions

View File

@ -703,8 +703,8 @@ jobs:
- { WIDEMUL: 'int128', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', MUSIG: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', FROST: 'yes' } - { WIDEMUL: 'int128', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', MUSIG: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', FROST: 'yes' }
- { WIDEMUL: 'int128', RECOVERY: 'yes' } - { WIDEMUL: 'int128', RECOVERY: 'yes' }
- { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', MUSIG: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', FROST: 'yes' } - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', MUSIG: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', FROST: 'yes' }
- { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', MUSIG: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', FROST: 'yes', CC: 'gcc' } - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', MUSIG: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', CC: 'gcc', FROST: 'yes' }
- { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', MUSIG: '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', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', MUSIG: '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', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', MUSIG: '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', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', MUSIG: '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', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', MUSIG: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', FROST: 'yes', CPPFLAGS: '-DVERIFY', CTIMETESTS: 'no' } - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', MUSIG: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', FROST: 'yes', CPPFLAGS: '-DVERIFY', CTIMETESTS: 'no' }
- BUILD: 'distcheck' - BUILD: 'distcheck'

1
.gitignore vendored
View File

@ -72,3 +72,4 @@ frost_example
/CMakeUserPresets.json /CMakeUserPresets.json
# Default CMake build directory. # Default CMake build directory.
/build /build
xconfigure.sh

View File

@ -3,7 +3,7 @@ libsecp256k1-zkp
![Dependencies: None](https://img.shields.io/badge/dependencies-none-success) ![Dependencies: None](https://img.shields.io/badge/dependencies-none-success)
A fork of [libsecp256k1](https://github.com/bitcoin-core/secp256k1) with support for advanced and experimental features such as Confidential Assets and MuSig2 A fork of [libsecp256k1](https://github.com/bitcoin-core/secp256k1) with support for advanced and experimental features such as Confidential Assets, MuSig2, and FROST.
Added features: Added features:
* Experimental module for ECDSA adaptor signatures. * Experimental module for ECDSA adaptor signatures.
@ -12,7 +12,7 @@ Added features:
* Experimental module for Confidential Assets (Pedersen commitments, range proofs, and [surjection proofs](src/modules/surjection/surjection.md)). * Experimental module for Confidential Assets (Pedersen commitments, range proofs, and [surjection proofs](src/modules/surjection/surjection.md)).
* Experimental module for Bulletproofs++ range proofs. * Experimental module for Bulletproofs++ range proofs.
* Experimental module for [address whitelisting](src/modules/whitelist/whitelist.md). * Experimental module for [address whitelisting](src/modules/whitelist/whitelist.md).
* Experimental module for FROST. * Experimental module for [FROST](src/modules/frost/frost.md).
Experimental features are made available for testing and review by the community. The APIs of these features should not be considered stable. Experimental features are made available for testing and review by the community. The APIs of these features should not be considered stable.

View File

@ -14,8 +14,8 @@ print_environment() {
for var in WERROR_CFLAGS MAKEFLAGS BUILD \ for var in WERROR_CFLAGS MAKEFLAGS BUILD \
ECMULTWINDOW ECMULTGENPRECISION ASM WIDEMUL WITH_VALGRIND EXTRAFLAGS \ ECMULTWINDOW ECMULTGENPRECISION ASM WIDEMUL WITH_VALGRIND EXTRAFLAGS \
EXPERIMENTAL ECDH RECOVERY SCHNORRSIG SCHNORRSIG_HALFAGG ELLSWIFT \ EXPERIMENTAL ECDH RECOVERY SCHNORRSIG SCHNORRSIG_HALFAGG ELLSWIFT \
ECDSA_S2C GENERATOR RANGEPROOF WHITELIST MUSIG ECDSAADAPTOR BPPP FROST \ ECDSA_S2C GENERATOR RANGEPROOF WHITELIST MUSIG ECDSAADAPTOR BPPP \
SECP256K1_TEST_ITERS BENCH SECP256K1_BENCH_ITERS CTIMETESTS\ FROST SECP256K1_TEST_ITERS BENCH SECP256K1_BENCH_ITERS CTIMETESTS\
EXAMPLES \ EXAMPLES \
HOST WRAPPER_CMD \ HOST WRAPPER_CMD \
CC CFLAGS CPPFLAGS AR NM CC CFLAGS CPPFLAGS AR NM

View File

@ -236,15 +236,15 @@ AC_ARG_ENABLE(module_ecdsa-adaptor,
[], [],
[SECP_SET_DEFAULT([enable_module_ecdsa_adaptor], [no], [yes])]) [SECP_SET_DEFAULT([enable_module_ecdsa_adaptor], [no], [yes])])
AC_ARG_ENABLE(module_frost,
AS_HELP_STRING([--enable-module-frost],[enable FROST module [default=no]]),
[],
[SECP_SET_DEFAULT([enable_module_frost], [no], [yes])])
AC_ARG_ENABLE(external_default_callbacks, AC_ARG_ENABLE(external_default_callbacks,
AS_HELP_STRING([--enable-external-default-callbacks],[enable external default callback functions [default=no]]), [], AS_HELP_STRING([--enable-external-default-callbacks],[enable external default callback functions [default=no]]), [],
[SECP_SET_DEFAULT([enable_external_default_callbacks], [no], [no])]) [SECP_SET_DEFAULT([enable_external_default_callbacks], [no], [no])])
AC_ARG_ENABLE(module_frost,
AS_HELP_STRING([--enable-module-frost],[enable FROST module (experimental)]),
[],
[SECP_SET_DEFAULT([enable_module_frost], [no], [yes])])
# Test-only override of the (autodetected by the C code) "widemul" setting. # Test-only override of the (autodetected by the C code) "widemul" setting.
# Legal values are: # Legal values are:
# * int64 (for [u]int64_t), # * int64 (for [u]int64_t),
@ -475,14 +475,6 @@ if test x"$enable_module_ecdsa_adaptor" = x"yes"; then
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_ECDSA_ADAPTOR=1" SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_ECDSA_ADAPTOR=1"
fi fi
if test x"$enable_module_frost" = x"yes"; then
if test x"$enable_module_schnorrsig" = x"no"; then
AC_MSG_ERROR([Module dependency error: You have disabled the schnorrsig module explicitly, but it is required by the musig module.])
fi
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_FROST=1"
enable_module_schnorrsig=yes
fi
if test x"$enable_module_musig" = x"yes"; then if test x"$enable_module_musig" = x"yes"; then
if test x"$enable_module_schnorrsig" = x"no"; then if test x"$enable_module_schnorrsig" = x"no"; then
AC_MSG_ERROR([Module dependency error: You have disabled the schnorrsig module explicitly, but it is required by the musig module.]) AC_MSG_ERROR([Module dependency error: You have disabled the schnorrsig module explicitly, but it is required by the musig module.])
@ -543,6 +535,14 @@ if test x"$enable_module_ecdh" = x"yes"; then
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_ECDH=1" SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_ECDH=1"
fi fi
if test x"$enable_module_frost" = x"yes"; then
if test x"$enable_module_schnorrsig" = x"no"; then
AC_MSG_ERROR([Module dependency error: You have disabled the schnorrsig module explicitly, but it is required by the frost module.])
fi
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_FROST=1"
enable_module_schnorrsig=yes
fi
if test x"$enable_external_default_callbacks" = x"yes"; then if test x"$enable_external_default_callbacks" = x"yes"; then
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DUSE_EXTERNAL_DEFAULT_CALLBACKS=1" SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DUSE_EXTERNAL_DEFAULT_CALLBACKS=1"
fi fi
@ -592,12 +592,12 @@ else
if test x"$enable_module_generator" = x"yes"; then if test x"$enable_module_generator" = x"yes"; then
AC_MSG_ERROR([NUMS generator module is experimental. Use --enable-experimental to allow.]) AC_MSG_ERROR([NUMS generator module is experimental. Use --enable-experimental to allow.])
fi fi
if test x"$enable_module_frost" = x"yes"; then
AC_MSG_ERROR([FROST module is experimental. Use --enable-experimental to allow.])
fi
if test x"$set_asm" = x"arm32"; then if test x"$set_asm" = x"arm32"; then
AC_MSG_ERROR([ARM32 assembly is experimental. Use --enable-experimental to allow.]) AC_MSG_ERROR([ARM32 assembly is experimental. Use --enable-experimental to allow.])
fi fi
if test x"$enable_module_frost" = x"yes"; then
AC_MSG_ERROR([FROST module is experimental. Use --enable-experimental to allow.])
fi
fi fi
### ###
@ -666,9 +666,9 @@ echo " module whitelist = $enable_module_whitelist"
echo " module musig = $enable_module_musig" echo " module musig = $enable_module_musig"
echo " module ecdsa-s2c = $enable_module_ecdsa_s2c" echo " module ecdsa-s2c = $enable_module_ecdsa_s2c"
echo " module ecdsa-adaptor = $enable_module_ecdsa_adaptor" echo " module ecdsa-adaptor = $enable_module_ecdsa_adaptor"
echo " module frost = $enable_module_frost"
echo " module bppp = $enable_module_bppp" echo " module bppp = $enable_module_bppp"
echo " module schnorrsig-halfagg = $enable_module_schnorrsig_halfagg" echo " module schnorrsig-halfagg = $enable_module_schnorrsig_halfagg"
echo " module frost = $enable_module_frost"
echo echo
echo " asm = $set_asm" echo " asm = $set_asm"
echo " ecmult window size = $set_ecmult_window" echo " ecmult window size = $set_ecmult_window"

View File

@ -1,5 +1,5 @@
/*********************************************************************** /***********************************************************************
* Copyright (c) 2021-2023 Jesse Posner * * Copyright (c) 2021-2024 Jesse Posner *
* Distributed under the MIT software license, see the accompanying * * Distributed under the MIT software license, see the accompanying *
* file COPYING or https://www.opensource.org/licenses/mit-license.php.* * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
***********************************************************************/ ***********************************************************************/
@ -25,8 +25,10 @@
#define THRESHOLD 3 #define THRESHOLD 3
struct signer_secrets { struct signer_secrets {
secp256k1_frost_share share; secp256k1_keypair keypair;
secp256k1_frost_share agg_share;
secp256k1_frost_secnonce secnonce; secp256k1_frost_secnonce secnonce;
unsigned char seed[32];
}; };
struct signer { struct signer {
@ -34,26 +36,86 @@ struct signer {
secp256k1_frost_pubnonce pubnonce; secp256k1_frost_pubnonce pubnonce;
secp256k1_frost_session session; secp256k1_frost_session session;
secp256k1_frost_partial_sig partial_sig; secp256k1_frost_partial_sig partial_sig;
secp256k1_pubkey vss_commitment[THRESHOLD];
unsigned char vss_hash[32];
unsigned char pok[64];
unsigned char id[33];
}; };
/* Create shares and coefficient commitments */ /* Create a key pair and store it in seckey and pubkey */
int create_shares(const secp256k1_context* ctx, struct signer_secrets *signer_secrets, struct signer *signers, secp256k1_xonly_pubkey *pk) { int create_keypair_and_seed(const secp256k1_context* ctx, struct signer_secrets *signer_secrets, struct signer *signer) {
int i; unsigned char seckey[32];
secp256k1_frost_share shares[N_SIGNERS]; secp256k1_pubkey pubkey_tmp;
secp256k1_pubkey pubshares[N_SIGNERS]; size_t size = 33;
unsigned char seed[32];
if (!fill_random(seed, sizeof(seed))) { while (1) {
if (!fill_random(seckey, sizeof(seckey))) {
printf("Failed to generate randomness\n");
return 1;
}
if (secp256k1_keypair_create(ctx, &signer_secrets->keypair, seckey)) {
break;
}
}
if (!secp256k1_keypair_pub(ctx, &pubkey_tmp, &signer_secrets->keypair)) {
return 0; return 0;
} }
if (!secp256k1_ec_pubkey_serialize(ctx, signer->id, &size, &pubkey_tmp, SECP256K1_EC_COMPRESSED)) {
if (!secp256k1_frost_shares_trusted_gen(ctx, shares, pubshares, pk, seed, THRESHOLD, N_SIGNERS)) {
return 0; return 0;
} }
if (!fill_random(signer_secrets->seed, sizeof(signer_secrets->seed))) {
return 0;
}
return 1;
}
/* Create shares and coefficient commitments */
int create_shares(const secp256k1_context* ctx, struct signer_secrets *signer_secrets, struct signer *signer, secp256k1_xonly_pubkey *pk) {
int i, j;
secp256k1_frost_share shares[N_SIGNERS][N_SIGNERS];
const secp256k1_pubkey *vss_commitments[N_SIGNERS];
const unsigned char *ids[N_SIGNERS];
for (i = 0; i < N_SIGNERS; i++) {
vss_commitments[i] = signer[i].vss_commitment;
ids[i] = signer[i].id;
}
for (i = 0; i < N_SIGNERS; i++) { for (i = 0; i < N_SIGNERS; i++) {
signer_secrets[i].share = shares[i]; /* Generate a polynomial share for the participants */
signers[i].pubshare = pubshares[i]; if (!secp256k1_frost_shares_gen(ctx, shares[i], signer[i].vss_commitment, signer[i].pok, signer_secrets[i].seed, THRESHOLD, N_SIGNERS, ids)) {
return 0;
}
}
/* KeyGen communication round 1: exchange shares and coefficient
* commitments */
for (i = 0; i < N_SIGNERS; i++) {
const secp256k1_frost_share *assigned_shares[N_SIGNERS];
/* Each participant receives a share from each participant (including
* themselves) corresponding to their index. */
for (j = 0; j < N_SIGNERS; j++) {
assigned_shares[j] = &shares[j][i];
}
/* Each participant aggregates the shares they received. */
if (!secp256k1_frost_share_agg(ctx, &signer_secrets[i].agg_share, pk, assigned_shares, vss_commitments, N_SIGNERS, THRESHOLD, signer[i].id)) {
return 0;
}
for (j = 0; j < N_SIGNERS; j++) {
/* Each participant verifies their shares. share_agg calls this
* internally, so it is only neccessary to call this function if
* share_agg returns an error, to determine which participant(s)
* submitted faulty data. */
if (!secp256k1_frost_share_verify(ctx, THRESHOLD, signer[i].id, assigned_shares[j], &vss_commitments[j])) {
return 0;
}
/* Each participant generates public verification shares that are
* used for verifying partial signatures. */
if (!secp256k1_frost_compute_pubshare(ctx, &signer[j].pubshare, THRESHOLD, signer[j].id, vss_commitments, N_SIGNERS)) {
return 0;
}
}
} }
return 1; return 1;
@ -102,16 +164,15 @@ int tweak(const secp256k1_context* ctx, secp256k1_xonly_pubkey *pk, secp256k1_fr
* the result in sig */ * the result in sig */
int sign(const secp256k1_context* ctx, struct signer_secrets *signer_secrets, struct signer *signer, const unsigned char* msg32, secp256k1_xonly_pubkey *pk, unsigned char *sig64, const secp256k1_frost_tweak_cache *cache) { int sign(const secp256k1_context* ctx, struct signer_secrets *signer_secrets, struct signer *signer, const unsigned char* msg32, secp256k1_xonly_pubkey *pk, unsigned char *sig64, const secp256k1_frost_tweak_cache *cache) {
int i; int i;
size_t signer_id = 0; int signer_id = 0;
int signers[THRESHOLD]; int signers[THRESHOLD];
int is_signer[N_SIGNERS]; int is_signer[N_SIGNERS];
const secp256k1_frost_pubnonce *pubnonces[THRESHOLD]; const secp256k1_frost_pubnonce *pubnonces[THRESHOLD];
size_t ids[THRESHOLD]; const unsigned char *ids[THRESHOLD];
const secp256k1_frost_partial_sig *partial_sigs[THRESHOLD]; const secp256k1_frost_partial_sig *partial_sigs[THRESHOLD];
for (i = 0; i < N_SIGNERS; i++) { for (i = 0; i < N_SIGNERS; i++) {
unsigned char session_id[32]; unsigned char session_id[32];
/* Create random session ID. It is absolutely necessary that the session ID /* Create random session ID. It is absolutely necessary that the session ID
* is unique for every call of secp256k1_frost_nonce_gen. Otherwise * is unique for every call of secp256k1_frost_nonce_gen. Otherwise
* it's trivial for an attacker to extract the secret key! */ * it's trivial for an attacker to extract the secret key! */
@ -120,14 +181,14 @@ int sign(const secp256k1_context* ctx, struct signer_secrets *signer_secrets, st
} }
/* Initialize session and create secret nonce for signing and public /* Initialize session and create secret nonce for signing and public
* nonce to send to the other signers. */ * nonce to send to the other signers. */
if (!secp256k1_frost_nonce_gen(ctx, &signer_secrets[i].secnonce, &signer[i].pubnonce, session_id, &signer_secrets[i].share, msg32, pk, NULL)) { if (!secp256k1_frost_nonce_gen(ctx, &signer_secrets[i].secnonce, &signer[i].pubnonce, session_id, &signer_secrets[i].agg_share, msg32, pk, NULL)) {
return 0; return 0;
} }
is_signer[i] = 0; /* Initialize is_signer */ is_signer[i] = 0; /* Initialize is_signer */
} }
/* Select a random subset of signers */ /* Select a random subset of signers */
for (i = 0; i < THRESHOLD; i++) { for (i = 0; i < THRESHOLD; i++) {
size_t subset_seed; unsigned int subset_seed;
while (1) { while (1) {
if (!fill_random((unsigned char*)&subset_seed, sizeof(subset_seed))) { if (!fill_random((unsigned char*)&subset_seed, sizeof(subset_seed))) {
@ -144,19 +205,19 @@ int sign(const secp256k1_context* ctx, struct signer_secrets *signer_secrets, st
/* Mark signer as assigned */ /* Mark signer as assigned */
pubnonces[i] = &signer[signer_id].pubnonce; pubnonces[i] = &signer[signer_id].pubnonce;
/* pubkeys[i] = &signer[signer_id].pubkey; */ /* pubkeys[i] = &signer[signer_id].pubkey; */
ids[i] = signer_id + 1; ids[i] = signer[signer_id].id;
} }
/* Signing communication round 1: Exchange nonces */ /* Signing communication round 1: Exchange nonces */
for (i = 0; i < THRESHOLD; i++) { for (i = 0; i < THRESHOLD; i++) {
signer_id = signers[i]; signer_id = signers[i];
if (!secp256k1_frost_nonce_process(ctx, &signer[signer_id].session, pubnonces, THRESHOLD, msg32, pk, signer_id + 1, ids, cache, NULL)) { if (!secp256k1_frost_nonce_process(ctx, &signer[signer_id].session, pubnonces, THRESHOLD, msg32, pk, signer[signer_id].id, ids, cache, NULL)) {
return 0; return 0;
} }
/* partial_sign will clear the secnonce by setting it to 0. That's because /* partial_sign will clear the secnonce by setting it to 0. That's because
* you must _never_ reuse the secnonce (or use the same session_id to * you must _never_ reuse the secnonce (or use the same session_id to
* create a secnonce). If you do, you effectively reuse the nonce and * create a secnonce). If you do, you effectively reuse the nonce and
* leak the secret key. */ * leak the secret key. */
if (!secp256k1_frost_partial_sign(ctx, &signer[signer_id].partial_sig, &signer_secrets[signer_id].secnonce, &signer_secrets[signer_id].share, &signer[signer_id].session, cache)) { if (!secp256k1_frost_partial_sign(ctx, &signer[signer_id].partial_sig, &signer_secrets[signer_id].secnonce, &signer_secrets[signer_id].agg_share, &signer[signer_id].session, cache)) {
return 0; return 0;
} }
partial_sigs[i] = &signer[signer_id].partial_sig; partial_sigs[i] = &signer[signer_id].partial_sig;
@ -185,6 +246,7 @@ int sign(const secp256k1_context* ctx, struct signer_secrets *signer_secrets, st
int main(void) { int main(void) {
secp256k1_context* ctx; secp256k1_context* ctx;
int i;
struct signer_secrets signer_secrets[N_SIGNERS]; struct signer_secrets signer_secrets[N_SIGNERS];
struct signer signers[N_SIGNERS]; struct signer signers[N_SIGNERS];
secp256k1_xonly_pubkey pk; secp256k1_xonly_pubkey pk;
@ -194,6 +256,14 @@ int main(void) {
/* Create a context for signing and verification */ /* Create a context for signing and verification */
ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE); ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
printf("Creating key pairs......");
for (i = 0; i < N_SIGNERS; i++) {
if (!create_keypair_and_seed(ctx, &signer_secrets[i], &signers[i])) {
printf("FAILED\n");
return 1;
}
}
printf("ok\n");
printf("Creating shares........."); printf("Creating shares.........");
if (!create_shares(ctx, signer_secrets, signers, &pk)) { if (!create_shares(ctx, signer_secrets, signers, &pk)) {
printf("FAILED\n"); printf("FAILED\n");
@ -201,7 +271,7 @@ int main(void) {
} }
printf("ok\n"); printf("ok\n");
printf("Tweaking................"); printf("Tweaking................");
/* Optionally tweak the key */ /* Optionally tweak the aggregate key */
if (!tweak(ctx, &pk, &cache)) { if (!tweak(ctx, &pk, &cache)) {
printf("FAILED\n"); printf("FAILED\n");
return 1; return 1;

View File

@ -9,9 +9,9 @@ extern "C" {
#include <stdint.h> #include <stdint.h>
/** This code is currently a work in progress. It's not secure nor stable. IT /** This code is currently a work in progress. It's not secure nor stable.
* IS EXTREMELY DANGEROUS AND RECKLESS TO USE THIS MODULE IN PRODUCTION! * IT IS EXTREMELY DANGEROUS AND RECKLESS TO USE THIS MODULE IN PRODUCTION!
*
* This module implements a variant of Flexible Round-Optimized Schnorr * This module implements a variant of Flexible Round-Optimized Schnorr
* Threshold Signatures (FROST) by Chelsea Komlo and Ian Goldberg * Threshold Signatures (FROST) by Chelsea Komlo and Ian Goldberg
* (https://crysp.uwaterloo.ca/software/frost/). Signatures are compatible with * (https://crysp.uwaterloo.ca/software/frost/). Signatures are compatible with
@ -21,6 +21,9 @@ extern "C" {
* The module also supports BIP-341 ("Taproot") and BIP-32 ("ordinary") public * The module also supports BIP-341 ("Taproot") and BIP-32 ("ordinary") public
* key tweaking, and adaptor signatures. * key tweaking, and adaptor signatures.
* *
* It is recommended to read the documentation in this include file carefully.
* Further notes on API usage can be found in src/modules/frost/frost.md
*
* Following the convention used in the MuSig module, the API uses the singular * Following the convention used in the MuSig module, the API uses the singular
* term "nonce" to refer to the two "nonces" used by the FROST scheme. * term "nonce" to refer to the two "nonces" used by the FROST scheme.
*/ */
@ -183,11 +186,13 @@ SECP256K1_API int secp256k1_frost_share_parse(
/** Creates key shares /** Creates key shares
* *
* To generate a key, a trusted dealer generates a share for each other * To generate a key, each participant generates a share for each other
* participant. * participant. For example, in the case of 2 particpants, Alice and Bob, they
* each generate 2 shares, distribute 1 share to each other using a secure
* channel, and keep 1 for themselves.
* *
* The trusted dealer must transmit shares over secure channels to * Each participant must transmit shares over secure channels to each other
* participants. * participant.
* *
* Each call to this function must have a UNIQUE and uniformly RANDOM seed32 * Each call to this function must have a UNIQUE and uniformly RANDOM seed32
* that must that must NOT BE REUSED in subsequent calls to this function and * that must that must NOT BE REUSED in subsequent calls to this function and
@ -196,25 +201,109 @@ SECP256K1_API int secp256k1_frost_share_parse(
* Returns: 0 if the arguments are invalid, 1 otherwise * Returns: 0 if the arguments are invalid, 1 otherwise
* Args: ctx: pointer to a context object * Args: ctx: pointer to a context object
* Out: shares: pointer to the key shares * Out: shares: pointer to the key shares
* pubshares: pointer to the public verification shares * vss_commitment: pointer to the VSS commitment
* pk: pointer to the x-only public key * pok64: pointer to the proof of knowledge
* In: seed32: a 32-byte random seed as explained above. Must be * In: seed32: 32-byte random seed as explained above. Must be
* unique to this call to * unique to this call to secp256k1_frost_shares_gen
* secp256k1_frost_shares_trusted_gen and must be * and must be uniformly random.
* uniformly random.
* threshold: the minimum number of signers required to produce a * threshold: the minimum number of signers required to produce a
* signature * signature
* n_participants: the total number of participants * n_participants: the total number of participants
* ids33: array of 33-byte participant IDs
*/ */
SECP256K1_API int secp256k1_frost_shares_trusted_gen( SECP256K1_API int secp256k1_frost_shares_gen(
const secp256k1_context *ctx, const secp256k1_context *ctx,
secp256k1_frost_share *shares, secp256k1_frost_share *shares,
secp256k1_pubkey *pubshares, secp256k1_pubkey *vss_commitment,
secp256k1_xonly_pubkey *pk, unsigned char *pok64,
const unsigned char *seed32, const unsigned char *seed32,
size_t threshold, size_t threshold,
size_t n_participants,
const unsigned char * const* ids33
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(8);
/** Aggregates shares
*
* As part of the key generation protocol, each participant receives a share
* from each participant, including a share they "receive" from themselves.
* This function verifies those shares against their VSS commitments,
* aggregates the shares, and then aggregates the commitments to each
* participant's first polynomial coefficient to derive the aggregate public
* key.
*
* If this function returns an error, `secp256k1_frost_share_verify` can be
* called on each share to determine which participants submitted faulty
* shares.
*
* Returns: 0 if the arguments are invalid, 1 otherwise (which does NOT mean
* the resulting signature verifies).
* Args: ctx: pointer to a context object
* Out: agg_share: the aggregated share
* agg_pk: the aggregated x-only public key
* In: shares: all key generation shares for the partcipant's index
* vss_commitments: coefficient commitments of all participants ordered by
* the x-only pubkeys of the participants
* n_shares: the total number of shares
* threshold: the minimum number of shares required to produce a
* signature
* id33: the 33-byte ID of the participant whose shares are being
* aggregated
*/
SECP256K1_API int secp256k1_frost_share_agg(
const secp256k1_context *ctx,
secp256k1_frost_share *agg_share,
secp256k1_xonly_pubkey *agg_pk,
const secp256k1_frost_share * const *shares,
const secp256k1_pubkey * const *vss_commitments,
size_t n_shares,
size_t threshold,
const unsigned char *id33
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(8);
/** Verifies a share received during a key generation session
*
* The signature is verified against the VSS commitment received with the
* share. This is only useful for purposes of determining which share(s) are
* invalid if share_agg returns an error.
*
* Returns: 0 if the arguments are invalid or the share does not verify, 1
* otherwise
* Args ctx: pointer to a context object
* In: threshold: the minimum number of signers required to produce a
* signature
* id33: the 33-byte participant ID of the share recipient
* share: pointer to a key generation share
* vss_commitment: the VSS commitment associated with the share
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_frost_share_verify(
const secp256k1_context *ctx,
size_t threshold,
const unsigned char *id33,
const secp256k1_frost_share *share,
const secp256k1_pubkey * const *vss_commitment
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5);
/** Computes a public verification share used for verifying partial signatures
*
* Returns: 0 if the arguments are invalid, 1 otherwise
* Args: ctx: pointer to a context object
* Out: pubshare: pointer to a struct to store the public verification
* share
* In: threshold: the minimum number of signers required to produce a
* signature
* id33: the 33-byte participant ID of the participant whose
* partial signature will be verified with the pubshare
* vss_commitments: coefficient commitments of all participants
* n_participants: the total number of participants
*/
SECP256K1_API int secp256k1_frost_compute_pubshare(
const secp256k1_context *ctx,
secp256k1_pubkey *pubshare,
size_t threshold,
const unsigned char *id33,
const secp256k1_pubkey * const *vss_commitments,
size_t n_participants size_t n_participants
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5);
/** Obtain the aggregate public key from a FROST x-only aggregate public key. /** Obtain the aggregate public key from a FROST x-only aggregate public key.
* *
@ -226,7 +315,7 @@ SECP256K1_API int secp256k1_frost_shares_trusted_gen(
* Args: ctx: pointer to a context object * Args: ctx: pointer to a context object
* Out: ec_agg_pk: the FROST-aggregated public key. * Out: ec_agg_pk: the FROST-aggregated public key.
* In: xonly_agg_pk: the aggregated x-only public key that is the output of * In: xonly_agg_pk: the aggregated x-only public key that is the output of
* `secp256k1_frost_shares_gen` * `secp256k1_frost_share_agg`
*/ */
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_frost_pubkey_get( SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_frost_pubkey_get(
const secp256k1_context *ctx, const secp256k1_context *ctx,
@ -241,7 +330,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_frost_pubkey_get(
* Out: tweak_cache: pointer to a frost_tweak_cache struct that is required * Out: tweak_cache: pointer to a frost_tweak_cache struct that is required
* for key tweaking * for key tweaking
* In: agg_pk: the aggregated x-only public key that is the output of * In: agg_pk: the aggregated x-only public key that is the output of
* `secp256k1_frost_shares_gen` * `secp256k1_frost_share_agg`
*/ */
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_frost_pubkey_tweak( SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_frost_pubkey_tweak(
const secp256k1_context *ctx, const secp256k1_context *ctx,
@ -257,7 +346,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_frost_pubkey_tweak(
* the following pseudocode buf and buf2 have identical contents (absent * the following pseudocode buf and buf2 have identical contents (absent
* earlier failures). * earlier failures).
* *
* secp256k1_frost_shares_gen(..., xonly_agg_pk, ...) * secp256k1_frost_share_agg(..., xonly_agg_pk, ...)
* secp256k1_frost_pubkey_tweak(..., tweak_cache, xonly_agg_pk) * secp256k1_frost_pubkey_tweak(..., tweak_cache, xonly_agg_pk)
* secp256k1_frost_pubkey_ec_tweak_add(..., output_pk, tweak_cache, tweak32) * secp256k1_frost_pubkey_ec_tweak_add(..., output_pk, tweak_cache, tweak32)
* secp256k1_ec_pubkey_serialize(..., buf, output_pk) * secp256k1_ec_pubkey_serialize(..., buf, output_pk)
@ -300,7 +389,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_frost_pubkey_ec_tweak_a
* the following pseudocode xonly_pubkey_tweak_add_check (absent earlier * the following pseudocode xonly_pubkey_tweak_add_check (absent earlier
* failures) returns 1. * failures) returns 1.
* *
* secp256k1_frost_shares_gen(..., agg_pk, ...) * secp256k1_frost_share_agg(..., agg_pk, ...)
* secp256k1_frost_pubkey_tweak(..., tweak_cache, agg_pk) * secp256k1_frost_pubkey_tweak(..., tweak_cache, agg_pk)
* secp256k1_frost_pubkey_xonly_tweak_add(..., output_pk, tweak_cache, tweak32) * secp256k1_frost_pubkey_xonly_tweak_add(..., output_pk, tweak_cache, tweak32)
* secp256k1_xonly_pubkey_serialize(..., buf, output_pk) * secp256k1_xonly_pubkey_serialize(..., buf, output_pk)
@ -387,10 +476,10 @@ SECP256K1_API int secp256k1_frost_nonce_gen(
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
/** Takes the public nonces of all signers and computes a session that is /** Takes the public nonces of all signers and computes a session that is
* required for signing and verification of partial signatures. The participant * required for signing and verification of partial signatures. The participant
* IDs can be sorted before combining, but the corresponding pubnonces must be * IDs can be sorted before combining, but the corresponding pubnonces must be
* resorted as well. All signers must use the same sorting of pubnonces, * resorted as well. All signers must use the same sorting of pubnonces,
* otherwise signing will fail. * otherwise signing will fail.
* *
* Returns: 0 if the arguments are invalid or if some signer sent invalid * Returns: 0 if the arguments are invalid or if some signer sent invalid
* pubnonces, 1 otherwise * pubnonces, 1 otherwise
@ -401,9 +490,9 @@ SECP256K1_API int secp256k1_frost_nonce_gen(
* greater than 0. * greater than 0.
* msg32: the 32-byte message to sign * msg32: the 32-byte message to sign
* agg_pk: the FROST-aggregated public key * agg_pk: the FROST-aggregated public key
* my_id: the ID of the participant who will use the session for * myd_id33: the 33-byte ID of the participant who will use the
* signing * session for signing
* ids: array of the IDs of the signers * ids33: array of the 33-byte participant IDs of the signers
* tweak_cache: pointer to frost_tweak_cache struct (can be NULL) * tweak_cache: pointer to frost_tweak_cache struct (can be NULL)
* adaptor: optional pointer to an adaptor point encoded as a * adaptor: optional pointer to an adaptor point encoded as a
* public key if this signing session is part of an * public key if this signing session is part of an
@ -416,11 +505,11 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_frost_nonce_process(
size_t n_pubnonces, size_t n_pubnonces,
const unsigned char *msg32, const unsigned char *msg32,
const secp256k1_xonly_pubkey *agg_pk, const secp256k1_xonly_pubkey *agg_pk,
size_t my_id, const unsigned char *my_id33,
const size_t *ids, const unsigned char * const* ids33,
const secp256k1_frost_tweak_cache *tweak_cache, const secp256k1_frost_tweak_cache *tweak_cache,
const secp256k1_pubkey *adaptor const secp256k1_pubkey *adaptor
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6) SECP256K1_ARG_NONNULL(8); ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6) SECP256K1_ARG_NONNULL(7) SECP256K1_ARG_NONNULL(8);
/** Produces a partial signature /** Produces a partial signature
* *
@ -458,7 +547,7 @@ SECP256K1_API int secp256k1_frost_partial_sign(
* 1. The `tweak_cache` argument is identical to the one used to create the * 1. The `tweak_cache` argument is identical to the one used to create the
* `session` with `frost_nonce_process`. * `session` with `frost_nonce_process`.
* 2. The `pubshare` argument must be the output of * 2. The `pubshare` argument must be the output of
* `secp256k1_frost_shares_trusted_gen` for the signer's 'pk'. * `secp256k1_frost_compute_pubshare` for the signer's ID.
* 3. The `pubnonce` argument must be identical to the one sent by the * 3. The `pubnonce` argument must be identical to the one sent by the
* signer and used to create the `session` with `frost_nonce_process`. * signer and used to create the `session` with `frost_nonce_process`.
* *
@ -472,7 +561,7 @@ SECP256K1_API int secp256k1_frost_partial_sign(
* pubnonce: public nonce of the signer in the signing session * pubnonce: public nonce of the signer in the signing session
* pubshare: public verification share of the signer in the signing * pubshare: public verification share of the signer in the signing
* session that is the output of * session that is the output of
* `secp256k1_frost_shares_trusted_gen` * `secp256k1_frost_compute_pubshare`
* session: pointer to the session that was created with * session: pointer to the session that was created with
* `frost_nonce_process` * `frost_nonce_process`
* tweak_cache: pointer to frost_tweak_cache struct (can be NULL) * tweak_cache: pointer to frost_tweak_cache struct (can be NULL)

View File

@ -356,8 +356,10 @@ static void run_tests(secp256k1_context *ctx, unsigned char *key) {
#ifdef ENABLE_MODULE_FROST #ifdef ENABLE_MODULE_FROST
{ {
secp256k1_xonly_pubkey pk; secp256k1_pubkey pk[2];
secp256k1_xonly_pubkey agg_pk;
unsigned char session_id[32]; unsigned char session_id[32];
unsigned char seed[2][32];
secp256k1_frost_secnonce secnonce[2]; secp256k1_frost_secnonce secnonce[2];
secp256k1_frost_pubnonce pubnonce[2]; secp256k1_frost_pubnonce pubnonce[2];
const secp256k1_frost_pubnonce *pubnonce_ptr[2]; const secp256k1_frost_pubnonce *pubnonce_ptr[2];
@ -370,48 +372,86 @@ static void run_tests(secp256k1_context *ctx, unsigned char *key) {
secp256k1_pubkey adaptor; secp256k1_pubkey adaptor;
unsigned char pre_sig[64]; unsigned char pre_sig[64];
int nonce_parity; int nonce_parity;
secp256k1_frost_share shares[2]; secp256k1_frost_share shares[2][2];
secp256k1_pubkey pubshares[2]; const secp256k1_frost_share *share_ptr[2];
size_t ids[2]; secp256k1_frost_share agg_share;
const secp256k1_pubkey *vss_ptr[2];
unsigned char pok[2][64];
secp256k1_pubkey vss_commitment[2][2];
unsigned char key2[32];
secp256k1_keypair keypair2;
unsigned char id[2][33];
const unsigned char *id_ptr[2];
size_t size = 33;
id_ptr[0] = id[0];
id_ptr[1] = id[1];
pubnonce_ptr[0] = &pubnonce[0]; pubnonce_ptr[0] = &pubnonce[0];
pubnonce_ptr[1] = &pubnonce[1]; pubnonce_ptr[1] = &pubnonce[1];
SECP256K1_CHECKMEM_DEFINE(key, 32); SECP256K1_CHECKMEM_DEFINE(key, 32);
memcpy(seed[0], key, 32);
seed[0][0] = seed[0][0] + 1;
memcpy(seed[0], key, 32);
seed[1][0] = seed[1][0] + 2;
memcpy(extra_input, key, sizeof(extra_input)); memcpy(extra_input, key, sizeof(extra_input));
extra_input[0] = extra_input[0] + 1; extra_input[0] = extra_input[0] + 3;
memcpy(sec_adaptor, key, sizeof(sec_adaptor)); memcpy(sec_adaptor, key, sizeof(sec_adaptor));
sec_adaptor[0] = extra_input[0] + 2; sec_adaptor[0] = extra_input[0] + 4;
memcpy(key2, key, sizeof(key2));
key2[0] = key2[0] + 5;
memcpy(session_id, key, sizeof(session_id)); memcpy(session_id, key, sizeof(session_id));
session_id[0] = session_id[0] + 3; session_id[0] = session_id[0] + 6;
partial_sig_ptr[0] = &partial_sig; partial_sig_ptr[0] = &partial_sig;
ids[0] = 1; share_ptr[0] = &shares[0][0];
ids[1] = 2; share_ptr[1] = &shares[1][0];
vss_ptr[0] = vss_commitment[0];
vss_ptr[1] = vss_commitment[1];
CHECK(secp256k1_keypair_create(ctx, &keypair, key));
CHECK(secp256k1_keypair_create(ctx, &keypair2, key2));
CHECK(secp256k1_keypair_pub(ctx, &pk[0], &keypair));
CHECK(secp256k1_keypair_pub(ctx, &pk[1], &keypair2));
CHECK(secp256k1_ec_pubkey_serialize(ctx, id[0], &size, &pk[0], SECP256K1_EC_COMPRESSED));
CHECK(secp256k1_ec_pubkey_serialize(ctx, id[1], &size, &pk[1], SECP256K1_EC_COMPRESSED));
/* shares_gen */ /* shares_gen */
SECP256K1_CHECKMEM_UNDEFINE(key, 32); SECP256K1_CHECKMEM_UNDEFINE(key, 32);
ret = secp256k1_frost_shares_trusted_gen(ctx, shares, pubshares, &pk, key, 2, 2); SECP256K1_CHECKMEM_UNDEFINE(key2, 32);
SECP256K1_CHECKMEM_UNDEFINE(seed[0], 32);
SECP256K1_CHECKMEM_UNDEFINE(seed[1], 32);
ret = secp256k1_frost_shares_gen(ctx, shares[0], vss_commitment[0], pok[0], seed[0], 2, 2, id_ptr);
SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
CHECK(ret == 1);
ret = secp256k1_frost_shares_gen(ctx, shares[1], vss_commitment[1], pok[1], seed[1], 2, 2, id_ptr);
SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
CHECK(ret == 1);
/* share_agg */
SECP256K1_CHECKMEM_DEFINE(&vss_commitment[0][0], sizeof(secp256k1_pubkey));
SECP256K1_CHECKMEM_DEFINE(&vss_commitment[0][1], sizeof(secp256k1_pubkey));
SECP256K1_CHECKMEM_DEFINE(&vss_commitment[1][0], sizeof(secp256k1_pubkey));
SECP256K1_CHECKMEM_DEFINE(&vss_commitment[1][1], sizeof(secp256k1_pubkey));
SECP256K1_CHECKMEM_DEFINE(pok[0], 64);
ret = secp256k1_frost_share_agg(ctx, &agg_share, &agg_pk, share_ptr, vss_ptr, 2, 2, id_ptr[0]);
SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret)); SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
CHECK(ret == 1); CHECK(ret == 1);
SECP256K1_CHECKMEM_UNDEFINE(&shares[0], sizeof(shares[0]));
SECP256K1_CHECKMEM_UNDEFINE(&shares[1], sizeof(shares[1]));
/* nonce_gen */ /* nonce_gen */
SECP256K1_CHECKMEM_UNDEFINE(session_id, sizeof(session_id)); SECP256K1_CHECKMEM_UNDEFINE(session_id, sizeof(session_id));
CHECK(secp256k1_ec_pubkey_create(ctx, &adaptor, sec_adaptor)); CHECK(secp256k1_ec_pubkey_create(ctx, &adaptor, sec_adaptor));
SECP256K1_CHECKMEM_UNDEFINE(extra_input, sizeof(extra_input)); SECP256K1_CHECKMEM_UNDEFINE(extra_input, sizeof(extra_input));
SECP256K1_CHECKMEM_UNDEFINE(sec_adaptor, sizeof(sec_adaptor)); SECP256K1_CHECKMEM_UNDEFINE(sec_adaptor, sizeof(sec_adaptor));
CHECK(secp256k1_frost_pubkey_tweak(ctx, &cache, &pk) == 1); CHECK(secp256k1_frost_pubkey_tweak(ctx, &cache, &agg_pk) == 1);
ret = secp256k1_frost_nonce_gen(ctx, &secnonce[0], &pubnonce[0], session_id, &shares[0], msg, &pk, extra_input); ret = secp256k1_frost_nonce_gen(ctx, &secnonce[0], &pubnonce[0], session_id, &agg_share, msg, &agg_pk, extra_input);
SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret)); SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
CHECK(ret == 1); CHECK(ret == 1);
ret = secp256k1_frost_nonce_gen(ctx, &secnonce[1], &pubnonce[1], session_id, &shares[1], msg, &pk, extra_input); ret = secp256k1_frost_nonce_gen(ctx, &secnonce[1], &pubnonce[1], session_id, &agg_share, msg, &agg_pk, extra_input);
SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret)); SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
CHECK(ret == 1); CHECK(ret == 1);
/* partial_sign */ /* partial_sign */
CHECK(secp256k1_frost_nonce_process(ctx, &session, pubnonce_ptr, 2, msg, &pk, 1, ids, &cache, &adaptor) == 1); CHECK(secp256k1_frost_nonce_process(ctx, &session, pubnonce_ptr, 2, msg, &agg_pk, id_ptr[0], id_ptr, &cache, &adaptor) == 1);
ret = secp256k1_keypair_create(ctx, &keypair, key); ret = secp256k1_keypair_create(ctx, &keypair, key);
SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret)); SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
CHECK(ret == 1); CHECK(ret == 1);
ret = secp256k1_frost_partial_sign(ctx, &partial_sig, &secnonce[0], &shares[0], &session, &cache); ret = secp256k1_frost_partial_sign(ctx, &partial_sig, &secnonce[0], &agg_share, &session, &cache);
SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret)); SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
CHECK(ret == 1); CHECK(ret == 1);
/* adapt */ /* adapt */
@ -425,7 +465,7 @@ static void run_tests(secp256k1_context *ctx, unsigned char *key) {
/* extract_adaptor */ /* extract_adaptor */
ret = secp256k1_frost_extract_adaptor(ctx, sec_adaptor, sig, pre_sig, nonce_parity); ret = secp256k1_frost_extract_adaptor(ctx, sec_adaptor, sig, pre_sig, nonce_parity);
SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret)); SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
CHECK(ret == 1); CHECK(ret == 1);
} }
#endif #endif
} }

View File

@ -1,5 +1,5 @@
/*********************************************************************** /***********************************************************************
* Copyright (c) 2022-2023 Jesse Posner * * Copyright (c) 2022-2024 Jesse Posner *
* Distributed under the MIT software license, see the accompanying * * Distributed under the MIT software license, see the accompanying *
* file COPYING or https://www.opensource.org/licenses/mit-license.php.* * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
***********************************************************************/ ***********************************************************************/

View File

@ -9,33 +9,50 @@ The following sections contain additional notes on the API of the frost module
Users of the frost module must take great care to make sure of the following: Users of the frost module must take great care to make sure of the following:
1. The dealer establishes a secure communications channel with each participant 1. Each participant exchanges public keys for identification and authentication
and uses that channel to transmit shares during key generation. purposes. Partipants must provide the same public key to each other
2. A unique set of coefficients per key generation session is generated in participant.
`secp256k1_frost_share_gen`. See the corresponding comment in 2. Each participant establishes a secure communications channel with each other
participant and uses that channel to transmit shares and commitments during
key generation.
3. A unique set of coefficients per key generation session is generated in
`secp256k1_frost_shares_gen`. See the corresponding comment in
`include/secp256k1_frost.h` for how to ensure that. `include/secp256k1_frost.h` for how to ensure that.
3. The `pubnonces` provided to `secp256k1_frost_nonce_process` are sorted by 4. The `pubnonces` provided to `secp256k1_frost_nonce_process` are sorted by
the corresponding lexicographic ordering of the x-only pubkey of each the corresponding lexicographic ordering of the x-only pubkey of each
participant, and the `pubkeys` provided to `secp256k1_frost_nonce_process` participant, and the `ids33` provided to `secp256k1_frost_nonce_process`
are sorted lexicographically. are sorted lexicographically.
4. A unique nonce per signing session is generated in 5. A unique nonce per signing session is generated in `secp256k1_frost_nonce_gen`.
`secp256k1_frost_nonce_gen`. See the corresponding comment in See the corresponding comment in `include/secp256k1_frost.h` for how to ensure that.
`include/secp256k1_frost.h` for how to ensure that. 6. The `secp256k1_frost_secnonce` structure is never copied or serialized.
5. The `secp256k1_frost_secnonce` structure is never copied or serialized. See See also the comment on `secp256k1_frost_secnonce` in `include/secp256k1_frost.h`.
also the comment on `secp256k1_frost_secnonce` in 7. Opaque data structures are never written to or read from directly.
`include/secp256k1_frost.h`. Instead, only the provided accessor functions are used.
6. Opaque data structures are never written to or read from directly. Instead, 8. If adaptor signatures are used, all partial signatures are verified.
only the provided accessor functions are used.
7. If adaptor signatures are used, all partial signatures are verified.
# Key Generation # Key Generation
1. A trusted dealer generates shares with `secp256k1_frost_shares_trusted_gen` 1. Generate a keypair with `secp256k1_keypair_create` and obtain the x-only
and distributes a share and the public key to each participant using a public key with `secp256k1_keypair_xonly_pub`, and distribute it to each
secure channel. other participant to be used as an authentication key and identifier.
2. Generate a VSS commitment, proof-of-knowledge, and shares with
`secp256k1_frost_shares_gen`. The VSS commitment and proof-of-knowledge must
be broadcast to all participants. Assign each participant a share according
to the order of `ids33` and distribute the shares to the participants using
a secure channel.
3. After receiving a share and commitment set from each participant, call
`secp256k1_frost_share_agg` to compute the aggregate share, group public
key, and VSS hash. If this function returns an error,
`secp256k1_frost_share_verify` is called on each share to determine which
participants submitted faulty shares.
4. Optionally compute the public verification share by calling
`secp256k1_frost_compute_pubshare` with the x-only public key of each
participant. This share is required by `secp256k1_frost_partial_sig_verify`
to verify partial signatures generated by `secp256k1_frost_partial_sign`.
# Tweaking # Tweaking
A (Taproot) tweak can be added to the resulting public key with A (Taproot) tweak can be added to the resulting public key with
`secp256k1_xonly_pubkey_tweak_add`, after converting it to an xonly pubkey if `secp256k1_xonly_pubkey_tweak_add`, after converting it to an xonly pubkey if
necessary with `secp256k1_xonly_pubkey_from_pubkey`. necessary with `secp256k1_xonly_pubkey_from_pubkey`.

View File

@ -1,5 +1,5 @@
/********************************************************************** /**********************************************************************
* Copyright (c) 2021-2023 Jesse Posner * * Copyright (c) 2021-2024 Jesse Posner *
* Distributed under the MIT software license, see the accompanying * * Distributed under the MIT software license, see the accompanying *
* file COPYING or http://www.opensource.org/licenses/mit-license.php.* * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/ **********************************************************************/
@ -23,4 +23,6 @@ static int secp256k1_tweak_cache_load(const secp256k1_context* ctx, secp256k1_tw
static int secp256k1_frost_share_load(const secp256k1_context* ctx, secp256k1_scalar *s, const secp256k1_frost_share* share); static int secp256k1_frost_share_load(const secp256k1_context* ctx, secp256k1_scalar *s, const secp256k1_frost_share* share);
static int secp256k1_frost_compute_indexhash(secp256k1_scalar *indexhash, const unsigned char *id33);
#endif #endif

View File

@ -1,5 +1,5 @@
/********************************************************************** /**********************************************************************
* Copyright (c) 2021-2023 Jesse Posner * * Copyright (c) 2021-2024 Jesse Posner *
* Distributed under the MIT software license, see the accompanying * * Distributed under the MIT software license, see the accompanying *
* file COPYING or http://www.opensource.org/licenses/mit-license.php.* * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/ **********************************************************************/
@ -53,6 +53,24 @@ static int secp256k1_tweak_cache_load(const secp256k1_context* ctx, secp256k1_tw
return 1; return 1;
} }
/* Computes indexhash = tagged_hash(pk) */
static int secp256k1_frost_compute_indexhash(secp256k1_scalar *indexhash, const unsigned char *id33) {
secp256k1_sha256 sha;
unsigned char buf[32];
secp256k1_sha256_initialize_tagged(&sha, (unsigned char*)"FROST/index", sizeof("FROST/index") - 1);
secp256k1_sha256_write(&sha, id33, 33);
secp256k1_sha256_finalize(&sha, buf);
secp256k1_scalar_set_b32(indexhash, buf, NULL);
/* The x-coordinate must not be zero (see
* draft-irtf-cfrg-frost-08#section-4.2.2) */
if (secp256k1_scalar_is_zero(indexhash)) {
return 0;
}
return 1;
}
static const unsigned char secp256k1_frost_share_magic[4] = { 0xa1, 0x6a, 0x42, 0x03 }; static const unsigned char secp256k1_frost_share_magic[4] = { 0xa1, 0x6a, 0x42, 0x03 };
static void secp256k1_frost_share_save(secp256k1_frost_share* share, secp256k1_scalar *s) { static void secp256k1_frost_share_save(secp256k1_frost_share* share, secp256k1_scalar *s) {
@ -63,8 +81,6 @@ static void secp256k1_frost_share_save(secp256k1_frost_share* share, secp256k1_s
static int secp256k1_frost_share_load(const secp256k1_context* ctx, secp256k1_scalar *s, const secp256k1_frost_share* share) { static int secp256k1_frost_share_load(const secp256k1_context* ctx, secp256k1_scalar *s, const secp256k1_frost_share* share) {
int overflow; int overflow;
/* The magic is non-secret so it can be declassified to allow branching. */
secp256k1_declassify(ctx, &share->data[0], 4);
ARG_CHECK(secp256k1_memcmp_var(&share->data[0], secp256k1_frost_share_magic, 4) == 0); ARG_CHECK(secp256k1_memcmp_var(&share->data[0], secp256k1_frost_share_magic, 4) == 0);
secp256k1_scalar_set_b32(s, &share->data[4], &overflow); secp256k1_scalar_set_b32(s, &share->data[4], &overflow);
/* Parsed shares cannot overflow */ /* Parsed shares cannot overflow */
@ -95,14 +111,82 @@ int secp256k1_frost_share_parse(const secp256k1_context* ctx, secp256k1_frost_sh
return 1; return 1;
} }
int secp256k1_frost_shares_trusted_gen(const secp256k1_context *ctx, secp256k1_frost_share *shares, secp256k1_pubkey *pubshares, secp256k1_xonly_pubkey *pk, const unsigned char *seed32, size_t threshold, size_t n_participants) { static void secp256k1_frost_derive_coeff(secp256k1_scalar *coeff, const unsigned char *polygen32, size_t i) {
secp256k1_sha256 sha; secp256k1_sha256 sha;
unsigned char buf[32];
secp256k1_sha256_initialize_tagged(&sha, (unsigned char*)"FROST/coeffgen", sizeof("FROST/coeffgen") - 1);
secp256k1_sha256_write(&sha, polygen32, 32);
secp256k1_write_be64(&buf[0], i);
secp256k1_sha256_write(&sha, buf, 8);
secp256k1_sha256_finalize(&sha, buf);
secp256k1_scalar_set_b32(coeff, buf, NULL);
}
static int secp256k1_frost_vss_gen(const secp256k1_context *ctx, secp256k1_pubkey *vss_commitment, unsigned char *pok64, const unsigned char *polygen32, size_t threshold) {
secp256k1_sha256 sha;
unsigned char buf[32];
secp256k1_keypair keypair;
secp256k1_gej rj; secp256k1_gej rj;
secp256k1_ge rp; secp256k1_ge rp;
unsigned char polygen[32]; size_t i;
size_t i, j; int ret = 1;
for (i = 0; i < threshold; i++) {
secp256k1_scalar coeff_i;
secp256k1_frost_derive_coeff(&coeff_i, polygen32, i);
/* Compute proof-of-knowledge for constant term */
if (i == threshold - 1) {
secp256k1_scalar_get_b32(buf, &coeff_i);
ret &= secp256k1_keypair_create(ctx, &keypair, buf);
secp256k1_sha256_initialize_tagged(&sha, (unsigned char*)"FROST/KeygenPoK", sizeof("FROST/KeygenPoK") - 1);
secp256k1_sha256_finalize(&sha, buf);
ret &= secp256k1_schnorrsig_sign32(ctx, pok64, buf, &keypair, NULL);
}
/* Compute commitment to each coefficient */
secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &rj, &coeff_i);
secp256k1_ge_set_gej(&rp, &rj);
secp256k1_pubkey_save(&vss_commitment[threshold - i - 1], &rp);
}
return ret;
}
static int secp256k1_frost_share_gen(secp256k1_frost_share *share, const unsigned char *polygen32, size_t threshold, const unsigned char *id33) {
secp256k1_scalar idx;
secp256k1_scalar share_i;
size_t i;
int ret = 1;
/* Derive share */
/* See draft-irtf-cfrg-frost-08#appendix-C.1 */
secp256k1_scalar_set_int(&share_i, 0);
if (!secp256k1_frost_compute_indexhash(&idx, id33)) {
return 0;
}
for (i = 0; i < threshold; i++) {
secp256k1_scalar coeff_i;
secp256k1_frost_derive_coeff(&coeff_i, polygen32, i);
/* Horner's method to evaluate polynomial to derive shares */
secp256k1_scalar_add(&share_i, &share_i, &coeff_i);
if (i < threshold - 1) {
secp256k1_scalar_mul(&share_i, &share_i, &idx);
}
}
secp256k1_frost_share_save(share, &share_i);
return ret;
}
int secp256k1_frost_shares_gen(const secp256k1_context *ctx, secp256k1_frost_share *shares, secp256k1_pubkey *vss_commitment, unsigned char *pok64, const unsigned char *seed32, size_t threshold, size_t n_participants, const unsigned char * const* ids33) {
secp256k1_sha256 sha;
unsigned char polygen[32];
size_t i;
int ret = 1; int ret = 1;
int pk_parity = 0;
VERIFY_CHECK(ctx != NULL); VERIFY_CHECK(ctx != NULL);
ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
@ -110,72 +194,257 @@ int secp256k1_frost_shares_trusted_gen(const secp256k1_context *ctx, secp256k1_f
for (i = 0; i < n_participants; i++) { for (i = 0; i < n_participants; i++) {
memset(&shares[i], 0, sizeof(shares[i])); memset(&shares[i], 0, sizeof(shares[i]));
} }
ARG_CHECK(pubshares != NULL); ARG_CHECK(vss_commitment != NULL);
ARG_CHECK(pk != NULL); ARG_CHECK(pok64 != NULL);
ARG_CHECK(seed32 != NULL); ARG_CHECK(seed32 != NULL);
ARG_CHECK(ids33 != NULL);
ARG_CHECK(threshold > 1); ARG_CHECK(threshold > 1);
ARG_CHECK(n_participants >= threshold); ARG_CHECK(n_participants >= threshold);
/* Commit to threshold, n_participants, and seed */ /* Commit to all inputs */
secp256k1_sha256_initialize_tagged(&sha, (unsigned char*)"FROST/trusted-shares-polygen", sizeof("FROST/trusted-shares-polygen") - 1); secp256k1_sha256_initialize(&sha);
secp256k1_sha256_write(&sha, seed32, 32); secp256k1_sha256_write(&sha, seed32, 32);
secp256k1_write_be64(&polygen[0], threshold); secp256k1_write_be64(&polygen[0], threshold);
secp256k1_write_be64(&polygen[8], n_participants); secp256k1_write_be64(&polygen[8], n_participants);
secp256k1_sha256_write(&sha, polygen, 16); secp256k1_sha256_write(&sha, polygen, 16);
for (i = 0; i < n_participants; i++) {
secp256k1_sha256_write(&sha, ids33[i], 33);
}
secp256k1_sha256_finalize(&sha, polygen); secp256k1_sha256_finalize(&sha, polygen);
/* Derive shares */ ret &= secp256k1_frost_vss_gen(ctx, vss_commitment, pok64, polygen, threshold);
/* See draft-irtf-cfrg-frost-08#appendix-C.1 */
for (i = 0; i < n_participants; i++) { for (i = 0; i < n_participants; i++) {
secp256k1_scalar share_i, idx; ret &= secp256k1_frost_share_gen(&shares[i], polygen, threshold, ids33[i]);
secp256k1_scalar_set_int(&share_i, 0);
for (j = 0; j < threshold; j++) {
unsigned char buf[32];
secp256k1_scalar coeff_i;
secp256k1_sha256_initialize_tagged(&sha, (unsigned char*)"FROST/trusted-shares-coeffgen", sizeof("FROST/trusted-shares-coeffgen") - 1);
secp256k1_sha256_write(&sha, polygen, 32);
secp256k1_write_be64(&buf[0], j);
secp256k1_sha256_write(&sha, buf, 8);
secp256k1_sha256_finalize(&sha, buf);
secp256k1_scalar_set_b32(&coeff_i, buf, NULL);
/* Horner's method to evaluate polynomial to derive shares */
secp256k1_scalar_add(&share_i, &share_i, &coeff_i);
if (j < threshold - 1) {
secp256k1_scalar_set_int(&idx, i + 1);
secp256k1_scalar_mul(&share_i, &share_i, &idx);
}
/* Compute x-only public key for constant term */
if (i == 0 && j == threshold - 1) {
/* Compute commitment to constant term */
secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &rj, &coeff_i);
secp256k1_ge_set_gej(&rp, &rj);
/* The commitment is non-secret so it can be declassified to
* allow branching. */
secp256k1_declassify(ctx, &rp, sizeof(rp));
secp256k1_fe_normalize_var(&rp.y);
pk_parity = secp256k1_extrakeys_ge_even_y(&rp);
secp256k1_xonly_pubkey_save(pk, &rp);
}
}
if (pk_parity == 1) {
secp256k1_scalar_negate(&share_i, &share_i);
}
secp256k1_frost_share_save(&shares[i], &share_i);
/* Compute pubshare */
secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &rj, &share_i);
secp256k1_ge_set_gej(&rp, &rj);
secp256k1_pubkey_save(&pubshares[i], &rp);
} }
return ret; return ret;
} }
typedef struct {
const secp256k1_context *ctx;
secp256k1_scalar idx;
secp256k1_scalar idxn;
const secp256k1_pubkey * const* vss_commitment;
} secp256k1_frost_verify_share_ecmult_data;
typedef struct {
const secp256k1_context *ctx;
secp256k1_scalar idx;
secp256k1_scalar idxn;
const secp256k1_pubkey * const* vss_commitments;
size_t threshold;
} secp256k1_frost_compute_pubshare_ecmult_data;
typedef struct {
const secp256k1_context *ctx;
const secp256k1_pubkey * const* pks;
size_t threshold;
} secp256k1_frost_pubkey_combine_ecmult_data;
static int secp256k1_frost_verify_share_ecmult_callback(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *data) {
secp256k1_frost_verify_share_ecmult_data *ctx = (secp256k1_frost_verify_share_ecmult_data *) data;
if (!secp256k1_pubkey_load(ctx->ctx, pt, *(ctx->vss_commitment)+idx)) {
return 0;
}
*sc = ctx->idxn;
secp256k1_scalar_mul(&ctx->idxn, &ctx->idxn, &ctx->idx);
return 1;
}
static int secp256k1_frost_compute_pubshare_ecmult_callback(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *data) {
secp256k1_frost_compute_pubshare_ecmult_data *ctx = (secp256k1_frost_compute_pubshare_ecmult_data *) data;
if (!secp256k1_pubkey_load(ctx->ctx, pt, &ctx->vss_commitments[idx/ctx->threshold][idx % ctx->threshold])) {
return 0;
}
if (idx != 0 && idx % ctx->threshold == 0) {
secp256k1_scalar_set_int(&ctx->idxn, 1);
}
*sc = ctx->idxn;
secp256k1_scalar_mul(&ctx->idxn, &ctx->idxn, &ctx->idx);
return 1;
}
static int secp256k1_frost_pubkey_combine_callback(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *data) {
secp256k1_frost_pubkey_combine_ecmult_data *ctx = (secp256k1_frost_pubkey_combine_ecmult_data *) data;
secp256k1_scalar_set_int(sc, 1);
/* the public key is the first index of each set of coefficients */
return secp256k1_pubkey_load(ctx->ctx, pt, &ctx->pks[idx][0]);
}
/* See draft-irtf-cfrg-frost-08#appendix-C.2 */
static int secp256k1_frost_vss_verify_internal(const secp256k1_context* ctx, size_t threshold, const unsigned char *id33, const secp256k1_scalar *share, const secp256k1_pubkey * const* vss_commitment) {
secp256k1_scalar share_neg;
secp256k1_gej tmpj, snj;
secp256k1_ge sng;
secp256k1_frost_verify_share_ecmult_data verify_share_ecmult_data;
ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
/* Use an EC multi-multiplication to verify the following equation:
* 0 = - share_i*G + idx^0*vss_commitment[0]
* + ...
* + idx^(threshold - 1)*vss_commitment[threshold - 1]*/
verify_share_ecmult_data.ctx = ctx;
verify_share_ecmult_data.vss_commitment = vss_commitment;
/* Evaluate the public polynomial at the idx */
if (!secp256k1_frost_compute_indexhash(&verify_share_ecmult_data.idx, id33)) {
return 0;
}
secp256k1_scalar_set_int(&verify_share_ecmult_data.idxn, 1);
/* TODO: add scratch */
if (!secp256k1_ecmult_multi_var(&ctx->error_callback, NULL, &tmpj, NULL, secp256k1_frost_verify_share_ecmult_callback, (void *) &verify_share_ecmult_data, threshold)) {
return 0;
}
secp256k1_scalar_negate(&share_neg, share);
secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &snj, &share_neg);
secp256k1_ge_set_gej(&sng, &snj);
secp256k1_gej_add_ge(&tmpj, &tmpj, &sng);
return secp256k1_gej_is_infinity(&tmpj);
}
/* See draft-irtf-cfrg-frost-08#appendix-C.2 */
int secp256k1_frost_share_verify(const secp256k1_context* ctx, size_t threshold, const unsigned char *id33, const secp256k1_frost_share *share, const secp256k1_pubkey * const* vss_commitment) {
secp256k1_scalar share_i;
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(id33 != NULL);
ARG_CHECK(share != NULL);
ARG_CHECK(vss_commitment != NULL);
ARG_CHECK(threshold > 1);
if (!secp256k1_frost_share_load(ctx, &share_i, share)) {
return 0;
}
return secp256k1_frost_vss_verify_internal(ctx, threshold, id33, &share_i, vss_commitment);
}
int secp256k1_frost_compute_pubshare(const secp256k1_context* ctx, secp256k1_pubkey *pubshare, size_t threshold, const unsigned char *id33, const secp256k1_pubkey * const* vss_commitments, size_t n_participants) {
secp256k1_gej pkj;
secp256k1_ge pkp, tmp;
secp256k1_frost_compute_pubshare_ecmult_data compute_pubshare_ecmult_data;
secp256k1_frost_pubkey_combine_ecmult_data pubkey_combine_ecmult_data;
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(pubshare != NULL);
memset(pubshare, 0, sizeof(*pubshare));
ARG_CHECK(id33 != NULL);
ARG_CHECK(vss_commitments != NULL);
ARG_CHECK(n_participants > 1);
ARG_CHECK(threshold > 1);
if (threshold > n_participants) {
return 0;
}
/* Use an EC multi-multiplication to compute the following equation:
* agg_share_i*G = (
* idx^0*vss_commitment[0][0] + ...
* + idx^(t - 1)*vss_commitment[0][t - 1]
* ) + ...
* + (
* idx^0*vss_commitment[n - 1][0] + ...
* + idx^(t - 1)*vss_commitment[n - 1][t - 1]
* )*/
compute_pubshare_ecmult_data.ctx = ctx;
compute_pubshare_ecmult_data.vss_commitments = vss_commitments;
compute_pubshare_ecmult_data.threshold = threshold;
/* Evaluate the public polynomial at the idx */
if (!secp256k1_frost_compute_indexhash(&compute_pubshare_ecmult_data.idx, id33)) {
return 0;
}
secp256k1_scalar_set_int(&compute_pubshare_ecmult_data.idxn, 1);
/* TODO: add scratch */
if (!secp256k1_ecmult_multi_var(&ctx->error_callback, NULL, &pkj, NULL, secp256k1_frost_compute_pubshare_ecmult_callback, (void *) &compute_pubshare_ecmult_data, n_participants*threshold)) {
return 0;
}
secp256k1_ge_set_gej(&tmp, &pkj);
/* Combine pubkeys */
pubkey_combine_ecmult_data.ctx = ctx;
pubkey_combine_ecmult_data.pks = vss_commitments;
pubkey_combine_ecmult_data.threshold = threshold;
/* TODO: add scratch */
if (!secp256k1_ecmult_multi_var(&ctx->error_callback, NULL, &pkj, NULL, secp256k1_frost_pubkey_combine_callback, (void *) &pubkey_combine_ecmult_data, n_participants)) {
return 0;
}
secp256k1_ge_set_gej(&pkp, &pkj);
secp256k1_fe_normalize_var(&pkp.y);
if (secp256k1_fe_is_odd(&pkp.y)) {
secp256k1_ge_neg(&tmp, &tmp);
}
secp256k1_pubkey_save(pubshare, &tmp);
return 1;
}
int secp256k1_frost_share_agg(const secp256k1_context* ctx, secp256k1_frost_share *agg_share, secp256k1_xonly_pubkey *agg_pk, const secp256k1_frost_share * const* shares, const secp256k1_pubkey * const* vss_commitments, size_t n_shares, size_t threshold, const unsigned char *id33) {
secp256k1_frost_pubkey_combine_ecmult_data pubkey_combine_ecmult_data;
secp256k1_gej pkj;
secp256k1_ge pkp;
int pk_parity;
secp256k1_scalar acc;
size_t i;
int ret = 1;
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(agg_share != NULL);
memset(agg_share, 0, sizeof(*agg_share));
ARG_CHECK(agg_pk != NULL);
memset(agg_pk, 0, sizeof(*agg_pk));
ARG_CHECK(shares != NULL);
ARG_CHECK(vss_commitments != NULL);
ARG_CHECK(id33 != NULL);
ARG_CHECK(n_shares > 1);
ARG_CHECK(threshold > 1);
if (threshold > n_shares) {
return 0;
}
secp256k1_scalar_clear(&acc);
for (i = 0; i < n_shares; i++) {
secp256k1_scalar share_i;
if (!secp256k1_frost_share_load(ctx, &share_i, shares[i])) {
return 0;
}
/* Verify share against commitments */
ret &= secp256k1_frost_vss_verify_internal(ctx, threshold, id33, &share_i, &vss_commitments[i]);
secp256k1_scalar_add(&acc, &acc, &share_i);
}
/* Combine pubkeys */
pubkey_combine_ecmult_data.ctx = ctx;
pubkey_combine_ecmult_data.pks = vss_commitments;
pubkey_combine_ecmult_data.threshold = threshold;
/* TODO: add scratch */
if (!secp256k1_ecmult_multi_var(&ctx->error_callback, NULL, &pkj, NULL, secp256k1_frost_pubkey_combine_callback, (void *) &pubkey_combine_ecmult_data, n_shares)) {
return 0;
}
secp256k1_ge_set_gej(&pkp, &pkj);
secp256k1_fe_normalize_var(&pkp.y);
pk_parity = secp256k1_extrakeys_ge_even_y(&pkp);
secp256k1_xonly_pubkey_save(agg_pk, &pkp);
/* Invert the aggregate share if the combined pubkey has an odd Y coordinate. */
if (pk_parity == 1) {
secp256k1_scalar_negate(&acc, &acc);
}
secp256k1_frost_share_save(agg_share, &acc);
return ret;
}
int secp256k1_frost_pubkey_get(const secp256k1_context* ctx, secp256k1_pubkey *ec_pk, const secp256k1_xonly_pubkey *xonly_pk) { int secp256k1_frost_pubkey_get(const secp256k1_context* ctx, secp256k1_pubkey *ec_pk, const secp256k1_xonly_pubkey *xonly_pk) {
secp256k1_ge pk; secp256k1_ge pk;

View File

@ -1,5 +1,5 @@
/********************************************************************** /**********************************************************************
* Copyright (c) 2021-2023 Jesse Posner * * Copyright (c) 2021-2024 Jesse Posner *
* Distributed under the MIT software license, see the accompanying * * Distributed under the MIT software license, see the accompanying *
* file COPYING or http://www.opensource.org/licenses/mit-license.php.* * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/ **********************************************************************/

View File

@ -1,5 +1,5 @@
/********************************************************************** /**********************************************************************
* Copyright (c) 2021-2023 Jesse Posner * * Copyright (c) 2021-2024 Jesse Posner *
* Distributed under the MIT software license, see the accompanying * * Distributed under the MIT software license, see the accompanying *
* file COPYING or http://www.opensource.org/licenses/mit-license.php.* * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/ **********************************************************************/

View File

@ -1,5 +1,5 @@
/********************************************************************** /**********************************************************************
* Copyright (c) 2021-2023 Jesse Posner * * Copyright (c) 2021-2024 Jesse Posner *
* Distributed under the MIT software license, see the accompanying * * Distributed under the MIT software license, see the accompanying *
* file COPYING or http://www.opensource.org/licenses/mit-license.php.* * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/ **********************************************************************/
@ -313,7 +313,7 @@ int secp256k1_frost_nonce_gen(const secp256k1_context* ctx, secp256k1_frost_secn
return ret; return ret;
} }
static int secp256k1_frost_sum_nonces(const secp256k1_context* ctx, secp256k1_gej *summed_nonces, const secp256k1_frost_pubnonce * const* pubnonces, size_t n_pubnonces) { static int secp256k1_frost_sum_nonces(const secp256k1_context* ctx, secp256k1_gej *summed_nonces, const secp256k1_frost_pubnonce * const *pubnonces, size_t n_pubnonces) {
size_t i; size_t i;
int j; int j;
@ -334,16 +334,19 @@ static int secp256k1_frost_sum_nonces(const secp256k1_context* ctx, secp256k1_ge
/* TODO: consider updating to frost-08 to address maleability at the cost of performance */ /* TODO: consider updating to frost-08 to address maleability at the cost of performance */
/* See https://github.com/cfrg/draft-irtf-cfrg-frost/pull/217 */ /* See https://github.com/cfrg/draft-irtf-cfrg-frost/pull/217 */
static int secp256k1_frost_compute_noncehash(const secp256k1_context* ctx, unsigned char *noncehash, const unsigned char *msg, const secp256k1_frost_pubnonce * const* pubnonces, size_t n_pubnonces, const unsigned char *pk32, const size_t *ids) { static int secp256k1_frost_compute_noncehash(const secp256k1_context* ctx, unsigned char *noncehash, const unsigned char *msg, const secp256k1_frost_pubnonce * const *pubnonces, size_t n_pubnonces, const unsigned char *pk32, const unsigned char * const *ids33) {
unsigned char buf[66]; unsigned char buf[66];
secp256k1_sha256 sha; secp256k1_sha256 sha;
size_t i; size_t i;
secp256k1_sha256_initialize_tagged(&sha, (unsigned char*)"FROST/noncecoef", sizeof("FROST/noncecoef") - 1); secp256k1_sha256_initialize_tagged(&sha, (unsigned char*)"FROST/noncecoef", sizeof("FROST/noncecoef") - 1);
/* TODO: sort by index */
for (i = 0; i < n_pubnonces; i++) { for (i = 0; i < n_pubnonces; i++) {
secp256k1_scalar idx; secp256k1_scalar idx;
secp256k1_scalar_set_int(&idx, ids[i]); if (!secp256k1_frost_compute_indexhash(&idx, ids33[i])) {
return 0;
}
secp256k1_scalar_get_b32(buf, &idx); secp256k1_scalar_get_b32(buf, &idx);
secp256k1_sha256_write(&sha, buf, 32); secp256k1_sha256_write(&sha, buf, 32);
if (!secp256k1_frost_pubnonce_serialize(ctx, buf, pubnonces[i])) { if (!secp256k1_frost_pubnonce_serialize(ctx, buf, pubnonces[i])) {
@ -357,7 +360,7 @@ static int secp256k1_frost_compute_noncehash(const secp256k1_context* ctx, unsig
return 1; return 1;
} }
static int secp256k1_frost_nonce_process_internal(const secp256k1_context* ctx, int *fin_nonce_parity, unsigned char *fin_nonce, secp256k1_scalar *b, secp256k1_gej *aggnoncej, const unsigned char *msg, const secp256k1_frost_pubnonce * const* pubnonces, size_t n_pubnonces, const unsigned char *pk32, const size_t *ids) { static int secp256k1_frost_nonce_process_internal(const secp256k1_context* ctx, int *fin_nonce_parity, unsigned char *fin_nonce, secp256k1_scalar *b, secp256k1_gej *aggnoncej, const unsigned char *msg, const secp256k1_frost_pubnonce * const *pubnonces, size_t n_pubnonces, const unsigned char *pk32, const unsigned char * const *ids33) {
unsigned char noncehash[32]; unsigned char noncehash[32];
secp256k1_ge fin_nonce_pt; secp256k1_ge fin_nonce_pt;
secp256k1_gej fin_nonce_ptj; secp256k1_gej fin_nonce_ptj;
@ -365,7 +368,7 @@ static int secp256k1_frost_nonce_process_internal(const secp256k1_context* ctx,
secp256k1_ge_set_gej(&aggnonce[0], &aggnoncej[0]); secp256k1_ge_set_gej(&aggnonce[0], &aggnoncej[0]);
secp256k1_ge_set_gej(&aggnonce[1], &aggnoncej[1]); secp256k1_ge_set_gej(&aggnonce[1], &aggnoncej[1]);
if (!secp256k1_frost_compute_noncehash(ctx, noncehash, msg, pubnonces, n_pubnonces, pk32, ids)) { if (!secp256k1_frost_compute_noncehash(ctx, noncehash, msg, pubnonces, n_pubnonces, pk32, ids33)) {
return 0; return 0;
} }
/* fin_nonce = aggnonce[0] + b*aggnonce[1] */ /* fin_nonce = aggnonce[0] + b*aggnonce[1] */
@ -386,7 +389,7 @@ static int secp256k1_frost_nonce_process_internal(const secp256k1_context* ctx,
return 1; return 1;
} }
static int secp256k1_frost_lagrange_coefficient(secp256k1_scalar *r, const size_t *ids, size_t n_participants, size_t my_id) { static int secp256k1_frost_lagrange_coefficient(secp256k1_scalar *r, const unsigned char * const *ids33, size_t n_participants, const unsigned char *my_id33) {
size_t i; size_t i;
secp256k1_scalar num; secp256k1_scalar num;
secp256k1_scalar den; secp256k1_scalar den;
@ -394,11 +397,15 @@ static int secp256k1_frost_lagrange_coefficient(secp256k1_scalar *r, const size_
secp256k1_scalar_set_int(&num, 1); secp256k1_scalar_set_int(&num, 1);
secp256k1_scalar_set_int(&den, 1); secp256k1_scalar_set_int(&den, 1);
secp256k1_scalar_set_int(&party_idx, my_id); if (!secp256k1_frost_compute_indexhash(&party_idx, my_id33)) {
return 0;
}
for (i = 0; i < n_participants; i++) { for (i = 0; i < n_participants; i++) {
secp256k1_scalar mul; secp256k1_scalar mul;
secp256k1_scalar_set_int(&mul, ids[i]); if (!secp256k1_frost_compute_indexhash(&mul, ids33[i])) {
return 0;
}
if (secp256k1_scalar_eq(&mul, &party_idx)) { if (secp256k1_scalar_eq(&mul, &party_idx)) {
continue; continue;
} }
@ -415,7 +422,7 @@ static int secp256k1_frost_lagrange_coefficient(secp256k1_scalar *r, const size_
return 1; return 1;
} }
int secp256k1_frost_nonce_process(const secp256k1_context* ctx, secp256k1_frost_session *session, const secp256k1_frost_pubnonce * const* pubnonces, size_t n_pubnonces, const unsigned char *msg32, const secp256k1_xonly_pubkey *pk, size_t my_id, const size_t *ids, const secp256k1_frost_tweak_cache *tweak_cache, const secp256k1_pubkey *adaptor) { int secp256k1_frost_nonce_process(const secp256k1_context* ctx, secp256k1_frost_session *session, const secp256k1_frost_pubnonce * const* pubnonces, size_t n_pubnonces, const unsigned char *msg32, const secp256k1_xonly_pubkey *pk, const unsigned char *my_id33, const unsigned char * const *ids33, const secp256k1_frost_tweak_cache *tweak_cache, const secp256k1_pubkey *adaptor) {
secp256k1_ge aggnonce_pt[2]; secp256k1_ge aggnonce_pt[2];
secp256k1_gej aggnonce_ptj[2]; secp256k1_gej aggnonce_ptj[2];
unsigned char fin_nonce[32]; unsigned char fin_nonce[32];
@ -428,12 +435,10 @@ int secp256k1_frost_nonce_process(const secp256k1_context* ctx, secp256k1_frost_
ARG_CHECK(session != NULL); ARG_CHECK(session != NULL);
ARG_CHECK(msg32 != NULL); ARG_CHECK(msg32 != NULL);
ARG_CHECK(pubnonces != NULL); ARG_CHECK(pubnonces != NULL);
ARG_CHECK(ids != NULL); ARG_CHECK(ids33 != NULL);
ARG_CHECK(my_id33 != NULL);
ARG_CHECK(pk != NULL);
ARG_CHECK(n_pubnonces > 1); ARG_CHECK(n_pubnonces > 1);
ARG_CHECK(my_id != 0);
for (i = 0; i < n_pubnonces; i++) {
ARG_CHECK(ids[i] != 0);
}
if (!secp256k1_xonly_pubkey_serialize(ctx, pk32, pk)) { if (!secp256k1_xonly_pubkey_serialize(ctx, pk32, pk)) {
return 0; return 0;
@ -448,7 +453,6 @@ int secp256k1_frost_nonce_process(const secp256k1_context* ctx, secp256k1_frost_
here, we will never be able to determine who it is. Therefore, we here, we will never be able to determine who it is. Therefore, we
should continue such that the culprit is revealed when collecting should continue such that the culprit is revealed when collecting
and verifying partial signatures. and verifying partial signatures.
However, dealing with the point at infinity (loading, However, dealing with the point at infinity (loading,
de-/serializing) would require a lot of extra code complexity. de-/serializing) would require a lot of extra code complexity.
Instead, we set the aggregate nonce to some arbitrary point (the Instead, we set the aggregate nonce to some arbitrary point (the
@ -472,7 +476,7 @@ int secp256k1_frost_nonce_process(const secp256k1_context* ctx, secp256k1_frost_
} }
secp256k1_gej_add_ge_var(&aggnonce_ptj[0], &aggnonce_ptj[0], &adaptorp, NULL); secp256k1_gej_add_ge_var(&aggnonce_ptj[0], &aggnonce_ptj[0], &adaptorp, NULL);
} }
if (!secp256k1_frost_nonce_process_internal(ctx, &session_i.fin_nonce_parity, fin_nonce, &session_i.noncecoef, aggnonce_ptj, msg32, pubnonces, n_pubnonces, pk32, ids)) { if (!secp256k1_frost_nonce_process_internal(ctx, &session_i.fin_nonce_parity, fin_nonce, &session_i.noncecoef, aggnonce_ptj, msg32, pubnonces, n_pubnonces, pk32, ids33)) {
return 0; return 0;
} }
@ -496,7 +500,7 @@ int secp256k1_frost_nonce_process(const secp256k1_context* ctx, secp256k1_frost_
} }
/* Update the challenge by multiplying the Lagrange coefficient to prepare /* Update the challenge by multiplying the Lagrange coefficient to prepare
* for signing. */ * for signing. */
if (!secp256k1_frost_lagrange_coefficient(&l, ids, n_pubnonces, my_id)) { if (!secp256k1_frost_lagrange_coefficient(&l, ids33, n_pubnonces, my_id33)) {
return 0; return 0;
} }
secp256k1_scalar_mul(&session_i.challenge, &session_i.challenge, &l); secp256k1_scalar_mul(&session_i.challenge, &session_i.challenge, &l);

View File

@ -1,5 +1,5 @@
/*********************************************************************** /***********************************************************************
* Copyright (c) 2022, 2023 Jesse Posner * * Copyright (c) 2022-2024 Jesse Posner *
* Distributed under the MIT software license, see the accompanying * * Distributed under the MIT software license, see the accompanying *
* file COPYING or https://www.opensource.org/licenses/mit-license.php.* * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
***********************************************************************/ ***********************************************************************/
@ -23,49 +23,77 @@
#include "../../hash.h" #include "../../hash.h"
#include "../../util.h" #include "../../util.h"
static int frost_create_pk(unsigned char *id, const unsigned char *sk) {
int ret;
secp256k1_pubkey pubkey_tmp;
size_t size = 33;
ret = secp256k1_ec_pubkey_create(CTX, &pubkey_tmp, sk);
ret &= secp256k1_ec_pubkey_serialize(CTX, id, &size, &pubkey_tmp, SECP256K1_EC_COMPRESSED);
return ret;
}
/* Simple (non-adaptor, non-tweaked) 3-of-5 FROST aggregate, sign, verify /* Simple (non-adaptor, non-tweaked) 3-of-5 FROST aggregate, sign, verify
* test. */ * test. */
void frost_simple_test(void) { void frost_simple_test(void) {
unsigned char sk[5][32];
secp256k1_frost_pubnonce pubnonce[5]; secp256k1_frost_pubnonce pubnonce[5];
const secp256k1_frost_pubnonce *pubnonce_ptr[5]; const secp256k1_frost_pubnonce *pubnonce_ptr[5];
unsigned char msg[32]; unsigned char msg[32];
secp256k1_xonly_pubkey pk; secp256k1_pubkey vss_commitment[5][3];
unsigned char seed[32]; const secp256k1_pubkey *vss_ptr[5];
secp256k1_frost_share shares[5]; unsigned char pok[5][64];
secp256k1_xonly_pubkey agg_pk;
unsigned char buf[5][32];
secp256k1_frost_share shares[5][5];
const secp256k1_frost_share *share_ptr[5];
secp256k1_frost_share agg_share[5];
secp256k1_frost_secnonce secnonce[5]; secp256k1_frost_secnonce secnonce[5];
secp256k1_pubkey pubshares[5]; secp256k1_pubkey pubshare[5];
secp256k1_frost_partial_sig partial_sig[5]; secp256k1_frost_partial_sig partial_sig[5];
const secp256k1_frost_partial_sig *partial_sig_ptr[5]; const secp256k1_frost_partial_sig *partial_sig_ptr[5];
unsigned char final_sig[64]; unsigned char final_sig[64];
secp256k1_frost_session session; secp256k1_frost_session session;
int i; int i, j;
size_t ids[5]; unsigned char id[5][33];
const unsigned char *id_ptr[5];
secp256k1_testrand256(seed);
for (i = 0; i < 5; i++) { for (i = 0; i < 5; i++) {
secp256k1_testrand256(buf[i]);
secp256k1_testrand256(sk[i]);
vss_ptr[i] = vss_commitment[i];
pubnonce_ptr[i] = &pubnonce[i]; pubnonce_ptr[i] = &pubnonce[i];
partial_sig_ptr[i] = &partial_sig[i]; partial_sig_ptr[i] = &partial_sig[i];
ids[i] = i + 1; id_ptr[i] = id[i];
}
CHECK(secp256k1_frost_shares_trusted_gen(CTX, shares, pubshares, &pk, seed, 3, 5) == 1); CHECK(frost_create_pk(id[i], sk[i]));
}
for (i = 0; i < 5; i++) {
CHECK(secp256k1_frost_shares_gen(CTX, shares[i], vss_commitment[i], pok[i], buf[i], 3, 5, id_ptr) == 1);
}
for (i = 0; i < 5; i++) {
for (j = 0; j < 5; j++) {
share_ptr[j] = &shares[j][i];
CHECK(secp256k1_frost_share_verify(CTX, 3, id_ptr[i], share_ptr[j], &vss_ptr[j]) == 1);
CHECK(secp256k1_frost_compute_pubshare(CTX, &pubshare[j], 3, id_ptr[j], vss_ptr, 5) == 1);
}
CHECK(secp256k1_frost_share_agg(CTX, &agg_share[i], &agg_pk, share_ptr, vss_ptr, 5, 3, id_ptr[i]) == 1);
}
secp256k1_testrand256(msg); secp256k1_testrand256(msg);
for (i = 0; i < 3; i++) { for (i = 0; i < 3; i++) {
unsigned char session_id[32]; secp256k1_testrand256(buf[i]);
secp256k1_testrand256(session_id); CHECK(secp256k1_frost_nonce_gen(CTX, &secnonce[i], &pubnonce[i], buf[i], &agg_share[i], NULL, NULL, NULL) == 1);
CHECK(secp256k1_frost_nonce_gen(CTX, &secnonce[i], &pubnonce[i], session_id, &shares[i], NULL, NULL, NULL) == 1);
} }
for (i = 0; i < 3; i++) { for (i = 0; i < 3; i++) {
CHECK(secp256k1_frost_nonce_process(CTX, &session, pubnonce_ptr, 3, msg, &pk, ids[i], ids, NULL, NULL) == 1); CHECK(secp256k1_frost_nonce_process(CTX, &session, pubnonce_ptr, 3, msg, &agg_pk, id_ptr[i], id_ptr, NULL, NULL) == 1);
CHECK(secp256k1_frost_partial_sign(CTX, &partial_sig[i], &secnonce[i], &shares[i], &session, NULL) == 1); CHECK(secp256k1_frost_partial_sign(CTX, &partial_sig[i], &secnonce[i], &agg_share[i], &session, NULL) == 1);
CHECK(secp256k1_frost_partial_sig_verify(CTX, &partial_sig[i], &pubnonce[i], &pubshares[i], &session, NULL) == 1); CHECK(secp256k1_frost_partial_sig_verify(CTX, &partial_sig[i], &pubnonce[i], &pubshare[i], &session, NULL) == 1);
} }
CHECK(secp256k1_frost_partial_sig_agg(CTX, final_sig, &session, partial_sig_ptr, 3) == 1); CHECK(secp256k1_frost_partial_sig_agg(CTX, final_sig, &session, partial_sig_ptr, 3) == 1);
CHECK(secp256k1_schnorrsig_verify(CTX, final_sig, msg, sizeof(msg), &pk) == 1); CHECK(secp256k1_schnorrsig_verify(CTX, final_sig, msg, sizeof(msg), &agg_pk) == 1);
} }
void frost_pubnonce_summing_to_inf(secp256k1_frost_pubnonce *pubnonce) { void frost_pubnonce_summing_to_inf(secp256k1_frost_pubnonce *pubnonce) {
@ -107,11 +135,11 @@ void frost_api_tests(void) {
unsigned char final_sig[64]; unsigned char final_sig[64];
unsigned char pre_sig[64]; unsigned char pre_sig[64];
unsigned char buf[32]; unsigned char buf[32];
/* unsigned char sk[5][32]; */ unsigned char sk[5][32];
unsigned char max64[64]; unsigned char max64[64];
unsigned char zeros68[68] = { 0 }; unsigned char zeros68[68] = { 0 };
unsigned char session_id[5][32]; unsigned char session_id[5][32];
unsigned char seed[32]; unsigned char seed[5][32];
secp256k1_frost_secnonce secnonce[5]; secp256k1_frost_secnonce secnonce[5];
secp256k1_frost_secnonce secnonce_tmp; secp256k1_frost_secnonce secnonce_tmp;
secp256k1_frost_secnonce invalid_secnonce; secp256k1_frost_secnonce invalid_secnonce;
@ -122,8 +150,8 @@ void frost_api_tests(void) {
secp256k1_frost_pubnonce invalid_pubnonce; secp256k1_frost_pubnonce invalid_pubnonce;
const secp256k1_frost_pubnonce *invalid_pubnonce_ptr[5]; const secp256k1_frost_pubnonce *invalid_pubnonce_ptr[5];
unsigned char msg[32]; unsigned char msg[32];
secp256k1_xonly_pubkey pk; secp256k1_xonly_pubkey agg_pk;
secp256k1_pubkey full_pk; secp256k1_pubkey full_agg_pk;
secp256k1_frost_tweak_cache tweak_cache; secp256k1_frost_tweak_cache tweak_cache;
secp256k1_frost_tweak_cache invalid_tweak_cache; secp256k1_frost_tweak_cache invalid_tweak_cache;
secp256k1_frost_session session[5]; secp256k1_frost_session session[5];
@ -134,13 +162,21 @@ void frost_api_tests(void) {
unsigned char sec_adaptor[32]; unsigned char sec_adaptor[32];
unsigned char sec_adaptor1[32]; unsigned char sec_adaptor1[32];
secp256k1_pubkey adaptor; secp256k1_pubkey adaptor;
secp256k1_pubkey vss_commitment[5][3];
secp256k1_pubkey invalid_vss_commitment[5][3];
const secp256k1_pubkey *vss_ptr[5];
const secp256k1_pubkey *invalid_vss_ptr[5];
secp256k1_pubkey invalid_vss_pk; secp256k1_pubkey invalid_vss_pk;
secp256k1_frost_share shares[5][5];
secp256k1_frost_share invalid_share; secp256k1_frost_share invalid_share;
secp256k1_frost_share shares[5]; secp256k1_frost_share agg_share[5];
secp256k1_pubkey pubshares[5]; unsigned char pok[5][64];
int i; const secp256k1_frost_share *share_ptr[5];
size_t ids[5]; const secp256k1_frost_share *invalid_share_ptr[5];
size_t invalid_ids[5]; secp256k1_pubkey pubshare[5];
int i, j;
unsigned char id[5][33];
const unsigned char *id_ptr[5];
/** setup **/ /** setup **/
memset(max64, 0xff, sizeof(max64)); memset(max64, 0xff, sizeof(max64));
@ -160,68 +196,158 @@ void frost_api_tests(void) {
secp256k1_testrand256(sec_adaptor); secp256k1_testrand256(sec_adaptor);
secp256k1_testrand256(msg); secp256k1_testrand256(msg);
secp256k1_testrand256(tweak); secp256k1_testrand256(tweak);
secp256k1_testrand256(seed);
CHECK(secp256k1_ec_pubkey_create(CTX, &adaptor, sec_adaptor) == 1); CHECK(secp256k1_ec_pubkey_create(CTX, &adaptor, sec_adaptor) == 1);
for (i = 0; i < 5; i++) { for (i = 0; i < 5; i++) {
pubnonce_ptr[i] = &pubnonce[i]; pubnonce_ptr[i] = &pubnonce[i];
vss_ptr[i] = vss_commitment[i];
invalid_vss_ptr[i] = invalid_vss_commitment[i];
partial_sig_ptr[i] = &partial_sig[i]; partial_sig_ptr[i] = &partial_sig[i];
invalid_partial_sig_ptr[i] = &partial_sig[i]; invalid_partial_sig_ptr[i] = &partial_sig[i];
ids[i] = i + 1; id_ptr[i] = id[i];
invalid_ids[i] = i + 1;
secp256k1_testrand256(session_id[i]); secp256k1_testrand256(session_id[i]);
secp256k1_testrand256(seed[i]);
secp256k1_testrand256(sk[i]);
CHECK(frost_create_pk(id[i], sk[i]));
} }
invalid_pubnonce_ptr[0] = &invalid_pubnonce; invalid_pubnonce_ptr[0] = &invalid_pubnonce;
invalid_share_ptr[0] = &invalid_share;
invalid_partial_sig_ptr[0] = &invalid_partial_sig; invalid_partial_sig_ptr[0] = &invalid_partial_sig;
invalid_ids[2] = 0; for (i = 0; i < 5; i++) {
for (j = 0; j < 3; j++) {
invalid_vss_commitment[i][j] = invalid_vss_pk;
}
}
/** main test body **/ /** main test body **/
/** Key generation **/ /** Key generation **/
CHECK(secp256k1_frost_shares_trusted_gen(CTX, shares, pubshares, &pk, seed, 3, 5) == 1);
CHECK_ILLEGAL(CTX, secp256k1_frost_shares_trusted_gen(CTX, NULL, pubshares, &pk, seed, 3, 5));
CHECK_ILLEGAL(CTX, secp256k1_frost_shares_trusted_gen(CTX, shares, NULL, &pk, seed, 3, 5));
for (i = 0; i < 5; i++) { for (i = 0; i < 5; i++) {
CHECK(frost_memcmp_and_randomize(shares[i].data, zeros68, sizeof(shares[i].data)) == 0); CHECK(secp256k1_frost_shares_gen(CTX, shares[i], vss_commitment[i], pok[i], seed[i], 3, 5, id_ptr) == 1);
} CHECK_ILLEGAL(CTX, secp256k1_frost_shares_gen(CTX, NULL, vss_commitment[i], pok[i], seed[i], 3, 5, id_ptr));
CHECK_ILLEGAL(CTX, secp256k1_frost_shares_trusted_gen(CTX, shares, pubshares, NULL, seed, 3, 5)); CHECK_ILLEGAL(CTX, secp256k1_frost_shares_gen(CTX, shares[i], NULL, pok[i], seed[i], 3, 5, id_ptr));
for (i = 0; i < 5; i++) { for (j = 0; j < 5; j++) {
CHECK(frost_memcmp_and_randomize(shares[i].data, zeros68, sizeof(shares[i].data)) == 0); CHECK(frost_memcmp_and_randomize(shares[i][j].data, zeros68, sizeof(shares[i][j].data)) == 0);
} }
CHECK_ILLEGAL(CTX, secp256k1_frost_shares_trusted_gen(CTX, shares, pubshares, &pk, NULL, 3, 5)); CHECK_ILLEGAL(CTX, secp256k1_frost_shares_gen(CTX, shares[i], vss_commitment[i], NULL, seed[i], 3, 5, id_ptr));
for (i = 0; i < 5; i++) { for (j = 0; j < 5; j++) {
CHECK(frost_memcmp_and_randomize(shares[i].data, zeros68, sizeof(shares[i].data)) == 0); CHECK(frost_memcmp_and_randomize(shares[i][j].data, zeros68, sizeof(shares[i][j].data)) == 0);
} }
CHECK_ILLEGAL(CTX, secp256k1_frost_shares_trusted_gen(CTX, shares, pubshares, &pk, seed, 0, 5)); CHECK_ILLEGAL(CTX, secp256k1_frost_shares_gen(CTX, shares[i], vss_commitment[i], pok[i], NULL, 3, 5, id_ptr));
for (i = 0; i < 5; i++) { for (j = 0; j < 5; j++) {
CHECK(frost_memcmp_and_randomize(shares[i].data, zeros68, sizeof(shares[i].data)) == 0); CHECK(frost_memcmp_and_randomize(shares[i][j].data, zeros68, sizeof(shares[i][j].data)) == 0);
} }
CHECK_ILLEGAL(CTX, secp256k1_frost_shares_trusted_gen(CTX, shares, pubshares, &pk, seed, 3, 0)); CHECK_ILLEGAL(CTX, secp256k1_frost_shares_gen(CTX, shares[i], vss_commitment[i], pok[i], seed[i], 0, 5, id_ptr));
CHECK_ILLEGAL(CTX, secp256k1_frost_shares_trusted_gen(CTX, shares, pubshares, &pk, seed, 3, 2)); for (j = 0; j < 5; j++) {
CHECK(frost_memcmp_and_randomize(shares[i][j].data, zeros68, sizeof(shares[i][j].data)) == 0);
}
CHECK_ILLEGAL(CTX, secp256k1_frost_shares_gen(CTX, shares[i], vss_commitment[i], pok[i], seed[i], 3, 0, id_ptr));
CHECK_ILLEGAL(CTX, secp256k1_frost_shares_gen(CTX, shares[i], vss_commitment[i], pok[i], seed[i], 3, 2, id_ptr));
CHECK_ILLEGAL(CTX, secp256k1_frost_shares_gen(CTX, shares[i], vss_commitment[i], pok[i], seed[i], 3, 5, NULL));
for (j = 0; j < 5; j++) {
CHECK(frost_memcmp_and_randomize(shares[i][j].data, zeros68, sizeof(shares[i][j].data)) == 0);
}
CHECK(secp256k1_frost_shares_trusted_gen(CTX, shares, pubshares, &pk, seed, 3, 5) == 1); CHECK(secp256k1_frost_shares_gen(CTX, shares[i], vss_commitment[i], pok[i], seed[i], 3, 5, id_ptr) == 1);
}
/* Share aggregation */
for (i = 0; i < 5; i++) {
for (j = 0; j < 5; j++) {
share_ptr[j] = &shares[j][i];
}
CHECK(secp256k1_frost_share_agg(CTX, &agg_share[i], &agg_pk, share_ptr, vss_ptr, 5, 3, id_ptr[i]) == 1);
CHECK_ILLEGAL(CTX, secp256k1_frost_share_agg(CTX, NULL, &agg_pk, share_ptr, vss_ptr, 5, 3, id_ptr[i]));
CHECK_ILLEGAL(CTX, secp256k1_frost_share_agg(CTX, &agg_share[i], NULL, share_ptr, vss_ptr, 5, 3, id_ptr[i]));
CHECK(frost_memcmp_and_randomize(agg_share[i].data, zeros68, sizeof(agg_share[i].data)) == 0);
CHECK_ILLEGAL(CTX, secp256k1_frost_share_agg(CTX, &agg_share[i], &agg_pk, NULL, vss_ptr, 5, 3, id_ptr[i]));
CHECK(frost_memcmp_and_randomize(agg_share[i].data, zeros68, sizeof(agg_share[i].data)) == 0);
CHECK(frost_memcmp_and_randomize(agg_pk.data, zeros68, sizeof(agg_pk.data)) == 0);
CHECK_ILLEGAL(CTX, secp256k1_frost_share_agg(CTX, &agg_share[i], &agg_pk, share_ptr, NULL, 5, 3, id_ptr[i]));
CHECK(frost_memcmp_and_randomize(agg_share[i].data, zeros68, sizeof(agg_share[i].data)) == 0);
CHECK(frost_memcmp_and_randomize(agg_pk.data, zeros68, sizeof(agg_pk.data)) == 0);
/* TODO: fix test */
/* CHECK_ILLEGAL(CTX, secp256k1_frost_share_agg(CTX, &agg_share[i], &agg_pk, share_ptr, invalid_vss_ptr, 5, 3, id_ptr[i])); */
/* CHECK(frost_memcmp_and_randomize(agg_share[i].data, zeros68, sizeof(agg_share[i].data)) == 0); */
/* CHECK(frost_memcmp_and_randomize(agg_pk.data, zeros68, sizeof(agg_pk.data)) == 0); */
CHECK_ILLEGAL(CTX, secp256k1_frost_share_agg(CTX, &agg_share[i], &agg_pk, share_ptr, vss_ptr, 5, 3, NULL));
CHECK(frost_memcmp_and_randomize(agg_share[i].data, zeros68, sizeof(agg_share[i].data)) == 0);
CHECK(frost_memcmp_and_randomize(agg_pk.data, zeros68, sizeof(agg_pk.data)) == 0);
CHECK_ILLEGAL(CTX, secp256k1_frost_share_agg(CTX, &agg_share[i], &agg_pk, invalid_share_ptr, vss_ptr, 5, 3, id_ptr[i]));
CHECK(frost_memcmp_and_randomize(agg_share[i].data, zeros68, sizeof(agg_share[i].data)) == 0);
CHECK(frost_memcmp_and_randomize(agg_pk.data, zeros68, sizeof(agg_pk.data)) == 0);
CHECK_ILLEGAL(CTX, secp256k1_frost_share_agg(CTX, &agg_share[i], &agg_pk, share_ptr, vss_ptr, 0, 3, id_ptr[i]));
CHECK(frost_memcmp_and_randomize(agg_share[i].data, zeros68, sizeof(agg_share[i].data)) == 0);
CHECK(frost_memcmp_and_randomize(agg_pk.data, zeros68, sizeof(agg_pk.data)) == 0);
CHECK_ILLEGAL(CTX, secp256k1_frost_share_agg(CTX, &agg_share[i], &agg_pk, NULL, vss_ptr, 0, 3, id_ptr[i]));
CHECK(frost_memcmp_and_randomize(agg_share[i].data, zeros68, sizeof(agg_share[i].data)) == 0);
CHECK(frost_memcmp_and_randomize(agg_pk.data, zeros68, sizeof(agg_pk.data)) == 0);
CHECK_ILLEGAL(CTX, secp256k1_frost_share_agg(CTX, &agg_share[i], &agg_pk, share_ptr, vss_ptr, 5, 0, id_ptr[i]));
CHECK(frost_memcmp_and_randomize(agg_share[i].data, zeros68, sizeof(agg_share[i].data)) == 0);
CHECK(frost_memcmp_and_randomize(agg_pk.data, zeros68, sizeof(agg_pk.data)) == 0);
CHECK_ILLEGAL(CTX, secp256k1_frost_share_agg(CTX, &agg_share[i], &agg_pk, share_ptr, NULL, 5, 0, id_ptr[i]));
CHECK(frost_memcmp_and_randomize(agg_share[i].data, zeros68, sizeof(agg_share[i].data)) == 0);
CHECK(frost_memcmp_and_randomize(agg_pk.data, zeros68, sizeof(agg_pk.data)) == 0);
CHECK(secp256k1_frost_share_agg(CTX, &agg_share[i], &agg_pk, share_ptr, vss_ptr, 5, 3, id_ptr[i]) == 1);
}
/* Share verification */
CHECK(secp256k1_frost_share_verify(CTX, 3, id_ptr[4], share_ptr[0], &vss_ptr[0]) == 1);
CHECK(secp256k1_frost_share_verify(CTX, 3, id_ptr[4], share_ptr[0], &vss_ptr[1]) == 0);
CHECK_ILLEGAL(CTX, secp256k1_frost_share_verify(CTX, 3, NULL, share_ptr[0], &vss_ptr[0]));
CHECK_ILLEGAL(CTX, secp256k1_frost_share_verify(CTX, 3, id_ptr[4], NULL, &vss_ptr[1]));
CHECK_ILLEGAL(CTX, secp256k1_frost_share_verify(CTX, 3, id_ptr[4], &invalid_share, &vss_ptr[0]));
CHECK_ILLEGAL(CTX, secp256k1_frost_share_verify(CTX, 3, id_ptr[4], share_ptr[0], NULL));
CHECK_ILLEGAL(CTX, secp256k1_frost_share_verify(CTX, 3, id_ptr[4], share_ptr[0], &invalid_vss_ptr[0]));
CHECK_ILLEGAL(CTX, secp256k1_frost_share_verify(CTX, 0, id_ptr[4], share_ptr[0], &vss_ptr[0]));
CHECK(secp256k1_frost_share_verify(CTX, 3, id_ptr[4], share_ptr[0], &vss_ptr[0]) == 1);
CHECK(secp256k1_frost_share_verify(CTX, 3, id_ptr[4], share_ptr[1], &vss_ptr[1]) == 1);
/* Compute public verification share */
CHECK(secp256k1_frost_compute_pubshare(CTX, &pubshare[0], 3, id_ptr[0], vss_ptr, 5) == 1);
CHECK_ILLEGAL(CTX, secp256k1_frost_compute_pubshare(CTX, NULL, 3, id_ptr[0], vss_ptr, 5));
CHECK_ILLEGAL(CTX, secp256k1_frost_compute_pubshare(CTX, &pubshare[0], 3, NULL, vss_ptr, 5));
CHECK(frost_memcmp_and_randomize(pubshare[0].data, zeros68, sizeof(pubshare[0].data)) == 0);
CHECK_ILLEGAL(CTX, secp256k1_frost_compute_pubshare(CTX, &pubshare[0], 3, id_ptr[0], NULL, 5));
CHECK(frost_memcmp_and_randomize(pubshare[0].data, zeros68, sizeof(pubshare[0].data)) == 0);
CHECK_ILLEGAL(CTX, secp256k1_frost_compute_pubshare(CTX, &pubshare[0], 3, id_ptr[0], invalid_vss_ptr, 5));
CHECK(frost_memcmp_and_randomize(pubshare[0].data, zeros68, sizeof(pubshare[0].data)) == 0);
CHECK_ILLEGAL(CTX, secp256k1_frost_compute_pubshare(CTX, &pubshare[0], 0, id_ptr[0], invalid_vss_ptr, 5));
CHECK(frost_memcmp_and_randomize(pubshare[0].data, zeros68, sizeof(pubshare[0].data)) == 0);
CHECK_ILLEGAL(CTX, secp256k1_frost_compute_pubshare(CTX, &pubshare[0], 0, id_ptr[0], NULL, 5));
CHECK(frost_memcmp_and_randomize(pubshare[0].data, zeros68, sizeof(pubshare[0].data)) == 0);
CHECK_ILLEGAL(CTX, secp256k1_frost_compute_pubshare(CTX, &pubshare[0], 3, id_ptr[0], invalid_vss_ptr, 0));
CHECK(frost_memcmp_and_randomize(pubshare[0].data, zeros68, sizeof(pubshare[0].data)) == 0);
CHECK_ILLEGAL(CTX, secp256k1_frost_compute_pubshare(CTX, &pubshare[0], 3, id_ptr[0], NULL, 0));
CHECK(frost_memcmp_and_randomize(pubshare[0].data, zeros68, sizeof(pubshare[0].data)) == 0);
CHECK(secp256k1_frost_compute_pubshare(CTX, &pubshare[0], 3, id_ptr[0], vss_ptr, 5) == 1);
CHECK(secp256k1_frost_compute_pubshare(CTX, &pubshare[1], 3, id_ptr[1], vss_ptr, 5) == 1);
CHECK(secp256k1_frost_compute_pubshare(CTX, &pubshare[2], 3, id_ptr[2], vss_ptr, 5) == 1);
/* pubkey_get */ /* pubkey_get */
CHECK(secp256k1_frost_pubkey_get(CTX, &full_pk, &pk) == 1); CHECK(secp256k1_frost_pubkey_get(CTX, &full_agg_pk, &agg_pk) == 1);
CHECK_ILLEGAL(CTX, secp256k1_frost_pubkey_get(CTX, NULL, &pk)); CHECK_ILLEGAL(CTX, secp256k1_frost_pubkey_get(CTX, NULL, &agg_pk));
CHECK_ILLEGAL(CTX, secp256k1_frost_pubkey_get(CTX, &full_pk, NULL)); CHECK_ILLEGAL(CTX, secp256k1_frost_pubkey_get(CTX, &full_agg_pk, NULL));
CHECK(secp256k1_memcmp_var(&full_pk, zeros68, sizeof(full_pk)) == 0); CHECK(secp256k1_memcmp_var(&full_agg_pk, zeros68, sizeof(full_agg_pk)) == 0);
/** Tweaking **/ /** Tweaking **/
/* pubkey_tweak */ /* pubkey_tweak */
CHECK(secp256k1_frost_pubkey_tweak(CTX, &tweak_cache, &pk) == 1); CHECK(secp256k1_frost_pubkey_tweak(CTX, &tweak_cache, &agg_pk) == 1);
CHECK_ILLEGAL(CTX, secp256k1_frost_pubkey_tweak(CTX, NULL, &pk)); CHECK_ILLEGAL(CTX, secp256k1_frost_pubkey_tweak(CTX, NULL, &agg_pk));
CHECK_ILLEGAL(CTX, secp256k1_frost_pubkey_tweak(CTX, &tweak_cache, NULL)); CHECK_ILLEGAL(CTX, secp256k1_frost_pubkey_tweak(CTX, &tweak_cache, NULL));
CHECK_ILLEGAL(CTX, secp256k1_frost_pubkey_tweak(CTX, &tweak_cache, &invalid_pk)); CHECK_ILLEGAL(CTX, secp256k1_frost_pubkey_tweak(CTX, &tweak_cache, &invalid_pk));
CHECK(secp256k1_frost_pubkey_tweak(CTX, &tweak_cache, &pk) == 1); CHECK(secp256k1_frost_pubkey_tweak(CTX, &tweak_cache, &agg_pk) == 1);
/* tweak_add */ /* tweak_add */
{ {
int (*tweak_func[2]) (const secp256k1_context* ctx, secp256k1_pubkey *output_pubkey, secp256k1_frost_tweak_cache *tweak_cache, const unsigned char *tweak32); int (*tweak_func[2]) (const secp256k1_context* ctx, secp256k1_pubkey *output_pubkey, secp256k1_frost_tweak_cache *tweak_cache, const unsigned char *tweak32);
tweak_func[0] = secp256k1_frost_pubkey_ec_tweak_add; tweak_func[0] = secp256k1_frost_pubkey_ec_tweak_add;
tweak_func[1] = secp256k1_frost_pubkey_xonly_tweak_add; tweak_func[1] = secp256k1_frost_pubkey_xonly_tweak_add;
CHECK(secp256k1_frost_pubkey_tweak(CTX, &tweak_cache, &pk) == 1); CHECK(secp256k1_frost_pubkey_tweak(CTX, &tweak_cache, &agg_pk) == 1);
for (i = 0; i < 2; i++) { for (i = 0; i < 2; i++) {
secp256k1_pubkey tmp_output_pk; secp256k1_pubkey tmp_output_pk;
secp256k1_frost_tweak_cache tmp_tweak_cache = tweak_cache; secp256k1_frost_tweak_cache tmp_tweak_cache = tweak_cache;
@ -248,31 +374,31 @@ void frost_api_tests(void) {
} }
/** Session creation **/ /** Session creation **/
CHECK(secp256k1_frost_nonce_gen(CTX, &secnonce[0], &pubnonce[0], session_id[0], &shares[0], msg, &pk, max64) == 1); CHECK(secp256k1_frost_nonce_gen(CTX, &secnonce[0], &pubnonce[0], session_id[0], &agg_share[0], msg, &agg_pk, max64) == 1);
CHECK_ILLEGAL(STATIC_CTX, secp256k1_frost_nonce_gen(STATIC_CTX, &secnonce[0], &pubnonce[0], session_id[0], &shares[0], msg, &pk, max64)); CHECK_ILLEGAL(STATIC_CTX, secp256k1_frost_nonce_gen(STATIC_CTX, &secnonce[0], &pubnonce[0], session_id[0], &agg_share[0], msg, &agg_pk, max64));
CHECK_ILLEGAL(CTX, secp256k1_frost_nonce_gen(CTX, NULL, &pubnonce[0], session_id[0], &shares[0], msg, &pk, max64)); CHECK_ILLEGAL(CTX, secp256k1_frost_nonce_gen(CTX, NULL, &pubnonce[0], session_id[0], &agg_share[0], msg, &agg_pk, max64));
CHECK_ILLEGAL(CTX, secp256k1_frost_nonce_gen(CTX, &secnonce[0], NULL, session_id[0], &shares[0], msg, &pk, max64)); CHECK_ILLEGAL(CTX, secp256k1_frost_nonce_gen(CTX, &secnonce[0], NULL, session_id[0], &agg_share[0], msg, &agg_pk, max64));
CHECK_ILLEGAL(CTX, secp256k1_frost_nonce_gen(CTX, &secnonce[0], &pubnonce[0], NULL, &shares[0], msg, &pk, max64)); CHECK_ILLEGAL(CTX, secp256k1_frost_nonce_gen(CTX, &secnonce[0], &pubnonce[0], NULL, &agg_share[0], msg, &agg_pk, max64));
CHECK(frost_memcmp_and_randomize(secnonce[0].data, zeros68, sizeof(secnonce[0].data)) == 0); CHECK(frost_memcmp_and_randomize(secnonce[0].data, zeros68, sizeof(secnonce[0].data)) == 0);
/* no seckey and session_id is 0 */ /* no seckey and session_id is 0 */
CHECK(secp256k1_frost_nonce_gen(CTX, &secnonce[0], &pubnonce[0], zeros68, NULL, msg, &pk, max64) == 0); CHECK(secp256k1_frost_nonce_gen(CTX, &secnonce[0], &pubnonce[0], zeros68, NULL, msg, &agg_pk, max64) == 0);
CHECK(frost_memcmp_and_randomize(secnonce[0].data, zeros68, sizeof(secnonce[0].data)) == 0); CHECK(frost_memcmp_and_randomize(secnonce[0].data, zeros68, sizeof(secnonce[0].data)) == 0);
/* session_id 0 is fine when a seckey is provided */ /* session_id 0 is fine when a seckey is provided */
CHECK(secp256k1_frost_nonce_gen(CTX, &secnonce[0], &pubnonce[0], zeros68, &shares[0], msg, &pk, max64) == 1); CHECK(secp256k1_frost_nonce_gen(CTX, &secnonce[0], &pubnonce[0], zeros68, &agg_share[0], msg, &agg_pk, max64) == 1);
CHECK(secp256k1_frost_nonce_gen(CTX, &secnonce[0], &pubnonce[0], session_id[0], NULL, msg, &pk, max64) == 1); CHECK(secp256k1_frost_nonce_gen(CTX, &secnonce[0], &pubnonce[0], session_id[0], NULL, msg, &agg_pk, max64) == 1);
/* invalid share */ /* invalid agg_share */
CHECK_ILLEGAL(CTX, secp256k1_frost_nonce_gen(CTX, &secnonce[0], &pubnonce[0], session_id[0], &invalid_share, msg, &pk, max64)); CHECK_ILLEGAL(CTX, secp256k1_frost_nonce_gen(CTX, &secnonce[0], &pubnonce[0], session_id[0], &invalid_share, msg, &agg_pk, max64));
CHECK(frost_memcmp_and_randomize(secnonce[0].data, zeros68, sizeof(secnonce[0].data)) == 0); CHECK(frost_memcmp_and_randomize(secnonce[0].data, zeros68, sizeof(secnonce[0].data)) == 0);
CHECK(secp256k1_frost_nonce_gen(CTX, &secnonce[0], &pubnonce[0], session_id[0], &shares[0], NULL, &pk, max64) == 1); CHECK(secp256k1_frost_nonce_gen(CTX, &secnonce[0], &pubnonce[0], session_id[0], &agg_share[0], NULL, &agg_pk, max64) == 1);
CHECK(secp256k1_frost_nonce_gen(CTX, &secnonce[0], &pubnonce[0], session_id[0], &shares[0], msg, NULL, max64) == 1); CHECK(secp256k1_frost_nonce_gen(CTX, &secnonce[0], &pubnonce[0], session_id[0], &agg_share[0], msg, NULL, max64) == 1);
CHECK_ILLEGAL(CTX, secp256k1_frost_nonce_gen(CTX, &secnonce[0], &pubnonce[0], session_id[0], &shares[0], msg, &invalid_pk, max64)); CHECK_ILLEGAL(CTX, secp256k1_frost_nonce_gen(CTX, &secnonce[0], &pubnonce[0], session_id[0], &agg_share[0], msg, &invalid_pk, max64));
CHECK(frost_memcmp_and_randomize(secnonce[0].data, zeros68, sizeof(secnonce[0].data)) == 0); CHECK(frost_memcmp_and_randomize(secnonce[0].data, zeros68, sizeof(secnonce[0].data)) == 0);
CHECK(secp256k1_frost_nonce_gen(CTX, &secnonce[0], &pubnonce[0], session_id[0], &shares[0], msg, &pk, NULL) == 1); CHECK(secp256k1_frost_nonce_gen(CTX, &secnonce[0], &pubnonce[0], session_id[0], &agg_share[0], msg, &agg_pk, NULL) == 1);
/* Every in-argument except session_id can be NULL */ /* Every in-argument except session_id can be NULL */
CHECK(secp256k1_frost_nonce_gen(CTX, &secnonce[0], &pubnonce[0], session_id[0], NULL, NULL, NULL, NULL) == 1); CHECK(secp256k1_frost_nonce_gen(CTX, &secnonce[0], &pubnonce[0], session_id[0], NULL, NULL, NULL, NULL) == 1);
CHECK(secp256k1_frost_nonce_gen(CTX, &secnonce[1], &pubnonce[1], session_id[1], &shares[1], NULL, NULL, NULL) == 1); CHECK(secp256k1_frost_nonce_gen(CTX, &secnonce[1], &pubnonce[1], session_id[1], &agg_share[1], NULL, NULL, NULL) == 1);
CHECK(secp256k1_frost_nonce_gen(CTX, &secnonce[2], &pubnonce[2], session_id[2], &shares[2], NULL, NULL, NULL) == 1); CHECK(secp256k1_frost_nonce_gen(CTX, &secnonce[2], &pubnonce[2], session_id[2], &agg_share[2], NULL, NULL, NULL) == 1);
/** Serialize and parse public nonces **/ /** Serialize and parse public nonces **/
CHECK_ILLEGAL(CTX, secp256k1_frost_pubnonce_serialize(CTX, NULL, &pubnonce[0])); CHECK_ILLEGAL(CTX, secp256k1_frost_pubnonce_serialize(CTX, NULL, &pubnonce[0]));
@ -297,51 +423,50 @@ void frost_api_tests(void) {
} }
/** Process nonces **/ /** Process nonces **/
CHECK(secp256k1_frost_nonce_process(CTX, &session[0], pubnonce_ptr, 3, msg, &pk, ids[0], ids, &tweak_cache, &adaptor) == 1); CHECK(secp256k1_frost_nonce_process(CTX, &session[0], pubnonce_ptr, 3, msg, &agg_pk, id_ptr[0], id_ptr, &tweak_cache, &adaptor) == 1);
CHECK_ILLEGAL(CTX, secp256k1_frost_nonce_process(CTX, NULL, pubnonce_ptr, 3, msg, &pk, ids[0], ids, &tweak_cache, &adaptor)); CHECK_ILLEGAL(CTX, secp256k1_frost_nonce_process(CTX, NULL, pubnonce_ptr, 3, msg, &agg_pk, id_ptr[0], id_ptr, &tweak_cache, &adaptor));
CHECK_ILLEGAL(CTX, secp256k1_frost_nonce_process(CTX, &session[0], NULL, 3, msg, &pk, ids[0], ids, &tweak_cache, &adaptor)); CHECK_ILLEGAL(CTX, secp256k1_frost_nonce_process(CTX, &session[0], NULL, 3, msg, &agg_pk, id_ptr[0], id_ptr, &tweak_cache, &adaptor));
CHECK_ILLEGAL(CTX, secp256k1_frost_nonce_process(CTX, &session[0], pubnonce_ptr, 0, msg, &pk, ids[0], ids, &tweak_cache, &adaptor)); CHECK_ILLEGAL(CTX, secp256k1_frost_nonce_process(CTX, &session[0], pubnonce_ptr, 0, msg, &agg_pk, id_ptr[0], id_ptr, &tweak_cache, &adaptor));
CHECK_ILLEGAL(CTX, secp256k1_frost_nonce_process(CTX, &session[0], invalid_pubnonce_ptr, 3, msg, &pk, ids[0], ids, &tweak_cache, &adaptor)); CHECK_ILLEGAL(CTX, secp256k1_frost_nonce_process(CTX, &session[0], invalid_pubnonce_ptr, 3, msg, &agg_pk, id_ptr[0], id_ptr, &tweak_cache, &adaptor));
CHECK_ILLEGAL(CTX, secp256k1_frost_nonce_process(CTX, &session[0], pubnonce_ptr, 3, NULL, &pk, ids[0], ids, &tweak_cache, &adaptor)); CHECK_ILLEGAL(CTX, secp256k1_frost_nonce_process(CTX, &session[0], pubnonce_ptr, 3, NULL, &agg_pk, id_ptr[0], id_ptr, &tweak_cache, &adaptor));
CHECK_ILLEGAL(CTX, secp256k1_frost_nonce_process(CTX, &session[0], pubnonce_ptr, 3, msg, NULL, ids[0], ids, &tweak_cache, &adaptor)); CHECK_ILLEGAL(CTX, secp256k1_frost_nonce_process(CTX, &session[0], pubnonce_ptr, 3, msg, NULL, id_ptr[0], id_ptr, &tweak_cache, &adaptor));
CHECK_ILLEGAL(CTX, secp256k1_frost_nonce_process(CTX, &session[0], pubnonce_ptr, 3, msg, &pk, 0, ids, &tweak_cache, &adaptor)); CHECK_ILLEGAL(CTX, secp256k1_frost_nonce_process(CTX, &session[0], pubnonce_ptr, 3, msg, &agg_pk, NULL, id_ptr, &tweak_cache, &adaptor));
CHECK_ILLEGAL(CTX, secp256k1_frost_nonce_process(CTX, &session[0], pubnonce_ptr, 3, msg, &pk, ids[0], invalid_ids, &tweak_cache, &adaptor)); CHECK_ILLEGAL(CTX, secp256k1_frost_nonce_process(CTX, &session[0], pubnonce_ptr, 3, msg, &agg_pk, id_ptr[0], NULL, &tweak_cache, &adaptor));
CHECK_ILLEGAL(CTX, secp256k1_frost_nonce_process(CTX, &session[0], pubnonce_ptr, 3, msg, &pk, ids[0], NULL, &tweak_cache, &adaptor)); CHECK(secp256k1_frost_nonce_process(CTX, &session[0], pubnonce_ptr, 3, msg, &agg_pk, id_ptr[0], id_ptr, NULL, &adaptor) == 1);
CHECK(secp256k1_frost_nonce_process(CTX, &session[0], pubnonce_ptr, 3, msg, &pk, ids[0], ids, NULL, &adaptor) == 1); CHECK_ILLEGAL(CTX, secp256k1_frost_nonce_process(CTX, &session[0], pubnonce_ptr, 3, msg, &agg_pk, id_ptr[0], id_ptr, &invalid_tweak_cache, &adaptor));
CHECK_ILLEGAL(CTX, secp256k1_frost_nonce_process(CTX, &session[0], pubnonce_ptr, 3, msg, &pk, ids[0], ids, &invalid_tweak_cache, &adaptor)); CHECK(secp256k1_frost_nonce_process(CTX, &session[0], pubnonce_ptr, 3, msg, &agg_pk, id_ptr[0], id_ptr, &tweak_cache, NULL) == 1);
CHECK(secp256k1_frost_nonce_process(CTX, &session[0], pubnonce_ptr, 3, msg, &pk, ids[0], ids, &tweak_cache, NULL) == 1); CHECK_ILLEGAL(CTX, secp256k1_frost_nonce_process(CTX, &session[0], pubnonce_ptr, 3, msg, &agg_pk, id_ptr[0], id_ptr, &tweak_cache, (secp256k1_pubkey *)&invalid_pk));
CHECK_ILLEGAL(CTX, secp256k1_frost_nonce_process(CTX, &session[0], pubnonce_ptr, 3, msg, &pk, ids[0], ids, &tweak_cache, (secp256k1_pubkey *)&invalid_pk));
CHECK(secp256k1_frost_nonce_process(CTX, &session[0], pubnonce_ptr, 3, msg, &pk, ids[0], ids, &tweak_cache, &adaptor) == 1); CHECK(secp256k1_frost_nonce_process(CTX, &session[0], pubnonce_ptr, 3, msg, &agg_pk, id_ptr[0], id_ptr, &tweak_cache, &adaptor) == 1);
CHECK(secp256k1_frost_nonce_process(CTX, &session[1], pubnonce_ptr, 3, msg, &pk, ids[1], ids, &tweak_cache, &adaptor) == 1); CHECK(secp256k1_frost_nonce_process(CTX, &session[1], pubnonce_ptr, 3, msg, &agg_pk, id_ptr[1], id_ptr, &tweak_cache, &adaptor) == 1);
CHECK(secp256k1_frost_nonce_process(CTX, &session[2], pubnonce_ptr, 3, msg, &pk, ids[2], ids, &tweak_cache, &adaptor) == 1); CHECK(secp256k1_frost_nonce_process(CTX, &session[2], pubnonce_ptr, 3, msg, &agg_pk, id_ptr[2], id_ptr, &tweak_cache, &adaptor) == 1);
memcpy(&secnonce_tmp, &secnonce[0], sizeof(secnonce_tmp)); memcpy(&secnonce_tmp, &secnonce[0], sizeof(secnonce_tmp));
CHECK(secp256k1_frost_partial_sign(CTX, &partial_sig[0], &secnonce_tmp, &shares[0], &session[0], &tweak_cache) == 1); CHECK(secp256k1_frost_partial_sign(CTX, &partial_sig[0], &secnonce_tmp, &agg_share[0], &session[0], &tweak_cache) == 1);
/* The secnonce is set to 0 and subsequent signing attempts fail */ /* The secnonce is set to 0 and subsequent signing attempts fail */
CHECK(secp256k1_memcmp_var(&secnonce_tmp, zeros68, sizeof(secnonce_tmp)) == 0); CHECK(secp256k1_memcmp_var(&secnonce_tmp, zeros68, sizeof(secnonce_tmp)) == 0);
CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sign(CTX, &partial_sig[0], &secnonce_tmp, &shares[0], &session[0], &tweak_cache)); CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sign(CTX, &partial_sig[0], &secnonce_tmp, &agg_share[0], &session[0], &tweak_cache));
memcpy(&secnonce_tmp, &secnonce[0], sizeof(secnonce_tmp)); memcpy(&secnonce_tmp, &secnonce[0], sizeof(secnonce_tmp));
CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sign(CTX, NULL, &secnonce_tmp, &shares[0], &session[0], &tweak_cache)); CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sign(CTX, NULL, &secnonce_tmp, &agg_share[0], &session[0], &tweak_cache));
memcpy(&secnonce_tmp, &secnonce[0], sizeof(secnonce_tmp)); memcpy(&secnonce_tmp, &secnonce[0], sizeof(secnonce_tmp));
CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sign(CTX, &partial_sig[0], NULL, &shares[0], &session[0], &tweak_cache)); CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sign(CTX, &partial_sig[0], NULL, &agg_share[0], &session[0], &tweak_cache));
CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sign(CTX, &partial_sig[0], &invalid_secnonce, &shares[0], &session[0], &tweak_cache)); CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sign(CTX, &partial_sig[0], &invalid_secnonce, &agg_share[0], &session[0], &tweak_cache));
CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sign(CTX, &partial_sig[0], &secnonce_tmp, NULL, &session[0], &tweak_cache)); CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sign(CTX, &partial_sig[0], &secnonce_tmp, NULL, &session[0], &tweak_cache));
memcpy(&secnonce_tmp, &secnonce[0], sizeof(secnonce_tmp)); memcpy(&secnonce_tmp, &secnonce[0], sizeof(secnonce_tmp));
CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sign(CTX, &partial_sig[0], &secnonce_tmp, &invalid_share, &session[0], &tweak_cache)); CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sign(CTX, &partial_sig[0], &secnonce_tmp, &invalid_share, &session[0], &tweak_cache));
memcpy(&secnonce_tmp, &secnonce[0], sizeof(secnonce_tmp)); memcpy(&secnonce_tmp, &secnonce[0], sizeof(secnonce_tmp));
CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sign(CTX, &partial_sig[0], &secnonce_tmp, &shares[0], NULL, &tweak_cache)); CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sign(CTX, &partial_sig[0], &secnonce_tmp, &agg_share[0], NULL, &tweak_cache));
memcpy(&secnonce_tmp, &secnonce[0], sizeof(secnonce_tmp)); memcpy(&secnonce_tmp, &secnonce[0], sizeof(secnonce_tmp));
CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sign(CTX, &partial_sig[0], &secnonce_tmp, &shares[0], &invalid_session, &tweak_cache)); CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sign(CTX, &partial_sig[0], &secnonce_tmp, &agg_share[0], &invalid_session, &tweak_cache));
memcpy(&secnonce_tmp, &secnonce[0], sizeof(secnonce_tmp)); memcpy(&secnonce_tmp, &secnonce[0], sizeof(secnonce_tmp));
CHECK(secp256k1_frost_partial_sign(CTX, &partial_sig[0], &secnonce_tmp, &shares[0], &session[0], NULL) == 1); CHECK(secp256k1_frost_partial_sign(CTX, &partial_sig[0], &secnonce_tmp, &agg_share[0], &session[0], NULL) == 1);
memcpy(&secnonce_tmp, &secnonce[0], sizeof(secnonce_tmp)); memcpy(&secnonce_tmp, &secnonce[0], sizeof(secnonce_tmp));
CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sign(CTX, &partial_sig[0], &secnonce_tmp, &shares[0], &session[0], &invalid_tweak_cache)); CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sign(CTX, &partial_sig[0], &secnonce_tmp, &agg_share[0], &session[0], &invalid_tweak_cache));
memcpy(&secnonce_tmp, &secnonce[0], sizeof(secnonce_tmp)); memcpy(&secnonce_tmp, &secnonce[0], sizeof(secnonce_tmp));
CHECK(secp256k1_frost_partial_sign(CTX, &partial_sig[0], &secnonce[0], &shares[0], &session[0], &tweak_cache) == 1); CHECK(secp256k1_frost_partial_sign(CTX, &partial_sig[0], &secnonce[0], &agg_share[0], &session[0], &tweak_cache) == 1);
CHECK(secp256k1_frost_partial_sign(CTX, &partial_sig[1], &secnonce[1], &shares[1], &session[1], &tweak_cache) == 1); CHECK(secp256k1_frost_partial_sign(CTX, &partial_sig[1], &secnonce[1], &agg_share[1], &session[1], &tweak_cache) == 1);
CHECK(secp256k1_frost_partial_sign(CTX, &partial_sig[2], &secnonce[2], &shares[2], &session[2], &tweak_cache) == 1); CHECK(secp256k1_frost_partial_sign(CTX, &partial_sig[2], &secnonce[2], &agg_share[2], &session[2], &tweak_cache) == 1);
CHECK(secp256k1_frost_partial_sig_serialize(CTX, buf, &partial_sig[0]) == 1); CHECK(secp256k1_frost_partial_sig_serialize(CTX, buf, &partial_sig[0]) == 1);
CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sig_serialize(CTX, NULL, &partial_sig[0])); CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sig_serialize(CTX, NULL, &partial_sig[0]));
@ -360,22 +485,22 @@ void frost_api_tests(void) {
} }
/** Partial signature verification */ /** Partial signature verification */
CHECK(secp256k1_frost_partial_sig_verify(CTX, &partial_sig[0], &pubnonce[0], &pubshares[0], &session[0], &tweak_cache) == 1); CHECK(secp256k1_frost_partial_sig_verify(CTX, &partial_sig[0], &pubnonce[0], &pubshare[0], &session[0], &tweak_cache) == 1);
CHECK(secp256k1_frost_partial_sig_verify(CTX, &partial_sig[1], &pubnonce[0], &pubshares[0], &session[0], &tweak_cache) == 0); CHECK(secp256k1_frost_partial_sig_verify(CTX, &partial_sig[1], &pubnonce[0], &pubshare[0], &session[0], &tweak_cache) == 0);
CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sig_verify(CTX, NULL, &pubnonce[0], &pubshares[0], &session[0], &tweak_cache)); CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sig_verify(CTX, NULL, &pubnonce[0], &pubshare[0], &session[0], &tweak_cache));
CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sig_verify(CTX, &invalid_partial_sig, &pubnonce[0], &pubshares[0], &session[0], &tweak_cache)); CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sig_verify(CTX, &invalid_partial_sig, &pubnonce[0], &pubshare[0], &session[0], &tweak_cache));
CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sig_verify(CTX, &partial_sig[0], NULL, &pubshares[0], &session[0], &tweak_cache)); CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sig_verify(CTX, &partial_sig[0], NULL, &pubshare[0], &session[0], &tweak_cache));
CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sig_verify(CTX, &partial_sig[0], &invalid_pubnonce, &pubshares[0], &session[0], &tweak_cache)); CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sig_verify(CTX, &partial_sig[0], &invalid_pubnonce, &pubshare[0], &session[0], &tweak_cache));
CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sig_verify(CTX, &partial_sig[0], &pubnonce[0], NULL, &session[0], &tweak_cache)); CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sig_verify(CTX, &partial_sig[0], &pubnonce[0], NULL, &session[0], &tweak_cache));
CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sig_verify(CTX, &partial_sig[0], &pubnonce[0], &invalid_vss_pk, &session[0], &tweak_cache)); CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sig_verify(CTX, &partial_sig[0], &pubnonce[0], &invalid_vss_pk, &session[0], &tweak_cache));
CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sig_verify(CTX, &partial_sig[0], &pubnonce[0], &pubshares[0], NULL, &tweak_cache)); CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sig_verify(CTX, &partial_sig[0], &pubnonce[0], &pubshare[0], NULL, &tweak_cache));
CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sig_verify(CTX, &partial_sig[0], &pubnonce[0], &pubshares[0], &invalid_session, &tweak_cache)); CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sig_verify(CTX, &partial_sig[0], &pubnonce[0], &pubshare[0], &invalid_session, &tweak_cache));
CHECK(secp256k1_frost_partial_sig_verify(CTX, &partial_sig[0], &pubnonce[0], &pubshares[0], &session[0], NULL) == 1); CHECK(secp256k1_frost_partial_sig_verify(CTX, &partial_sig[0], &pubnonce[0], &pubshare[0], &session[0], NULL) == 1);
CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sig_verify(CTX, &partial_sig[0], &pubnonce[0], &pubshares[0], &session[0], &invalid_tweak_cache)); CHECK_ILLEGAL(CTX, secp256k1_frost_partial_sig_verify(CTX, &partial_sig[0], &pubnonce[0], &pubshare[0], &session[0], &invalid_tweak_cache));
CHECK(secp256k1_frost_partial_sig_verify(CTX, &partial_sig[0], &pubnonce[0], &pubshares[0], &session[0], &tweak_cache) == 1); CHECK(secp256k1_frost_partial_sig_verify(CTX, &partial_sig[0], &pubnonce[0], &pubshare[0], &session[0], &tweak_cache) == 1);
CHECK(secp256k1_frost_partial_sig_verify(CTX, &partial_sig[1], &pubnonce[1], &pubshares[1], &session[1], &tweak_cache) == 1); CHECK(secp256k1_frost_partial_sig_verify(CTX, &partial_sig[1], &pubnonce[1], &pubshare[1], &session[1], &tweak_cache) == 1);
CHECK(secp256k1_frost_partial_sig_verify(CTX, &partial_sig[2], &pubnonce[2], &pubshares[2], &session[2], &tweak_cache) == 1); CHECK(secp256k1_frost_partial_sig_verify(CTX, &partial_sig[2], &pubnonce[2], &pubshare[2], &session[2], &tweak_cache) == 1);
/** Signature aggregation and verification */ /** Signature aggregation and verification */
CHECK(secp256k1_frost_partial_sig_agg(CTX, pre_sig, &session[0], partial_sig_ptr, 3) == 1); CHECK(secp256k1_frost_partial_sig_agg(CTX, pre_sig, &session[0], partial_sig_ptr, 3) == 1);
@ -405,10 +530,10 @@ void frost_api_tests(void) {
/* sig and pre_sig argument point to the same location */ /* sig and pre_sig argument point to the same location */
memcpy(final_sig, pre_sig, sizeof(final_sig)); memcpy(final_sig, pre_sig, sizeof(final_sig));
CHECK(secp256k1_frost_adapt(CTX, final_sig, final_sig, sec_adaptor, nonce_parity) == 1); CHECK(secp256k1_frost_adapt(CTX, final_sig, final_sig, sec_adaptor, nonce_parity) == 1);
CHECK(secp256k1_schnorrsig_verify(CTX, final_sig, msg, sizeof(msg), &pk) == 1); CHECK(secp256k1_schnorrsig_verify(CTX, final_sig, msg, sizeof(msg), &agg_pk) == 1);
CHECK(secp256k1_frost_adapt(CTX, final_sig, pre_sig, sec_adaptor, nonce_parity) == 1); CHECK(secp256k1_frost_adapt(CTX, final_sig, pre_sig, sec_adaptor, nonce_parity) == 1);
CHECK(secp256k1_schnorrsig_verify(CTX, final_sig, msg, sizeof(msg), &pk) == 1); CHECK(secp256k1_schnorrsig_verify(CTX, final_sig, msg, sizeof(msg), &agg_pk) == 1);
/** Secret adaptor can be extracted from signature */ /** Secret adaptor can be extracted from signature */
CHECK(secp256k1_frost_extract_adaptor(CTX, sec_adaptor1, final_sig, pre_sig, nonce_parity) == 1); CHECK(secp256k1_frost_extract_adaptor(CTX, sec_adaptor1, final_sig, pre_sig, nonce_parity) == 1);
@ -482,9 +607,37 @@ void frost_nonce_test(void) {
} }
} }
void frost_sha256_tag_test_internal(secp256k1_sha256 *sha_tagged, unsigned char *tag, size_t taglen) {
secp256k1_sha256 sha;
unsigned char buf[32];
unsigned char buf2[32];
size_t i;
secp256k1_sha256_initialize(&sha);
secp256k1_sha256_write(&sha, tag, taglen);
secp256k1_sha256_finalize(&sha, buf);
/* buf = SHA256(tag) */
secp256k1_sha256_initialize(&sha);
secp256k1_sha256_write(&sha, buf, 32);
secp256k1_sha256_write(&sha, buf, 32);
/* Is buffer fully consumed? */
CHECK((sha.bytes & 0x3F) == 0);
/* Compare with tagged SHA */
for (i = 0; i < 8; i++) {
CHECK(sha_tagged->s[i] == sha.s[i]);
}
secp256k1_sha256_write(&sha, buf, 32);
secp256k1_sha256_write(sha_tagged, buf, 32);
secp256k1_sha256_finalize(&sha, buf);
secp256k1_sha256_finalize(sha_tagged, buf2);
CHECK(secp256k1_memcmp_var(buf, buf2, 32) == 0);
}
/* Attempts to create a signature for the aggregate public key using given secret /* Attempts to create a signature for the aggregate public key using given secret
* keys and tweak_cache. */ * keys and tweak_cache. */
void frost_tweak_test_helper(const secp256k1_xonly_pubkey* pk, const secp256k1_frost_share *sr0, const secp256k1_frost_share *sr1, const secp256k1_frost_share *sr2, secp256k1_frost_tweak_cache *tweak_cache, const size_t *ids, const secp256k1_pubkey *sr_pk0, const secp256k1_pubkey *sr_pk1, const secp256k1_pubkey *sr_pk2) { void frost_tweak_test_helper(const secp256k1_xonly_pubkey* agg_pk, const secp256k1_frost_share *sr0, const secp256k1_frost_share *sr1, const secp256k1_frost_share *sr2, secp256k1_frost_tweak_cache *tweak_cache, const unsigned char * const* ids33, const secp256k1_pubkey *sr_pk0, const secp256k1_pubkey *sr_pk1, const secp256k1_pubkey *sr_pk2) {
unsigned char session_id[3][32]; unsigned char session_id[3][32];
unsigned char msg[32]; unsigned char msg[32];
secp256k1_frost_secnonce secnonce[3]; secp256k1_frost_secnonce secnonce[3];
@ -509,9 +662,9 @@ void frost_tweak_test_helper(const secp256k1_xonly_pubkey* pk, const secp256k1_f
CHECK(secp256k1_frost_nonce_gen(CTX, &secnonce[1], &pubnonce[1], session_id[1], sr1, NULL, NULL, NULL) == 1); CHECK(secp256k1_frost_nonce_gen(CTX, &secnonce[1], &pubnonce[1], session_id[1], sr1, NULL, NULL, NULL) == 1);
CHECK(secp256k1_frost_nonce_gen(CTX, &secnonce[2], &pubnonce[2], session_id[2], sr2, NULL, NULL, NULL) == 1); CHECK(secp256k1_frost_nonce_gen(CTX, &secnonce[2], &pubnonce[2], session_id[2], sr2, NULL, NULL, NULL) == 1);
CHECK(secp256k1_frost_nonce_process(CTX, &session[0], pubnonce_ptr, 3, msg, pk, ids[0], ids, tweak_cache, NULL) == 1); CHECK(secp256k1_frost_nonce_process(CTX, &session[0], pubnonce_ptr, 3, msg, agg_pk, ids33[0], ids33, tweak_cache, NULL) == 1);
CHECK(secp256k1_frost_nonce_process(CTX, &session[1], pubnonce_ptr, 3, msg, pk, ids[1], ids, tweak_cache, NULL) == 1); CHECK(secp256k1_frost_nonce_process(CTX, &session[1], pubnonce_ptr, 3, msg, agg_pk, ids33[1], ids33, tweak_cache, NULL) == 1);
CHECK(secp256k1_frost_nonce_process(CTX, &session[2], pubnonce_ptr, 3, msg, pk, ids[2], ids, tweak_cache, NULL) == 1); CHECK(secp256k1_frost_nonce_process(CTX, &session[2], pubnonce_ptr, 3, msg, agg_pk, ids33[2], ids33, tweak_cache, NULL) == 1);
CHECK(secp256k1_frost_partial_sign(CTX, &partial_sig[0], &secnonce[0], sr0, &session[0], tweak_cache) == 1); CHECK(secp256k1_frost_partial_sign(CTX, &partial_sig[0], &secnonce[0], sr0, &session[0], tweak_cache) == 1);
@ -523,31 +676,51 @@ void frost_tweak_test_helper(const secp256k1_xonly_pubkey* pk, const secp256k1_f
CHECK(secp256k1_frost_partial_sig_verify(CTX, &partial_sig[2], &pubnonce[2], sr_pk2, &session[2], tweak_cache) == 1); CHECK(secp256k1_frost_partial_sig_verify(CTX, &partial_sig[2], &pubnonce[2], sr_pk2, &session[2], tweak_cache) == 1);
CHECK(secp256k1_frost_partial_sig_agg(CTX, final_sig, &session[0], partial_sig_ptr, 3) == 1); CHECK(secp256k1_frost_partial_sig_agg(CTX, final_sig, &session[0], partial_sig_ptr, 3) == 1);
CHECK(secp256k1_schnorrsig_verify(CTX, final_sig, msg, sizeof(msg), pk) == 1); CHECK(secp256k1_schnorrsig_verify(CTX, final_sig, msg, sizeof(msg), agg_pk) == 1);
} }
/* Create aggregate public key P[0], tweak multiple times (using xonly and /* Create aggregate public key P[0], tweak multiple times (using xonly and
* ordinary tweaking) and test signing. */ * ordinary tweaking) and test signing. */
void frost_tweak_test(void) { void frost_tweak_test(void) {
secp256k1_pubkey pubshares[5]; unsigned char sk[5][32];
secp256k1_pubkey pubshare[5];
secp256k1_frost_tweak_cache tweak_cache; secp256k1_frost_tweak_cache tweak_cache;
enum { N_TWEAKS = 8 }; enum { N_TWEAKS = 8 };
secp256k1_pubkey P[N_TWEAKS + 1]; secp256k1_pubkey P[N_TWEAKS + 1];
secp256k1_xonly_pubkey P_xonly[N_TWEAKS + 1]; secp256k1_xonly_pubkey P_xonly[N_TWEAKS + 1];
unsigned char seed[32]; unsigned char seed[5][32];
secp256k1_frost_share shares[5]; secp256k1_pubkey vss_commitment[5][3];
int i; const secp256k1_pubkey *vss_ptr[5];
size_t ids[5]; unsigned char pok[5][64];
secp256k1_frost_share shares[5][5];
secp256k1_testrand256(seed); const secp256k1_frost_share *share_ptr[5];
secp256k1_frost_share agg_share[5];
int i, j;
unsigned char id[5][33];
const unsigned char *id_ptr[5];
/* Key Setup */ /* Key Setup */
for (i = 0; i < 5; i++) { for (i = 0; i < 5; i++) {
ids[i] = i + 1; secp256k1_testrand256(seed[i]);
} secp256k1_testrand256(sk[i]);
CHECK(secp256k1_frost_shares_trusted_gen(CTX, shares, pubshares, &P_xonly[0], seed, 3, 5) == 1); vss_ptr[i] = vss_commitment[i];
id_ptr[i] = id[i];
frost_tweak_test_helper(&P_xonly[0], &shares[0], &shares[1], &shares[2], NULL, ids, &pubshares[0], &pubshares[1], &pubshares[2]); CHECK(frost_create_pk(id[i], sk[i]));
}
for (i = 0; i < 5; i++) {
CHECK(secp256k1_frost_shares_gen(CTX, shares[i], vss_commitment[i], pok[i], seed[i], 3, 5, id_ptr) == 1);
}
for (i = 0; i < 5; i++) {
for (j = 0; j < 5; j++) {
share_ptr[j] = &shares[j][i];
CHECK(secp256k1_frost_share_verify(CTX, 3, id_ptr[i], share_ptr[j], &vss_ptr[j]) == 1);
CHECK(secp256k1_frost_compute_pubshare(CTX, &pubshare[j], 3, id_ptr[j], vss_ptr, 5) == 1);
}
CHECK(secp256k1_frost_share_agg(CTX, &agg_share[i], &P_xonly[0], share_ptr, vss_ptr, 5, 3, id_ptr[i]) == 1);
}
frost_tweak_test_helper(&P_xonly[0], &agg_share[0], &agg_share[1], &agg_share[2], NULL, id_ptr, &pubshare[0], &pubshare[1], &pubshare[2]);
CHECK(secp256k1_frost_pubkey_get(CTX, &P[0], &P_xonly[0])); CHECK(secp256k1_frost_pubkey_get(CTX, &P[0], &P_xonly[0]));
CHECK(secp256k1_frost_pubkey_tweak(CTX, &tweak_cache, &P_xonly[0]) == 1); CHECK(secp256k1_frost_pubkey_tweak(CTX, &tweak_cache, &P_xonly[0]) == 1);
@ -579,22 +752,37 @@ void frost_tweak_test(void) {
CHECK(secp256k1_memcmp_var(&tmp_key, &P[i], sizeof(tmp_key)) == 0); CHECK(secp256k1_memcmp_var(&tmp_key, &P[i], sizeof(tmp_key)) == 0);
} }
/* Test signing for P[i] */ /* Test signing for P[i] */
frost_tweak_test_helper(&P_xonly[i], &shares[0], &shares[1], &shares[2], &tweak_cache, ids, &pubshares[0], &pubshares[1], &pubshares[2]); frost_tweak_test_helper(&P_xonly[i], &agg_share[0], &agg_share[1], &agg_share[2], &tweak_cache, id_ptr, &pubshare[0], &pubshare[1], &pubshare[2]);
} }
} }
/* Performs a FROST DKG */ /* Performs a FROST DKG */
void frost_dkg_test_helper(secp256k1_frost_share *shares, secp256k1_xonly_pubkey *pk) { void frost_dkg_test_helper(secp256k1_frost_share *agg_share, secp256k1_xonly_pubkey *agg_pk, const unsigned char * const* ids33) {
unsigned char seed[32]; secp256k1_pubkey vss_commitment[5][3];
secp256k1_pubkey pubshares[5]; const secp256k1_pubkey *vss_ptr[5];
unsigned char pok[5][64];
unsigned char seed[5][32];
secp256k1_frost_share shares[5][5];
const secp256k1_frost_share *share_ptr[5];
int i, j;
secp256k1_testrand256(seed); for (i = 0; i < 5; i++) {
secp256k1_testrand256(seed[i]);
CHECK(secp256k1_frost_shares_trusted_gen(CTX, shares, pubshares, pk, seed, 3, 5) == 1); vss_ptr[i] = vss_commitment[i];
}
for (i = 0; i < 5; i++) {
CHECK(secp256k1_frost_shares_gen(CTX, shares[i], vss_commitment[i], pok[i], seed[i], 3, 5, ids33) == 1);
}
for (i = 0; i < 5; i++) {
for (j = 0; j < 5; j++) {
share_ptr[j] = &shares[j][i];
}
CHECK(secp256k1_frost_share_agg(CTX, &agg_share[i], agg_pk, share_ptr, vss_ptr, 5, 3, ids33[i]) == 1);
}
} }
/* Signs a message with a FROST keypair */ /* Signs a message with a FROST keypair */
int frost_sign_test_helper(unsigned char *final_sig, const secp256k1_frost_share *share, const secp256k1_xonly_pubkey *pk, const unsigned char *msg, const secp256k1_pubkey *adaptor) { int frost_sign_test_helper(unsigned char *final_sig, const secp256k1_frost_share *agg_share, const secp256k1_xonly_pubkey *agg_pk, const unsigned char * const* ids33, const unsigned char *msg, const secp256k1_pubkey *adaptor) {
unsigned char session_id[3][32]; unsigned char session_id[3][32];
secp256k1_frost_secnonce secnonce[3]; secp256k1_frost_secnonce secnonce[3];
secp256k1_frost_pubnonce pubnonce[3]; secp256k1_frost_pubnonce pubnonce[3];
@ -605,22 +793,20 @@ int frost_sign_test_helper(unsigned char *final_sig, const secp256k1_frost_share
int i; int i;
int nonce_parity; int nonce_parity;
secp256k1_frost_session_internal session_i; secp256k1_frost_session_internal session_i;
size_t ids[5];
for (i = 0; i < 3; i++) { for (i = 0; i < 3; i++) {
pubnonce_ptr[i] = &pubnonce[i]; pubnonce_ptr[i] = &pubnonce[i];
partial_sig_ptr[i] = &partial_sig[i]; partial_sig_ptr[i] = &partial_sig[i];
ids[i] = i + 1;
} }
for (i = 0; i < 3; i++) { for (i = 0; i < 3; i++) {
secp256k1_testrand256(session_id[i]); secp256k1_testrand256(session_id[i]);
CHECK(secp256k1_frost_nonce_gen(CTX, &secnonce[i], &pubnonce[i], session_id[i], &share[i], NULL, NULL, NULL) == 1); CHECK(secp256k1_frost_nonce_gen(CTX, &secnonce[i], &pubnonce[i], session_id[i], agg_share, NULL, NULL, NULL) == 1);
} }
for (i = 0; i < 3; i++) { for (i = 0; i < 3; i++) {
CHECK(secp256k1_frost_nonce_process(CTX, &session, pubnonce_ptr, 3, msg, pk, i + 1, ids, NULL, adaptor) == 1); CHECK(secp256k1_frost_nonce_process(CTX, &session, pubnonce_ptr, 3, msg, agg_pk, ids33[i], ids33, NULL, adaptor) == 1);
CHECK(secp256k1_frost_partial_sign(CTX, &partial_sig[i], &secnonce[i], &share[i], &session, NULL) == 1); CHECK(secp256k1_frost_partial_sign(CTX, &partial_sig[i], &secnonce[i], &agg_share[i], &session, NULL) == 1);
} }
CHECK(secp256k1_frost_partial_sig_agg(CTX, final_sig, &session, partial_sig_ptr, 3) == 1); CHECK(secp256k1_frost_partial_sig_agg(CTX, final_sig, &session, partial_sig_ptr, 3) == 1);
@ -638,10 +824,12 @@ void frost_rand_scalar(secp256k1_scalar *scalar) {
} }
void frost_multi_hop_lock_tests(void) { void frost_multi_hop_lock_tests(void) {
secp256k1_frost_share share_a[5]; secp256k1_frost_share agg_share_a[5];
secp256k1_frost_share share_b[5]; secp256k1_frost_share agg_share_b[5];
secp256k1_xonly_pubkey agg_pk_a; secp256k1_xonly_pubkey agg_pk_a;
secp256k1_xonly_pubkey agg_pk_b; secp256k1_xonly_pubkey agg_pk_b;
unsigned char sk_a[5][32];
unsigned char sk_b[5][32];
unsigned char asig_ab[64]; unsigned char asig_ab[64];
unsigned char asig_bc[64]; unsigned char asig_bc[64];
unsigned char pop[32]; unsigned char pop[32];
@ -657,12 +845,29 @@ void frost_multi_hop_lock_tests(void) {
unsigned char sig_bc[64]; unsigned char sig_bc[64];
int nonce_parity_ab; int nonce_parity_ab;
int nonce_parity_bc; int nonce_parity_bc;
int i;
unsigned char id_a[5][33];
const unsigned char *id_ptr_a[5];
unsigned char id_b[5][33];
const unsigned char *id_ptr_b[5];
/* Alice DKG */ /* Alice DKG */
frost_dkg_test_helper(share_a, &agg_pk_a); for (i = 0; i < 5; i++) {
secp256k1_testrand256(sk_a[i]);
id_ptr_a[i] = id_a[i];
CHECK(frost_create_pk(id_a[i], sk_a[i]));
}
frost_dkg_test_helper(agg_share_a, &agg_pk_a, id_ptr_a);
/* Bob DKG */ /* Bob DKG */
frost_dkg_test_helper(share_b, &agg_pk_b); for (i = 0; i < 5; i++) {
secp256k1_testrand256(sk_b[i]);
id_ptr_b[i] = id_b[i];
CHECK(frost_create_pk(id_b[i], sk_b[i]));
}
frost_dkg_test_helper(agg_share_b, &agg_pk_b, id_ptr_b);
/* Carol setup */ /* Carol setup */
/* Proof of payment */ /* Proof of payment */
@ -683,13 +888,13 @@ void frost_multi_hop_lock_tests(void) {
CHECK(secp256k1_eckey_pubkey_tweak_add(&r_ge, &tp)); CHECK(secp256k1_eckey_pubkey_tweak_add(&r_ge, &tp));
secp256k1_pubkey_save(&r, &r_ge); secp256k1_pubkey_save(&r, &r_ge);
/* Encrypt Alice's signature with the left lock as the encryption key */ /* Encrypt Alice's signature with the left lock as the encryption key */
nonce_parity_ab = frost_sign_test_helper(asig_ab, share_a, &agg_pk_a, tx_ab, &l); nonce_parity_ab = frost_sign_test_helper(asig_ab, agg_share_a, &agg_pk_a, id_ptr_a, tx_ab, &l);
/* Bob setup */ /* Bob setup */
CHECK(secp256k1_frost_verify_adaptor(CTX, asig_ab, tx_ab, &agg_pk_a, &l, nonce_parity_ab) == 1); CHECK(secp256k1_frost_verify_adaptor(CTX, asig_ab, tx_ab, &agg_pk_a, &l, nonce_parity_ab) == 1);
secp256k1_testrand256(tx_bc); secp256k1_testrand256(tx_bc);
/* Encrypt Bob's signature with the right lock as the encryption key */ /* Encrypt Bob's signature with the right lock as the encryption key */
nonce_parity_bc = frost_sign_test_helper(asig_bc, share_b, &agg_pk_b, tx_bc, &r); nonce_parity_bc = frost_sign_test_helper(asig_bc, agg_share_b, &agg_pk_b, id_ptr_b, tx_bc, &r);
/* Carol decrypt */ /* Carol decrypt */
CHECK(secp256k1_frost_verify_adaptor(CTX, asig_bc, tx_bc, &agg_pk_b, &r, nonce_parity_bc) == 1); CHECK(secp256k1_frost_verify_adaptor(CTX, asig_bc, tx_bc, &agg_pk_b, &r, nonce_parity_bc) == 1);

View File

@ -276,7 +276,6 @@ static void secp256k1_pedersen_commitment_save(secp256k1_pedersen_commitment* co
int secp256k1_pedersen_commitment_parse(const secp256k1_context* ctx, secp256k1_pedersen_commitment* commit, const unsigned char *input) { int secp256k1_pedersen_commitment_parse(const secp256k1_context* ctx, secp256k1_pedersen_commitment* commit, const unsigned char *input) {
secp256k1_fe x; secp256k1_fe x;
secp256k1_ge ge;
VERIFY_CHECK(ctx != NULL); VERIFY_CHECK(ctx != NULL);
ARG_CHECK(commit != NULL); ARG_CHECK(commit != NULL);
@ -285,28 +284,20 @@ int secp256k1_pedersen_commitment_parse(const secp256k1_context* ctx, secp256k1_
if ((input[0] & 0xFE) != 8 || if ((input[0] & 0xFE) != 8 ||
!secp256k1_fe_set_b32_limit(&x, &input[1]) || !secp256k1_fe_set_b32_limit(&x, &input[1]) ||
!secp256k1_ge_set_xquad(&ge, &x)) { !secp256k1_ge_x_on_curve_var(&x)) {
return 0; return 0;
} }
if (input[0] & 1) {
secp256k1_ge_neg(&ge, &ge); memcpy(commit->data, input, 33);
}
secp256k1_pedersen_commitment_save(commit, &ge);
return 1; return 1;
} }
int secp256k1_pedersen_commitment_serialize(const secp256k1_context* ctx, unsigned char *output, const secp256k1_pedersen_commitment* commit) { int secp256k1_pedersen_commitment_serialize(const secp256k1_context* ctx, unsigned char *output, const secp256k1_pedersen_commitment* commit) {
secp256k1_ge ge;
VERIFY_CHECK(ctx != NULL); VERIFY_CHECK(ctx != NULL);
ARG_CHECK(output != NULL); ARG_CHECK(output != NULL);
ARG_CHECK(commit != NULL); ARG_CHECK(commit != NULL);
secp256k1_pedersen_commitment_load(&ge, commit); memcpy(output, commit->data, 33);
output[0] = 9 ^ secp256k1_fe_is_square_var(&ge.y);
secp256k1_fe_normalize_var(&ge.x);
secp256k1_fe_get_b32(&output[1], &ge.x);
return 1; return 1;
} }

View File

@ -264,7 +264,13 @@ static void test_pedersen(void) {
} }
CHECK(secp256k1_pedersen_blind_sum(CTX, &blinds[(total - 1) * 32], bptr, total - 1, inputs)); CHECK(secp256k1_pedersen_blind_sum(CTX, &blinds[(total - 1) * 32], bptr, total - 1, inputs));
for (i = 0; i < total; i++) { for (i = 0; i < total; i++) {
unsigned char result[33];
secp256k1_pedersen_commitment parse;
CHECK(secp256k1_pedersen_commit(CTX, &commits[i], &blinds[i * 32], values[i], secp256k1_generator_h)); CHECK(secp256k1_pedersen_commit(CTX, &commits[i], &blinds[i * 32], values[i], secp256k1_generator_h));
CHECK(secp256k1_pedersen_commitment_serialize(CTX, result, &commits[i]));
CHECK(secp256k1_pedersen_commitment_parse(CTX, &parse, result));
CHECK(secp256k1_memcmp_var(&commits[i], &parse, 33) == 0);
} }
CHECK(secp256k1_pedersen_verify_tally(CTX, cptr, inputs, &cptr[inputs], outputs)); CHECK(secp256k1_pedersen_verify_tally(CTX, cptr, inputs, &cptr[inputs], outputs));
CHECK(secp256k1_pedersen_verify_tally(CTX, &cptr[inputs], outputs, cptr, inputs)); CHECK(secp256k1_pedersen_verify_tally(CTX, &cptr[inputs], outputs, cptr, inputs));

View File

@ -889,10 +889,6 @@ static int secp256k1_ge_parse_ext(secp256k1_ge* ge, const unsigned char *in33) {
# include "modules/ecdsa_adaptor/main_impl.h" # include "modules/ecdsa_adaptor/main_impl.h"
#endif #endif
#ifdef ENABLE_MODULE_FROST
# include "modules/frost/main_impl.h"
#endif
#ifdef ENABLE_MODULE_MUSIG #ifdef ENABLE_MODULE_MUSIG
# include "modules/musig/main_impl.h" # include "modules/musig/main_impl.h"
#endif #endif
@ -912,3 +908,7 @@ static int secp256k1_ge_parse_ext(secp256k1_ge* ge, const unsigned char *in33) {
#ifdef ENABLE_MODULE_SURJECTIONPROOF #ifdef ENABLE_MODULE_SURJECTIONPROOF
# include "modules/surjection/main_impl.h" # include "modules/surjection/main_impl.h"
#endif #endif
#ifdef ENABLE_MODULE_FROST
# include "modules/frost/main_impl.h"
#endif