Merge commit '3660fe5e' into temp-merge-1479
This commit is contained in:
commit
248358f2bc
28 files changed
+4452
-34
No files matched your search
@@ -22,6 +22,7 @@ env:
|
||||
RECOVERY: no
|
||||
EXTRAKEYS: no
|
||||
SCHNORRSIG: no
|
||||
MUSIG: no
|
||||
ELLSWIFT: no
|
||||
### test options
|
||||
SECP256K1_TEST_ITERS:
|
||||
@@ -69,6 +70,7 @@ task:
|
||||
RECOVERY: yes
|
||||
EXTRAKEYS: yes
|
||||
SCHNORRSIG: yes
|
||||
MUSIG: yes
|
||||
ELLSWIFT: yes
|
||||
matrix:
|
||||
# Currently only gcc-snapshot, the other compilers are tested on GHA with QEMU
|
||||
@@ -86,6 +88,7 @@ task:
|
||||
RECOVERY: yes
|
||||
EXTRAKEYS: yes
|
||||
SCHNORRSIG: yes
|
||||
MUSIG: yes
|
||||
ELLSWIFT: yes
|
||||
WRAPPER_CMD: 'valgrind --error-exitcode=42'
|
||||
SECP256K1_TEST_ITERS: 2
|
||||
|
||||
+26
-26
@@ -33,12 +33,12 @@ env:
|
||||
RECOVERY: 'no'
|
||||
EXTRAKEYS: 'no'
|
||||
SCHNORRSIG: 'no'
|
||||
MUSIG: 'no'
|
||||
ELLSWIFT: 'no'
|
||||
ECDSA_S2C: 'no'
|
||||
GENERATOR: 'no'
|
||||
RANGEPROOF: 'no'
|
||||
WHITELIST: 'no'
|
||||
MUSIG: 'no'
|
||||
ECDSAADAPTOR: 'no'
|
||||
BPPP: 'no'
|
||||
SCHNORRSIG_HALFAGG: 'no'
|
||||
@@ -80,18 +80,18 @@ jobs:
|
||||
matrix:
|
||||
configuration:
|
||||
- env_vars: { WIDEMUL: 'int64', RECOVERY: 'yes' }
|
||||
- env_vars: { WIDEMUL: 'int64', ECDH: 'yes', EXTRAKEYS: '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'}
|
||||
- env_vars: { WIDEMUL: 'int64', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes'}
|
||||
- env_vars: { WIDEMUL: 'int128' }
|
||||
- env_vars: { WIDEMUL: 'int128_struct', ELLSWIFT: 'yes' }
|
||||
- env_vars: { WIDEMUL: 'int128', RECOVERY: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes' }
|
||||
- env_vars: { WIDEMUL: 'int128', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', MUSIG: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes'}
|
||||
- env_vars: { WIDEMUL: 'int128', ASM: 'x86_64', ELLSWIFT: 'yes' }
|
||||
- env_vars: { RECOVERY: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', MUSIG: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes'}
|
||||
- env_vars: { CTIMETESTS: 'no', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', MUSIG: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', CPPFLAGS: '-DVERIFY' }
|
||||
- env_vars: { WIDEMUL: 'int128_struct', ELLSWIFT: 'yes' }
|
||||
- env_vars: { WIDEMUL: 'int128', RECOVERY: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes' }
|
||||
- env_vars: { WIDEMUL: 'int128', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes'}
|
||||
- env_vars: { WIDEMUL: 'int128', ASM: 'x86_64', ELLSWIFT: 'yes' }
|
||||
- env_vars: { RECOVERY: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes'}
|
||||
- env_vars: { CTIMETESTS: 'no', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', CPPFLAGS: '-DVERIFY' }
|
||||
- env_vars: { BUILD: 'distcheck', WITH_VALGRIND: 'no', CTIMETESTS: 'no', BENCH: 'no' }
|
||||
- env_vars: { CPPFLAGS: '-DDETERMINISTIC' }
|
||||
- env_vars: { CFLAGS: '-O0', CTIMETESTS: 'no' }
|
||||
- env_vars: { CFLAGS: '-O1', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes' }
|
||||
- env_vars: { CFLAGS: '-O1', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes' }
|
||||
- env_vars: { ECMULTGENKB: 2, ECMULTWINDOW: 2 }
|
||||
- env_vars: { ECMULTGENKB: 86, ECMULTWINDOW: 4 }
|
||||
cc:
|
||||
@@ -150,13 +150,13 @@ jobs:
|
||||
RECOVERY: 'yes'
|
||||
EXTRAKEYS: 'yes'
|
||||
SCHNORRSIG: 'yes'
|
||||
MUSIG: 'yes'
|
||||
ELLSWIFT: 'yes'
|
||||
EXPERIMENTAL: 'yes'
|
||||
ECDSA_S2C: 'yes'
|
||||
RANGEPROOF: 'yes'
|
||||
WHITELIST: 'yes'
|
||||
GENERATOR: 'yes'
|
||||
MUSIG: 'yes'
|
||||
ECDSAADAPTOR: 'yes'
|
||||
BPPP: 'yes'
|
||||
SCHNORRSIG_HALFAGG: 'yes'
|
||||
@@ -204,13 +204,13 @@ jobs:
|
||||
RECOVERY: 'yes'
|
||||
EXTRAKEYS: 'yes'
|
||||
SCHNORRSIG: 'yes'
|
||||
MUSIG: 'yes'
|
||||
ELLSWIFT: 'yes'
|
||||
EXPERIMENTAL: 'yes'
|
||||
ECDSA_S2C: 'yes'
|
||||
RANGEPROOF: 'yes'
|
||||
WHITELIST: 'yes'
|
||||
GENERATOR: 'yes'
|
||||
MUSIG: 'yes'
|
||||
ECDSAADAPTOR: 'yes'
|
||||
BPPP: 'yes'
|
||||
SCHNORRSIG_HALFAGG: 'yes'
|
||||
@@ -265,13 +265,13 @@ jobs:
|
||||
RECOVERY: 'yes'
|
||||
EXTRAKEYS: 'yes'
|
||||
SCHNORRSIG: 'yes'
|
||||
MUSIG: 'yes'
|
||||
ELLSWIFT: 'yes'
|
||||
EXPERIMENTAL: 'yes'
|
||||
ECDSA_S2C: 'yes'
|
||||
GENERATOR: 'yes'
|
||||
RANGEPROOF: 'yes'
|
||||
WHITELIST: 'yes'
|
||||
MUSIG: 'yes'
|
||||
ECDSAADAPTOR: 'yes'
|
||||
BPPP: 'yes'
|
||||
SCHNORRSIG_HALFAGG: 'yes'
|
||||
@@ -320,13 +320,13 @@ jobs:
|
||||
RECOVERY: 'yes'
|
||||
EXTRAKEYS: 'yes'
|
||||
SCHNORRSIG: 'yes'
|
||||
MUSIG: 'yes'
|
||||
ELLSWIFT: 'yes'
|
||||
EXPERIMENTAL: 'yes'
|
||||
ECDSA_S2C: 'yes'
|
||||
GENERATOR: 'yes'
|
||||
RANGEPROOF: 'yes'
|
||||
WHITELIST: 'yes'
|
||||
MUSIG: 'yes'
|
||||
ECDSAADAPTOR: 'yes'
|
||||
BPPP: 'yes'
|
||||
SCHNORRSIG_HALFAGG: 'yes'
|
||||
@@ -385,13 +385,13 @@ jobs:
|
||||
RECOVERY: 'yes'
|
||||
EXTRAKEYS: 'yes'
|
||||
SCHNORRSIG: 'yes'
|
||||
MUSIG: 'yes'
|
||||
ELLSWIFT: 'yes'
|
||||
EXPERIMENTAL: 'yes'
|
||||
ECDSA_S2C: 'yes'
|
||||
GENERATOR: 'yes'
|
||||
RANGEPROOF: 'yes'
|
||||
WHITELIST: 'yes'
|
||||
MUSIG: 'yes'
|
||||
ECDSAADAPTOR: 'yes'
|
||||
BPPP: 'yes'
|
||||
SCHNORRSIG_HALFAGG: 'yes'
|
||||
@@ -447,13 +447,13 @@ jobs:
|
||||
RECOVERY: 'yes'
|
||||
EXTRAKEYS: 'yes'
|
||||
SCHNORRSIG: 'yes'
|
||||
MUSIG: 'yes'
|
||||
ELLSWIFT: 'yes'
|
||||
EXPERIMENTAL: 'yes'
|
||||
ECDSA_S2C: 'yes'
|
||||
GENERATOR: 'yes'
|
||||
RANGEPROOF: 'yes'
|
||||
WHITELIST: 'yes'
|
||||
MUSIG: 'yes'
|
||||
ECDSAADAPTOR: 'yes'
|
||||
BPPP: 'yes'
|
||||
SCHNORRSIG_HALFAGG: 'yes'
|
||||
@@ -508,13 +508,13 @@ jobs:
|
||||
RECOVERY: 'yes'
|
||||
EXTRAKEYS: 'yes'
|
||||
SCHNORRSIG: 'yes'
|
||||
MUSIG: 'yes'
|
||||
ELLSWIFT: 'yes'
|
||||
EXPERIMENTAL: 'yes'
|
||||
ECDSA_S2C: 'yes'
|
||||
GENERATOR: 'yes'
|
||||
RANGEPROOF: 'yes'
|
||||
WHITELIST: 'yes'
|
||||
MUSIG: 'yes'
|
||||
ECDSAADAPTOR: 'yes'
|
||||
BPPP: 'yes'
|
||||
SCHNORRSIG_HALFAGG: 'yes'
|
||||
@@ -582,13 +582,13 @@ jobs:
|
||||
RECOVERY: 'yes'
|
||||
EXTRAKEYS: 'yes'
|
||||
SCHNORRSIG: 'yes'
|
||||
MUSIG: 'yes'
|
||||
ELLSWIFT: 'yes'
|
||||
EXPERIMENTAL: 'yes'
|
||||
ECDSA_S2C: 'yes'
|
||||
GENERATOR: 'yes'
|
||||
RANGEPROOF: 'yes'
|
||||
WHITELIST: 'yes'
|
||||
MUSIG: 'yes'
|
||||
ECDSAADAPTOR: 'yes'
|
||||
BPPP: 'yes'
|
||||
SCHNORRSIG_HALFAGG: 'yes'
|
||||
@@ -638,13 +638,13 @@ jobs:
|
||||
RECOVERY: 'yes'
|
||||
EXTRAKEYS: 'yes'
|
||||
SCHNORRSIG: 'yes'
|
||||
MUSIG: 'yes'
|
||||
ELLSWIFT: 'yes'
|
||||
EXPERIMENTAL: 'yes'
|
||||
ECDSA_S2C: 'yes'
|
||||
GENERATOR: 'yes'
|
||||
RANGEPROOF: 'yes'
|
||||
WHITELIST: 'yes'
|
||||
MUSIG: 'yes'
|
||||
ECDSAADAPTOR: 'yes'
|
||||
BPPP: 'yes'
|
||||
SCHNORRSIG_HALFAGG: 'yes'
|
||||
@@ -704,15 +704,15 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
env_vars:
|
||||
- { WIDEMUL: 'int64', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: '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' }
|
||||
- { WIDEMUL: 'int64', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes' }
|
||||
- { WIDEMUL: 'int128_struct', ECMULTGENKB: 2, ECMULTWINDOW: 4 }
|
||||
- { WIDEMUL: 'int128', ECDH: 'yes', EXTRAKEYS: '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' }
|
||||
- { WIDEMUL: 'int128', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes' }
|
||||
- { WIDEMUL: 'int128', RECOVERY: 'yes' }
|
||||
- { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: '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' }
|
||||
- { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: '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' }
|
||||
- { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: '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', WRAPPER_CMD: 'valgrind --error-exitcode=42', SECP256K1_TEST_ITERS: 2 }
|
||||
- { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: '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', WRAPPER_CMD: 'valgrind --error-exitcode=42', SECP256K1_TEST_ITERS: 2 }
|
||||
- { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: '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', CPPFLAGS: '-DVERIFY', CTIMETESTS: 'no' }
|
||||
- { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes' }
|
||||
- { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', CC: 'gcc' }
|
||||
- { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', WRAPPER_CMD: 'valgrind --error-exitcode=42', SECP256K1_TEST_ITERS: 2 }
|
||||
- { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', CC: 'gcc', WRAPPER_CMD: 'valgrind --error-exitcode=42', SECP256K1_TEST_ITERS: 2 }
|
||||
- { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', CPPFLAGS: '-DVERIFY', CTIMETESTS: 'no' }
|
||||
- BUILD: 'distcheck'
|
||||
|
||||
steps:
|
||||
@@ -879,13 +879,13 @@ jobs:
|
||||
RECOVERY: 'yes'
|
||||
EXTRAKEYS: 'yes'
|
||||
SCHNORRSIG: 'yes'
|
||||
MUSIG: 'yes'
|
||||
ELLSWIFT: 'yes'
|
||||
EXPERIMENTAL: 'yes'
|
||||
ECDSA_S2C: 'yes'
|
||||
GENERATOR: 'yes'
|
||||
RANGEPROOF: 'yes'
|
||||
WHITELIST: 'yes'
|
||||
MUSIG: 'yes'
|
||||
ECDSAADAPTOR: 'yes'
|
||||
BPPP: 'yes'
|
||||
SCHNORRSIG_HALFAGG: 'yes'
|
||||
|
||||
@@ -15,6 +15,7 @@ ecdh_example
|
||||
ecdsa_example
|
||||
schnorr_example
|
||||
ellswift_example
|
||||
musig_example
|
||||
*.exe
|
||||
*.so
|
||||
*.a
|
||||
|
||||
@@ -10,6 +10,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
#### Added
|
||||
- New module `musig` implements the MuSig2 multisignature scheme according to the [BIP 327 specification](https://github.com/bitcoin/bips/blob/master/bip-0327.mediawiki). See:
|
||||
- Header file `include/secp256k1_musig.h` which defines the new API.
|
||||
- Document `doc/musig.md` for further notes on API usage.
|
||||
- Usage example `examples/musig.c`.
|
||||
|
||||
## [0.5.1] - 2024-08-01
|
||||
|
||||
#### Added
|
||||
|
||||
@@ -60,6 +60,7 @@ option(SECP256K1_ENABLE_MODULE_ECDH "Enable ECDH module." ON)
|
||||
option(SECP256K1_ENABLE_MODULE_RECOVERY "Enable ECDSA pubkey recovery module." OFF)
|
||||
option(SECP256K1_ENABLE_MODULE_EXTRAKEYS "Enable extrakeys module." ON)
|
||||
option(SECP256K1_ENABLE_MODULE_SCHNORRSIG "Enable schnorrsig module." ON)
|
||||
option(SECP256K1_ENABLE_MODULE_MUSIG "Enable musig module." ON)
|
||||
option(SECP256K1_ENABLE_MODULE_ELLSWIFT "Enable ElligatorSwift module." ON)
|
||||
|
||||
option(SECP256K1_ENABLE_MODULE_GENERATOR "Enable NUMS generator module." ON)
|
||||
@@ -121,6 +122,14 @@ if(SECP256K1_ENABLE_MODULE_ELLSWIFT)
|
||||
add_compile_definitions(ENABLE_MODULE_ELLSWIFT=1)
|
||||
endif()
|
||||
|
||||
if(SECP256K1_ENABLE_MODULE_MUSIG)
|
||||
if(DEFINED SECP256K1_ENABLE_MODULE_SCHNORRSIG AND NOT SECP256K1_ENABLE_MODULE_SCHNORRSIG)
|
||||
message(FATAL_ERROR "Module dependency error: You have disabled the schnorrsig module explicitly, but it is required by the musig module.")
|
||||
endif()
|
||||
set(SECP256K1_ENABLE_MODULE_SCHNORRSIG ON)
|
||||
add_compile_definitions(ENABLE_MODULE_MUSIG=1)
|
||||
endif()
|
||||
|
||||
if(SECP256K1_ENABLE_MODULE_SCHNORRSIG)
|
||||
if(DEFINED SECP256K1_ENABLE_MODULE_EXTRAKEYS AND NOT SECP256K1_ENABLE_MODULE_EXTRAKEYS)
|
||||
message(FATAL_ERROR "Module dependency error: You have disabled the extrakeys module explicitly, but it is required by the schnorrsig module.")
|
||||
@@ -360,6 +369,7 @@ message(" ECDH ................................ ${SECP256K1_ENABLE_MODULE_ECDH}
|
||||
message(" ECDSA pubkey recovery ............... ${SECP256K1_ENABLE_MODULE_RECOVERY}")
|
||||
message(" extrakeys ........................... ${SECP256K1_ENABLE_MODULE_EXTRAKEYS}")
|
||||
message(" schnorrsig .......................... ${SECP256K1_ENABLE_MODULE_SCHNORRSIG}")
|
||||
message(" musig ............................... ${SECP256K1_ENABLE_MODULE_MUSIG}")
|
||||
message(" ElligatorSwift ...................... ${SECP256K1_ENABLE_MODULE_ELLSWIFT}")
|
||||
message(" generator ........................... ${SECP256K1_ENABLE_MODULE_GENERATOR}")
|
||||
message(" rangeproof .......................... ${SECP256K1_ENABLE_MODULE_RANGEPROOF}")
|
||||
|
||||
+15
@@ -197,6 +197,17 @@ ellswift_example_LDFLAGS += -lbcrypt
|
||||
endif
|
||||
TESTS += ellswift_example
|
||||
endif
|
||||
if ENABLE_MODULE_MUSIG
|
||||
noinst_PROGRAMS += musig_example
|
||||
musig_example_SOURCES = examples/musig.c
|
||||
musig_example_CPPFLAGS = -I$(top_srcdir)/include -DSECP256K1_STATIC
|
||||
musig_example_LDADD = libsecp256k1.la
|
||||
musig_example_LDFLAGS = -static
|
||||
if BUILD_WINDOWS
|
||||
musig_example_LDFLAGS += -lbcrypt
|
||||
endif
|
||||
TESTS += musig_example
|
||||
endif
|
||||
endif
|
||||
|
||||
### Precomputed tables
|
||||
@@ -307,6 +318,10 @@ if ENABLE_MODULE_SCHNORRSIG
|
||||
include src/modules/schnorrsig/Makefile.am.include
|
||||
endif
|
||||
|
||||
if ENABLE_MODULE_MUSIG
|
||||
include src/modules/musig/Makefile.am.include
|
||||
endif
|
||||
|
||||
if ENABLE_MODULE_ELLSWIFT
|
||||
include src/modules/ellswift/Makefile.am.include
|
||||
endif
|
||||
|
||||
@@ -13,7 +13,7 @@ print_environment() {
|
||||
# does not rely on bash.
|
||||
for var in WERROR_CFLAGS MAKEFLAGS BUILD \
|
||||
ECMULTWINDOW ECMULTGENKB ASM WIDEMUL WITH_VALGRIND EXTRAFLAGS \
|
||||
EXPERIMENTAL ECDH RECOVERY EXTRAKEYS SCHNORRSIG SCHNORRSIG_HALFAGG ELLSWIFT \
|
||||
EXPERIMENTAL ECDH RECOVERY EXTRAKEYS SCHNORRSIG MUSIG SCHNORRSIG_HALFAGG ELLSWIFT \
|
||||
ECDSA_S2C GENERATOR RANGEPROOF WHITELIST ECDSAADAPTOR BPPP \
|
||||
SECP256K1_TEST_ITERS BENCH SECP256K1_BENCH_ITERS CTIMETESTS\
|
||||
EXAMPLES \
|
||||
@@ -83,7 +83,7 @@ esac
|
||||
--enable-module-bppp="$BPPP" \
|
||||
--enable-module-rangeproof="$RANGEPROOF" --enable-module-whitelist="$WHITELIST" --enable-module-generator="$GENERATOR" \
|
||||
--enable-module-schnorrsig="$SCHNORRSIG" --enable-module-ecdsa-adaptor="$ECDSAADAPTOR" \
|
||||
--enable-module-schnorrsig="$SCHNORRSIG" \
|
||||
--enable-module-musig="$MUSIG" \
|
||||
--enable-module-schnorrsig-halfagg="$SCHNORRSIG_HALFAGG" \
|
||||
--enable-examples="$EXAMPLES" \
|
||||
--enable-ctime-tests="$CTIMETESTS" \
|
||||
|
||||
@@ -184,6 +184,10 @@ AC_ARG_ENABLE(module_schnorrsig,
|
||||
AS_HELP_STRING([--enable-module-schnorrsig],[enable schnorrsig module [default=yes]]), [],
|
||||
[SECP_SET_DEFAULT([enable_module_schnorrsig], [yes], [yes])])
|
||||
|
||||
AC_ARG_ENABLE(module_musig,
|
||||
AS_HELP_STRING([--enable-module-musig],[enable MuSig2 module [default=yes]]), [],
|
||||
[SECP_SET_DEFAULT([enable_module_musig], [yes], [yes])])
|
||||
|
||||
AC_ARG_ENABLE(module_schnorrsig_halfagg,
|
||||
AS_HELP_STRING([--enable-module-schnorrsig-halfagg],[enable schnorrsig half-aggregation module (experimental) [default=no]]), [],
|
||||
[SECP_SET_DEFAULT([enable_module_schnorrsig_halfagg], [no], [yes])])
|
||||
@@ -502,6 +506,14 @@ if test x"$enable_module_ellswift" = x"yes"; then
|
||||
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_ELLSWIFT=1"
|
||||
fi
|
||||
|
||||
if test x"$enable_module_musig" = 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
|
||||
enable_module_schnorrsig=yes
|
||||
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_MUSIG=1"
|
||||
fi
|
||||
|
||||
if test x"$enable_module_schnorrsig" = x"yes"; then
|
||||
if test x"$enable_module_extrakeys" = x"no"; then
|
||||
AC_MSG_ERROR([Module dependency error: You have disabled the extrakeys module explicitly, but it is required by the schnorrsig module.])
|
||||
@@ -585,6 +597,7 @@ AM_CONDITIONAL([ENABLE_MODULE_ECDH], [test x"$enable_module_ecdh" = x"yes"])
|
||||
AM_CONDITIONAL([ENABLE_MODULE_RECOVERY], [test x"$enable_module_recovery" = x"yes"])
|
||||
AM_CONDITIONAL([ENABLE_MODULE_EXTRAKEYS], [test x"$enable_module_extrakeys" = x"yes"])
|
||||
AM_CONDITIONAL([ENABLE_MODULE_SCHNORRSIG], [test x"$enable_module_schnorrsig" = x"yes"])
|
||||
AM_CONDITIONAL([ENABLE_MODULE_MUSIG], [test x"$enable_module_musig" = x"yes"])
|
||||
AM_CONDITIONAL([ENABLE_MODULE_ELLSWIFT], [test x"$enable_module_ellswift" = x"yes"])
|
||||
AM_CONDITIONAL([ENABLE_MODULE_GENERATOR], [test x"$enable_module_generator" = x"yes"])
|
||||
AM_CONDITIONAL([ENABLE_MODULE_RANGEPROOF], [test x"$enable_module_rangeproof" = x"yes"])
|
||||
@@ -620,6 +633,7 @@ echo " module ecdh = $enable_module_ecdh"
|
||||
echo " module recovery = $enable_module_recovery"
|
||||
echo " module extrakeys = $enable_module_extrakeys"
|
||||
echo " module schnorrsig = $enable_module_schnorrsig"
|
||||
echo " module musig = $enable_module_musig"
|
||||
echo " module ellswift = $enable_module_ellswift"
|
||||
# libsecp256k1-zkp modules, in the order they were added to the libsecp256k1-zkp
|
||||
echo " module generator = $enable_module_generator"
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
Notes on the musig module API
|
||||
===========================
|
||||
|
||||
The following sections contain additional notes on the API of the musig module (`include/secp256k1_musig.h`).
|
||||
A usage example can be found in `examples/musig.c`.
|
||||
|
||||
## API misuse
|
||||
|
||||
The musig API is designed with a focus on misuse resistance.
|
||||
However, due to the interactive nature of the MuSig protocol, there are additional failure modes that are not present in regular (single-party) Schnorr signature creation.
|
||||
While the results can be catastrophic (e.g. leaking of the secret key), it is unfortunately not possible for the musig implementation to prevent all such failure modes.
|
||||
|
||||
Therefore, users of the musig module must take great care to make sure of the following:
|
||||
|
||||
1. A unique nonce per signing session is generated in `secp256k1_musig_nonce_gen`.
|
||||
See the corresponding comment in `include/secp256k1_musig.h` for how to ensure that.
|
||||
2. The `secp256k1_musig_secnonce` structure is never copied or serialized.
|
||||
See also the comment on `secp256k1_musig_secnonce` in `include/secp256k1_musig.h`.
|
||||
3. Opaque data structures are never written to or read from directly.
|
||||
Instead, only the provided accessor functions are used.
|
||||
|
||||
## Key Aggregation and (Taproot) Tweaking
|
||||
|
||||
Given a set of public keys, the aggregate public key is computed with `secp256k1_musig_pubkey_agg`.
|
||||
A plain tweak can be added to the resulting public key with `secp256k1_ec_pubkey_tweak_add` by setting the `tweak32` argument to the hash defined in BIP 32. Similarly, a Taproot tweak can be added with `secp256k1_xonly_pubkey_tweak_add` by setting the `tweak32` argument to the TapTweak hash defined in BIP 341.
|
||||
Both types of tweaking can be combined and invoked multiple times if the specific application requires it.
|
||||
|
||||
## Signing
|
||||
|
||||
This is covered by `examples/musig.c`.
|
||||
Essentially, the protocol proceeds in the following steps:
|
||||
|
||||
1. Generate a keypair with `secp256k1_keypair_create` and obtain the public key with `secp256k1_keypair_pub`.
|
||||
2. Call `secp256k1_musig_pubkey_agg` with the pubkeys of all participants.
|
||||
3. Optionally add a (Taproot) tweak with `secp256k1_musig_pubkey_xonly_tweak_add` and a plain tweak with `secp256k1_musig_pubkey_ec_tweak_add`.
|
||||
4. Generate a pair of secret and public nonce with `secp256k1_musig_nonce_gen` and send the public nonce to the other signers.
|
||||
5. Someone (not necessarily the signer) aggregates the public nonces with `secp256k1_musig_nonce_agg` and sends it to the signers.
|
||||
6. Process the aggregate nonce with `secp256k1_musig_nonce_process`.
|
||||
7. Create a partial signature with `secp256k1_musig_partial_sign`.
|
||||
8. Verify the partial signatures (optional in some scenarios) with `secp256k1_musig_partial_sig_verify`.
|
||||
9. Someone (not necessarily the signer) obtains all partial signatures and aggregates them into the final Schnorr signature using `secp256k1_musig_partial_sig_agg`.
|
||||
|
||||
The aggregate signature can be verified with `secp256k1_schnorrsig_verify`.
|
||||
|
||||
Steps 1 through 5 above can occur before or after the signers are aware of the message to be signed.
|
||||
Whenever possible, it is recommended to generate the nonces only after the message is known.
|
||||
This provides enhanced defense-in-depth measures, protecting against potential API misuse in certain scenarios.
|
||||
However, it does require two rounds of communication during the signing process.
|
||||
The alternative, generating the nonces in a pre-processing step before the message is known, eliminates these additional protective measures but allows for non-interactive signing.
|
||||
Similarly, the API supports an alternative protocol flow where generating the aggregate key (steps 1 to 3) is allowed to happen after exchanging nonces (steps 4 to 5).
|
||||
|
||||
## Verification
|
||||
|
||||
A participant who wants to verify the partial signatures, but does not sign itself may do so using the above instructions except that the verifier skips steps 1, 4 and 7.
|
||||
@@ -32,3 +32,7 @@ endif()
|
||||
if(SECP256K1_ENABLE_MODULE_ELLSWIFT)
|
||||
add_example(ellswift)
|
||||
endif()
|
||||
|
||||
if(SECP256K1_ENABLE_MODULE_MUSIG)
|
||||
add_example(musig)
|
||||
endif()
|
||||
@@ -0,0 +1,257 @@
|
||||
/*************************************************************************
|
||||
* To the extent possible under law, the author(s) have dedicated all *
|
||||
* copyright and related and neighboring rights to the software in this *
|
||||
* file to the public domain worldwide. This software is distributed *
|
||||
* without any warranty. For the CC0 Public Domain Dedication, see *
|
||||
* EXAMPLES_COPYING or https://creativecommons.org/publicdomain/zero/1.0 *
|
||||
*************************************************************************/
|
||||
|
||||
/** This file demonstrates how to use the MuSig module to create a
|
||||
* 3-of-3 multisignature. Additionally, see the documentation in
|
||||
* include/secp256k1_musig.h and doc/musig.md.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <secp256k1.h>
|
||||
#include <secp256k1_extrakeys.h>
|
||||
#include <secp256k1_musig.h>
|
||||
#include <secp256k1_schnorrsig.h>
|
||||
|
||||
#include "examples_util.h"
|
||||
|
||||
struct signer_secrets {
|
||||
secp256k1_keypair keypair;
|
||||
secp256k1_musig_secnonce secnonce;
|
||||
};
|
||||
|
||||
struct signer {
|
||||
secp256k1_pubkey pubkey;
|
||||
secp256k1_musig_pubnonce pubnonce;
|
||||
secp256k1_musig_partial_sig partial_sig;
|
||||
};
|
||||
|
||||
/* Number of public keys involved in creating the aggregate signature */
|
||||
#define N_SIGNERS 3
|
||||
/* Create a key pair, store it in signer_secrets->keypair and signer->pubkey */
|
||||
static int create_keypair(const secp256k1_context* ctx, struct signer_secrets *signer_secrets, struct signer *signer) {
|
||||
unsigned char seckey[32];
|
||||
while (1) {
|
||||
if (!fill_random(seckey, sizeof(seckey))) {
|
||||
printf("Failed to generate randomness\n");
|
||||
return 0;
|
||||
}
|
||||
if (secp256k1_keypair_create(ctx, &signer_secrets->keypair, seckey)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!secp256k1_keypair_pub(ctx, &signer->pubkey, &signer_secrets->keypair)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
secure_erase(seckey, sizeof(seckey));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Tweak the pubkey corresponding to the provided keyagg cache, update the cache
|
||||
* and return the tweaked aggregate pk. */
|
||||
static int tweak(const secp256k1_context* ctx, secp256k1_xonly_pubkey *agg_pk, secp256k1_musig_keyagg_cache *cache) {
|
||||
secp256k1_pubkey output_pk;
|
||||
/* For BIP 32 tweaking the plain_tweak is set to a hash as defined in BIP
|
||||
* 32. */
|
||||
unsigned char plain_tweak[32] = "this could be a BIP32 tweak....";
|
||||
/* For Taproot tweaking the xonly_tweak is set to the TapTweak hash as
|
||||
* defined in BIP 341 */
|
||||
unsigned char xonly_tweak[32] = "this could be a Taproot tweak..";
|
||||
|
||||
|
||||
/* Plain tweaking which, for example, allows deriving multiple child
|
||||
* public keys from a single aggregate key using BIP32 */
|
||||
if (!secp256k1_musig_pubkey_ec_tweak_add(ctx, NULL, cache, plain_tweak)) {
|
||||
return 0;
|
||||
}
|
||||
/* Note that we did not provide an output_pk argument, because the
|
||||
* resulting pk is also saved in the cache and so if one is just interested
|
||||
* in signing, the output_pk argument is unnecessary. On the other hand, if
|
||||
* one is not interested in signing, the same output_pk can be obtained by
|
||||
* calling `secp256k1_musig_pubkey_get` right after key aggregation to get
|
||||
* the full pubkey and then call `secp256k1_ec_pubkey_tweak_add`. */
|
||||
|
||||
/* Xonly tweaking which, for example, allows creating Taproot commitments */
|
||||
if (!secp256k1_musig_pubkey_xonly_tweak_add(ctx, &output_pk, cache, xonly_tweak)) {
|
||||
return 0;
|
||||
}
|
||||
/* Note that if we wouldn't care about signing, we can arrive at the same
|
||||
* output_pk by providing the untweaked public key to
|
||||
* `secp256k1_xonly_pubkey_tweak_add` (after converting it to an xonly pubkey
|
||||
* if necessary with `secp256k1_xonly_pubkey_from_pubkey`). */
|
||||
|
||||
/* Now we convert the output_pk to an xonly pubkey to allow to later verify
|
||||
* the Schnorr signature against it. For this purpose we can ignore the
|
||||
* `pk_parity` output argument; we would need it if we would have to open
|
||||
* the Taproot commitment. */
|
||||
if (!secp256k1_xonly_pubkey_from_pubkey(ctx, agg_pk, NULL, &output_pk)) {
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Sign a message hash with the given key pairs and store the result in sig */
|
||||
static int sign(const secp256k1_context* ctx, struct signer_secrets *signer_secrets, struct signer *signer, const secp256k1_musig_keyagg_cache *cache, const unsigned char *msg32, unsigned char *sig64) {
|
||||
int i;
|
||||
const secp256k1_musig_pubnonce *pubnonces[N_SIGNERS];
|
||||
const secp256k1_musig_partial_sig *partial_sigs[N_SIGNERS];
|
||||
/* The same for all signers */
|
||||
secp256k1_musig_session session;
|
||||
secp256k1_musig_aggnonce agg_pubnonce;
|
||||
|
||||
for (i = 0; i < N_SIGNERS; i++) {
|
||||
unsigned char seckey[32];
|
||||
unsigned char session_secrand[32];
|
||||
/* Create random session ID. It is absolutely necessary that the session ID
|
||||
* is unique for every call of secp256k1_musig_nonce_gen. Otherwise
|
||||
* it's trivial for an attacker to extract the secret key! */
|
||||
if (!fill_random(session_secrand, sizeof(session_secrand))) {
|
||||
return 0;
|
||||
}
|
||||
if (!secp256k1_keypair_sec(ctx, seckey, &signer_secrets[i].keypair)) {
|
||||
return 0;
|
||||
}
|
||||
/* Initialize session and create secret nonce for signing and public
|
||||
* nonce to send to the other signers. */
|
||||
if (!secp256k1_musig_nonce_gen(ctx, &signer_secrets[i].secnonce, &signer[i].pubnonce, session_secrand, seckey, &signer[i].pubkey, msg32, NULL, NULL)) {
|
||||
return 0;
|
||||
}
|
||||
pubnonces[i] = &signer[i].pubnonce;
|
||||
|
||||
secure_erase(seckey, sizeof(seckey));
|
||||
}
|
||||
|
||||
/* Communication round 1: Every signer sends their pubnonce to the
|
||||
* coordinator. The coordinator runs secp256k1_musig_nonce_agg and sends
|
||||
* agg_pubnonce to each signer */
|
||||
if (!secp256k1_musig_nonce_agg(ctx, &agg_pubnonce, pubnonces, N_SIGNERS)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Every signer creates a partial signature */
|
||||
for (i = 0; i < N_SIGNERS; i++) {
|
||||
/* Initialize the signing session by processing the aggregate nonce */
|
||||
if (!secp256k1_musig_nonce_process(ctx, &session, &agg_pubnonce, msg32, cache)) {
|
||||
return 0;
|
||||
}
|
||||
/* 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_secrand to
|
||||
* create a secnonce). If you do, you effectively reuse the nonce and
|
||||
* leak the secret key. */
|
||||
if (!secp256k1_musig_partial_sign(ctx, &signer[i].partial_sig, &signer_secrets[i].secnonce, &signer_secrets[i].keypair, cache, &session)) {
|
||||
return 0;
|
||||
}
|
||||
partial_sigs[i] = &signer[i].partial_sig;
|
||||
}
|
||||
/* Communication round 2: Every signer sends their partial signature to the
|
||||
* coordinator, who verifies the partial signatures and aggregates them. */
|
||||
for (i = 0; i < N_SIGNERS; i++) {
|
||||
/* To check whether signing was successful, it suffices to either verify
|
||||
* the aggregate signature with the aggregate public key using
|
||||
* secp256k1_schnorrsig_verify, or verify all partial signatures of all
|
||||
* signers individually. Verifying the aggregate signature is cheaper but
|
||||
* verifying the individual partial signatures has the advantage that it
|
||||
* can be used to determine which of the partial signatures are invalid
|
||||
* (if any), i.e., which of the partial signatures cause the aggregate
|
||||
* signature to be invalid and thus the protocol run to fail. It's also
|
||||
* fine to first verify the aggregate sig, and only verify the individual
|
||||
* sigs if it does not work.
|
||||
*/
|
||||
if (!secp256k1_musig_partial_sig_verify(ctx, &signer[i].partial_sig, &signer[i].pubnonce, &signer[i].pubkey, cache, &session)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return secp256k1_musig_partial_sig_agg(ctx, sig64, &session, partial_sigs, N_SIGNERS);
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
secp256k1_context* ctx;
|
||||
int i;
|
||||
struct signer_secrets signer_secrets[N_SIGNERS];
|
||||
struct signer signers[N_SIGNERS];
|
||||
const secp256k1_pubkey *pubkeys_ptr[N_SIGNERS];
|
||||
secp256k1_xonly_pubkey agg_pk;
|
||||
secp256k1_musig_keyagg_cache cache;
|
||||
unsigned char msg[32] = "this_could_be_the_hash_of_a_msg";
|
||||
unsigned char sig[64];
|
||||
|
||||
/* Create a secp256k1 context */
|
||||
ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
|
||||
printf("Creating key pairs......");
|
||||
fflush(stdout);
|
||||
for (i = 0; i < N_SIGNERS; i++) {
|
||||
if (!create_keypair(ctx, &signer_secrets[i], &signers[i])) {
|
||||
printf("FAILED\n");
|
||||
return 1;
|
||||
}
|
||||
pubkeys_ptr[i] = &signers[i].pubkey;
|
||||
}
|
||||
printf("ok\n");
|
||||
|
||||
/* The aggregate public key produced by secp256k1_musig_pubkey_agg depends
|
||||
* on the order of the provided public keys. If there is no canonical order
|
||||
* of the signers, the individual public keys can optionally be sorted with
|
||||
* secp256k1_ec_pubkey_sort to ensure that the aggregate public key is
|
||||
* independent of the order of signers. */
|
||||
printf("Sorting public keys.....");
|
||||
fflush(stdout);
|
||||
if (!secp256k1_ec_pubkey_sort(ctx, pubkeys_ptr, N_SIGNERS)) {
|
||||
printf("FAILED\n");
|
||||
return 1;
|
||||
}
|
||||
printf("ok\n");
|
||||
|
||||
printf("Combining public keys...");
|
||||
fflush(stdout);
|
||||
/* If you just want to aggregate and not sign, you can call
|
||||
* secp256k1_musig_pubkey_agg with the keyagg_cache argument set to NULL
|
||||
* while providing a non-NULL agg_pk argument. */
|
||||
if (!secp256k1_musig_pubkey_agg(ctx, NULL, &cache, pubkeys_ptr, N_SIGNERS)) {
|
||||
printf("FAILED\n");
|
||||
return 1;
|
||||
}
|
||||
printf("ok\n");
|
||||
printf("Tweaking................");
|
||||
fflush(stdout);
|
||||
/* Optionally tweak the aggregate key */
|
||||
if (!tweak(ctx, &agg_pk, &cache)) {
|
||||
printf("FAILED\n");
|
||||
return 1;
|
||||
}
|
||||
printf("ok\n");
|
||||
printf("Signing message.........");
|
||||
fflush(stdout);
|
||||
if (!sign(ctx, signer_secrets, signers, &cache, msg, sig)) {
|
||||
printf("FAILED\n");
|
||||
return 1;
|
||||
}
|
||||
printf("ok\n");
|
||||
printf("Verifying signature.....");
|
||||
fflush(stdout);
|
||||
if (!secp256k1_schnorrsig_verify(ctx, sig, msg, 32, &agg_pk)) {
|
||||
printf("FAILED\n");
|
||||
return 1;
|
||||
}
|
||||
printf("ok\n");
|
||||
|
||||
/* It's best practice to try to clear secrets from memory after using them.
|
||||
* This is done because some bugs can allow an attacker to leak memory, for
|
||||
* example through "out of bounds" array access (see Heartbleed), or the OS
|
||||
* swapping them to disk. Hence, we overwrite secret key material with zeros.
|
||||
*
|
||||
* Here we are preventing these writes from being optimized out, as any good compiler
|
||||
* will remove any writes that aren't used. */
|
||||
for (i = 0; i < N_SIGNERS; i++) {
|
||||
secure_erase(&signer_secrets[i], sizeof(signer_secrets[i]));
|
||||
}
|
||||
secp256k1_context_destroy(ctx);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,588 @@
|
||||
#ifndef SECP256K1_MUSIG_H
|
||||
#define SECP256K1_MUSIG_H
|
||||
|
||||
#include "secp256k1_extrakeys.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/** This module implements BIP 327 "MuSig2 for BIP340-compatible
|
||||
* Multi-Signatures"
|
||||
* (https://github.com/bitcoin/bips/blob/master/bip-0327.mediawiki)
|
||||
* v1.0.0. You can find an example demonstrating the musig module in
|
||||
* examples/musig.c.
|
||||
*
|
||||
* The module also supports BIP 341 ("Taproot") public key tweaking.
|
||||
*
|
||||
* It is recommended to read the documentation in this include file carefully.
|
||||
* Further notes on API usage can be found in doc/musig.md
|
||||
*
|
||||
* Since the first version of MuSig is essentially replaced by MuSig2, we use
|
||||
* MuSig, musig and MuSig2 synonymously unless noted otherwise.
|
||||
*/
|
||||
|
||||
/** Opaque data structures
|
||||
*
|
||||
* The exact representation of data inside the opaque data structures is
|
||||
* implementation defined and not guaranteed to be portable between different
|
||||
* platforms or versions. With the exception of `secp256k1_musig_secnonce`, the
|
||||
* data structures can be safely copied/moved. If you need to convert to a
|
||||
* format suitable for storage, transmission, or comparison, use the
|
||||
* corresponding serialization and parsing functions.
|
||||
*/
|
||||
|
||||
/** Opaque data structure that caches information about public key aggregation.
|
||||
*
|
||||
* Guaranteed to be 197 bytes in size. No serialization and parsing functions
|
||||
* (yet).
|
||||
*/
|
||||
typedef struct {
|
||||
unsigned char data[197];
|
||||
} secp256k1_musig_keyagg_cache;
|
||||
|
||||
/** Opaque data structure that holds a signer's _secret_ nonce.
|
||||
*
|
||||
* Guaranteed to be 132 bytes in size.
|
||||
*
|
||||
* WARNING: This structure MUST NOT be copied or read or written to directly. A
|
||||
* signer who is online throughout the whole process and can keep this
|
||||
* structure in memory can use the provided API functions for a safe standard
|
||||
* workflow.
|
||||
*
|
||||
* Copying this data structure can result in nonce reuse which will leak the
|
||||
* secret signing key.
|
||||
*/
|
||||
typedef struct {
|
||||
unsigned char data[132];
|
||||
} secp256k1_musig_secnonce;
|
||||
|
||||
/** Opaque data structure that holds a signer's public nonce.
|
||||
*
|
||||
* Guaranteed to be 132 bytes in size. Serialized and parsed with
|
||||
* `musig_pubnonce_serialize` and `musig_pubnonce_parse`.
|
||||
*/
|
||||
typedef struct {
|
||||
unsigned char data[132];
|
||||
} secp256k1_musig_pubnonce;
|
||||
|
||||
/** Opaque data structure that holds an aggregate public nonce.
|
||||
*
|
||||
* Guaranteed to be 132 bytes in size. Serialized and parsed with
|
||||
* `musig_aggnonce_serialize` and `musig_aggnonce_parse`.
|
||||
*/
|
||||
typedef struct {
|
||||
unsigned char data[132];
|
||||
} secp256k1_musig_aggnonce;
|
||||
|
||||
/** Opaque data structure that holds a MuSig session.
|
||||
*
|
||||
* This structure is not required to be kept secret for the signing protocol to
|
||||
* be secure. Guaranteed to be 133 bytes in size. No serialization and parsing
|
||||
* functions (yet).
|
||||
*/
|
||||
typedef struct {
|
||||
unsigned char data[133];
|
||||
} secp256k1_musig_session;
|
||||
|
||||
/** Opaque data structure that holds a partial MuSig signature.
|
||||
*
|
||||
* Guaranteed to be 36 bytes in size. Serialized and parsed with
|
||||
* `musig_partial_sig_serialize` and `musig_partial_sig_parse`.
|
||||
*/
|
||||
typedef struct {
|
||||
unsigned char data[36];
|
||||
} secp256k1_musig_partial_sig;
|
||||
|
||||
/** Parse a signer's public nonce.
|
||||
*
|
||||
* Returns: 1 when the nonce could be parsed, 0 otherwise.
|
||||
* Args: ctx: pointer to a context object
|
||||
* Out: nonce: pointer to a nonce object
|
||||
* In: in66: pointer to the 66-byte nonce to be parsed
|
||||
*/
|
||||
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_pubnonce_parse(
|
||||
const secp256k1_context *ctx,
|
||||
secp256k1_musig_pubnonce *nonce,
|
||||
const unsigned char *in66
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
|
||||
|
||||
/** Serialize a signer's public nonce
|
||||
*
|
||||
* Returns: 1 always
|
||||
* Args: ctx: pointer to a context object
|
||||
* Out: out66: pointer to a 66-byte array to store the serialized nonce
|
||||
* In: nonce: pointer to the nonce
|
||||
*/
|
||||
SECP256K1_API int secp256k1_musig_pubnonce_serialize(
|
||||
const secp256k1_context *ctx,
|
||||
unsigned char *out66,
|
||||
const secp256k1_musig_pubnonce *nonce
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
|
||||
|
||||
/** Parse an aggregate public nonce.
|
||||
*
|
||||
* Returns: 1 when the nonce could be parsed, 0 otherwise.
|
||||
* Args: ctx: pointer to a context object
|
||||
* Out: nonce: pointer to a nonce object
|
||||
* In: in66: pointer to the 66-byte nonce to be parsed
|
||||
*/
|
||||
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_aggnonce_parse(
|
||||
const secp256k1_context *ctx,
|
||||
secp256k1_musig_aggnonce *nonce,
|
||||
const unsigned char *in66
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
|
||||
|
||||
/** Serialize an aggregate public nonce
|
||||
*
|
||||
* Returns: 1 always
|
||||
* Args: ctx: pointer to a context object
|
||||
* Out: out66: pointer to a 66-byte array to store the serialized nonce
|
||||
* In: nonce: pointer to the nonce
|
||||
*/
|
||||
SECP256K1_API int secp256k1_musig_aggnonce_serialize(
|
||||
const secp256k1_context *ctx,
|
||||
unsigned char *out66,
|
||||
const secp256k1_musig_aggnonce *nonce
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
|
||||
|
||||
/** Parse a MuSig partial signature.
|
||||
*
|
||||
* Returns: 1 when the signature could be parsed, 0 otherwise.
|
||||
* Args: ctx: pointer to a context object
|
||||
* Out: sig: pointer to a signature object
|
||||
* In: in32: pointer to the 32-byte signature to be parsed
|
||||
*/
|
||||
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_partial_sig_parse(
|
||||
const secp256k1_context *ctx,
|
||||
secp256k1_musig_partial_sig *sig,
|
||||
const unsigned char *in32
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
|
||||
|
||||
/** Serialize a MuSig partial signature
|
||||
*
|
||||
* Returns: 1 always
|
||||
* Args: ctx: pointer to a context object
|
||||
* Out: out32: pointer to a 32-byte array to store the serialized signature
|
||||
* In: sig: pointer to the signature
|
||||
*/
|
||||
SECP256K1_API int secp256k1_musig_partial_sig_serialize(
|
||||
const secp256k1_context *ctx,
|
||||
unsigned char *out32,
|
||||
const secp256k1_musig_partial_sig *sig
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
|
||||
|
||||
/** Computes an aggregate public key and uses it to initialize a keyagg_cache
|
||||
*
|
||||
* Different orders of `pubkeys` result in different `agg_pk`s.
|
||||
*
|
||||
* Before aggregating, the pubkeys can be sorted with `secp256k1_ec_pubkey_sort`
|
||||
* which ensures the same `agg_pk` result for the same multiset of pubkeys.
|
||||
* This is useful to do before `pubkey_agg`, such that the order of pubkeys
|
||||
* does not affect the aggregate public key.
|
||||
*
|
||||
* Returns: 0 if the arguments are invalid, 1 otherwise
|
||||
* Args: ctx: pointer to a context object
|
||||
* Out: agg_pk: the MuSig-aggregated x-only public key. If you do not need it,
|
||||
* this arg can be NULL.
|
||||
* keyagg_cache: if non-NULL, pointer to a musig_keyagg_cache struct that
|
||||
* is required for signing (or observing the signing session
|
||||
* and verifying partial signatures).
|
||||
* In: pubkeys: input array of pointers to public keys to aggregate. The order
|
||||
* is important; a different order will result in a different
|
||||
* aggregate public key.
|
||||
* n_pubkeys: length of pubkeys array. Must be greater than 0.
|
||||
*/
|
||||
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_pubkey_agg(
|
||||
const secp256k1_context *ctx,
|
||||
secp256k1_xonly_pubkey *agg_pk,
|
||||
secp256k1_musig_keyagg_cache *keyagg_cache,
|
||||
const secp256k1_pubkey * const *pubkeys,
|
||||
size_t n_pubkeys
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(4);
|
||||
|
||||
/** Obtain the aggregate public key from a keyagg_cache.
|
||||
*
|
||||
* This is only useful if you need the non-xonly public key, in particular for
|
||||
* plain (non-xonly) tweaking or batch-verifying multiple key aggregations
|
||||
* (not implemented).
|
||||
*
|
||||
* Returns: 0 if the arguments are invalid, 1 otherwise
|
||||
* Args: ctx: pointer to a context object
|
||||
* Out: agg_pk: the MuSig-aggregated public key.
|
||||
* In: keyagg_cache: pointer to a `musig_keyagg_cache` struct initialized by
|
||||
* `musig_pubkey_agg`
|
||||
*/
|
||||
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_pubkey_get(
|
||||
const secp256k1_context *ctx,
|
||||
secp256k1_pubkey *agg_pk,
|
||||
const secp256k1_musig_keyagg_cache *keyagg_cache
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
|
||||
|
||||
/** Apply plain "EC" tweaking to a public key in a given keyagg_cache by adding
|
||||
* the generator multiplied with `tweak32` to it. This is useful for deriving
|
||||
* child keys from an aggregate public key via BIP 32 where `tweak32` is set to
|
||||
* a hash as defined in BIP 32.
|
||||
*
|
||||
* Callers are responsible for deriving `tweak32` in a way that does not reduce
|
||||
* the security of MuSig (for example, by following BIP 32).
|
||||
*
|
||||
* The tweaking method is the same as `secp256k1_ec_pubkey_tweak_add`. So after
|
||||
* the following pseudocode buf and buf2 have identical contents (absent
|
||||
* earlier failures).
|
||||
*
|
||||
* secp256k1_musig_pubkey_agg(..., keyagg_cache, pubkeys, ...)
|
||||
* secp256k1_musig_pubkey_get(..., agg_pk, keyagg_cache)
|
||||
* secp256k1_musig_pubkey_ec_tweak_add(..., output_pk, tweak32, keyagg_cache)
|
||||
* secp256k1_ec_pubkey_serialize(..., buf, ..., output_pk, ...)
|
||||
* secp256k1_ec_pubkey_tweak_add(..., agg_pk, tweak32)
|
||||
* secp256k1_ec_pubkey_serialize(..., buf2, ..., agg_pk, ...)
|
||||
*
|
||||
* This function is required if you want to _sign_ for a tweaked aggregate key.
|
||||
* If you are only computing a public key but not intending to create a
|
||||
* signature for it, use `secp256k1_ec_pubkey_tweak_add` instead.
|
||||
*
|
||||
* Returns: 0 if the arguments are invalid, 1 otherwise
|
||||
* Args: ctx: pointer to a context object
|
||||
* Out: output_pubkey: pointer to a public key to store the result. Will be set
|
||||
* to an invalid value if this function returns 0. If you
|
||||
* do not need it, this arg can be NULL.
|
||||
* In/Out: keyagg_cache: pointer to a `musig_keyagg_cache` struct initialized by
|
||||
* `musig_pubkey_agg`
|
||||
* In: tweak32: pointer to a 32-byte tweak. The tweak is valid if it passes
|
||||
* `secp256k1_ec_seckey_verify` and is not equal to the
|
||||
* secret key corresponding to the public key represented
|
||||
* by keyagg_cache or its negation. For uniformly random
|
||||
* 32-byte arrays the chance of being invalid is
|
||||
* negligible (around 1 in 2^128).
|
||||
*/
|
||||
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_pubkey_ec_tweak_add(
|
||||
const secp256k1_context *ctx,
|
||||
secp256k1_pubkey *output_pubkey,
|
||||
secp256k1_musig_keyagg_cache *keyagg_cache,
|
||||
const unsigned char *tweak32
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
|
||||
|
||||
/** Apply x-only tweaking to a public key in a given keyagg_cache by adding the
|
||||
* generator multiplied with `tweak32` to it. This is useful for creating
|
||||
* Taproot outputs where `tweak32` is set to a TapTweak hash as defined in BIP
|
||||
* 341.
|
||||
*
|
||||
* Callers are responsible for deriving `tweak32` in a way that does not reduce
|
||||
* the security of MuSig (for example, by following Taproot BIP 341).
|
||||
*
|
||||
* The tweaking method is the same as `secp256k1_xonly_pubkey_tweak_add`. So in
|
||||
* the following pseudocode xonly_pubkey_tweak_add_check (absent earlier
|
||||
* failures) returns 1.
|
||||
*
|
||||
* secp256k1_musig_pubkey_agg(..., agg_pk, keyagg_cache, pubkeys, ...)
|
||||
* secp256k1_musig_pubkey_xonly_tweak_add(..., output_pk, keyagg_cache, tweak32)
|
||||
* secp256k1_xonly_pubkey_serialize(..., buf, output_pk)
|
||||
* secp256k1_xonly_pubkey_tweak_add_check(..., buf, ..., agg_pk, tweak32)
|
||||
*
|
||||
* This function is required if you want to _sign_ for a tweaked aggregate key.
|
||||
* If you are only computing a public key but not intending to create a
|
||||
* signature for it, use `secp256k1_xonly_pubkey_tweak_add` instead.
|
||||
*
|
||||
* Returns: 0 if the arguments are invalid, 1 otherwise
|
||||
* Args: ctx: pointer to a context object
|
||||
* Out: output_pubkey: pointer to a public key to store the result. Will be set
|
||||
* to an invalid value if this function returns 0. If you
|
||||
* do not need it, this arg can be NULL.
|
||||
* In/Out: keyagg_cache: pointer to a `musig_keyagg_cache` struct initialized by
|
||||
* `musig_pubkey_agg`
|
||||
* In: tweak32: pointer to a 32-byte tweak. The tweak is valid if it passes
|
||||
* `secp256k1_ec_seckey_verify` and is not equal to the
|
||||
* secret key corresponding to the public key represented
|
||||
* by keyagg_cache or its negation. For uniformly random
|
||||
* 32-byte arrays the chance of being invalid is
|
||||
* negligible (around 1 in 2^128).
|
||||
*/
|
||||
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_pubkey_xonly_tweak_add(
|
||||
const secp256k1_context *ctx,
|
||||
secp256k1_pubkey *output_pubkey,
|
||||
secp256k1_musig_keyagg_cache *keyagg_cache,
|
||||
const unsigned char *tweak32
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
|
||||
|
||||
/** Starts a signing session by generating a nonce
|
||||
*
|
||||
* This function outputs a secret nonce that will be required for signing and a
|
||||
* corresponding public nonce that is intended to be sent to other signers.
|
||||
*
|
||||
* MuSig differs from regular Schnorr signing in that implementers _must_ take
|
||||
* special care to not reuse a nonce. This can be ensured by following these rules:
|
||||
*
|
||||
* 1. Each call to this function must have a UNIQUE session_secrand32 that must
|
||||
* NOT BE REUSED in subsequent calls to this function and must be KEPT
|
||||
* SECRET (even from other signers).
|
||||
* 2. If you already know the seckey, message or aggregate public key
|
||||
* cache, they can be optionally provided to derive the nonce and increase
|
||||
* misuse-resistance. The extra_input32 argument can be used to provide
|
||||
* additional data that does not repeat in normal scenarios, such as the
|
||||
* current time.
|
||||
* 3. Avoid copying (or serializing) the secnonce. This reduces the possibility
|
||||
* that it is used more than once for signing.
|
||||
*
|
||||
* If you don't have access to good randomness for session_secrand32, but you
|
||||
* have access to a non-repeating counter, then see
|
||||
* secp256k1_musig_nonce_gen_counter.
|
||||
*
|
||||
* Remember that nonce reuse will leak the secret key!
|
||||
* Note that using the same seckey for multiple MuSig sessions is fine.
|
||||
*
|
||||
* Returns: 0 if the arguments are invalid and 1 otherwise
|
||||
* Args: ctx: pointer to a context object (not secp256k1_context_static)
|
||||
* Out: secnonce: pointer to a structure to store the secret nonce
|
||||
* pubnonce: pointer to a structure to store the public nonce
|
||||
* In/Out:
|
||||
* session_secrand32: a 32-byte session_secrand32 as explained above. Must be unique to this
|
||||
* call to secp256k1_musig_nonce_gen and must be uniformly
|
||||
* random. If the function call is successful, the
|
||||
* session_secrand32 buffer is invalidated to prevent reuse.
|
||||
* In:
|
||||
* seckey: the 32-byte secret key that will later be used for signing, if
|
||||
* already known (can be NULL)
|
||||
* pubkey: public key of the signer creating the nonce. The secnonce
|
||||
* output of this function cannot be used to sign for any
|
||||
* other public key. While the public key should correspond
|
||||
* to the provided seckey, a mismatch will not cause the
|
||||
* function to return 0.
|
||||
* msg32: the 32-byte message that will later be signed, if already known
|
||||
* (can be NULL)
|
||||
* keyagg_cache: pointer to the keyagg_cache that was used to create the aggregate
|
||||
* (and potentially tweaked) public key if already known
|
||||
* (can be NULL)
|
||||
* extra_input32: an optional 32-byte array that is input to the nonce
|
||||
* derivation function (can be NULL)
|
||||
*/
|
||||
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_nonce_gen(
|
||||
const secp256k1_context *ctx,
|
||||
secp256k1_musig_secnonce *secnonce,
|
||||
secp256k1_musig_pubnonce *pubnonce,
|
||||
unsigned char *session_secrand32,
|
||||
const unsigned char *seckey,
|
||||
const secp256k1_pubkey *pubkey,
|
||||
const unsigned char *msg32,
|
||||
const secp256k1_musig_keyagg_cache *keyagg_cache,
|
||||
const unsigned char *extra_input32
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(6);
|
||||
|
||||
|
||||
/** Alternative way to generate a nonce and start a signing session
|
||||
*
|
||||
* This function outputs a secret nonce that will be required for signing and a
|
||||
* corresponding public nonce that is intended to be sent to other signers.
|
||||
*
|
||||
* This function differs from `secp256k1_musig_nonce_gen` by accepting a
|
||||
* non-repeating counter value instead of a secret random value. This requires
|
||||
* that a secret key is provided to `secp256k1_musig_nonce_gen_counter`
|
||||
* (through the keypair argument), as opposed to `secp256k1_musig_nonce_gen`
|
||||
* where the seckey argument is optional.
|
||||
*
|
||||
* MuSig differs from regular Schnorr signing in that implementers _must_ take
|
||||
* special care to not reuse a nonce. This can be ensured by following these rules:
|
||||
*
|
||||
* 1. The nonrepeating_cnt argument must be a counter value that never repeats,
|
||||
* i.e., you must never call `secp256k1_musig_nonce_gen_counter` twice with
|
||||
* the same keypair and nonrepeating_cnt value. For example, this implies
|
||||
* that if the same keypair is used with `secp256k1_musig_nonce_gen_counter`
|
||||
* on multiple devices, none of the devices should have the same counter
|
||||
* value as any other device.
|
||||
* 2. If the seckey, message or aggregate public key cache is already available
|
||||
* at this stage, any of these can be optionally provided, in which case
|
||||
* they will be used in the derivation of the nonce and increase
|
||||
* misuse-resistance. The extra_input32 argument can be used to provide
|
||||
* additional data that does not repeat in normal scenarios, such as the
|
||||
* current time.
|
||||
* 3. Avoid copying (or serializing) the secnonce. This reduces the possibility
|
||||
* that it is used more than once for signing.
|
||||
*
|
||||
* Remember that nonce reuse will leak the secret key!
|
||||
* Note that using the same keypair for multiple MuSig sessions is fine.
|
||||
*
|
||||
* Returns: 0 if the arguments are invalid and 1 otherwise
|
||||
* Args: ctx: pointer to a context object (not secp256k1_context_static)
|
||||
* Out: secnonce: pointer to a structure to store the secret nonce
|
||||
* pubnonce: pointer to a structure to store the public nonce
|
||||
* In:
|
||||
* nonrepeating_cnt: the value of a counter as explained above. Must be
|
||||
* unique to this call to secp256k1_musig_nonce_gen.
|
||||
* keypair: keypair of the signer creating the nonce. The secnonce
|
||||
* output of this function cannot be used to sign for any
|
||||
* other keypair.
|
||||
* msg32: the 32-byte message that will later be signed, if already known
|
||||
* (can be NULL)
|
||||
* keyagg_cache: pointer to the keyagg_cache that was used to create the aggregate
|
||||
* (and potentially tweaked) public key if already known
|
||||
* (can be NULL)
|
||||
* extra_input32: an optional 32-byte array that is input to the nonce
|
||||
* derivation function (can be NULL)
|
||||
*/
|
||||
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_nonce_gen_counter(
|
||||
const secp256k1_context *ctx,
|
||||
secp256k1_musig_secnonce *secnonce,
|
||||
secp256k1_musig_pubnonce *pubnonce,
|
||||
uint64_t nonrepeating_cnt,
|
||||
const secp256k1_keypair *keypair,
|
||||
const unsigned char *msg32,
|
||||
const secp256k1_musig_keyagg_cache *keyagg_cache,
|
||||
const unsigned char *extra_input32
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5);
|
||||
|
||||
/** Aggregates the nonces of all signers into a single nonce
|
||||
*
|
||||
* This can be done by an untrusted party to reduce the communication
|
||||
* between signers. Instead of everyone sending nonces to everyone else, there
|
||||
* can be one party receiving all nonces, aggregating the nonces with this
|
||||
* function and then sending only the aggregate nonce back to the signers.
|
||||
*
|
||||
* If the aggregator does not compute the aggregate nonce correctly, the final
|
||||
* signature will be invalid.
|
||||
*
|
||||
* Returns: 0 if the arguments are invalid, 1 otherwise
|
||||
* Args: ctx: pointer to a context object
|
||||
* Out: aggnonce: pointer to an aggregate public nonce object for
|
||||
* musig_nonce_process
|
||||
* In: pubnonces: array of pointers to public nonces sent by the
|
||||
* signers
|
||||
* n_pubnonces: number of elements in the pubnonces array. Must be
|
||||
* greater than 0.
|
||||
*/
|
||||
SECP256K1_API int secp256k1_musig_nonce_agg(
|
||||
const secp256k1_context *ctx,
|
||||
secp256k1_musig_aggnonce *aggnonce,
|
||||
const secp256k1_musig_pubnonce * const *pubnonces,
|
||||
size_t n_pubnonces
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
|
||||
|
||||
/** Takes the aggregate nonce and creates a session that is required for signing
|
||||
* and verification of partial signatures.
|
||||
*
|
||||
* Returns: 0 if the arguments are invalid, 1 otherwise
|
||||
* Args: ctx: pointer to a context object
|
||||
* Out: session: pointer to a struct to store the session
|
||||
* In: aggnonce: pointer to an aggregate public nonce object that is the
|
||||
* output of musig_nonce_agg
|
||||
* msg32: the 32-byte message to sign
|
||||
* keyagg_cache: pointer to the keyagg_cache that was used to create the
|
||||
* aggregate (and potentially tweaked) pubkey
|
||||
*/
|
||||
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_nonce_process(
|
||||
const secp256k1_context *ctx,
|
||||
secp256k1_musig_session *session,
|
||||
const secp256k1_musig_aggnonce *aggnonce,
|
||||
const unsigned char *msg32,
|
||||
const secp256k1_musig_keyagg_cache *keyagg_cache
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5);
|
||||
|
||||
/** Produces a partial signature
|
||||
*
|
||||
* This function overwrites the given secnonce with zeros and will abort if given a
|
||||
* secnonce that is all zeros. This is a best effort attempt to protect against nonce
|
||||
* reuse. However, this is of course easily defeated if the secnonce has been
|
||||
* copied (or serialized). Remember that nonce reuse will leak the secret key!
|
||||
*
|
||||
* For signing to succeed, the secnonce provided to this function must have
|
||||
* been generated for the provided keypair. This means that when signing for a
|
||||
* keypair consisting of a seckey and pubkey, the secnonce must have been
|
||||
* created by calling musig_nonce_gen with that pubkey. Otherwise, the
|
||||
* illegal_callback is called.
|
||||
*
|
||||
* This function does not verify the output partial signature, deviating from
|
||||
* the BIP 327 specification. It is recommended to verify the output partial
|
||||
* signature with `secp256k1_musig_partial_sig_verify` to prevent random or
|
||||
* adversarially provoked computation errors.
|
||||
*
|
||||
* Returns: 0 if the arguments are invalid or the provided secnonce has already
|
||||
* been used for signing, 1 otherwise
|
||||
* Args: ctx: pointer to a context object
|
||||
* Out: partial_sig: pointer to struct to store the partial signature
|
||||
* In/Out: secnonce: pointer to the secnonce struct created in
|
||||
* musig_nonce_gen that has been never used in a
|
||||
* partial_sign call before and has been created for the
|
||||
* keypair
|
||||
* In: keypair: pointer to keypair to sign the message with
|
||||
* keyagg_cache: pointer to the keyagg_cache that was output when the
|
||||
* aggregate public key for this session
|
||||
* session: pointer to the session that was created with
|
||||
* musig_nonce_process
|
||||
*/
|
||||
SECP256K1_API int secp256k1_musig_partial_sign(
|
||||
const secp256k1_context *ctx,
|
||||
secp256k1_musig_partial_sig *partial_sig,
|
||||
secp256k1_musig_secnonce *secnonce,
|
||||
const secp256k1_keypair *keypair,
|
||||
const secp256k1_musig_keyagg_cache *keyagg_cache,
|
||||
const secp256k1_musig_session *session
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6);
|
||||
|
||||
/** Verifies an individual signer's partial signature
|
||||
*
|
||||
* The signature is verified for a specific signing session. In order to avoid
|
||||
* accidentally verifying a signature from a different or non-existing signing
|
||||
* session, you must ensure the following:
|
||||
* 1. The `keyagg_cache` argument is identical to the one used to create the
|
||||
* `session` with `musig_nonce_process`.
|
||||
* 2. The `pubkey` argument must be identical to the one sent by the signer
|
||||
* before aggregating it with `musig_pubkey_agg` to create the
|
||||
* `keyagg_cache`.
|
||||
* 3. The `pubnonce` argument must be identical to the one sent by the signer
|
||||
* before aggregating it with `musig_nonce_agg` and using the result to
|
||||
* create the `session` with `musig_nonce_process`.
|
||||
*
|
||||
* It is not required to call this function in regular MuSig sessions, because
|
||||
* if any partial signature does not verify, the final signature will not
|
||||
* verify either, so the problem will be caught. However, this function
|
||||
* provides the ability to identify which specific partial signature fails
|
||||
* verification.
|
||||
*
|
||||
* Returns: 0 if the arguments are invalid or the partial signature does not
|
||||
* verify, 1 otherwise
|
||||
* Args ctx: pointer to a context object
|
||||
* In: partial_sig: pointer to partial signature to verify, sent by
|
||||
* the signer associated with `pubnonce` and `pubkey`
|
||||
* pubnonce: public nonce of the signer in the signing session
|
||||
* pubkey: public key of the signer in the signing session
|
||||
* keyagg_cache: pointer to the keyagg_cache that was output when the
|
||||
* aggregate public key for this signing session
|
||||
* session: pointer to the session that was created with
|
||||
* `musig_nonce_process`
|
||||
*/
|
||||
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_partial_sig_verify(
|
||||
const secp256k1_context *ctx,
|
||||
const secp256k1_musig_partial_sig *partial_sig,
|
||||
const secp256k1_musig_pubnonce *pubnonce,
|
||||
const secp256k1_pubkey *pubkey,
|
||||
const secp256k1_musig_keyagg_cache *keyagg_cache,
|
||||
const secp256k1_musig_session *session
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6);
|
||||
|
||||
/** Aggregates partial signatures
|
||||
*
|
||||
* 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: sig64: complete (but possibly invalid) Schnorr signature
|
||||
* In: session: pointer to the session that was created with
|
||||
* musig_nonce_process
|
||||
* partial_sigs: array of pointers to partial signatures to aggregate
|
||||
* n_sigs: number of elements in the partial_sigs array. Must be
|
||||
* greater than 0.
|
||||
*/
|
||||
SECP256K1_API int secp256k1_musig_partial_sig_agg(
|
||||
const secp256k1_context *ctx,
|
||||
unsigned char *sig64,
|
||||
const secp256k1_musig_session *session,
|
||||
const secp256k1_musig_partial_sig * const *partial_sigs,
|
||||
size_t n_sigs
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -154,6 +154,9 @@ if(SECP256K1_INSTALL)
|
||||
if(SECP256K1_ENABLE_MODULE_SCHNORRSIG)
|
||||
list(APPEND ${PROJECT_NAME}_headers "${PROJECT_SOURCE_DIR}/include/secp256k1_schnorrsig.h")
|
||||
endif()
|
||||
if(SECP256K1_ENABLE_MODULE_MUSIG)
|
||||
list(APPEND ${PROJECT_NAME}_headers "${PROJECT_SOURCE_DIR}/include/secp256k1_musig.h")
|
||||
endif()
|
||||
if(SECP256K1_ENABLE_MODULE_ELLSWIFT)
|
||||
list(APPEND ${PROJECT_NAME}_headers "${PROJECT_SOURCE_DIR}/include/secp256k1_ellswift.h")
|
||||
endif()
|
||||
|
||||
@@ -31,6 +31,10 @@
|
||||
#include "../include/secp256k1_schnorrsig.h"
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_MODULE_MUSIG
|
||||
#include "../include/secp256k1_musig.h"
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_MODULE_ELLSWIFT
|
||||
#include "../include/secp256k1_ellswift.h"
|
||||
#endif
|
||||
@@ -189,6 +193,58 @@ static void run_tests(secp256k1_context *ctx, unsigned char *key) {
|
||||
CHECK(ret == 1);
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_MODULE_MUSIG
|
||||
{
|
||||
secp256k1_pubkey pk;
|
||||
const secp256k1_pubkey *pk_ptr[1];
|
||||
secp256k1_xonly_pubkey agg_pk;
|
||||
unsigned char session_secrand[32];
|
||||
uint64_t nonrepeating_cnt = 0;
|
||||
secp256k1_musig_secnonce secnonce;
|
||||
secp256k1_musig_pubnonce pubnonce;
|
||||
const secp256k1_musig_pubnonce *pubnonce_ptr[1];
|
||||
secp256k1_musig_aggnonce aggnonce;
|
||||
secp256k1_musig_keyagg_cache cache;
|
||||
secp256k1_musig_session session;
|
||||
secp256k1_musig_partial_sig partial_sig;
|
||||
unsigned char extra_input[32];
|
||||
|
||||
pk_ptr[0] = &pk;
|
||||
pubnonce_ptr[0] = &pubnonce;
|
||||
SECP256K1_CHECKMEM_DEFINE(key, 32);
|
||||
memcpy(session_secrand, key, sizeof(session_secrand));
|
||||
session_secrand[0] = session_secrand[0] + 1;
|
||||
memcpy(extra_input, key, sizeof(extra_input));
|
||||
extra_input[0] = extra_input[0] + 2;
|
||||
|
||||
CHECK(secp256k1_keypair_create(ctx, &keypair, key));
|
||||
CHECK(secp256k1_keypair_pub(ctx, &pk, &keypair));
|
||||
CHECK(secp256k1_musig_pubkey_agg(ctx, &agg_pk, &cache, pk_ptr, 1));
|
||||
|
||||
SECP256K1_CHECKMEM_UNDEFINE(key, 32);
|
||||
SECP256K1_CHECKMEM_UNDEFINE(session_secrand, sizeof(session_secrand));
|
||||
SECP256K1_CHECKMEM_UNDEFINE(extra_input, sizeof(extra_input));
|
||||
ret = secp256k1_musig_nonce_gen(ctx, &secnonce, &pubnonce, session_secrand, key, &pk, msg, &cache, extra_input);
|
||||
SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
|
||||
CHECK(ret == 1);
|
||||
ret = secp256k1_musig_nonce_gen_counter(ctx, &secnonce, &pubnonce, nonrepeating_cnt, &keypair, msg, &cache, extra_input);
|
||||
SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
|
||||
CHECK(ret == 1);
|
||||
|
||||
CHECK(secp256k1_musig_nonce_agg(ctx, &aggnonce, pubnonce_ptr, 1));
|
||||
/* Make sure that previous tests don't undefine msg. It's not used as a secret here. */
|
||||
SECP256K1_CHECKMEM_DEFINE(msg, sizeof(msg));
|
||||
CHECK(secp256k1_musig_nonce_process(ctx, &session, &aggnonce, msg, &cache) == 1);
|
||||
|
||||
ret = secp256k1_keypair_create(ctx, &keypair, key);
|
||||
SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
|
||||
CHECK(ret == 1);
|
||||
ret = secp256k1_musig_partial_sign(ctx, &partial_sig, &secnonce, &keypair, &cache, &session);
|
||||
SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
|
||||
CHECK(ret == 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_MODULE_ELLSWIFT
|
||||
SECP256K1_CHECKMEM_UNDEFINE(key, 32);
|
||||
ret = secp256k1_ellswift_create(ctx, ellswift, key, NULL);
|
||||
|
||||
+9
-1
@@ -185,12 +185,20 @@ static void secp256k1_gej_rescale(secp256k1_gej *r, const secp256k1_fe *b);
|
||||
|
||||
/** Convert a group element that is not infinity to a 64-byte array. The output
|
||||
* array is platform-dependent. */
|
||||
static void secp256k1_ge_to_bytes(unsigned char *buf, secp256k1_ge *a);
|
||||
static void secp256k1_ge_to_bytes(unsigned char *buf, const secp256k1_ge *a);
|
||||
|
||||
/** Convert a 64-byte array into group element. This function assumes that the
|
||||
* provided buffer correctly encodes a group element. */
|
||||
static void secp256k1_ge_from_bytes(secp256k1_ge *r, const unsigned char *buf);
|
||||
|
||||
/** Convert a group element (that is allowed to be infinity) to a 64-byte
|
||||
* array. The output array is platform-dependent. */
|
||||
static void secp256k1_ge_to_bytes_ext(unsigned char *data, const secp256k1_ge *ge);
|
||||
|
||||
/** Convert a 64-byte array into a group element. This function assumes that the
|
||||
* provided buffer is the output of secp256k1_ge_to_bytes_ext. */
|
||||
static void secp256k1_ge_from_bytes_ext(secp256k1_ge *ge, const unsigned char *data);
|
||||
|
||||
/** Determine if a point (which is assumed to be on the curve) is in the correct (sub)group of the curve.
|
||||
*
|
||||
* In normal mode, the used group is secp256k1, which has cofactor=1 meaning that every point on the curve is in the
|
||||
|
||||
+18
-1
@@ -965,7 +965,7 @@ static int secp256k1_ge_x_frac_on_curve_var(const secp256k1_fe *xn, const secp25
|
||||
return secp256k1_fe_is_square_var(&r);
|
||||
}
|
||||
|
||||
static void secp256k1_ge_to_bytes(unsigned char *buf, secp256k1_ge *a) {
|
||||
static void secp256k1_ge_to_bytes(unsigned char *buf, const secp256k1_ge *a) {
|
||||
secp256k1_ge_storage s;
|
||||
|
||||
/* We require that the secp256k1_ge_storage type is exactly 64 bytes.
|
||||
@@ -985,4 +985,21 @@ static void secp256k1_ge_from_bytes(secp256k1_ge *r, const unsigned char *buf) {
|
||||
secp256k1_ge_from_storage(r, &s);
|
||||
}
|
||||
|
||||
static void secp256k1_ge_to_bytes_ext(unsigned char *data, const secp256k1_ge *ge) {
|
||||
if (secp256k1_ge_is_infinity(ge)) {
|
||||
memset(data, 0, 64);
|
||||
} else {
|
||||
secp256k1_ge_to_bytes(data, ge);
|
||||
}
|
||||
}
|
||||
|
||||
static void secp256k1_ge_from_bytes_ext(secp256k1_ge *ge, const unsigned char *data) {
|
||||
static const unsigned char zeros[64] = { 0 };
|
||||
if (secp256k1_memcmp_var(data, zeros, sizeof(zeros)) == 0) {
|
||||
secp256k1_ge_set_infinity(ge);
|
||||
} else {
|
||||
secp256k1_ge_from_bytes(ge, data);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* SECP256K1_GROUP_IMPL_H */
|
||||
@@ -0,0 +1,8 @@
|
||||
include_HEADERS += include/secp256k1_musig.h
|
||||
noinst_HEADERS += src/modules/musig/main_impl.h
|
||||
noinst_HEADERS += src/modules/musig/keyagg.h
|
||||
noinst_HEADERS += src/modules/musig/keyagg_impl.h
|
||||
noinst_HEADERS += src/modules/musig/session.h
|
||||
noinst_HEADERS += src/modules/musig/session_impl.h
|
||||
noinst_HEADERS += src/modules/musig/tests_impl.h
|
||||
noinst_HEADERS += src/modules/musig/vectors.h
|
||||
@@ -0,0 +1,32 @@
|
||||
/***********************************************************************
|
||||
* Distributed under the MIT software license, see the accompanying *
|
||||
* file COPYING or https://www.opensource.org/licenses/mit-license.php.*
|
||||
***********************************************************************/
|
||||
|
||||
#ifndef SECP256K1_MODULE_MUSIG_KEYAGG_H
|
||||
#define SECP256K1_MODULE_MUSIG_KEYAGG_H
|
||||
|
||||
#include "../../../include/secp256k1.h"
|
||||
#include "../../../include/secp256k1_musig.h"
|
||||
|
||||
#include "../../group.h"
|
||||
#include "../../scalar.h"
|
||||
|
||||
typedef struct {
|
||||
secp256k1_ge pk;
|
||||
/* If there is no "second" public key, second_pk is set to the point at
|
||||
* infinity */
|
||||
secp256k1_ge second_pk;
|
||||
unsigned char pks_hash[32];
|
||||
/* tweak is identical to value tacc[v] in the specification. */
|
||||
secp256k1_scalar tweak;
|
||||
/* parity_acc corresponds to (1 - gacc[v])/2 in the spec. So if gacc[v] is
|
||||
* -1, parity_acc is 1. Otherwise, parity_acc is 0. */
|
||||
int parity_acc;
|
||||
} secp256k1_keyagg_cache_internal;
|
||||
|
||||
static int secp256k1_keyagg_cache_load(const secp256k1_context* ctx, secp256k1_keyagg_cache_internal *cache_i, const secp256k1_musig_keyagg_cache *cache);
|
||||
|
||||
static void secp256k1_musig_keyaggcoef(secp256k1_scalar *r, const secp256k1_keyagg_cache_internal *cache_i, secp256k1_ge *pk);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,291 @@
|
||||
/***********************************************************************
|
||||
* Distributed under the MIT software license, see the accompanying *
|
||||
* file COPYING or https://www.opensource.org/licenses/mit-license.php.*
|
||||
***********************************************************************/
|
||||
|
||||
#ifndef SECP256K1_MODULE_MUSIG_KEYAGG_IMPL_H
|
||||
#define SECP256K1_MODULE_MUSIG_KEYAGG_IMPL_H
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "keyagg.h"
|
||||
#include "../../eckey.h"
|
||||
#include "../../ecmult.h"
|
||||
#include "../../field.h"
|
||||
#include "../../group.h"
|
||||
#include "../../hash.h"
|
||||
#include "../../util.h"
|
||||
|
||||
static const unsigned char secp256k1_musig_keyagg_cache_magic[4] = { 0xf4, 0xad, 0xbb, 0xdf };
|
||||
|
||||
/* A keyagg cache consists of
|
||||
* - 4 byte magic set during initialization to allow detecting an uninitialized
|
||||
* object.
|
||||
* - 64 byte aggregate (and potentially tweaked) public key
|
||||
* - 64 byte "second" public key (set to the point at infinity if not present)
|
||||
* - 32 byte hash of all public keys
|
||||
* - 1 byte the parity of the internal key (if tweaked, otherwise 0)
|
||||
* - 32 byte tweak
|
||||
*/
|
||||
/* Requires that cache_i->pk is not infinity. */
|
||||
static void secp256k1_keyagg_cache_save(secp256k1_musig_keyagg_cache *cache, const secp256k1_keyagg_cache_internal *cache_i) {
|
||||
unsigned char *ptr = cache->data;
|
||||
memcpy(ptr, secp256k1_musig_keyagg_cache_magic, 4);
|
||||
ptr += 4;
|
||||
secp256k1_ge_to_bytes(ptr, &cache_i->pk);
|
||||
ptr += 64;
|
||||
secp256k1_ge_to_bytes_ext(ptr, &cache_i->second_pk);
|
||||
ptr += 64;
|
||||
memcpy(ptr, cache_i->pks_hash, 32);
|
||||
ptr += 32;
|
||||
*ptr = cache_i->parity_acc;
|
||||
ptr += 1;
|
||||
secp256k1_scalar_get_b32(ptr, &cache_i->tweak);
|
||||
}
|
||||
|
||||
static int secp256k1_keyagg_cache_load(const secp256k1_context* ctx, secp256k1_keyagg_cache_internal *cache_i, const secp256k1_musig_keyagg_cache *cache) {
|
||||
const unsigned char *ptr = cache->data;
|
||||
ARG_CHECK(secp256k1_memcmp_var(ptr, secp256k1_musig_keyagg_cache_magic, 4) == 0);
|
||||
ptr += 4;
|
||||
secp256k1_ge_from_bytes(&cache_i->pk, ptr);
|
||||
ptr += 64;
|
||||
secp256k1_ge_from_bytes_ext(&cache_i->second_pk, ptr);
|
||||
ptr += 64;
|
||||
memcpy(cache_i->pks_hash, ptr, 32);
|
||||
ptr += 32;
|
||||
cache_i->parity_acc = *ptr & 1;
|
||||
ptr += 1;
|
||||
secp256k1_scalar_set_b32(&cache_i->tweak, ptr, NULL);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Initializes SHA256 with fixed midstate. This midstate was computed by applying
|
||||
* SHA256 to SHA256("KeyAgg list")||SHA256("KeyAgg list"). */
|
||||
static void secp256k1_musig_keyagglist_sha256(secp256k1_sha256 *sha) {
|
||||
secp256k1_sha256_initialize(sha);
|
||||
|
||||
sha->s[0] = 0xb399d5e0ul;
|
||||
sha->s[1] = 0xc8fff302ul;
|
||||
sha->s[2] = 0x6badac71ul;
|
||||
sha->s[3] = 0x07c5b7f1ul;
|
||||
sha->s[4] = 0x9701e2eful;
|
||||
sha->s[5] = 0x2a72ecf8ul;
|
||||
sha->s[6] = 0x201a4c7bul;
|
||||
sha->s[7] = 0xab148a38ul;
|
||||
sha->bytes = 64;
|
||||
}
|
||||
|
||||
/* Computes pks_hash = tagged_hash(pk[0], ..., pk[np-1]) */
|
||||
static int secp256k1_musig_compute_pks_hash(const secp256k1_context *ctx, unsigned char *pks_hash, const secp256k1_pubkey * const* pks, size_t np) {
|
||||
secp256k1_sha256 sha;
|
||||
size_t i;
|
||||
|
||||
secp256k1_musig_keyagglist_sha256(&sha);
|
||||
for (i = 0; i < np; i++) {
|
||||
unsigned char ser[33];
|
||||
size_t ser_len = sizeof(ser);
|
||||
if (!secp256k1_ec_pubkey_serialize(ctx, ser, &ser_len, pks[i], SECP256K1_EC_COMPRESSED)) {
|
||||
return 0;
|
||||
}
|
||||
VERIFY_CHECK(ser_len == sizeof(ser));
|
||||
secp256k1_sha256_write(&sha, ser, sizeof(ser));
|
||||
}
|
||||
secp256k1_sha256_finalize(&sha, pks_hash);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Initializes SHA256 with fixed midstate. This midstate was computed by applying
|
||||
* SHA256 to SHA256("KeyAgg coefficient")||SHA256("KeyAgg coefficient"). */
|
||||
static void secp256k1_musig_keyaggcoef_sha256(secp256k1_sha256 *sha) {
|
||||
secp256k1_sha256_initialize(sha);
|
||||
|
||||
sha->s[0] = 0x6ef02c5aul;
|
||||
sha->s[1] = 0x06a480deul;
|
||||
sha->s[2] = 0x1f298665ul;
|
||||
sha->s[3] = 0x1d1134f2ul;
|
||||
sha->s[4] = 0x56a0b063ul;
|
||||
sha->s[5] = 0x52da4147ul;
|
||||
sha->s[6] = 0xf280d9d4ul;
|
||||
sha->s[7] = 0x4484be15ul;
|
||||
sha->bytes = 64;
|
||||
}
|
||||
|
||||
/* Compute KeyAgg coefficient which is constant 1 for the second pubkey and
|
||||
* otherwise tagged_hash(pks_hash, pk) where pks_hash is the hash of public keys.
|
||||
* second_pk is the point at infinity in case there is no second_pk. Assumes
|
||||
* that pk is not the point at infinity and that the Y-coordinates of pk and
|
||||
* second_pk are normalized. */
|
||||
static void secp256k1_musig_keyaggcoef_internal(secp256k1_scalar *r, const unsigned char *pks_hash, secp256k1_ge *pk, const secp256k1_ge *second_pk) {
|
||||
VERIFY_CHECK(!secp256k1_ge_is_infinity(pk));
|
||||
|
||||
if (!secp256k1_ge_is_infinity(second_pk)
|
||||
&& secp256k1_ge_eq_var(pk, second_pk)) {
|
||||
secp256k1_scalar_set_int(r, 1);
|
||||
} else {
|
||||
secp256k1_sha256 sha;
|
||||
unsigned char buf[33];
|
||||
size_t buflen = sizeof(buf);
|
||||
int ret;
|
||||
secp256k1_musig_keyaggcoef_sha256(&sha);
|
||||
secp256k1_sha256_write(&sha, pks_hash, 32);
|
||||
ret = secp256k1_eckey_pubkey_serialize(pk, buf, &buflen, 1);
|
||||
#ifdef VERIFY
|
||||
/* Serialization does not fail since the pk is not the point at infinity
|
||||
* (according to this function's precondition). */
|
||||
VERIFY_CHECK(ret && buflen == sizeof(buf));
|
||||
#else
|
||||
(void) ret;
|
||||
#endif
|
||||
secp256k1_sha256_write(&sha, buf, sizeof(buf));
|
||||
secp256k1_sha256_finalize(&sha, buf);
|
||||
secp256k1_scalar_set_b32(r, buf, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/* Assumes that pk is not the point at infinity and that the Y-coordinates of pk
|
||||
* and cache_i->second_pk are normalized. */
|
||||
static void secp256k1_musig_keyaggcoef(secp256k1_scalar *r, const secp256k1_keyagg_cache_internal *cache_i, secp256k1_ge *pk) {
|
||||
secp256k1_musig_keyaggcoef_internal(r, cache_i->pks_hash, pk, &cache_i->second_pk);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
const secp256k1_context *ctx;
|
||||
/* pks_hash is the hash of the public keys */
|
||||
unsigned char pks_hash[32];
|
||||
const secp256k1_pubkey * const* pks;
|
||||
secp256k1_ge second_pk;
|
||||
} secp256k1_musig_pubkey_agg_ecmult_data;
|
||||
|
||||
/* Callback for batch EC multiplication to compute keyaggcoef_0*P0 + keyaggcoef_1*P1 + ... */
|
||||
static int secp256k1_musig_pubkey_agg_callback(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *data) {
|
||||
secp256k1_musig_pubkey_agg_ecmult_data *ctx = (secp256k1_musig_pubkey_agg_ecmult_data *) data;
|
||||
int ret;
|
||||
ret = secp256k1_pubkey_load(ctx->ctx, pt, ctx->pks[idx]);
|
||||
#ifdef VERIFY
|
||||
/* pubkey_load can't fail because the same pks have already been loaded in
|
||||
* `musig_compute_pks_hash` (and we test this). */
|
||||
VERIFY_CHECK(ret);
|
||||
#else
|
||||
(void) ret;
|
||||
#endif
|
||||
secp256k1_musig_keyaggcoef_internal(sc, ctx->pks_hash, pt, &ctx->second_pk);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int secp256k1_musig_pubkey_agg(const secp256k1_context* ctx, secp256k1_xonly_pubkey *agg_pk, secp256k1_musig_keyagg_cache *keyagg_cache, const secp256k1_pubkey * const* pubkeys, size_t n_pubkeys) {
|
||||
secp256k1_musig_pubkey_agg_ecmult_data ecmult_data;
|
||||
secp256k1_gej pkj;
|
||||
secp256k1_ge pkp;
|
||||
size_t i;
|
||||
|
||||
VERIFY_CHECK(ctx != NULL);
|
||||
if (agg_pk != NULL) {
|
||||
memset(agg_pk, 0, sizeof(*agg_pk));
|
||||
}
|
||||
ARG_CHECK(pubkeys != NULL);
|
||||
ARG_CHECK(n_pubkeys > 0);
|
||||
|
||||
ecmult_data.ctx = ctx;
|
||||
ecmult_data.pks = pubkeys;
|
||||
|
||||
secp256k1_ge_set_infinity(&ecmult_data.second_pk);
|
||||
for (i = 1; i < n_pubkeys; i++) {
|
||||
if (secp256k1_memcmp_var(pubkeys[0], pubkeys[i], sizeof(*pubkeys[0])) != 0) {
|
||||
secp256k1_ge pk;
|
||||
if (!secp256k1_pubkey_load(ctx, &pk, pubkeys[i])) {
|
||||
return 0;
|
||||
}
|
||||
ecmult_data.second_pk = pk;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!secp256k1_musig_compute_pks_hash(ctx, ecmult_data.pks_hash, pubkeys, n_pubkeys)) {
|
||||
return 0;
|
||||
}
|
||||
/* TODO: actually use optimized ecmult_multi algorithms by providing a
|
||||
* scratch space */
|
||||
if (!secp256k1_ecmult_multi_var(&ctx->error_callback, NULL, &pkj, NULL, secp256k1_musig_pubkey_agg_callback, (void *) &ecmult_data, n_pubkeys)) {
|
||||
/* In order to reach this line with the current implementation of
|
||||
* ecmult_multi_var one would need to provide a callback that can
|
||||
* fail. */
|
||||
return 0;
|
||||
}
|
||||
secp256k1_ge_set_gej(&pkp, &pkj);
|
||||
secp256k1_fe_normalize_var(&pkp.y);
|
||||
/* The resulting public key is infinity with negligible probability */
|
||||
VERIFY_CHECK(!secp256k1_ge_is_infinity(&pkp));
|
||||
if (keyagg_cache != NULL) {
|
||||
secp256k1_keyagg_cache_internal cache_i = { 0 };
|
||||
cache_i.pk = pkp;
|
||||
cache_i.second_pk = ecmult_data.second_pk;
|
||||
memcpy(cache_i.pks_hash, ecmult_data.pks_hash, sizeof(cache_i.pks_hash));
|
||||
secp256k1_keyagg_cache_save(keyagg_cache, &cache_i);
|
||||
}
|
||||
|
||||
if (agg_pk != NULL) {
|
||||
secp256k1_extrakeys_ge_even_y(&pkp);
|
||||
secp256k1_xonly_pubkey_save(agg_pk, &pkp);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int secp256k1_musig_pubkey_get(const secp256k1_context* ctx, secp256k1_pubkey *agg_pk, const secp256k1_musig_keyagg_cache *keyagg_cache) {
|
||||
secp256k1_keyagg_cache_internal cache_i;
|
||||
VERIFY_CHECK(ctx != NULL);
|
||||
ARG_CHECK(agg_pk != NULL);
|
||||
memset(agg_pk, 0, sizeof(*agg_pk));
|
||||
ARG_CHECK(keyagg_cache != NULL);
|
||||
|
||||
if (!secp256k1_keyagg_cache_load(ctx, &cache_i, keyagg_cache)) {
|
||||
return 0;
|
||||
}
|
||||
secp256k1_pubkey_save(agg_pk, &cache_i.pk);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int secp256k1_musig_pubkey_tweak_add_internal(const secp256k1_context* ctx, secp256k1_pubkey *output_pubkey, secp256k1_musig_keyagg_cache *keyagg_cache, const unsigned char *tweak32, int xonly) {
|
||||
secp256k1_keyagg_cache_internal cache_i;
|
||||
int overflow = 0;
|
||||
secp256k1_scalar tweak;
|
||||
|
||||
VERIFY_CHECK(ctx != NULL);
|
||||
if (output_pubkey != NULL) {
|
||||
memset(output_pubkey, 0, sizeof(*output_pubkey));
|
||||
}
|
||||
ARG_CHECK(keyagg_cache != NULL);
|
||||
ARG_CHECK(tweak32 != NULL);
|
||||
|
||||
if (!secp256k1_keyagg_cache_load(ctx, &cache_i, keyagg_cache)) {
|
||||
return 0;
|
||||
}
|
||||
secp256k1_scalar_set_b32(&tweak, tweak32, &overflow);
|
||||
if (overflow) {
|
||||
return 0;
|
||||
}
|
||||
if (xonly && secp256k1_extrakeys_ge_even_y(&cache_i.pk)) {
|
||||
cache_i.parity_acc ^= 1;
|
||||
secp256k1_scalar_negate(&cache_i.tweak, &cache_i.tweak);
|
||||
}
|
||||
secp256k1_scalar_add(&cache_i.tweak, &cache_i.tweak, &tweak);
|
||||
if (!secp256k1_eckey_pubkey_tweak_add(&cache_i.pk, &tweak)) {
|
||||
return 0;
|
||||
}
|
||||
/* eckey_pubkey_tweak_add fails if cache_i.pk is infinity */
|
||||
VERIFY_CHECK(!secp256k1_ge_is_infinity(&cache_i.pk));
|
||||
secp256k1_keyagg_cache_save(keyagg_cache, &cache_i);
|
||||
if (output_pubkey != NULL) {
|
||||
secp256k1_pubkey_save(output_pubkey, &cache_i.pk);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int secp256k1_musig_pubkey_ec_tweak_add(const secp256k1_context* ctx, secp256k1_pubkey *output_pubkey, secp256k1_musig_keyagg_cache *keyagg_cache, const unsigned char *tweak32) {
|
||||
return secp256k1_musig_pubkey_tweak_add_internal(ctx, output_pubkey, keyagg_cache, tweak32, 0);
|
||||
}
|
||||
|
||||
int secp256k1_musig_pubkey_xonly_tweak_add(const secp256k1_context* ctx, secp256k1_pubkey *output_pubkey, secp256k1_musig_keyagg_cache *keyagg_cache, const unsigned char *tweak32) {
|
||||
return secp256k1_musig_pubkey_tweak_add_internal(ctx, output_pubkey, keyagg_cache, tweak32, 1);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,12 @@
|
||||
/**********************************************************************
|
||||
* Distributed under the MIT software license, see the accompanying *
|
||||
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
|
||||
**********************************************************************/
|
||||
|
||||
#ifndef SECP256K1_MODULE_MUSIG_MAIN_H
|
||||
#define SECP256K1_MODULE_MUSIG_MAIN_H
|
||||
|
||||
#include "keyagg_impl.h"
|
||||
#include "session_impl.h"
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,24 @@
|
||||
/***********************************************************************
|
||||
* Distributed under the MIT software license, see the accompanying *
|
||||
* file COPYING or https://www.opensource.org/licenses/mit-license.php.*
|
||||
***********************************************************************/
|
||||
|
||||
#ifndef SECP256K1_MODULE_MUSIG_SESSION_H
|
||||
#define SECP256K1_MODULE_MUSIG_SESSION_H
|
||||
|
||||
#include "../../../include/secp256k1.h"
|
||||
#include "../../../include/secp256k1_musig.h"
|
||||
|
||||
#include "../../scalar.h"
|
||||
|
||||
typedef struct {
|
||||
int fin_nonce_parity;
|
||||
unsigned char fin_nonce[32];
|
||||
secp256k1_scalar noncecoef;
|
||||
secp256k1_scalar challenge;
|
||||
secp256k1_scalar s_part;
|
||||
} secp256k1_musig_session_internal;
|
||||
|
||||
static int secp256k1_musig_session_load(const secp256k1_context* ctx, secp256k1_musig_session_internal *session_i, const secp256k1_musig_session *session);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,815 @@
|
||||
/***********************************************************************
|
||||
* Distributed under the MIT software license, see the accompanying *
|
||||
* file COPYING or https://www.opensource.org/licenses/mit-license.php.*
|
||||
***********************************************************************/
|
||||
|
||||
#ifndef SECP256K1_MODULE_MUSIG_SESSION_IMPL_H
|
||||
#define SECP256K1_MODULE_MUSIG_SESSION_IMPL_H
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "../../../include/secp256k1.h"
|
||||
#include "../../../include/secp256k1_extrakeys.h"
|
||||
#include "../../../include/secp256k1_musig.h"
|
||||
|
||||
#include "keyagg.h"
|
||||
#include "session.h"
|
||||
#include "../../eckey.h"
|
||||
#include "../../hash.h"
|
||||
#include "../../scalar.h"
|
||||
#include "../../util.h"
|
||||
|
||||
/* Outputs 33 zero bytes if the given group element is the point at infinity and
|
||||
* otherwise outputs the compressed serialization */
|
||||
static void secp256k1_musig_ge_serialize_ext(unsigned char *out33, secp256k1_ge* ge) {
|
||||
if (secp256k1_ge_is_infinity(ge)) {
|
||||
memset(out33, 0, 33);
|
||||
} else {
|
||||
int ret;
|
||||
size_t size = 33;
|
||||
ret = secp256k1_eckey_pubkey_serialize(ge, out33, &size, 1);
|
||||
#ifdef VERIFY
|
||||
/* Serialize must succeed because the point is not at infinity */
|
||||
VERIFY_CHECK(ret && size == 33);
|
||||
#else
|
||||
(void) ret;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/* Outputs the point at infinity if the given byte array is all zero, otherwise
|
||||
* attempts to parse compressed point serialization. */
|
||||
static int secp256k1_musig_ge_parse_ext(secp256k1_ge* ge, const unsigned char *in33) {
|
||||
unsigned char zeros[33] = { 0 };
|
||||
|
||||
if (secp256k1_memcmp_var(in33, zeros, sizeof(zeros)) == 0) {
|
||||
secp256k1_ge_set_infinity(ge);
|
||||
return 1;
|
||||
}
|
||||
if (!secp256k1_eckey_pubkey_parse(ge, in33, 33)) {
|
||||
return 0;
|
||||
}
|
||||
return secp256k1_ge_is_in_correct_subgroup(ge);
|
||||
}
|
||||
|
||||
static const unsigned char secp256k1_musig_secnonce_magic[4] = { 0x22, 0x0e, 0xdc, 0xf1 };
|
||||
|
||||
static void secp256k1_musig_secnonce_save(secp256k1_musig_secnonce *secnonce, const secp256k1_scalar *k, const secp256k1_ge *pk) {
|
||||
memcpy(&secnonce->data[0], secp256k1_musig_secnonce_magic, 4);
|
||||
secp256k1_scalar_get_b32(&secnonce->data[4], &k[0]);
|
||||
secp256k1_scalar_get_b32(&secnonce->data[36], &k[1]);
|
||||
secp256k1_ge_to_bytes(&secnonce->data[68], pk);
|
||||
}
|
||||
|
||||
static int secp256k1_musig_secnonce_load(const secp256k1_context* ctx, secp256k1_scalar *k, secp256k1_ge *pk, const secp256k1_musig_secnonce *secnonce) {
|
||||
int is_zero;
|
||||
ARG_CHECK(secp256k1_memcmp_var(&secnonce->data[0], secp256k1_musig_secnonce_magic, 4) == 0);
|
||||
/* We make very sure that the nonce isn't invalidated by checking the values
|
||||
* in addition to the magic. */
|
||||
is_zero = secp256k1_is_zero_array(&secnonce->data[4], 2 * 32);
|
||||
secp256k1_declassify(ctx, &is_zero, sizeof(is_zero));
|
||||
ARG_CHECK(!is_zero);
|
||||
|
||||
secp256k1_scalar_set_b32(&k[0], &secnonce->data[4], NULL);
|
||||
secp256k1_scalar_set_b32(&k[1], &secnonce->data[36], NULL);
|
||||
secp256k1_ge_from_bytes(pk, &secnonce->data[68]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* If flag is true, invalidate the secnonce; otherwise leave it. Constant-time. */
|
||||
static void secp256k1_musig_secnonce_invalidate(const secp256k1_context* ctx, secp256k1_musig_secnonce *secnonce, int flag) {
|
||||
secp256k1_memczero(secnonce->data, sizeof(secnonce->data), flag);
|
||||
/* The flag argument is usually classified. So, the line above makes the
|
||||
* magic and public key classified. However, we need both to be
|
||||
* declassified. Note that we don't declassify the entire object, because if
|
||||
* flag is 0, then k[0] and k[1] have not been zeroed. */
|
||||
secp256k1_declassify(ctx, secnonce->data, sizeof(secp256k1_musig_secnonce_magic));
|
||||
secp256k1_declassify(ctx, &secnonce->data[68], 64);
|
||||
}
|
||||
|
||||
static const unsigned char secp256k1_musig_pubnonce_magic[4] = { 0xf5, 0x7a, 0x3d, 0xa0 };
|
||||
|
||||
/* Saves two group elements into a pubnonce. Requires that none of the provided
|
||||
* group elements is infinity. */
|
||||
static void secp256k1_musig_pubnonce_save(secp256k1_musig_pubnonce* nonce, const secp256k1_ge* ges) {
|
||||
int i;
|
||||
memcpy(&nonce->data[0], secp256k1_musig_pubnonce_magic, 4);
|
||||
for (i = 0; i < 2; i++) {
|
||||
secp256k1_ge_to_bytes(nonce->data + 4+64*i, &ges[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/* Loads two group elements from a pubnonce. Returns 1 unless the nonce wasn't
|
||||
* properly initialized */
|
||||
static int secp256k1_musig_pubnonce_load(const secp256k1_context* ctx, secp256k1_ge* ges, const secp256k1_musig_pubnonce* nonce) {
|
||||
int i;
|
||||
|
||||
ARG_CHECK(secp256k1_memcmp_var(&nonce->data[0], secp256k1_musig_pubnonce_magic, 4) == 0);
|
||||
for (i = 0; i < 2; i++) {
|
||||
secp256k1_ge_from_bytes(&ges[i], nonce->data + 4 + 64*i);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const unsigned char secp256k1_musig_aggnonce_magic[4] = { 0xa8, 0xb7, 0xe4, 0x67 };
|
||||
|
||||
static void secp256k1_musig_aggnonce_save(secp256k1_musig_aggnonce* nonce, const secp256k1_ge* ges) {
|
||||
int i;
|
||||
memcpy(&nonce->data[0], secp256k1_musig_aggnonce_magic, 4);
|
||||
for (i = 0; i < 2; i++) {
|
||||
secp256k1_ge_to_bytes_ext(&nonce->data[4 + 64*i], &ges[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static int secp256k1_musig_aggnonce_load(const secp256k1_context* ctx, secp256k1_ge* ges, const secp256k1_musig_aggnonce* nonce) {
|
||||
int i;
|
||||
|
||||
ARG_CHECK(secp256k1_memcmp_var(&nonce->data[0], secp256k1_musig_aggnonce_magic, 4) == 0);
|
||||
for (i = 0; i < 2; i++) {
|
||||
secp256k1_ge_from_bytes_ext(&ges[i], &nonce->data[4 + 64*i]);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const unsigned char secp256k1_musig_session_cache_magic[4] = { 0x9d, 0xed, 0xe9, 0x17 };
|
||||
|
||||
/* A session consists of
|
||||
* - 4 byte session cache magic
|
||||
* - 1 byte the parity of the final nonce
|
||||
* - 32 byte serialized x-only final nonce
|
||||
* - 32 byte nonce coefficient b
|
||||
* - 32 byte signature challenge hash e
|
||||
* - 32 byte scalar s that is added to the partial signatures of the signers
|
||||
*/
|
||||
static void secp256k1_musig_session_save(secp256k1_musig_session *session, const secp256k1_musig_session_internal *session_i) {
|
||||
unsigned char *ptr = session->data;
|
||||
|
||||
memcpy(ptr, secp256k1_musig_session_cache_magic, 4);
|
||||
ptr += 4;
|
||||
*ptr = session_i->fin_nonce_parity;
|
||||
ptr += 1;
|
||||
memcpy(ptr, session_i->fin_nonce, 32);
|
||||
ptr += 32;
|
||||
secp256k1_scalar_get_b32(ptr, &session_i->noncecoef);
|
||||
ptr += 32;
|
||||
secp256k1_scalar_get_b32(ptr, &session_i->challenge);
|
||||
ptr += 32;
|
||||
secp256k1_scalar_get_b32(ptr, &session_i->s_part);
|
||||
}
|
||||
|
||||
static int secp256k1_musig_session_load(const secp256k1_context* ctx, secp256k1_musig_session_internal *session_i, const secp256k1_musig_session *session) {
|
||||
const unsigned char *ptr = session->data;
|
||||
|
||||
ARG_CHECK(secp256k1_memcmp_var(ptr, secp256k1_musig_session_cache_magic, 4) == 0);
|
||||
ptr += 4;
|
||||
session_i->fin_nonce_parity = *ptr;
|
||||
ptr += 1;
|
||||
memcpy(session_i->fin_nonce, ptr, 32);
|
||||
ptr += 32;
|
||||
secp256k1_scalar_set_b32(&session_i->noncecoef, ptr, NULL);
|
||||
ptr += 32;
|
||||
secp256k1_scalar_set_b32(&session_i->challenge, ptr, NULL);
|
||||
ptr += 32;
|
||||
secp256k1_scalar_set_b32(&session_i->s_part, ptr, NULL);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const unsigned char secp256k1_musig_partial_sig_magic[4] = { 0xeb, 0xfb, 0x1a, 0x32 };
|
||||
|
||||
static void secp256k1_musig_partial_sig_save(secp256k1_musig_partial_sig* sig, secp256k1_scalar *s) {
|
||||
memcpy(&sig->data[0], secp256k1_musig_partial_sig_magic, 4);
|
||||
secp256k1_scalar_get_b32(&sig->data[4], s);
|
||||
}
|
||||
|
||||
static int secp256k1_musig_partial_sig_load(const secp256k1_context* ctx, secp256k1_scalar *s, const secp256k1_musig_partial_sig* sig) {
|
||||
int overflow;
|
||||
|
||||
ARG_CHECK(secp256k1_memcmp_var(&sig->data[0], secp256k1_musig_partial_sig_magic, 4) == 0);
|
||||
secp256k1_scalar_set_b32(s, &sig->data[4], &overflow);
|
||||
/* Parsed signatures can not overflow */
|
||||
VERIFY_CHECK(!overflow);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int secp256k1_musig_pubnonce_parse(const secp256k1_context* ctx, secp256k1_musig_pubnonce* nonce, const unsigned char *in66) {
|
||||
secp256k1_ge ges[2];
|
||||
int i;
|
||||
|
||||
VERIFY_CHECK(ctx != NULL);
|
||||
ARG_CHECK(nonce != NULL);
|
||||
ARG_CHECK(in66 != NULL);
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
if (!secp256k1_eckey_pubkey_parse(&ges[i], &in66[33*i], 33)) {
|
||||
return 0;
|
||||
}
|
||||
if (!secp256k1_ge_is_in_correct_subgroup(&ges[i])) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
secp256k1_musig_pubnonce_save(nonce, ges);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int secp256k1_musig_pubnonce_serialize(const secp256k1_context* ctx, unsigned char *out66, const secp256k1_musig_pubnonce* nonce) {
|
||||
secp256k1_ge ges[2];
|
||||
int i;
|
||||
|
||||
VERIFY_CHECK(ctx != NULL);
|
||||
ARG_CHECK(out66 != NULL);
|
||||
memset(out66, 0, 66);
|
||||
ARG_CHECK(nonce != NULL);
|
||||
|
||||
if (!secp256k1_musig_pubnonce_load(ctx, ges, nonce)) {
|
||||
return 0;
|
||||
}
|
||||
for (i = 0; i < 2; i++) {
|
||||
int ret;
|
||||
size_t size = 33;
|
||||
ret = secp256k1_eckey_pubkey_serialize(&ges[i], &out66[33*i], &size, 1);
|
||||
#ifdef VERIFY
|
||||
/* serialize must succeed because the point was just loaded */
|
||||
VERIFY_CHECK(ret && size == 33);
|
||||
#else
|
||||
(void) ret;
|
||||
#endif
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int secp256k1_musig_aggnonce_parse(const secp256k1_context* ctx, secp256k1_musig_aggnonce* nonce, const unsigned char *in66) {
|
||||
secp256k1_ge ges[2];
|
||||
int i;
|
||||
|
||||
VERIFY_CHECK(ctx != NULL);
|
||||
ARG_CHECK(nonce != NULL);
|
||||
ARG_CHECK(in66 != NULL);
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
if (!secp256k1_musig_ge_parse_ext(&ges[i], &in66[33*i])) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
secp256k1_musig_aggnonce_save(nonce, ges);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int secp256k1_musig_aggnonce_serialize(const secp256k1_context* ctx, unsigned char *out66, const secp256k1_musig_aggnonce* nonce) {
|
||||
secp256k1_ge ges[2];
|
||||
int i;
|
||||
|
||||
VERIFY_CHECK(ctx != NULL);
|
||||
ARG_CHECK(out66 != NULL);
|
||||
memset(out66, 0, 66);
|
||||
ARG_CHECK(nonce != NULL);
|
||||
|
||||
if (!secp256k1_musig_aggnonce_load(ctx, ges, nonce)) {
|
||||
return 0;
|
||||
}
|
||||
for (i = 0; i < 2; i++) {
|
||||
secp256k1_musig_ge_serialize_ext(&out66[33*i], &ges[i]);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int secp256k1_musig_partial_sig_parse(const secp256k1_context* ctx, secp256k1_musig_partial_sig* sig, const unsigned char *in32) {
|
||||
secp256k1_scalar tmp;
|
||||
int overflow;
|
||||
VERIFY_CHECK(ctx != NULL);
|
||||
ARG_CHECK(sig != NULL);
|
||||
ARG_CHECK(in32 != NULL);
|
||||
|
||||
/* Ensure that using the signature will fail if parsing fails (and the user
|
||||
* doesn't check the return value). */
|
||||
memset(sig, 0, sizeof(*sig));
|
||||
|
||||
secp256k1_scalar_set_b32(&tmp, in32, &overflow);
|
||||
if (overflow) {
|
||||
return 0;
|
||||
}
|
||||
secp256k1_musig_partial_sig_save(sig, &tmp);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int secp256k1_musig_partial_sig_serialize(const secp256k1_context* ctx, unsigned char *out32, const secp256k1_musig_partial_sig* sig) {
|
||||
VERIFY_CHECK(ctx != NULL);
|
||||
ARG_CHECK(out32 != NULL);
|
||||
ARG_CHECK(sig != NULL);
|
||||
ARG_CHECK(secp256k1_memcmp_var(&sig->data[0], secp256k1_musig_partial_sig_magic, 4) == 0);
|
||||
|
||||
memcpy(out32, &sig->data[4], 32);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Write optional inputs into the hash */
|
||||
static void secp256k1_nonce_function_musig_helper(secp256k1_sha256 *sha, unsigned int prefix_size, const unsigned char *data, unsigned char len) {
|
||||
unsigned char zero[7] = { 0 };
|
||||
/* The spec requires length prefixes to be between 1 and 8 bytes
|
||||
* (inclusive) */
|
||||
VERIFY_CHECK(prefix_size >= 1 && prefix_size <= 8);
|
||||
/* Since the length of all input data fits in a byte, we can always pad the
|
||||
* length prefix with prefix_size - 1 zero bytes. */
|
||||
secp256k1_sha256_write(sha, zero, prefix_size - 1);
|
||||
if (data != NULL) {
|
||||
secp256k1_sha256_write(sha, &len, 1);
|
||||
secp256k1_sha256_write(sha, data, len);
|
||||
} else {
|
||||
len = 0;
|
||||
secp256k1_sha256_write(sha, &len, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Initializes SHA256 with fixed midstate. This midstate was computed by applying
|
||||
* SHA256 to SHA256("MuSig/aux")||SHA256("MuSig/aux"). */
|
||||
static void secp256k1_nonce_function_musig_sha256_tagged_aux(secp256k1_sha256 *sha) {
|
||||
secp256k1_sha256_initialize(sha);
|
||||
sha->s[0] = 0xa19e884bul;
|
||||
sha->s[1] = 0xf463fe7eul;
|
||||
sha->s[2] = 0x2f18f9a2ul;
|
||||
sha->s[3] = 0xbeb0f9fful;
|
||||
sha->s[4] = 0x0f37e8b0ul;
|
||||
sha->s[5] = 0x06ebd26ful;
|
||||
sha->s[6] = 0xe3b243d2ul;
|
||||
sha->s[7] = 0x522fb150ul;
|
||||
sha->bytes = 64;
|
||||
}
|
||||
|
||||
/* Initializes SHA256 with fixed midstate. This midstate was computed by applying
|
||||
* SHA256 to SHA256("MuSig/nonce")||SHA256("MuSig/nonce"). */
|
||||
static void secp256k1_nonce_function_musig_sha256_tagged(secp256k1_sha256 *sha) {
|
||||
secp256k1_sha256_initialize(sha);
|
||||
sha->s[0] = 0x07101b64ul;
|
||||
sha->s[1] = 0x18003414ul;
|
||||
sha->s[2] = 0x0391bc43ul;
|
||||
sha->s[3] = 0x0e6258eeul;
|
||||
sha->s[4] = 0x29d26b72ul;
|
||||
sha->s[5] = 0x8343937eul;
|
||||
sha->s[6] = 0xb7a0a4fbul;
|
||||
sha->s[7] = 0xff568a30ul;
|
||||
sha->bytes = 64;
|
||||
}
|
||||
|
||||
static void secp256k1_nonce_function_musig(secp256k1_scalar *k, const unsigned char *session_secrand, const unsigned char *msg32, const unsigned char *seckey32, const unsigned char *pk33, const unsigned char *agg_pk32, const unsigned char *extra_input32) {
|
||||
secp256k1_sha256 sha;
|
||||
unsigned char rand[32];
|
||||
unsigned char i;
|
||||
unsigned char msg_present;
|
||||
|
||||
if (seckey32 != NULL) {
|
||||
secp256k1_nonce_function_musig_sha256_tagged_aux(&sha);
|
||||
secp256k1_sha256_write(&sha, session_secrand, 32);
|
||||
secp256k1_sha256_finalize(&sha, rand);
|
||||
for (i = 0; i < 32; i++) {
|
||||
rand[i] ^= seckey32[i];
|
||||
}
|
||||
} else {
|
||||
memcpy(rand, session_secrand, sizeof(rand));
|
||||
}
|
||||
|
||||
secp256k1_nonce_function_musig_sha256_tagged(&sha);
|
||||
secp256k1_sha256_write(&sha, rand, sizeof(rand));
|
||||
secp256k1_nonce_function_musig_helper(&sha, 1, pk33, 33);
|
||||
secp256k1_nonce_function_musig_helper(&sha, 1, agg_pk32, 32);
|
||||
msg_present = msg32 != NULL;
|
||||
secp256k1_sha256_write(&sha, &msg_present, 1);
|
||||
if (msg_present) {
|
||||
secp256k1_nonce_function_musig_helper(&sha, 8, msg32, 32);
|
||||
}
|
||||
secp256k1_nonce_function_musig_helper(&sha, 4, extra_input32, 32);
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
unsigned char buf[32];
|
||||
secp256k1_sha256 sha_tmp = sha;
|
||||
secp256k1_sha256_write(&sha_tmp, &i, 1);
|
||||
secp256k1_sha256_finalize(&sha_tmp, buf);
|
||||
secp256k1_scalar_set_b32(&k[i], buf, NULL);
|
||||
|
||||
/* Attempt to erase secret data */
|
||||
memset(buf, 0, sizeof(buf));
|
||||
memset(&sha_tmp, 0, sizeof(sha_tmp));
|
||||
}
|
||||
memset(rand, 0, sizeof(rand));
|
||||
memset(&sha, 0, sizeof(sha));
|
||||
}
|
||||
|
||||
int secp256k1_musig_nonce_gen_internal(const secp256k1_context* ctx, secp256k1_musig_secnonce *secnonce, secp256k1_musig_pubnonce *pubnonce, const unsigned char *input_nonce, const unsigned char *seckey, const secp256k1_pubkey *pubkey, const unsigned char *msg32, const secp256k1_musig_keyagg_cache *keyagg_cache, const unsigned char *extra_input32) {
|
||||
secp256k1_scalar k[2];
|
||||
secp256k1_ge nonce_pts[2];
|
||||
int i;
|
||||
unsigned char pk_ser[33];
|
||||
size_t pk_ser_len = sizeof(pk_ser);
|
||||
unsigned char aggpk_ser[32];
|
||||
unsigned char *aggpk_ser_ptr = NULL;
|
||||
secp256k1_ge pk;
|
||||
int pk_serialize_success;
|
||||
int ret = 1;
|
||||
|
||||
ARG_CHECK(pubnonce != NULL);
|
||||
memset(pubnonce, 0, sizeof(*pubnonce));
|
||||
ARG_CHECK(pubkey != NULL);
|
||||
ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
|
||||
|
||||
/* Check that the seckey is valid to be able to sign for it later. */
|
||||
if (seckey != NULL) {
|
||||
secp256k1_scalar sk;
|
||||
ret &= secp256k1_scalar_set_b32_seckey(&sk, seckey);
|
||||
secp256k1_scalar_clear(&sk);
|
||||
}
|
||||
|
||||
if (keyagg_cache != NULL) {
|
||||
secp256k1_keyagg_cache_internal cache_i;
|
||||
if (!secp256k1_keyagg_cache_load(ctx, &cache_i, keyagg_cache)) {
|
||||
return 0;
|
||||
}
|
||||
/* The loaded point cache_i.pk can not be the point at infinity. */
|
||||
secp256k1_fe_get_b32(aggpk_ser, &cache_i.pk.x);
|
||||
aggpk_ser_ptr = aggpk_ser;
|
||||
}
|
||||
if (!secp256k1_pubkey_load(ctx, &pk, pubkey)) {
|
||||
return 0;
|
||||
}
|
||||
pk_serialize_success = secp256k1_eckey_pubkey_serialize(&pk, pk_ser, &pk_ser_len, 1);
|
||||
|
||||
#ifdef VERIFY
|
||||
/* A pubkey cannot be the point at infinity */
|
||||
VERIFY_CHECK(pk_serialize_success);
|
||||
VERIFY_CHECK(pk_ser_len == sizeof(pk_ser));
|
||||
#else
|
||||
(void) pk_serialize_success;
|
||||
#endif
|
||||
|
||||
secp256k1_nonce_function_musig(k, input_nonce, msg32, seckey, pk_ser, aggpk_ser_ptr, extra_input32);
|
||||
VERIFY_CHECK(!secp256k1_scalar_is_zero(&k[0]));
|
||||
VERIFY_CHECK(!secp256k1_scalar_is_zero(&k[1]));
|
||||
secp256k1_musig_secnonce_save(secnonce, k, &pk);
|
||||
secp256k1_musig_secnonce_invalidate(ctx, secnonce, !ret);
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
secp256k1_gej nonce_ptj;
|
||||
secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &nonce_ptj, &k[i]);
|
||||
secp256k1_ge_set_gej(&nonce_pts[i], &nonce_ptj);
|
||||
secp256k1_declassify(ctx, &nonce_pts[i], sizeof(nonce_pts));
|
||||
secp256k1_scalar_clear(&k[i]);
|
||||
}
|
||||
/* None of the nonce_pts will be infinity because k != 0 with overwhelming
|
||||
* probability */
|
||||
secp256k1_musig_pubnonce_save(pubnonce, nonce_pts);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int secp256k1_musig_nonce_gen(const secp256k1_context* ctx, secp256k1_musig_secnonce *secnonce, secp256k1_musig_pubnonce *pubnonce, unsigned char *session_secrand32, const unsigned char *seckey, const secp256k1_pubkey *pubkey, const unsigned char *msg32, const secp256k1_musig_keyagg_cache *keyagg_cache, const unsigned char *extra_input32) {
|
||||
int ret = 1;
|
||||
|
||||
VERIFY_CHECK(ctx != NULL);
|
||||
ARG_CHECK(secnonce != NULL);
|
||||
memset(secnonce, 0, sizeof(*secnonce));
|
||||
ARG_CHECK(session_secrand32 != NULL);
|
||||
|
||||
/* Check in constant time that the session_secrand32 is not 0 as a
|
||||
* defense-in-depth measure that may protect against a faulty RNG. */
|
||||
ret &= !secp256k1_is_zero_array(session_secrand32, 32);
|
||||
|
||||
/* We can declassify because branching on ret is only relevant when this
|
||||
* function called with an invalid session_secrand32 argument */
|
||||
secp256k1_declassify(ctx, &ret, sizeof(ret));
|
||||
if (ret == 0) {
|
||||
secp256k1_musig_secnonce_invalidate(ctx, secnonce, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret &= secp256k1_musig_nonce_gen_internal(ctx, secnonce, pubnonce, session_secrand32, seckey, pubkey, msg32, keyagg_cache, extra_input32);
|
||||
|
||||
/* Set the session_secrand32 buffer to zero to prevent the caller from using
|
||||
* nonce_gen multiple times with the same buffer. */
|
||||
secp256k1_memczero(session_secrand32, 32, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int secp256k1_musig_nonce_gen_counter(const secp256k1_context* ctx, secp256k1_musig_secnonce *secnonce, secp256k1_musig_pubnonce *pubnonce, uint64_t nonrepeating_cnt, const secp256k1_keypair *keypair, const unsigned char *msg32, const secp256k1_musig_keyagg_cache *keyagg_cache, const unsigned char *extra_input32) {
|
||||
unsigned char buf[32] = { 0 };
|
||||
unsigned char seckey[32];
|
||||
secp256k1_pubkey pubkey;
|
||||
int ret;
|
||||
|
||||
VERIFY_CHECK(ctx != NULL);
|
||||
ARG_CHECK(secnonce != NULL);
|
||||
memset(secnonce, 0, sizeof(*secnonce));
|
||||
ARG_CHECK(keypair != NULL);
|
||||
|
||||
secp256k1_write_be64(buf, nonrepeating_cnt);
|
||||
/* keypair_sec and keypair_pub do not fail if the arguments are not NULL */
|
||||
ret = secp256k1_keypair_sec(ctx, seckey, keypair);
|
||||
VERIFY_CHECK(ret);
|
||||
ret = secp256k1_keypair_pub(ctx, &pubkey, keypair);
|
||||
VERIFY_CHECK(ret);
|
||||
#ifndef VERIFY
|
||||
(void) ret;
|
||||
#endif
|
||||
|
||||
if (!secp256k1_musig_nonce_gen_internal(ctx, secnonce, pubnonce, buf, seckey, &pubkey, msg32, keyagg_cache, extra_input32)) {
|
||||
return 0;
|
||||
}
|
||||
memset(seckey, 0, sizeof(seckey));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int secp256k1_musig_sum_pubnonces(const secp256k1_context* ctx, secp256k1_gej *summed_pubnonces, const secp256k1_musig_pubnonce * const* pubnonces, size_t n_pubnonces) {
|
||||
size_t i;
|
||||
int j;
|
||||
|
||||
secp256k1_gej_set_infinity(&summed_pubnonces[0]);
|
||||
secp256k1_gej_set_infinity(&summed_pubnonces[1]);
|
||||
|
||||
for (i = 0; i < n_pubnonces; i++) {
|
||||
secp256k1_ge nonce_pts[2];
|
||||
if (!secp256k1_musig_pubnonce_load(ctx, nonce_pts, pubnonces[i])) {
|
||||
return 0;
|
||||
}
|
||||
for (j = 0; j < 2; j++) {
|
||||
secp256k1_gej_add_ge_var(&summed_pubnonces[j], &summed_pubnonces[j], &nonce_pts[j], NULL);
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int secp256k1_musig_nonce_agg(const secp256k1_context* ctx, secp256k1_musig_aggnonce *aggnonce, const secp256k1_musig_pubnonce * const* pubnonces, size_t n_pubnonces) {
|
||||
secp256k1_gej aggnonce_ptsj[2];
|
||||
secp256k1_ge aggnonce_pts[2];
|
||||
VERIFY_CHECK(ctx != NULL);
|
||||
ARG_CHECK(aggnonce != NULL);
|
||||
ARG_CHECK(pubnonces != NULL);
|
||||
ARG_CHECK(n_pubnonces > 0);
|
||||
|
||||
if (!secp256k1_musig_sum_pubnonces(ctx, aggnonce_ptsj, pubnonces, n_pubnonces)) {
|
||||
return 0;
|
||||
}
|
||||
secp256k1_ge_set_all_gej_var(aggnonce_pts, aggnonce_ptsj, 2);
|
||||
secp256k1_musig_aggnonce_save(aggnonce, aggnonce_pts);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Initializes SHA256 with fixed midstate. This midstate was computed by applying
|
||||
* SHA256 to SHA256("MuSig/noncecoef")||SHA256("MuSig/noncecoef"). */
|
||||
static void secp256k1_musig_compute_noncehash_sha256_tagged(secp256k1_sha256 *sha) {
|
||||
secp256k1_sha256_initialize(sha);
|
||||
sha->s[0] = 0x2c7d5a45ul;
|
||||
sha->s[1] = 0x06bf7e53ul;
|
||||
sha->s[2] = 0x89be68a6ul;
|
||||
sha->s[3] = 0x971254c0ul;
|
||||
sha->s[4] = 0x60ac12d2ul;
|
||||
sha->s[5] = 0x72846dcdul;
|
||||
sha->s[6] = 0x6c81212ful;
|
||||
sha->s[7] = 0xde7a2500ul;
|
||||
sha->bytes = 64;
|
||||
}
|
||||
|
||||
/* tagged_hash(aggnonce[0], aggnonce[1], agg_pk, msg) */
|
||||
static void secp256k1_musig_compute_noncehash(unsigned char *noncehash, secp256k1_ge *aggnonce, const unsigned char *agg_pk32, const unsigned char *msg) {
|
||||
unsigned char buf[33];
|
||||
secp256k1_sha256 sha;
|
||||
int i;
|
||||
|
||||
secp256k1_musig_compute_noncehash_sha256_tagged(&sha);
|
||||
for (i = 0; i < 2; i++) {
|
||||
secp256k1_musig_ge_serialize_ext(buf, &aggnonce[i]);
|
||||
secp256k1_sha256_write(&sha, buf, sizeof(buf));
|
||||
}
|
||||
secp256k1_sha256_write(&sha, agg_pk32, 32);
|
||||
secp256k1_sha256_write(&sha, msg, 32);
|
||||
secp256k1_sha256_finalize(&sha, noncehash);
|
||||
}
|
||||
|
||||
/* out_nonce = nonce_pts[0] + b*nonce_pts[1] */
|
||||
static void secp256k1_effective_nonce(secp256k1_gej *out_nonce, const secp256k1_ge *nonce_pts, const secp256k1_scalar *b) {
|
||||
secp256k1_gej tmp;
|
||||
|
||||
secp256k1_gej_set_ge(&tmp, &nonce_pts[1]);
|
||||
secp256k1_ecmult(out_nonce, &tmp, b, NULL);
|
||||
secp256k1_gej_add_ge_var(out_nonce, out_nonce, &nonce_pts[0], NULL);
|
||||
}
|
||||
|
||||
static void secp256k1_musig_nonce_process_internal(int *fin_nonce_parity, unsigned char *fin_nonce, secp256k1_scalar *b, secp256k1_ge *aggnonce_pts, const unsigned char *agg_pk32, const unsigned char *msg) {
|
||||
unsigned char noncehash[32];
|
||||
secp256k1_ge fin_nonce_pt;
|
||||
secp256k1_gej fin_nonce_ptj;
|
||||
|
||||
secp256k1_musig_compute_noncehash(noncehash, aggnonce_pts, agg_pk32, msg);
|
||||
secp256k1_scalar_set_b32(b, noncehash, NULL);
|
||||
/* fin_nonce = aggnonce_pts[0] + b*aggnonce_pts[1] */
|
||||
secp256k1_effective_nonce(&fin_nonce_ptj, aggnonce_pts, b);
|
||||
secp256k1_ge_set_gej(&fin_nonce_pt, &fin_nonce_ptj);
|
||||
if (secp256k1_ge_is_infinity(&fin_nonce_pt)) {
|
||||
fin_nonce_pt = secp256k1_ge_const_g;
|
||||
}
|
||||
/* fin_nonce_pt is not the point at infinity */
|
||||
secp256k1_fe_normalize_var(&fin_nonce_pt.x);
|
||||
secp256k1_fe_get_b32(fin_nonce, &fin_nonce_pt.x);
|
||||
secp256k1_fe_normalize_var(&fin_nonce_pt.y);
|
||||
*fin_nonce_parity = secp256k1_fe_is_odd(&fin_nonce_pt.y);
|
||||
}
|
||||
|
||||
int secp256k1_musig_nonce_process(const secp256k1_context* ctx, secp256k1_musig_session *session, const secp256k1_musig_aggnonce *aggnonce, const unsigned char *msg32, const secp256k1_musig_keyagg_cache *keyagg_cache) {
|
||||
secp256k1_keyagg_cache_internal cache_i;
|
||||
secp256k1_ge aggnonce_pts[2];
|
||||
unsigned char fin_nonce[32];
|
||||
secp256k1_musig_session_internal session_i;
|
||||
unsigned char agg_pk32[32];
|
||||
|
||||
VERIFY_CHECK(ctx != NULL);
|
||||
ARG_CHECK(session != NULL);
|
||||
ARG_CHECK(aggnonce != NULL);
|
||||
ARG_CHECK(msg32 != NULL);
|
||||
ARG_CHECK(keyagg_cache != NULL);
|
||||
|
||||
if (!secp256k1_keyagg_cache_load(ctx, &cache_i, keyagg_cache)) {
|
||||
return 0;
|
||||
}
|
||||
secp256k1_fe_get_b32(agg_pk32, &cache_i.pk.x);
|
||||
|
||||
if (!secp256k1_musig_aggnonce_load(ctx, aggnonce_pts, aggnonce)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
secp256k1_musig_nonce_process_internal(&session_i.fin_nonce_parity, fin_nonce, &session_i.noncecoef, aggnonce_pts, agg_pk32, msg32);
|
||||
secp256k1_schnorrsig_challenge(&session_i.challenge, fin_nonce, msg32, 32, agg_pk32);
|
||||
|
||||
/* If there is a tweak then set `challenge` times `tweak` to the `s`-part.*/
|
||||
secp256k1_scalar_set_int(&session_i.s_part, 0);
|
||||
if (!secp256k1_scalar_is_zero(&cache_i.tweak)) {
|
||||
secp256k1_scalar e_tmp;
|
||||
secp256k1_scalar_mul(&e_tmp, &session_i.challenge, &cache_i.tweak);
|
||||
if (secp256k1_fe_is_odd(&cache_i.pk.y)) {
|
||||
secp256k1_scalar_negate(&e_tmp, &e_tmp);
|
||||
}
|
||||
session_i.s_part = e_tmp;
|
||||
}
|
||||
memcpy(session_i.fin_nonce, fin_nonce, sizeof(session_i.fin_nonce));
|
||||
secp256k1_musig_session_save(session, &session_i);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void secp256k1_musig_partial_sign_clear(secp256k1_scalar *sk, secp256k1_scalar *k) {
|
||||
secp256k1_scalar_clear(sk);
|
||||
secp256k1_scalar_clear(&k[0]);
|
||||
secp256k1_scalar_clear(&k[1]);
|
||||
}
|
||||
|
||||
int secp256k1_musig_partial_sign(const secp256k1_context* ctx, secp256k1_musig_partial_sig *partial_sig, secp256k1_musig_secnonce *secnonce, const secp256k1_keypair *keypair, const secp256k1_musig_keyagg_cache *keyagg_cache, const secp256k1_musig_session *session) {
|
||||
secp256k1_scalar sk;
|
||||
secp256k1_ge pk, keypair_pk;
|
||||
secp256k1_scalar k[2];
|
||||
secp256k1_scalar mu, s;
|
||||
secp256k1_keyagg_cache_internal cache_i;
|
||||
secp256k1_musig_session_internal session_i;
|
||||
int ret;
|
||||
|
||||
VERIFY_CHECK(ctx != NULL);
|
||||
|
||||
ARG_CHECK(secnonce != NULL);
|
||||
/* Fails if the magic doesn't match */
|
||||
ret = secp256k1_musig_secnonce_load(ctx, k, &pk, secnonce);
|
||||
/* Set nonce to zero to avoid nonce reuse. This will cause subsequent calls
|
||||
* of this function to fail */
|
||||
memset(secnonce, 0, sizeof(*secnonce));
|
||||
if (!ret) {
|
||||
secp256k1_musig_partial_sign_clear(&sk, k);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ARG_CHECK(partial_sig != NULL);
|
||||
ARG_CHECK(keypair != NULL);
|
||||
ARG_CHECK(keyagg_cache != NULL);
|
||||
ARG_CHECK(session != NULL);
|
||||
|
||||
if (!secp256k1_keypair_load(ctx, &sk, &keypair_pk, keypair)) {
|
||||
secp256k1_musig_partial_sign_clear(&sk, k);
|
||||
return 0;
|
||||
}
|
||||
ARG_CHECK(secp256k1_fe_equal(&pk.x, &keypair_pk.x)
|
||||
&& secp256k1_fe_equal(&pk.y, &keypair_pk.y));
|
||||
if (!secp256k1_keyagg_cache_load(ctx, &cache_i, keyagg_cache)) {
|
||||
secp256k1_musig_partial_sign_clear(&sk, k);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Negate sk if secp256k1_fe_is_odd(&cache_i.pk.y)) XOR cache_i.parity_acc.
|
||||
* This corresponds to the line "Let d = g⋅gacc⋅d' mod n" in the
|
||||
* specification. */
|
||||
if ((secp256k1_fe_is_odd(&cache_i.pk.y)
|
||||
!= cache_i.parity_acc)) {
|
||||
secp256k1_scalar_negate(&sk, &sk);
|
||||
}
|
||||
|
||||
/* Multiply KeyAgg coefficient */
|
||||
secp256k1_musig_keyaggcoef(&mu, &cache_i, &pk);
|
||||
secp256k1_scalar_mul(&sk, &sk, &mu);
|
||||
|
||||
if (!secp256k1_musig_session_load(ctx, &session_i, session)) {
|
||||
secp256k1_musig_partial_sign_clear(&sk, k);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (session_i.fin_nonce_parity) {
|
||||
secp256k1_scalar_negate(&k[0], &k[0]);
|
||||
secp256k1_scalar_negate(&k[1], &k[1]);
|
||||
}
|
||||
|
||||
/* Sign */
|
||||
secp256k1_scalar_mul(&s, &session_i.challenge, &sk);
|
||||
secp256k1_scalar_mul(&k[1], &session_i.noncecoef, &k[1]);
|
||||
secp256k1_scalar_add(&k[0], &k[0], &k[1]);
|
||||
secp256k1_scalar_add(&s, &s, &k[0]);
|
||||
secp256k1_musig_partial_sig_save(partial_sig, &s);
|
||||
secp256k1_musig_partial_sign_clear(&sk, k);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int secp256k1_musig_partial_sig_verify(const secp256k1_context* ctx, const secp256k1_musig_partial_sig *partial_sig, const secp256k1_musig_pubnonce *pubnonce, const secp256k1_pubkey *pubkey, const secp256k1_musig_keyagg_cache *keyagg_cache, const secp256k1_musig_session *session) {
|
||||
secp256k1_keyagg_cache_internal cache_i;
|
||||
secp256k1_musig_session_internal session_i;
|
||||
secp256k1_scalar mu, e, s;
|
||||
secp256k1_gej pkj;
|
||||
secp256k1_ge nonce_pts[2];
|
||||
secp256k1_gej rj;
|
||||
secp256k1_gej tmp;
|
||||
secp256k1_ge pkp;
|
||||
|
||||
VERIFY_CHECK(ctx != NULL);
|
||||
ARG_CHECK(partial_sig != NULL);
|
||||
ARG_CHECK(pubnonce != NULL);
|
||||
ARG_CHECK(pubkey != NULL);
|
||||
ARG_CHECK(keyagg_cache != NULL);
|
||||
ARG_CHECK(session != NULL);
|
||||
|
||||
if (!secp256k1_musig_session_load(ctx, &session_i, session)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!secp256k1_musig_pubnonce_load(ctx, nonce_pts, pubnonce)) {
|
||||
return 0;
|
||||
}
|
||||
/* Compute "effective" nonce rj = nonce_pts[0] + b*nonce_pts[1] */
|
||||
/* TODO: use multiexp to compute -s*G + e*mu*pubkey + nonce_pts[0] + b*nonce_pts[1] */
|
||||
secp256k1_effective_nonce(&rj, nonce_pts, &session_i.noncecoef);
|
||||
|
||||
if (!secp256k1_pubkey_load(ctx, &pkp, pubkey)) {
|
||||
return 0;
|
||||
}
|
||||
if (!secp256k1_keyagg_cache_load(ctx, &cache_i, keyagg_cache)) {
|
||||
return 0;
|
||||
}
|
||||
/* Multiplying the challenge by the KeyAgg coefficient is equivalent
|
||||
* to multiplying the signer's public key by the coefficient, except
|
||||
* much easier to do. */
|
||||
secp256k1_musig_keyaggcoef(&mu, &cache_i, &pkp);
|
||||
secp256k1_scalar_mul(&e, &session_i.challenge, &mu);
|
||||
|
||||
/* Negate e if secp256k1_fe_is_odd(&cache_i.pk.y)) XOR cache_i.parity_acc.
|
||||
* This corresponds to the line "Let g' = g⋅gacc mod n" and the multiplication "g'⋅e"
|
||||
* in the specification. */
|
||||
if (secp256k1_fe_is_odd(&cache_i.pk.y)
|
||||
!= cache_i.parity_acc) {
|
||||
secp256k1_scalar_negate(&e, &e);
|
||||
}
|
||||
|
||||
if (!secp256k1_musig_partial_sig_load(ctx, &s, partial_sig)) {
|
||||
return 0;
|
||||
}
|
||||
/* Compute -s*G + e*pkj + rj (e already includes the keyagg coefficient mu) */
|
||||
secp256k1_scalar_negate(&s, &s);
|
||||
secp256k1_gej_set_ge(&pkj, &pkp);
|
||||
secp256k1_ecmult(&tmp, &pkj, &e, &s);
|
||||
if (session_i.fin_nonce_parity) {
|
||||
secp256k1_gej_neg(&rj, &rj);
|
||||
}
|
||||
secp256k1_gej_add_var(&tmp, &tmp, &rj, NULL);
|
||||
|
||||
return secp256k1_gej_is_infinity(&tmp);
|
||||
}
|
||||
|
||||
int secp256k1_musig_partial_sig_agg(const secp256k1_context* ctx, unsigned char *sig64, const secp256k1_musig_session *session, const secp256k1_musig_partial_sig * const* partial_sigs, size_t n_sigs) {
|
||||
size_t i;
|
||||
secp256k1_musig_session_internal session_i;
|
||||
|
||||
VERIFY_CHECK(ctx != NULL);
|
||||
ARG_CHECK(sig64 != NULL);
|
||||
ARG_CHECK(session != NULL);
|
||||
ARG_CHECK(partial_sigs != NULL);
|
||||
ARG_CHECK(n_sigs > 0);
|
||||
|
||||
if (!secp256k1_musig_session_load(ctx, &session_i, session)) {
|
||||
return 0;
|
||||
}
|
||||
for (i = 0; i < n_sigs; i++) {
|
||||
secp256k1_scalar term;
|
||||
if (!secp256k1_musig_partial_sig_load(ctx, &term, partial_sigs[i])) {
|
||||
return 0;
|
||||
}
|
||||
secp256k1_scalar_add(&session_i.s_part, &session_i.s_part, &term);
|
||||
}
|
||||
secp256k1_scalar_get_b32(&sig64[32], &session_i.s_part);
|
||||
memcpy(&sig64[0], session_i.fin_nonce, 32);
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,346 @@
|
||||
/**
|
||||
* Automatically generated by ./tools/test_vectors_musig2_generate.py.
|
||||
*
|
||||
* The test vectors for the KeySort function are included in this file. They can
|
||||
* be found in src/modules/extrakeys/tests_impl.h. */
|
||||
|
||||
enum MUSIG_ERROR {
|
||||
MUSIG_PUBKEY,
|
||||
MUSIG_TWEAK,
|
||||
MUSIG_PUBNONCE,
|
||||
MUSIG_AGGNONCE,
|
||||
MUSIG_SECNONCE,
|
||||
MUSIG_SIG,
|
||||
MUSIG_SIG_VERIFY,
|
||||
MUSIG_OTHER
|
||||
};
|
||||
|
||||
struct musig_key_agg_valid_test_case {
|
||||
size_t key_indices_len;
|
||||
size_t key_indices[4];
|
||||
unsigned char expected[32];
|
||||
};
|
||||
|
||||
struct musig_key_agg_error_test_case {
|
||||
size_t key_indices_len;
|
||||
size_t key_indices[4];
|
||||
size_t tweak_indices_len;
|
||||
size_t tweak_indices[1];
|
||||
int is_xonly[1];
|
||||
enum MUSIG_ERROR error;
|
||||
};
|
||||
|
||||
struct musig_key_agg_vector {
|
||||
unsigned char pubkeys[7][33];
|
||||
unsigned char tweaks[2][32];
|
||||
struct musig_key_agg_valid_test_case valid_case[4];
|
||||
struct musig_key_agg_error_test_case error_case[5];
|
||||
};
|
||||
|
||||
static const struct musig_key_agg_vector musig_key_agg_vector = {
|
||||
{
|
||||
{ 0x02, 0xF9, 0x30, 0x8A, 0x01, 0x92, 0x58, 0xC3, 0x10, 0x49, 0x34, 0x4F, 0x85, 0xF8, 0x9D, 0x52, 0x29, 0xB5, 0x31, 0xC8, 0x45, 0x83, 0x6F, 0x99, 0xB0, 0x86, 0x01, 0xF1, 0x13, 0xBC, 0xE0, 0x36, 0xF9 },
|
||||
{ 0x03, 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F, 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE, 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8, 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59 },
|
||||
{ 0x02, 0x35, 0x90, 0xA9, 0x4E, 0x76, 0x8F, 0x8E, 0x18, 0x15, 0xC2, 0xF2, 0x4B, 0x4D, 0x80, 0xA8, 0xE3, 0x14, 0x93, 0x16, 0xC3, 0x51, 0x8C, 0xE7, 0xB7, 0xAD, 0x33, 0x83, 0x68, 0xD0, 0x38, 0xCA, 0x66 },
|
||||
{ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05 },
|
||||
{ 0x02, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFC, 0x30 },
|
||||
{ 0x04, 0xF9, 0x30, 0x8A, 0x01, 0x92, 0x58, 0xC3, 0x10, 0x49, 0x34, 0x4F, 0x85, 0xF8, 0x9D, 0x52, 0x29, 0xB5, 0x31, 0xC8, 0x45, 0x83, 0x6F, 0x99, 0xB0, 0x86, 0x01, 0xF1, 0x13, 0xBC, 0xE0, 0x36, 0xF9 },
|
||||
{ 0x03, 0x93, 0x5F, 0x97, 0x2D, 0xA0, 0x13, 0xF8, 0x0A, 0xE0, 0x11, 0x89, 0x0F, 0xA8, 0x9B, 0x67, 0xA2, 0x7B, 0x7B, 0xE6, 0xCC, 0xB2, 0x4D, 0x32, 0x74, 0xD1, 0x8B, 0x2D, 0x40, 0x67, 0xF2, 0x61, 0xA9 }
|
||||
},
|
||||
{
|
||||
{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x41 },
|
||||
{ 0x25, 0x2E, 0x4B, 0xD6, 0x74, 0x10, 0xA7, 0x6C, 0xDF, 0x93, 0x3D, 0x30, 0xEA, 0xA1, 0x60, 0x82, 0x14, 0x03, 0x7F, 0x1B, 0x10, 0x5A, 0x01, 0x3E, 0xCC, 0xD3, 0xC5, 0xC1, 0x84, 0xA6, 0x11, 0x0B }
|
||||
},
|
||||
{
|
||||
{ 3, { 0, 1, 2 }, { 0x90, 0x53, 0x9E, 0xED, 0xE5, 0x65, 0xF5, 0xD0, 0x54, 0xF3, 0x2C, 0xC0, 0xC2, 0x20, 0x12, 0x68, 0x89, 0xED, 0x1E, 0x5D, 0x19, 0x3B, 0xAF, 0x15, 0xAE, 0xF3, 0x44, 0xFE, 0x59, 0xD4, 0x61, 0x0C }},
|
||||
{ 3, { 2, 1, 0 }, { 0x62, 0x04, 0xDE, 0x8B, 0x08, 0x34, 0x26, 0xDC, 0x6E, 0xAF, 0x95, 0x02, 0xD2, 0x70, 0x24, 0xD5, 0x3F, 0xC8, 0x26, 0xBF, 0x7D, 0x20, 0x12, 0x14, 0x8A, 0x05, 0x75, 0x43, 0x5D, 0xF5, 0x4B, 0x2B }},
|
||||
{ 3, { 0, 0, 0 }, { 0xB4, 0x36, 0xE3, 0xBA, 0xD6, 0x2B, 0x8C, 0xD4, 0x09, 0x96, 0x9A, 0x22, 0x47, 0x31, 0xC1, 0x93, 0xD0, 0x51, 0x16, 0x2D, 0x8C, 0x5A, 0xE8, 0xB1, 0x09, 0x30, 0x61, 0x27, 0xDA, 0x3A, 0xA9, 0x35 }},
|
||||
{ 4, { 0, 0, 1, 1 }, { 0x69, 0xBC, 0x22, 0xBF, 0xA5, 0xD1, 0x06, 0x30, 0x6E, 0x48, 0xA2, 0x06, 0x79, 0xDE, 0x1D, 0x73, 0x89, 0x38, 0x61, 0x24, 0xD0, 0x75, 0x71, 0xD0, 0xD8, 0x72, 0x68, 0x60, 0x28, 0xC2, 0x6A, 0x3E }},
|
||||
},
|
||||
{
|
||||
{ 2, { 0, 3 }, 0, { 0 }, { 0 }, MUSIG_PUBKEY },
|
||||
{ 2, { 0, 4 }, 0, { 0 }, { 0 }, MUSIG_PUBKEY },
|
||||
{ 2, { 5, 0 }, 0, { 0 }, { 0 }, MUSIG_PUBKEY },
|
||||
{ 2, { 0, 1 }, 1, { 0 }, { 1 }, MUSIG_TWEAK },
|
||||
{ 1, { 6 }, 1, { 1 }, { 0 }, MUSIG_TWEAK },
|
||||
},
|
||||
};
|
||||
|
||||
struct musig_nonce_gen_test_case {
|
||||
unsigned char rand_[32];
|
||||
int has_sk;
|
||||
unsigned char sk[32];
|
||||
unsigned char pk[33];
|
||||
int has_aggpk;
|
||||
unsigned char aggpk[32];
|
||||
int has_msg;
|
||||
unsigned char msg[32];
|
||||
int has_extra_in;
|
||||
unsigned char extra_in[32];
|
||||
unsigned char expected_secnonce[97];
|
||||
unsigned char expected_pubnonce[66];
|
||||
};
|
||||
|
||||
struct musig_nonce_gen_vector {
|
||||
struct musig_nonce_gen_test_case test_case[2];
|
||||
};
|
||||
|
||||
static const struct musig_nonce_gen_vector musig_nonce_gen_vector = {
|
||||
{
|
||||
{ { 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F }, 1 , { 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02 }, { 0x02, 0x4D, 0x4B, 0x6C, 0xD1, 0x36, 0x10, 0x32, 0xCA, 0x9B, 0xD2, 0xAE, 0xB9, 0xD9, 0x00, 0xAA, 0x4D, 0x45, 0xD9, 0xEA, 0xD8, 0x0A, 0xC9, 0x42, 0x33, 0x74, 0xC4, 0x51, 0xA7, 0x25, 0x4D, 0x07, 0x66 }, 1 , { 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07 }, 1 , { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 }, 1 , { 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08 }, { 0xB1, 0x14, 0xE5, 0x02, 0xBE, 0xAA, 0x4E, 0x30, 0x1D, 0xD0, 0x8A, 0x50, 0x26, 0x41, 0x72, 0xC8, 0x4E, 0x41, 0x65, 0x0E, 0x6C, 0xB7, 0x26, 0xB4, 0x10, 0xC0, 0x69, 0x4D, 0x59, 0xEF, 0xFB, 0x64, 0x95, 0xB5, 0xCA, 0xF2, 0x8D, 0x04, 0x5B, 0x97, 0x3D, 0x63, 0xE3, 0xC9, 0x9A, 0x44, 0xB8, 0x07, 0xBD, 0xE3, 0x75, 0xFD, 0x6C, 0xB3, 0x9E, 0x46, 0xDC, 0x4A, 0x51, 0x17, 0x08, 0xD0, 0xE9, 0xD2, 0x02, 0x4D, 0x4B, 0x6C, 0xD1, 0x36, 0x10, 0x32, 0xCA, 0x9B, 0xD2, 0xAE, 0xB9, 0xD9, 0x00, 0xAA, 0x4D, 0x45, 0xD9, 0xEA, 0xD8, 0x0A, 0xC9, 0x42, 0x33, 0x74, 0xC4, 0x51, 0xA7, 0x25, 0x4D, 0x07, 0x66 }, { 0x02, 0xF7, 0xBE, 0x70, 0x89, 0xE8, 0x37, 0x6E, 0xB3, 0x55, 0x27, 0x23, 0x68, 0x76, 0x6B, 0x17, 0xE8, 0x8E, 0x7D, 0xB7, 0x20, 0x47, 0xD0, 0x5E, 0x56, 0xAA, 0x88, 0x1E, 0xA5, 0x2B, 0x3B, 0x35, 0xDF, 0x02, 0xC2, 0x9C, 0x80, 0x46, 0xFD, 0xD0, 0xDE, 0xD4, 0xC7, 0xE5, 0x58, 0x69, 0x13, 0x72, 0x00, 0xFB, 0xDB, 0xFE, 0x2E, 0xB6, 0x54, 0x26, 0x7B, 0x6D, 0x70, 0x13, 0x60, 0x2C, 0xAE, 0xD3, 0x11, 0x5A } },
|
||||
{ { 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F }, 0 , { 0 }, { 0x02, 0xF9, 0x30, 0x8A, 0x01, 0x92, 0x58, 0xC3, 0x10, 0x49, 0x34, 0x4F, 0x85, 0xF8, 0x9D, 0x52, 0x29, 0xB5, 0x31, 0xC8, 0x45, 0x83, 0x6F, 0x99, 0xB0, 0x86, 0x01, 0xF1, 0x13, 0xBC, 0xE0, 0x36, 0xF9 }, 0 , { 0 }, 0 , { 0 }, 0 , { 0 }, { 0x89, 0xBD, 0xD7, 0x87, 0xD0, 0x28, 0x4E, 0x5E, 0x4D, 0x5F, 0xC5, 0x72, 0xE4, 0x9E, 0x31, 0x6B, 0xAB, 0x7E, 0x21, 0xE3, 0xB1, 0x83, 0x0D, 0xE3, 0x7D, 0xFE, 0x80, 0x15, 0x6F, 0xA4, 0x1A, 0x6D, 0x0B, 0x17, 0xAE, 0x8D, 0x02, 0x4C, 0x53, 0x67, 0x96, 0x99, 0xA6, 0xFD, 0x79, 0x44, 0xD9, 0xC4, 0xA3, 0x66, 0xB5, 0x14, 0xBA, 0xF4, 0x30, 0x88, 0xE0, 0x70, 0x8B, 0x10, 0x23, 0xDD, 0x28, 0x97, 0x02, 0xF9, 0x30, 0x8A, 0x01, 0x92, 0x58, 0xC3, 0x10, 0x49, 0x34, 0x4F, 0x85, 0xF8, 0x9D, 0x52, 0x29, 0xB5, 0x31, 0xC8, 0x45, 0x83, 0x6F, 0x99, 0xB0, 0x86, 0x01, 0xF1, 0x13, 0xBC, 0xE0, 0x36, 0xF9 }, { 0x02, 0xC9, 0x6E, 0x7C, 0xB1, 0xE8, 0xAA, 0x5D, 0xAC, 0x64, 0xD8, 0x72, 0x94, 0x79, 0x14, 0x19, 0x8F, 0x60, 0x7D, 0x90, 0xEC, 0xDE, 0x52, 0x00, 0xDE, 0x52, 0x97, 0x8A, 0xD5, 0xDE, 0xD6, 0x3C, 0x00, 0x02, 0x99, 0xEC, 0x51, 0x17, 0xC2, 0xD2, 0x9E, 0xDE, 0xE8, 0xA2, 0x09, 0x25, 0x87, 0xC3, 0x90, 0x9B, 0xE6, 0x94, 0xD5, 0xCF, 0xF0, 0x66, 0x7D, 0x6C, 0x02, 0xEA, 0x40, 0x59, 0xF7, 0xCD, 0x97, 0x86 } },
|
||||
},
|
||||
};
|
||||
|
||||
struct musig_nonce_agg_test_case {
|
||||
size_t pnonce_indices[2];
|
||||
/* if valid case */
|
||||
unsigned char expected[66];
|
||||
/* if error case */
|
||||
int invalid_nonce_idx;
|
||||
};
|
||||
|
||||
struct musig_nonce_agg_vector {
|
||||
unsigned char pnonces[7][66];
|
||||
struct musig_nonce_agg_test_case valid_case[2];
|
||||
struct musig_nonce_agg_test_case error_case[3];
|
||||
};
|
||||
|
||||
static const struct musig_nonce_agg_vector musig_nonce_agg_vector = {
|
||||
{
|
||||
{ 0x02, 0x01, 0x51, 0xC8, 0x0F, 0x43, 0x56, 0x48, 0xDF, 0x67, 0xA2, 0x2B, 0x74, 0x9C, 0xD7, 0x98, 0xCE, 0x54, 0xE0, 0x32, 0x1D, 0x03, 0x4B, 0x92, 0xB7, 0x09, 0xB5, 0x67, 0xD6, 0x0A, 0x42, 0xE6, 0x66, 0x03, 0xBA, 0x47, 0xFB, 0xC1, 0x83, 0x44, 0x37, 0xB3, 0x21, 0x2E, 0x89, 0xA8, 0x4D, 0x84, 0x25, 0xE7, 0xBF, 0x12, 0xE0, 0x24, 0x5D, 0x98, 0x26, 0x22, 0x68, 0xEB, 0xDC, 0xB3, 0x85, 0xD5, 0x06, 0x41 },
|
||||
{ 0x03, 0xFF, 0x40, 0x6F, 0xFD, 0x8A, 0xDB, 0x9C, 0xD2, 0x98, 0x77, 0xE4, 0x98, 0x50, 0x14, 0xF6, 0x6A, 0x59, 0xF6, 0xCD, 0x01, 0xC0, 0xE8, 0x8C, 0xAA, 0x8E, 0x5F, 0x31, 0x66, 0xB1, 0xF6, 0x76, 0xA6, 0x02, 0x48, 0xC2, 0x64, 0xCD, 0xD5, 0x7D, 0x3C, 0x24, 0xD7, 0x99, 0x90, 0xB0, 0xF8, 0x65, 0x67, 0x4E, 0xB6, 0x2A, 0x0F, 0x90, 0x18, 0x27, 0x7A, 0x95, 0x01, 0x1B, 0x41, 0xBF, 0xC1, 0x93, 0xB8, 0x33 },
|
||||
{ 0x02, 0x01, 0x51, 0xC8, 0x0F, 0x43, 0x56, 0x48, 0xDF, 0x67, 0xA2, 0x2B, 0x74, 0x9C, 0xD7, 0x98, 0xCE, 0x54, 0xE0, 0x32, 0x1D, 0x03, 0x4B, 0x92, 0xB7, 0x09, 0xB5, 0x67, 0xD6, 0x0A, 0x42, 0xE6, 0x66, 0x02, 0x79, 0xBE, 0x66, 0x7E, 0xF9, 0xDC, 0xBB, 0xAC, 0x55, 0xA0, 0x62, 0x95, 0xCE, 0x87, 0x0B, 0x07, 0x02, 0x9B, 0xFC, 0xDB, 0x2D, 0xCE, 0x28, 0xD9, 0x59, 0xF2, 0x81, 0x5B, 0x16, 0xF8, 0x17, 0x98 },
|
||||
{ 0x03, 0xFF, 0x40, 0x6F, 0xFD, 0x8A, 0xDB, 0x9C, 0xD2, 0x98, 0x77, 0xE4, 0x98, 0x50, 0x14, 0xF6, 0x6A, 0x59, 0xF6, 0xCD, 0x01, 0xC0, 0xE8, 0x8C, 0xAA, 0x8E, 0x5F, 0x31, 0x66, 0xB1, 0xF6, 0x76, 0xA6, 0x03, 0x79, 0xBE, 0x66, 0x7E, 0xF9, 0xDC, 0xBB, 0xAC, 0x55, 0xA0, 0x62, 0x95, 0xCE, 0x87, 0x0B, 0x07, 0x02, 0x9B, 0xFC, 0xDB, 0x2D, 0xCE, 0x28, 0xD9, 0x59, 0xF2, 0x81, 0x5B, 0x16, 0xF8, 0x17, 0x98 },
|
||||
{ 0x04, 0xFF, 0x40, 0x6F, 0xFD, 0x8A, 0xDB, 0x9C, 0xD2, 0x98, 0x77, 0xE4, 0x98, 0x50, 0x14, 0xF6, 0x6A, 0x59, 0xF6, 0xCD, 0x01, 0xC0, 0xE8, 0x8C, 0xAA, 0x8E, 0x5F, 0x31, 0x66, 0xB1, 0xF6, 0x76, 0xA6, 0x02, 0x48, 0xC2, 0x64, 0xCD, 0xD5, 0x7D, 0x3C, 0x24, 0xD7, 0x99, 0x90, 0xB0, 0xF8, 0x65, 0x67, 0x4E, 0xB6, 0x2A, 0x0F, 0x90, 0x18, 0x27, 0x7A, 0x95, 0x01, 0x1B, 0x41, 0xBF, 0xC1, 0x93, 0xB8, 0x33 },
|
||||
{ 0x03, 0xFF, 0x40, 0x6F, 0xFD, 0x8A, 0xDB, 0x9C, 0xD2, 0x98, 0x77, 0xE4, 0x98, 0x50, 0x14, 0xF6, 0x6A, 0x59, 0xF6, 0xCD, 0x01, 0xC0, 0xE8, 0x8C, 0xAA, 0x8E, 0x5F, 0x31, 0x66, 0xB1, 0xF6, 0x76, 0xA6, 0x02, 0x48, 0xC2, 0x64, 0xCD, 0xD5, 0x7D, 0x3C, 0x24, 0xD7, 0x99, 0x90, 0xB0, 0xF8, 0x65, 0x67, 0x4E, 0xB6, 0x2A, 0x0F, 0x90, 0x18, 0x27, 0x7A, 0x95, 0x01, 0x1B, 0x41, 0xBF, 0xC1, 0x93, 0xB8, 0x31 },
|
||||
{ 0x03, 0xFF, 0x40, 0x6F, 0xFD, 0x8A, 0xDB, 0x9C, 0xD2, 0x98, 0x77, 0xE4, 0x98, 0x50, 0x14, 0xF6, 0x6A, 0x59, 0xF6, 0xCD, 0x01, 0xC0, 0xE8, 0x8C, 0xAA, 0x8E, 0x5F, 0x31, 0x66, 0xB1, 0xF6, 0x76, 0xA6, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFC, 0x30 }
|
||||
},
|
||||
{
|
||||
{ { 0, 1 }, { 0x03, 0x5F, 0xE1, 0x87, 0x3B, 0x4F, 0x29, 0x67, 0xF5, 0x2F, 0xEA, 0x4A, 0x06, 0xAD, 0x5A, 0x8E, 0xCC, 0xBE, 0x9D, 0x0F, 0xD7, 0x30, 0x68, 0x01, 0x2C, 0x89, 0x4E, 0x2E, 0x87, 0xCC, 0xB5, 0x80, 0x4B, 0x02, 0x47, 0x25, 0x37, 0x73, 0x45, 0xBD, 0xE0, 0xE9, 0xC3, 0x3A, 0xF3, 0xC4, 0x3C, 0x0A, 0x29, 0xA9, 0x24, 0x9F, 0x2F, 0x29, 0x56, 0xFA, 0x8C, 0xFE, 0xB5, 0x5C, 0x85, 0x73, 0xD0, 0x26, 0x2D, 0xC8 }, 0 },
|
||||
{ { 2, 3 }, { 0x03, 0x5F, 0xE1, 0x87, 0x3B, 0x4F, 0x29, 0x67, 0xF5, 0x2F, 0xEA, 0x4A, 0x06, 0xAD, 0x5A, 0x8E, 0xCC, 0xBE, 0x9D, 0x0F, 0xD7, 0x30, 0x68, 0x01, 0x2C, 0x89, 0x4E, 0x2E, 0x87, 0xCC, 0xB5, 0x80, 0x4B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0 },
|
||||
},
|
||||
{
|
||||
{ { 0, 4 }, { 0 }, 1 },
|
||||
{ { 5, 1 }, { 0 }, 0 },
|
||||
{ { 6, 1 }, { 0 }, 0 },
|
||||
},
|
||||
};
|
||||
|
||||
/* Omit pubnonces in the test vectors because our partial signature verification
|
||||
* implementation is able to accept the aggnonce directly. */
|
||||
struct musig_valid_case {
|
||||
size_t key_indices_len;
|
||||
size_t key_indices[3];
|
||||
size_t aggnonce_index;
|
||||
size_t msg_index;
|
||||
size_t signer_index;
|
||||
unsigned char expected[32];
|
||||
};
|
||||
|
||||
struct musig_sign_error_case {
|
||||
size_t key_indices_len;
|
||||
size_t key_indices[3];
|
||||
size_t aggnonce_index;
|
||||
size_t msg_index;
|
||||
size_t secnonce_index;
|
||||
enum MUSIG_ERROR error;
|
||||
};
|
||||
|
||||
struct musig_verify_fail_error_case {
|
||||
unsigned char sig[32];
|
||||
size_t key_indices_len;
|
||||
size_t key_indices[3];
|
||||
size_t nonce_indices_len;
|
||||
size_t nonce_indices[3];
|
||||
size_t msg_index;
|
||||
size_t signer_index;
|
||||
enum MUSIG_ERROR error;
|
||||
};
|
||||
|
||||
struct musig_sign_verify_vector {
|
||||
unsigned char sk[32];
|
||||
unsigned char pubkeys[4][33];
|
||||
unsigned char secnonces[2][194];
|
||||
unsigned char pubnonces[5][194];
|
||||
unsigned char aggnonces[5][66];
|
||||
unsigned char msgs[1][32];
|
||||
struct musig_valid_case valid_case[4];
|
||||
struct musig_sign_error_case sign_error_case[6];
|
||||
struct musig_verify_fail_error_case verify_fail_case[3];
|
||||
struct musig_verify_fail_error_case verify_error_case[2];
|
||||
};
|
||||
|
||||
static const struct musig_sign_verify_vector musig_sign_verify_vector = {
|
||||
{ 0x7F, 0xB9, 0xE0, 0xE6, 0x87, 0xAD, 0xA1, 0xEE, 0xBF, 0x7E, 0xCF, 0xE2, 0xF2, 0x1E, 0x73, 0xEB, 0xDB, 0x51, 0xA7, 0xD4, 0x50, 0x94, 0x8D, 0xFE, 0x8D, 0x76, 0xD7, 0xF2, 0xD1, 0x00, 0x76, 0x71 },
|
||||
{
|
||||
{ 0x03, 0x93, 0x5F, 0x97, 0x2D, 0xA0, 0x13, 0xF8, 0x0A, 0xE0, 0x11, 0x89, 0x0F, 0xA8, 0x9B, 0x67, 0xA2, 0x7B, 0x7B, 0xE6, 0xCC, 0xB2, 0x4D, 0x32, 0x74, 0xD1, 0x8B, 0x2D, 0x40, 0x67, 0xF2, 0x61, 0xA9 },
|
||||
{ 0x02, 0xF9, 0x30, 0x8A, 0x01, 0x92, 0x58, 0xC3, 0x10, 0x49, 0x34, 0x4F, 0x85, 0xF8, 0x9D, 0x52, 0x29, 0xB5, 0x31, 0xC8, 0x45, 0x83, 0x6F, 0x99, 0xB0, 0x86, 0x01, 0xF1, 0x13, 0xBC, 0xE0, 0x36, 0xF9 },
|
||||
{ 0x02, 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F, 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE, 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8, 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x61 },
|
||||
{ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07 }
|
||||
},
|
||||
{
|
||||
{ 0x50, 0x8B, 0x81, 0xA6, 0x11, 0xF1, 0x00, 0xA6, 0xB2, 0xB6, 0xB2, 0x96, 0x56, 0x59, 0x08, 0x98, 0xAF, 0x48, 0x8B, 0xCF, 0x2E, 0x1F, 0x55, 0xCF, 0x22, 0xE5, 0xCF, 0xB8, 0x44, 0x21, 0xFE, 0x61, 0xFA, 0x27, 0xFD, 0x49, 0xB1, 0xD5, 0x00, 0x85, 0xB4, 0x81, 0x28, 0x5E, 0x1C, 0xA2, 0x05, 0xD5, 0x5C, 0x82, 0xCC, 0x1B, 0x31, 0xFF, 0x5C, 0xD5, 0x4A, 0x48, 0x98, 0x29, 0x35, 0x59, 0x01, 0xF7, 0x03, 0x93, 0x5F, 0x97, 0x2D, 0xA0, 0x13, 0xF8, 0x0A, 0xE0, 0x11, 0x89, 0x0F, 0xA8, 0x9B, 0x67, 0xA2, 0x7B, 0x7B, 0xE6, 0xCC, 0xB2, 0x4D, 0x32, 0x74, 0xD1, 0x8B, 0x2D, 0x40, 0x67, 0xF2, 0x61, 0xA9 },
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x93, 0x5F, 0x97, 0x2D, 0xA0, 0x13, 0xF8, 0x0A, 0xE0, 0x11, 0x89, 0x0F, 0xA8, 0x9B, 0x67, 0xA2, 0x7B, 0x7B, 0xE6, 0xCC, 0xB2, 0x4D, 0x32, 0x74, 0xD1, 0x8B, 0x2D, 0x40, 0x67, 0xF2, 0x61, 0xA9 }
|
||||
},
|
||||
{
|
||||
{ 0x03, 0x37, 0xC8, 0x78, 0x21, 0xAF, 0xD5, 0x0A, 0x86, 0x44, 0xD8, 0x20, 0xA8, 0xF3, 0xE0, 0x2E, 0x49, 0x9C, 0x93, 0x18, 0x65, 0xC2, 0x36, 0x0F, 0xB4, 0x3D, 0x0A, 0x0D, 0x20, 0xDA, 0xFE, 0x07, 0xEA, 0x02, 0x87, 0xBF, 0x89, 0x1D, 0x2A, 0x6D, 0xEA, 0xEB, 0xAD, 0xC9, 0x09, 0x35, 0x2A, 0xA9, 0x40, 0x5D, 0x14, 0x28, 0xC1, 0x5F, 0x4B, 0x75, 0xF0, 0x4D, 0xAE, 0x64, 0x2A, 0x95, 0xC2, 0x54, 0x84, 0x80 },
|
||||
{ 0x02, 0x79, 0xBE, 0x66, 0x7E, 0xF9, 0xDC, 0xBB, 0xAC, 0x55, 0xA0, 0x62, 0x95, 0xCE, 0x87, 0x0B, 0x07, 0x02, 0x9B, 0xFC, 0xDB, 0x2D, 0xCE, 0x28, 0xD9, 0x59, 0xF2, 0x81, 0x5B, 0x16, 0xF8, 0x17, 0x98, 0x02, 0x79, 0xBE, 0x66, 0x7E, 0xF9, 0xDC, 0xBB, 0xAC, 0x55, 0xA0, 0x62, 0x95, 0xCE, 0x87, 0x0B, 0x07, 0x02, 0x9B, 0xFC, 0xDB, 0x2D, 0xCE, 0x28, 0xD9, 0x59, 0xF2, 0x81, 0x5B, 0x16, 0xF8, 0x17, 0x98 },
|
||||
{ 0x03, 0x2D, 0xE2, 0x66, 0x26, 0x28, 0xC9, 0x0B, 0x03, 0xF5, 0xE7, 0x20, 0x28, 0x4E, 0xB5, 0x2F, 0xF7, 0xD7, 0x1F, 0x42, 0x84, 0xF6, 0x27, 0xB6, 0x8A, 0x85, 0x3D, 0x78, 0xC7, 0x8E, 0x1F, 0xFE, 0x93, 0x03, 0xE4, 0xC5, 0x52, 0x4E, 0x83, 0xFF, 0xE1, 0x49, 0x3B, 0x90, 0x77, 0xCF, 0x1C, 0xA6, 0xBE, 0xB2, 0x09, 0x0C, 0x93, 0xD9, 0x30, 0x32, 0x10, 0x71, 0xAD, 0x40, 0xB2, 0xF4, 0x4E, 0x59, 0x90, 0x46 },
|
||||
{ 0x02, 0x37, 0xC8, 0x78, 0x21, 0xAF, 0xD5, 0x0A, 0x86, 0x44, 0xD8, 0x20, 0xA8, 0xF3, 0xE0, 0x2E, 0x49, 0x9C, 0x93, 0x18, 0x65, 0xC2, 0x36, 0x0F, 0xB4, 0x3D, 0x0A, 0x0D, 0x20, 0xDA, 0xFE, 0x07, 0xEA, 0x03, 0x87, 0xBF, 0x89, 0x1D, 0x2A, 0x6D, 0xEA, 0xEB, 0xAD, 0xC9, 0x09, 0x35, 0x2A, 0xA9, 0x40, 0x5D, 0x14, 0x28, 0xC1, 0x5F, 0x4B, 0x75, 0xF0, 0x4D, 0xAE, 0x64, 0x2A, 0x95, 0xC2, 0x54, 0x84, 0x80 },
|
||||
{ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x02, 0x87, 0xBF, 0x89, 0x1D, 0x2A, 0x6D, 0xEA, 0xEB, 0xAD, 0xC9, 0x09, 0x35, 0x2A, 0xA9, 0x40, 0x5D, 0x14, 0x28, 0xC1, 0x5F, 0x4B, 0x75, 0xF0, 0x4D, 0xAE, 0x64, 0x2A, 0x95, 0xC2, 0x54, 0x84, 0x80 }
|
||||
},
|
||||
{
|
||||
{ 0x02, 0x84, 0x65, 0xFC, 0xF0, 0xBB, 0xDB, 0xCF, 0x44, 0x3A, 0xAB, 0xCC, 0xE5, 0x33, 0xD4, 0x2B, 0x4B, 0x5A, 0x10, 0x96, 0x6A, 0xC0, 0x9A, 0x49, 0x65, 0x5E, 0x8C, 0x42, 0xDA, 0xAB, 0x8F, 0xCD, 0x61, 0x03, 0x74, 0x96, 0xA3, 0xCC, 0x86, 0x92, 0x6D, 0x45, 0x2C, 0xAF, 0xCF, 0xD5, 0x5D, 0x25, 0x97, 0x2C, 0xA1, 0x67, 0x5D, 0x54, 0x93, 0x10, 0xDE, 0x29, 0x6B, 0xFF, 0x42, 0xF7, 0x2E, 0xEE, 0xA8, 0xC9 },
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x04, 0x84, 0x65, 0xFC, 0xF0, 0xBB, 0xDB, 0xCF, 0x44, 0x3A, 0xAB, 0xCC, 0xE5, 0x33, 0xD4, 0x2B, 0x4B, 0x5A, 0x10, 0x96, 0x6A, 0xC0, 0x9A, 0x49, 0x65, 0x5E, 0x8C, 0x42, 0xDA, 0xAB, 0x8F, 0xCD, 0x61, 0x03, 0x74, 0x96, 0xA3, 0xCC, 0x86, 0x92, 0x6D, 0x45, 0x2C, 0xAF, 0xCF, 0xD5, 0x5D, 0x25, 0x97, 0x2C, 0xA1, 0x67, 0x5D, 0x54, 0x93, 0x10, 0xDE, 0x29, 0x6B, 0xFF, 0x42, 0xF7, 0x2E, 0xEE, 0xA8, 0xC9 },
|
||||
{ 0x02, 0x84, 0x65, 0xFC, 0xF0, 0xBB, 0xDB, 0xCF, 0x44, 0x3A, 0xAB, 0xCC, 0xE5, 0x33, 0xD4, 0x2B, 0x4B, 0x5A, 0x10, 0x96, 0x6A, 0xC0, 0x9A, 0x49, 0x65, 0x5E, 0x8C, 0x42, 0xDA, 0xAB, 0x8F, 0xCD, 0x61, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09 },
|
||||
{ 0x02, 0x84, 0x65, 0xFC, 0xF0, 0xBB, 0xDB, 0xCF, 0x44, 0x3A, 0xAB, 0xCC, 0xE5, 0x33, 0xD4, 0x2B, 0x4B, 0x5A, 0x10, 0x96, 0x6A, 0xC0, 0x9A, 0x49, 0x65, 0x5E, 0x8C, 0x42, 0xDA, 0xAB, 0x8F, 0xCD, 0x61, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFC, 0x30 }
|
||||
},
|
||||
{
|
||||
{ 0xF9, 0x54, 0x66, 0xD0, 0x86, 0x77, 0x0E, 0x68, 0x99, 0x64, 0x66, 0x42, 0x19, 0x26, 0x6F, 0xE5, 0xED, 0x21, 0x5C, 0x92, 0xAE, 0x20, 0xBA, 0xB5, 0xC9, 0xD7, 0x9A, 0xDD, 0xDD, 0xF3, 0xC0, 0xCF }
|
||||
},
|
||||
{
|
||||
{ 3, { 0, 1, 2 }, 0, 0, 0, { 0x01, 0x2A, 0xBB, 0xCB, 0x52, 0xB3, 0x01, 0x6A, 0xC0, 0x3A, 0xD8, 0x23, 0x95, 0xA1, 0xA4, 0x15, 0xC4, 0x8B, 0x93, 0xDE, 0xF7, 0x87, 0x18, 0xE6, 0x2A, 0x7A, 0x90, 0x05, 0x2F, 0xE2, 0x24, 0xFB }},
|
||||
{ 3, { 1, 0, 2 }, 0, 0, 1, { 0x9F, 0xF2, 0xF7, 0xAA, 0xA8, 0x56, 0x15, 0x0C, 0xC8, 0x81, 0x92, 0x54, 0x21, 0x8D, 0x3A, 0xDE, 0xEB, 0x05, 0x35, 0x26, 0x90, 0x51, 0x89, 0x77, 0x24, 0xF9, 0xDB, 0x37, 0x89, 0x51, 0x3A, 0x52 }},
|
||||
{ 3, { 1, 2, 0 }, 0, 0, 2, { 0xFA, 0x23, 0xC3, 0x59, 0xF6, 0xFA, 0xC4, 0xE7, 0x79, 0x6B, 0xB9, 0x3B, 0xC9, 0xF0, 0x53, 0x2A, 0x95, 0x46, 0x8C, 0x53, 0x9B, 0xA2, 0x0F, 0xF8, 0x6D, 0x7C, 0x76, 0xED, 0x92, 0x22, 0x79, 0x00 }},
|
||||
{ 2, { 0, 1 }, 1, 0, 0, { 0xAE, 0x38, 0x60, 0x64, 0xB2, 0x61, 0x05, 0x40, 0x47, 0x98, 0xF7, 0x5D, 0xE2, 0xEB, 0x9A, 0xF5, 0xED, 0xA5, 0x38, 0x7B, 0x06, 0x4B, 0x83, 0xD0, 0x49, 0xCB, 0x7C, 0x5E, 0x08, 0x87, 0x95, 0x31 }},
|
||||
},
|
||||
{
|
||||
{ 2, { 1, 2 }, 0, 0, 0, MUSIG_PUBKEY },
|
||||
{ 3, { 1, 0, 3 }, 0, 0, 0, MUSIG_PUBKEY },
|
||||
{ 3, { 1, 2, 0 }, 2, 0, 0, MUSIG_AGGNONCE },
|
||||
{ 3, { 1, 2, 0 }, 3, 0, 0, MUSIG_AGGNONCE },
|
||||
{ 3, { 1, 2, 0 }, 4, 0, 0, MUSIG_AGGNONCE },
|
||||
{ 3, { 0, 1, 2 }, 0, 0, 1, MUSIG_SECNONCE },
|
||||
},
|
||||
{
|
||||
{ { 0xFE, 0xD5, 0x44, 0x34, 0xAD, 0x4C, 0xFE, 0x95, 0x3F, 0xC5, 0x27, 0xDC, 0x6A, 0x5E, 0x5B, 0xE8, 0xF6, 0x23, 0x49, 0x07, 0xB7, 0xC1, 0x87, 0x55, 0x95, 0x57, 0xCE, 0x87, 0xA0, 0x54, 0x1C, 0x46 }, 3, { 0, 1, 2 }, 3, { 0, 1, 2 }, 0, 0, MUSIG_SIG_VERIFY },
|
||||
{ { 0x01, 0x2A, 0xBB, 0xCB, 0x52, 0xB3, 0x01, 0x6A, 0xC0, 0x3A, 0xD8, 0x23, 0x95, 0xA1, 0xA4, 0x15, 0xC4, 0x8B, 0x93, 0xDE, 0xF7, 0x87, 0x18, 0xE6, 0x2A, 0x7A, 0x90, 0x05, 0x2F, 0xE2, 0x24, 0xFB }, 3, { 0, 1, 2 }, 3, { 0, 1, 2 }, 0, 1, MUSIG_SIG_VERIFY },
|
||||
{ { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x41 }, 3, { 0, 1, 2 }, 3, { 0, 1, 2 }, 0, 0, MUSIG_SIG },
|
||||
},
|
||||
{
|
||||
{ { 0x01, 0x2A, 0xBB, 0xCB, 0x52, 0xB3, 0x01, 0x6A, 0xC0, 0x3A, 0xD8, 0x23, 0x95, 0xA1, 0xA4, 0x15, 0xC4, 0x8B, 0x93, 0xDE, 0xF7, 0x87, 0x18, 0xE6, 0x2A, 0x7A, 0x90, 0x05, 0x2F, 0xE2, 0x24, 0xFB }, 3, { 0, 1, 2 }, 3, { 4, 1, 2 }, 0, 0, MUSIG_PUBNONCE },
|
||||
{ { 0x01, 0x2A, 0xBB, 0xCB, 0x52, 0xB3, 0x01, 0x6A, 0xC0, 0x3A, 0xD8, 0x23, 0x95, 0xA1, 0xA4, 0x15, 0xC4, 0x8B, 0x93, 0xDE, 0xF7, 0x87, 0x18, 0xE6, 0x2A, 0x7A, 0x90, 0x05, 0x2F, 0xE2, 0x24, 0xFB }, 3, { 3, 1, 2 }, 3, { 0, 1, 2 }, 0, 0, MUSIG_PUBKEY },
|
||||
},
|
||||
};
|
||||
|
||||
struct musig_tweak_case {
|
||||
size_t key_indices_len;
|
||||
size_t key_indices[3];
|
||||
size_t nonce_indices_len;
|
||||
size_t nonce_indices[3];
|
||||
size_t tweak_indices_len;
|
||||
size_t tweak_indices[4];
|
||||
int is_xonly[4];
|
||||
size_t signer_index;
|
||||
unsigned char expected[32];
|
||||
};
|
||||
|
||||
struct musig_tweak_vector {
|
||||
unsigned char sk[32];
|
||||
unsigned char secnonce[97];
|
||||
unsigned char aggnonce[66];
|
||||
unsigned char msg[32];
|
||||
unsigned char pubkeys[3][33];
|
||||
unsigned char pubnonces[3][194];
|
||||
unsigned char tweaks[5][32];
|
||||
struct musig_tweak_case valid_case[5];
|
||||
struct musig_tweak_case error_case[1];
|
||||
};
|
||||
|
||||
static const struct musig_tweak_vector musig_tweak_vector = {
|
||||
{ 0x7F, 0xB9, 0xE0, 0xE6, 0x87, 0xAD, 0xA1, 0xEE, 0xBF, 0x7E, 0xCF, 0xE2, 0xF2, 0x1E, 0x73, 0xEB, 0xDB, 0x51, 0xA7, 0xD4, 0x50, 0x94, 0x8D, 0xFE, 0x8D, 0x76, 0xD7, 0xF2, 0xD1, 0x00, 0x76, 0x71 },
|
||||
{ 0x50, 0x8B, 0x81, 0xA6, 0x11, 0xF1, 0x00, 0xA6, 0xB2, 0xB6, 0xB2, 0x96, 0x56, 0x59, 0x08, 0x98, 0xAF, 0x48, 0x8B, 0xCF, 0x2E, 0x1F, 0x55, 0xCF, 0x22, 0xE5, 0xCF, 0xB8, 0x44, 0x21, 0xFE, 0x61, 0xFA, 0x27, 0xFD, 0x49, 0xB1, 0xD5, 0x00, 0x85, 0xB4, 0x81, 0x28, 0x5E, 0x1C, 0xA2, 0x05, 0xD5, 0x5C, 0x82, 0xCC, 0x1B, 0x31, 0xFF, 0x5C, 0xD5, 0x4A, 0x48, 0x98, 0x29, 0x35, 0x59, 0x01, 0xF7, 0x03, 0x93, 0x5F, 0x97, 0x2D, 0xA0, 0x13, 0xF8, 0x0A, 0xE0, 0x11, 0x89, 0x0F, 0xA8, 0x9B, 0x67, 0xA2, 0x7B, 0x7B, 0xE6, 0xCC, 0xB2, 0x4D, 0x32, 0x74, 0xD1, 0x8B, 0x2D, 0x40, 0x67, 0xF2, 0x61, 0xA9 },
|
||||
{ 0x02, 0x84, 0x65, 0xFC, 0xF0, 0xBB, 0xDB, 0xCF, 0x44, 0x3A, 0xAB, 0xCC, 0xE5, 0x33, 0xD4, 0x2B, 0x4B, 0x5A, 0x10, 0x96, 0x6A, 0xC0, 0x9A, 0x49, 0x65, 0x5E, 0x8C, 0x42, 0xDA, 0xAB, 0x8F, 0xCD, 0x61, 0x03, 0x74, 0x96, 0xA3, 0xCC, 0x86, 0x92, 0x6D, 0x45, 0x2C, 0xAF, 0xCF, 0xD5, 0x5D, 0x25, 0x97, 0x2C, 0xA1, 0x67, 0x5D, 0x54, 0x93, 0x10, 0xDE, 0x29, 0x6B, 0xFF, 0x42, 0xF7, 0x2E, 0xEE, 0xA8, 0xC9 },
|
||||
{ 0xF9, 0x54, 0x66, 0xD0, 0x86, 0x77, 0x0E, 0x68, 0x99, 0x64, 0x66, 0x42, 0x19, 0x26, 0x6F, 0xE5, 0xED, 0x21, 0x5C, 0x92, 0xAE, 0x20, 0xBA, 0xB5, 0xC9, 0xD7, 0x9A, 0xDD, 0xDD, 0xF3, 0xC0, 0xCF },
|
||||
{
|
||||
{ 0x03, 0x93, 0x5F, 0x97, 0x2D, 0xA0, 0x13, 0xF8, 0x0A, 0xE0, 0x11, 0x89, 0x0F, 0xA8, 0x9B, 0x67, 0xA2, 0x7B, 0x7B, 0xE6, 0xCC, 0xB2, 0x4D, 0x32, 0x74, 0xD1, 0x8B, 0x2D, 0x40, 0x67, 0xF2, 0x61, 0xA9 },
|
||||
{ 0x02, 0xF9, 0x30, 0x8A, 0x01, 0x92, 0x58, 0xC3, 0x10, 0x49, 0x34, 0x4F, 0x85, 0xF8, 0x9D, 0x52, 0x29, 0xB5, 0x31, 0xC8, 0x45, 0x83, 0x6F, 0x99, 0xB0, 0x86, 0x01, 0xF1, 0x13, 0xBC, 0xE0, 0x36, 0xF9 },
|
||||
{ 0x02, 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F, 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE, 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8, 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59 }
|
||||
},
|
||||
{
|
||||
{ 0x03, 0x37, 0xC8, 0x78, 0x21, 0xAF, 0xD5, 0x0A, 0x86, 0x44, 0xD8, 0x20, 0xA8, 0xF3, 0xE0, 0x2E, 0x49, 0x9C, 0x93, 0x18, 0x65, 0xC2, 0x36, 0x0F, 0xB4, 0x3D, 0x0A, 0x0D, 0x20, 0xDA, 0xFE, 0x07, 0xEA, 0x02, 0x87, 0xBF, 0x89, 0x1D, 0x2A, 0x6D, 0xEA, 0xEB, 0xAD, 0xC9, 0x09, 0x35, 0x2A, 0xA9, 0x40, 0x5D, 0x14, 0x28, 0xC1, 0x5F, 0x4B, 0x75, 0xF0, 0x4D, 0xAE, 0x64, 0x2A, 0x95, 0xC2, 0x54, 0x84, 0x80 },
|
||||
{ 0x02, 0x79, 0xBE, 0x66, 0x7E, 0xF9, 0xDC, 0xBB, 0xAC, 0x55, 0xA0, 0x62, 0x95, 0xCE, 0x87, 0x0B, 0x07, 0x02, 0x9B, 0xFC, 0xDB, 0x2D, 0xCE, 0x28, 0xD9, 0x59, 0xF2, 0x81, 0x5B, 0x16, 0xF8, 0x17, 0x98, 0x02, 0x79, 0xBE, 0x66, 0x7E, 0xF9, 0xDC, 0xBB, 0xAC, 0x55, 0xA0, 0x62, 0x95, 0xCE, 0x87, 0x0B, 0x07, 0x02, 0x9B, 0xFC, 0xDB, 0x2D, 0xCE, 0x28, 0xD9, 0x59, 0xF2, 0x81, 0x5B, 0x16, 0xF8, 0x17, 0x98 },
|
||||
{ 0x03, 0x2D, 0xE2, 0x66, 0x26, 0x28, 0xC9, 0x0B, 0x03, 0xF5, 0xE7, 0x20, 0x28, 0x4E, 0xB5, 0x2F, 0xF7, 0xD7, 0x1F, 0x42, 0x84, 0xF6, 0x27, 0xB6, 0x8A, 0x85, 0x3D, 0x78, 0xC7, 0x8E, 0x1F, 0xFE, 0x93, 0x03, 0xE4, 0xC5, 0x52, 0x4E, 0x83, 0xFF, 0xE1, 0x49, 0x3B, 0x90, 0x77, 0xCF, 0x1C, 0xA6, 0xBE, 0xB2, 0x09, 0x0C, 0x93, 0xD9, 0x30, 0x32, 0x10, 0x71, 0xAD, 0x40, 0xB2, 0xF4, 0x4E, 0x59, 0x90, 0x46 }
|
||||
},
|
||||
{
|
||||
{ 0xE8, 0xF7, 0x91, 0xFF, 0x92, 0x25, 0xA2, 0xAF, 0x01, 0x02, 0xAF, 0xFF, 0x4A, 0x9A, 0x72, 0x3D, 0x96, 0x12, 0xA6, 0x82, 0xA2, 0x5E, 0xBE, 0x79, 0x80, 0x2B, 0x26, 0x3C, 0xDF, 0xCD, 0x83, 0xBB },
|
||||
{ 0xAE, 0x2E, 0xA7, 0x97, 0xCC, 0x0F, 0xE7, 0x2A, 0xC5, 0xB9, 0x7B, 0x97, 0xF3, 0xC6, 0x95, 0x7D, 0x7E, 0x41, 0x99, 0xA1, 0x67, 0xA5, 0x8E, 0xB0, 0x8B, 0xCA, 0xFF, 0xDA, 0x70, 0xAC, 0x04, 0x55 },
|
||||
{ 0xF5, 0x2E, 0xCB, 0xC5, 0x65, 0xB3, 0xD8, 0xBE, 0xA2, 0xDF, 0xD5, 0xB7, 0x5A, 0x4F, 0x45, 0x7E, 0x54, 0x36, 0x98, 0x09, 0x32, 0x2E, 0x41, 0x20, 0x83, 0x16, 0x26, 0xF2, 0x90, 0xFA, 0x87, 0xE0 },
|
||||
{ 0x19, 0x69, 0xAD, 0x73, 0xCC, 0x17, 0x7F, 0xA0, 0xB4, 0xFC, 0xED, 0x6D, 0xF1, 0xF7, 0xBF, 0x99, 0x07, 0xE6, 0x65, 0xFD, 0xE9, 0xBA, 0x19, 0x6A, 0x74, 0xFE, 0xD0, 0xA3, 0xCF, 0x5A, 0xEF, 0x9D },
|
||||
{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x41 }
|
||||
},
|
||||
{
|
||||
{ 3, { 1, 2, 0 }, 3, { 1, 2, 0 }, 1, { 0 }, { 1 }, 2, { 0xE2, 0x8A, 0x5C, 0x66, 0xE6, 0x1E, 0x17, 0x8C, 0x2B, 0xA1, 0x9D, 0xB7, 0x7B, 0x6C, 0xF9, 0xF7, 0xE2, 0xF0, 0xF5, 0x6C, 0x17, 0x91, 0x8C, 0xD1, 0x31, 0x35, 0xE6, 0x0C, 0xC8, 0x48, 0xFE, 0x91 }},
|
||||
{ 3, { 1, 2, 0 }, 3, { 1, 2, 0 }, 1, { 0 }, { 0 }, 2, { 0x38, 0xB0, 0x76, 0x77, 0x98, 0x25, 0x2F, 0x21, 0xBF, 0x57, 0x02, 0xC4, 0x80, 0x28, 0xB0, 0x95, 0x42, 0x83, 0x20, 0xF7, 0x3A, 0x4B, 0x14, 0xDB, 0x1E, 0x25, 0xDE, 0x58, 0x54, 0x3D, 0x2D, 0x2D }},
|
||||
{ 3, { 1, 2, 0 }, 3, { 1, 2, 0 }, 2, { 0, 1 }, { 0, 1 }, 2, { 0x40, 0x8A, 0x0A, 0x21, 0xC4, 0xA0, 0xF5, 0xDA, 0xCA, 0xF9, 0x64, 0x6A, 0xD6, 0xEB, 0x6F, 0xEC, 0xD7, 0xF7, 0xA1, 0x1F, 0x03, 0xED, 0x1F, 0x48, 0xDF, 0xFF, 0x21, 0x85, 0xBC, 0x2C, 0x24, 0x08 }},
|
||||
{ 3, { 1, 2, 0 }, 3, { 1, 2, 0 }, 4, { 0, 1, 2, 3 }, { 0, 0, 1, 1 }, 2, { 0x45, 0xAB, 0xD2, 0x06, 0xE6, 0x1E, 0x3D, 0xF2, 0xEC, 0x9E, 0x26, 0x4A, 0x6F, 0xEC, 0x82, 0x92, 0x14, 0x1A, 0x63, 0x3C, 0x28, 0x58, 0x63, 0x88, 0x23, 0x55, 0x41, 0xF9, 0xAD, 0xE7, 0x54, 0x35 }},
|
||||
{ 3, { 1, 2, 0 }, 3, { 1, 2, 0 }, 4, { 0, 1, 2, 3 }, { 1, 0, 1, 0 }, 2, { 0xB2, 0x55, 0xFD, 0xCA, 0xC2, 0x7B, 0x40, 0xC7, 0xCE, 0x78, 0x48, 0xE2, 0xD3, 0xB7, 0xBF, 0x5E, 0xA0, 0xED, 0x75, 0x6D, 0xA8, 0x15, 0x65, 0xAC, 0x80, 0x4C, 0xCC, 0xA3, 0xE1, 0xD5, 0xD2, 0x39 }},
|
||||
},
|
||||
{
|
||||
{ 3, { 1, 2, 0 }, 3, { 1, 2, 0 }, 1, { 4 }, { 0 }, 2, { 0 }},
|
||||
},
|
||||
};
|
||||
|
||||
/* Omit pubnonces in the test vectors because they're only needed for
|
||||
* implementations that do not directly accept an aggnonce. */
|
||||
struct musig_sig_agg_case {
|
||||
size_t key_indices_len;
|
||||
size_t key_indices[2];
|
||||
size_t tweak_indices_len;
|
||||
size_t tweak_indices[3];
|
||||
int is_xonly[3];
|
||||
unsigned char aggnonce[66];
|
||||
size_t psig_indices_len;
|
||||
size_t psig_indices[2];
|
||||
/* if valid case */
|
||||
unsigned char expected[64];
|
||||
/* if error case */
|
||||
int invalid_sig_idx;
|
||||
};
|
||||
|
||||
struct musig_sig_agg_vector {
|
||||
unsigned char pubkeys[4][33];
|
||||
unsigned char tweaks[3][32];
|
||||
unsigned char psigs[9][32];
|
||||
unsigned char msg[32];
|
||||
struct musig_sig_agg_case valid_case[4];
|
||||
struct musig_sig_agg_case error_case[1];
|
||||
};
|
||||
|
||||
static const struct musig_sig_agg_vector musig_sig_agg_vector = {
|
||||
{
|
||||
{ 0x03, 0x93, 0x5F, 0x97, 0x2D, 0xA0, 0x13, 0xF8, 0x0A, 0xE0, 0x11, 0x89, 0x0F, 0xA8, 0x9B, 0x67, 0xA2, 0x7B, 0x7B, 0xE6, 0xCC, 0xB2, 0x4D, 0x32, 0x74, 0xD1, 0x8B, 0x2D, 0x40, 0x67, 0xF2, 0x61, 0xA9 },
|
||||
{ 0x02, 0xD2, 0xDC, 0x6F, 0x5D, 0xF7, 0xC5, 0x6A, 0xCF, 0x38, 0xC7, 0xFA, 0x0A, 0xE7, 0xA7, 0x59, 0xAE, 0x30, 0xE1, 0x9B, 0x37, 0x35, 0x9D, 0xFD, 0xE0, 0x15, 0x87, 0x23, 0x24, 0xC7, 0xEF, 0x6E, 0x05 },
|
||||
{ 0x03, 0xC7, 0xFB, 0x10, 0x1D, 0x97, 0xFF, 0x93, 0x0A, 0xCD, 0x0C, 0x67, 0x60, 0x85, 0x2E, 0xF6, 0x4E, 0x69, 0x08, 0x3D, 0xE0, 0xB0, 0x6A, 0xC6, 0x33, 0x57, 0x24, 0x75, 0x4B, 0xB4, 0xB0, 0x52, 0x2C },
|
||||
{ 0x02, 0x35, 0x24, 0x33, 0xB2, 0x1E, 0x7E, 0x05, 0xD3, 0xB4, 0x52, 0xB8, 0x1C, 0xAE, 0x56, 0x6E, 0x06, 0xD2, 0xE0, 0x03, 0xEC, 0xE1, 0x6D, 0x10, 0x74, 0xAA, 0xBA, 0x42, 0x89, 0xE0, 0xE3, 0xD5, 0x81 }
|
||||
},
|
||||
{
|
||||
{ 0xB5, 0x11, 0xDA, 0x49, 0x21, 0x82, 0xA9, 0x1B, 0x0F, 0xFB, 0x9A, 0x98, 0x02, 0x0D, 0x55, 0xF2, 0x60, 0xAE, 0x86, 0xD7, 0xEC, 0xBD, 0x03, 0x99, 0xC7, 0x38, 0x3D, 0x59, 0xA5, 0xF2, 0xAF, 0x7C },
|
||||
{ 0xA8, 0x15, 0xFE, 0x04, 0x9E, 0xE3, 0xC5, 0xAA, 0xB6, 0x63, 0x10, 0x47, 0x7F, 0xBC, 0x8B, 0xCC, 0xCA, 0xC2, 0xF3, 0x39, 0x5F, 0x59, 0xF9, 0x21, 0xC3, 0x64, 0xAC, 0xD7, 0x8A, 0x2F, 0x48, 0xDC },
|
||||
{ 0x75, 0x44, 0x8A, 0x87, 0x27, 0x4B, 0x05, 0x64, 0x68, 0xB9, 0x77, 0xBE, 0x06, 0xEB, 0x1E, 0x9F, 0x65, 0x75, 0x77, 0xB7, 0x32, 0x0B, 0x0A, 0x33, 0x76, 0xEA, 0x51, 0xFD, 0x42, 0x0D, 0x18, 0xA8 }
|
||||
},
|
||||
{
|
||||
{ 0xB1, 0x5D, 0x2C, 0xD3, 0xC3, 0xD2, 0x2B, 0x04, 0xDA, 0xE4, 0x38, 0xCE, 0x65, 0x3F, 0x6B, 0x4E, 0xCF, 0x04, 0x2F, 0x42, 0xCF, 0xDE, 0xD7, 0xC4, 0x1B, 0x64, 0xAA, 0xF9, 0xB4, 0xAF, 0x53, 0xFB },
|
||||
{ 0x61, 0x93, 0xD6, 0xAC, 0x61, 0xB3, 0x54, 0xE9, 0x10, 0x5B, 0xBD, 0xC8, 0x93, 0x7A, 0x34, 0x54, 0xA6, 0xD7, 0x05, 0xB6, 0xD5, 0x73, 0x22, 0xA5, 0xA4, 0x72, 0xA0, 0x2C, 0xE9, 0x9F, 0xCB, 0x64 },
|
||||
{ 0x9A, 0x87, 0xD3, 0xB7, 0x9E, 0xC6, 0x72, 0x28, 0xCB, 0x97, 0x87, 0x8B, 0x76, 0x04, 0x9B, 0x15, 0xDB, 0xD0, 0x5B, 0x81, 0x58, 0xD1, 0x7B, 0x5B, 0x91, 0x14, 0xD3, 0xC2, 0x26, 0x88, 0x75, 0x05 },
|
||||
{ 0x66, 0xF8, 0x2E, 0xA9, 0x09, 0x23, 0x68, 0x9B, 0x85, 0x5D, 0x36, 0xC6, 0xB7, 0xE0, 0x32, 0xFB, 0x99, 0x70, 0x30, 0x14, 0x81, 0xB9, 0x9E, 0x01, 0xCD, 0xB4, 0xD6, 0xAC, 0x7C, 0x34, 0x7A, 0x15 },
|
||||
{ 0x4F, 0x5A, 0xEE, 0x41, 0x51, 0x08, 0x48, 0xA6, 0x44, 0x7D, 0xCD, 0x1B, 0xBC, 0x78, 0x45, 0x7E, 0xF6, 0x90, 0x24, 0x94, 0x4C, 0x87, 0xF4, 0x02, 0x50, 0xD3, 0xEF, 0x2C, 0x25, 0xD3, 0x3E, 0xFE },
|
||||
{ 0xDD, 0xEF, 0x42, 0x7B, 0xBB, 0x84, 0x7C, 0xC0, 0x27, 0xBE, 0xFF, 0x4E, 0xDB, 0x01, 0x03, 0x81, 0x48, 0x91, 0x78, 0x32, 0x25, 0x3E, 0xBC, 0x35, 0x5F, 0xC3, 0x3F, 0x4A, 0x8E, 0x2F, 0xCC, 0xE4 },
|
||||
{ 0x97, 0xB8, 0x90, 0xA2, 0x6C, 0x98, 0x1D, 0xA8, 0x10, 0x2D, 0x3B, 0xC2, 0x94, 0x15, 0x9D, 0x17, 0x1D, 0x72, 0x81, 0x0F, 0xDF, 0x7C, 0x6A, 0x69, 0x1D, 0xEF, 0x02, 0xF0, 0xF7, 0xAF, 0x3F, 0xDC },
|
||||
{ 0x53, 0xFA, 0x9E, 0x08, 0xBA, 0x52, 0x43, 0xCB, 0xCB, 0x0D, 0x79, 0x7C, 0x5E, 0xE8, 0x3B, 0xC6, 0x72, 0x8E, 0x53, 0x9E, 0xB7, 0x6C, 0x2D, 0x0B, 0xF0, 0xF9, 0x71, 0xEE, 0x4E, 0x90, 0x99, 0x71 },
|
||||
{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x41 }
|
||||
},
|
||||
{ 0x59, 0x9C, 0x67, 0xEA, 0x41, 0x0D, 0x00, 0x5B, 0x9D, 0xA9, 0x08, 0x17, 0xCF, 0x03, 0xED, 0x3B, 0x1C, 0x86, 0x8E, 0x4D, 0xA4, 0xED, 0xF0, 0x0A, 0x58, 0x80, 0xB0, 0x08, 0x2C, 0x23, 0x78, 0x69 },
|
||||
{
|
||||
{ 2, { 0, 1 }, 0, { 0 }, { 0 }, { 0x03, 0x41, 0x43, 0x27, 0x22, 0xC5, 0xCD, 0x02, 0x68, 0xD8, 0x29, 0xC7, 0x02, 0xCF, 0x0D, 0x1C, 0xBC, 0xE5, 0x70, 0x33, 0xEE, 0xD2, 0x01, 0xFD, 0x33, 0x51, 0x91, 0x38, 0x52, 0x27, 0xC3, 0x21, 0x0C, 0x03, 0xD3, 0x77, 0xF2, 0xD2, 0x58, 0xB6, 0x4A, 0xAD, 0xC0, 0xE1, 0x6F, 0x26, 0x46, 0x23, 0x23, 0xD7, 0x01, 0xD2, 0x86, 0x04, 0x6A, 0x2E, 0xA9, 0x33, 0x65, 0x65, 0x6A, 0xFD, 0x98, 0x75, 0x98, 0x2B }, 2, { 0, 1 }, { 0x04, 0x1D, 0xA2, 0x22, 0x23, 0xCE, 0x65, 0xC9, 0x2C, 0x9A, 0x0D, 0x6C, 0x2C, 0xAC, 0x82, 0x8A, 0xAF, 0x1E, 0xEE, 0x56, 0x30, 0x4F, 0xEC, 0x37, 0x1D, 0xDF, 0x91, 0xEB, 0xB2, 0xB9, 0xEF, 0x09, 0x12, 0xF1, 0x03, 0x80, 0x25, 0x85, 0x7F, 0xED, 0xEB, 0x3F, 0xF6, 0x96, 0xF8, 0xB9, 0x9F, 0xA4, 0xBB, 0x2C, 0x58, 0x12, 0xF6, 0x09, 0x5A, 0x2E, 0x00, 0x04, 0xEC, 0x99, 0xCE, 0x18, 0xDE, 0x1E }, 0 },
|
||||
{ 2, { 0, 2 }, 0, { 0 }, { 0 }, { 0x02, 0x24, 0xAF, 0xD3, 0x6C, 0x90, 0x20, 0x84, 0x05, 0x8B, 0x51, 0xB5, 0xD3, 0x66, 0x76, 0xBB, 0xA4, 0xDC, 0x97, 0xC7, 0x75, 0x87, 0x37, 0x68, 0xE5, 0x88, 0x22, 0xF8, 0x7F, 0xE4, 0x37, 0xD7, 0x92, 0x02, 0x8C, 0xB1, 0x59, 0x29, 0x09, 0x9E, 0xEE, 0x2F, 0x5D, 0xAE, 0x40, 0x4C, 0xD3, 0x93, 0x57, 0x59, 0x1B, 0xA3, 0x2E, 0x9A, 0xF4, 0xE1, 0x62, 0xB8, 0xD3, 0xE7, 0xCB, 0x5E, 0xFE, 0x31, 0xCB, 0x20 }, 2, { 2, 3 }, { 0x10, 0x69, 0xB6, 0x7E, 0xC3, 0xD2, 0xF3, 0xC7, 0xC0, 0x82, 0x91, 0xAC, 0xCB, 0x17, 0xA9, 0xC9, 0xB8, 0xF2, 0x81, 0x9A, 0x52, 0xEB, 0x5D, 0xF8, 0x72, 0x6E, 0x17, 0xE7, 0xD6, 0xB5, 0x2E, 0x9F, 0x01, 0x80, 0x02, 0x60, 0xA7, 0xE9, 0xDA, 0xC4, 0x50, 0xF4, 0xBE, 0x52, 0x2D, 0xE4, 0xCE, 0x12, 0xBA, 0x91, 0xAE, 0xAF, 0x2B, 0x42, 0x79, 0x21, 0x9E, 0xF7, 0x4B, 0xE1, 0xD2, 0x86, 0xAD, 0xD9 }, 0 },
|
||||
{ 2, { 0, 2 }, 1, { 0 }, { 0 }, { 0x02, 0x08, 0xC5, 0xC4, 0x38, 0xC7, 0x10, 0xF4, 0xF9, 0x6A, 0x61, 0xE9, 0xFF, 0x3C, 0x37, 0x75, 0x88, 0x14, 0xB8, 0xC3, 0xAE, 0x12, 0xBF, 0xEA, 0x0E, 0xD2, 0xC8, 0x7F, 0xF6, 0x95, 0x4F, 0xF1, 0x86, 0x02, 0x0B, 0x18, 0x16, 0xEA, 0x10, 0x4B, 0x4F, 0xCA, 0x2D, 0x30, 0x4D, 0x73, 0x3E, 0x0E, 0x19, 0xCE, 0xAD, 0x51, 0x30, 0x3F, 0xF6, 0x42, 0x0B, 0xFD, 0x22, 0x23, 0x35, 0xCA, 0xA4, 0x02, 0x91, 0x6D }, 2, { 4, 5 }, { 0x5C, 0x55, 0x8E, 0x1D, 0xCA, 0xDE, 0x86, 0xDA, 0x0B, 0x2F, 0x02, 0x62, 0x6A, 0x51, 0x2E, 0x30, 0xA2, 0x2C, 0xF5, 0x25, 0x5C, 0xAE, 0xA7, 0xEE, 0x32, 0xC3, 0x8E, 0x9A, 0x71, 0xA0, 0xE9, 0x14, 0x8B, 0xA6, 0xC0, 0xE6, 0xEC, 0x76, 0x83, 0xB6, 0x42, 0x20, 0xF0, 0x29, 0x86, 0x96, 0xF1, 0xB8, 0x78, 0xCD, 0x47, 0xB1, 0x07, 0xB8, 0x1F, 0x71, 0x88, 0x81, 0x2D, 0x59, 0x39, 0x71, 0xE0, 0xCC }, 0 },
|
||||
{ 2, { 0, 3 }, 3, { 0, 1, 2 }, { 1, 0, 1 }, { 0x02, 0xB5, 0xAD, 0x07, 0xAF, 0xCD, 0x99, 0xB6, 0xD9, 0x2C, 0xB4, 0x33, 0xFB, 0xD2, 0xA2, 0x8F, 0xDE, 0xB9, 0x8E, 0xAE, 0x2E, 0xB0, 0x9B, 0x60, 0x14, 0xEF, 0x0F, 0x81, 0x97, 0xCD, 0x58, 0x40, 0x33, 0x02, 0xE8, 0x61, 0x69, 0x10, 0xF9, 0x29, 0x3C, 0xF6, 0x92, 0xC4, 0x9F, 0x35, 0x1D, 0xB8, 0x6B, 0x25, 0xE3, 0x52, 0x90, 0x1F, 0x0E, 0x23, 0x7B, 0xAF, 0xDA, 0x11, 0xF1, 0xC1, 0xCE, 0xF2, 0x9F, 0xFD }, 2, { 6, 7 }, { 0x83, 0x9B, 0x08, 0x82, 0x0B, 0x68, 0x1D, 0xBA, 0x8D, 0xAF, 0x4C, 0xC7, 0xB1, 0x04, 0xE8, 0xF2, 0x63, 0x8F, 0x93, 0x88, 0xF8, 0xD7, 0xA5, 0x55, 0xDC, 0x17, 0xB6, 0xE6, 0x97, 0x1D, 0x74, 0x26, 0xCE, 0x07, 0xBF, 0x6A, 0xB0, 0x1F, 0x1D, 0xB5, 0x0E, 0x4E, 0x33, 0x71, 0x92, 0x95, 0xF4, 0x09, 0x45, 0x72, 0xB7, 0x98, 0x68, 0xE4, 0x40, 0xFB, 0x3D, 0xEF, 0xD3, 0xFA, 0xC1, 0xDB, 0x58, 0x9E }, 0 },
|
||||
},
|
||||
{
|
||||
{ 2, { 0, 3 }, 3, { 0, 1, 2 }, { 1, 0, 1 }, { 0x02, 0xB5, 0xAD, 0x07, 0xAF, 0xCD, 0x99, 0xB6, 0xD9, 0x2C, 0xB4, 0x33, 0xFB, 0xD2, 0xA2, 0x8F, 0xDE, 0xB9, 0x8E, 0xAE, 0x2E, 0xB0, 0x9B, 0x60, 0x14, 0xEF, 0x0F, 0x81, 0x97, 0xCD, 0x58, 0x40, 0x33, 0x02, 0xE8, 0x61, 0x69, 0x10, 0xF9, 0x29, 0x3C, 0xF6, 0x92, 0xC4, 0x9F, 0x35, 0x1D, 0xB8, 0x6B, 0x25, 0xE3, 0x52, 0x90, 0x1F, 0x0E, 0x23, 0x7B, 0xAF, 0xDA, 0x11, 0xF1, 0xC1, 0xCE, 0xF2, 0x9F, 0xFD }, 2, { 7, 8 }, { 0 }, 1 },
|
||||
},
|
||||
};
|
||||
enum { MUSIG_VECTORS_MAX_PUBKEYS = 7 };
|
||||
@@ -902,6 +902,10 @@ static int secp256k1_ge_parse_ext(secp256k1_ge* ge, const unsigned char *in33) {
|
||||
# include "modules/schnorrsig/main_impl.h"
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_MODULE_MUSIG
|
||||
# include "modules/musig/main_impl.h"
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_MODULE_SCHNORRSIG_HALFAGG
|
||||
# include "modules/schnorrsig_halfagg/main_impl.h"
|
||||
#endif
|
||||
|
||||
+39
-4
@@ -4041,13 +4041,27 @@ static void test_add_neg_y_diff_x(void) {
|
||||
static void test_ge_bytes(void) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < COUNT; i++) {
|
||||
for (i = 0; i < COUNT + 1; i++) {
|
||||
unsigned char buf[64];
|
||||
secp256k1_ge p, q;
|
||||
|
||||
testutil_random_ge_test(&p);
|
||||
secp256k1_ge_to_bytes(buf, &p);
|
||||
secp256k1_ge_from_bytes(&q, buf);
|
||||
if (i == 0) {
|
||||
secp256k1_ge_set_infinity(&p);
|
||||
} else {
|
||||
testutil_random_ge_test(&p);
|
||||
}
|
||||
|
||||
if (!secp256k1_ge_is_infinity(&p)) {
|
||||
secp256k1_ge_to_bytes(buf, &p);
|
||||
|
||||
secp256k1_ge_from_bytes(&q, buf);
|
||||
CHECK(secp256k1_ge_eq_var(&p, &q));
|
||||
|
||||
secp256k1_ge_from_bytes_ext(&q, buf);
|
||||
CHECK(secp256k1_ge_eq_var(&p, &q));
|
||||
}
|
||||
secp256k1_ge_to_bytes_ext(buf, &p);
|
||||
secp256k1_ge_from_bytes_ext(&q, buf);
|
||||
CHECK(secp256k1_ge_eq_var(&p, &q));
|
||||
}
|
||||
}
|
||||
@@ -7620,6 +7634,10 @@ static void run_ecdsa_wycheproof(void) {
|
||||
# include "modules/schnorrsig/tests_impl.h"
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_MODULE_MUSIG
|
||||
# include "modules/musig/tests_impl.h"
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_MODULE_ELLSWIFT
|
||||
# include "modules/ellswift/tests_impl.h"
|
||||
#endif
|
||||
@@ -7647,6 +7665,18 @@ static void run_secp256k1_memczero_test(void) {
|
||||
CHECK(secp256k1_memcmp_var(buf1, buf2, sizeof(buf1)) == 0);
|
||||
}
|
||||
|
||||
|
||||
static void run_secp256k1_is_zero_array_test(void) {
|
||||
unsigned char buf1[3] = {0, 1};
|
||||
unsigned char buf2[3] = {1, 0};
|
||||
|
||||
CHECK(secp256k1_is_zero_array(buf1, 0) == 1);
|
||||
CHECK(secp256k1_is_zero_array(buf1, 1) == 1);
|
||||
CHECK(secp256k1_is_zero_array(buf1, 2) == 0);
|
||||
CHECK(secp256k1_is_zero_array(buf2, 1) == 0);
|
||||
CHECK(secp256k1_is_zero_array(buf2, 2) == 0);
|
||||
}
|
||||
|
||||
static void run_secp256k1_byteorder_tests(void) {
|
||||
{
|
||||
const uint32_t x = 0xFF03AB45;
|
||||
@@ -8009,6 +8039,10 @@ int main(int argc, char **argv) {
|
||||
run_schnorrsig_tests();
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_MODULE_MUSIG
|
||||
run_musig_tests();
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_MODULE_ELLSWIFT
|
||||
run_ellswift_tests();
|
||||
#endif
|
||||
@@ -8024,6 +8058,7 @@ int main(int argc, char **argv) {
|
||||
|
||||
/* util tests */
|
||||
run_secp256k1_memczero_test();
|
||||
run_secp256k1_is_zero_array_test();
|
||||
run_secp256k1_byteorder_tests();
|
||||
|
||||
run_cmov_tests();
|
||||
|
||||
+16
@@ -265,6 +265,22 @@ static SECP256K1_INLINE int secp256k1_memcmp_var(const void *s1, const void *s2,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Return 1 if all elements of array s are 0 and otherwise return 0.
|
||||
* Constant-time. */
|
||||
static SECP256K1_INLINE int secp256k1_is_zero_array(const unsigned char *s, size_t len) {
|
||||
unsigned char acc = 0;
|
||||
int ret;
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
acc |= s[i];
|
||||
}
|
||||
ret = (acc == 0);
|
||||
/* acc may contain secret values. Try to explicitly clear it. */
|
||||
acc = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** If flag is true, set *r equal to *a; otherwise leave it. Constant-time. Both *r and *a must be initialized and non-negative.*/
|
||||
static SECP256K1_INLINE void secp256k1_int_cmov(int *r, const int *a, int flag) {
|
||||
unsigned int mask0, mask1, r_masked, a_masked;
|
||||
|
||||
Executable
+656
@@ -0,0 +1,656 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import sys
|
||||
import json
|
||||
import textwrap
|
||||
|
||||
max_pubkeys = 0
|
||||
|
||||
if len(sys.argv) < 2:
|
||||
print(
|
||||
"This script converts BIP MuSig2 test vectors in a given directory to a C file that can be used in the test framework."
|
||||
)
|
||||
print("Usage: %s <dir>" % sys.argv[0])
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def hexstr_to_intarray(str):
|
||||
return ", ".join([f"0x{b:02X}" for b in bytes.fromhex(str)])
|
||||
|
||||
|
||||
def create_init(name):
|
||||
return """
|
||||
static const struct musig_%s_vector musig_%s_vector = {
|
||||
""" % (
|
||||
name,
|
||||
name,
|
||||
)
|
||||
|
||||
|
||||
def init_array(key):
|
||||
return textwrap.indent("{ %s },\n" % hexstr_to_intarray(data[key]), 4 * " ")
|
||||
|
||||
|
||||
def init_arrays(key):
|
||||
s = textwrap.indent("{\n", 4 * " ")
|
||||
s += textwrap.indent(
|
||||
",\n".join(["{ %s }" % hexstr_to_intarray(x) for x in data[key]]), 8 * " "
|
||||
)
|
||||
s += textwrap.indent("\n},\n", 4 * " ")
|
||||
return s
|
||||
|
||||
|
||||
def init_indices(array):
|
||||
return " %d, { %s }" % (
|
||||
len(array),
|
||||
", ".join(map(str, array) if len(array) > 0 else "0"),
|
||||
)
|
||||
|
||||
|
||||
def init_is_xonly(case):
|
||||
if len(case["tweak_indices"]) > 0:
|
||||
return ", ".join(map(lambda x: "1" if x else "0", case["is_xonly"]))
|
||||
return "0"
|
||||
|
||||
|
||||
def init_optional_expected(case):
|
||||
return hexstr_to_intarray(case["expected"]) if "expected" in case else 0
|
||||
|
||||
|
||||
def init_cases(cases, f):
|
||||
s = textwrap.indent("{\n", 4 * " ")
|
||||
for (i, case) in enumerate(cases):
|
||||
s += textwrap.indent("%s\n" % f(case), 8 * " ")
|
||||
s += textwrap.indent("},\n", 4 * " ")
|
||||
return s
|
||||
|
||||
|
||||
def finish_init():
|
||||
return "};\n"
|
||||
|
||||
|
||||
s = (
|
||||
"""/**
|
||||
* Automatically generated by %s.
|
||||
*
|
||||
* The test vectors for the KeySort function are included in this file. They can
|
||||
* be found in src/modules/extrakeys/tests_impl.h. */
|
||||
"""
|
||||
% sys.argv[0]
|
||||
)
|
||||
|
||||
|
||||
s += """
|
||||
enum MUSIG_ERROR {
|
||||
MUSIG_PUBKEY,
|
||||
MUSIG_TWEAK,
|
||||
MUSIG_PUBNONCE,
|
||||
MUSIG_AGGNONCE,
|
||||
MUSIG_SECNONCE,
|
||||
MUSIG_SIG,
|
||||
MUSIG_SIG_VERIFY,
|
||||
MUSIG_OTHER
|
||||
};
|
||||
"""
|
||||
|
||||
# key agg vectors
|
||||
with open(sys.argv[1] + "/key_agg_vectors.json", "r") as f:
|
||||
data = json.load(f)
|
||||
|
||||
max_key_indices = max(
|
||||
len(test_case["key_indices"]) for test_case in data["valid_test_cases"]
|
||||
)
|
||||
max_tweak_indices = max(
|
||||
len(test_case["tweak_indices"]) for test_case in data["error_test_cases"]
|
||||
)
|
||||
num_pubkeys = len(data["pubkeys"])
|
||||
max_pubkeys = max(num_pubkeys, max_pubkeys)
|
||||
num_tweaks = len(data["tweaks"])
|
||||
num_valid_cases = len(data["valid_test_cases"])
|
||||
num_error_cases = len(data["error_test_cases"])
|
||||
|
||||
# Add structures for valid and error cases
|
||||
s += (
|
||||
"""
|
||||
struct musig_key_agg_valid_test_case {
|
||||
size_t key_indices_len;
|
||||
size_t key_indices[%d];
|
||||
unsigned char expected[32];
|
||||
};
|
||||
"""
|
||||
% max_key_indices
|
||||
)
|
||||
s += """
|
||||
struct musig_key_agg_error_test_case {
|
||||
size_t key_indices_len;
|
||||
size_t key_indices[%d];
|
||||
size_t tweak_indices_len;
|
||||
size_t tweak_indices[%d];
|
||||
int is_xonly[%d];
|
||||
enum MUSIG_ERROR error;
|
||||
};
|
||||
""" % (
|
||||
max_key_indices,
|
||||
max_tweak_indices,
|
||||
max_tweak_indices,
|
||||
)
|
||||
|
||||
# Add structure for entire vector
|
||||
s += """
|
||||
struct musig_key_agg_vector {
|
||||
unsigned char pubkeys[%d][33];
|
||||
unsigned char tweaks[%d][32];
|
||||
struct musig_key_agg_valid_test_case valid_case[%d];
|
||||
struct musig_key_agg_error_test_case error_case[%d];
|
||||
};
|
||||
""" % (
|
||||
num_pubkeys,
|
||||
num_tweaks,
|
||||
num_valid_cases,
|
||||
num_error_cases,
|
||||
)
|
||||
|
||||
s += create_init("key_agg")
|
||||
# Add pubkeys and tweaks to the vector
|
||||
s += init_arrays("pubkeys")
|
||||
s += init_arrays("tweaks")
|
||||
|
||||
# Add valid cases to the vector
|
||||
s += init_cases(
|
||||
data["valid_test_cases"],
|
||||
lambda case: "{ %s, { %s }},"
|
||||
% (init_indices(case["key_indices"]), hexstr_to_intarray(case["expected"])),
|
||||
)
|
||||
|
||||
def comment_to_error(case):
|
||||
comment = case["comment"]
|
||||
if "public key" in comment.lower():
|
||||
return "MUSIG_PUBKEY"
|
||||
elif "tweak" in comment.lower():
|
||||
return "MUSIG_TWEAK"
|
||||
else:
|
||||
sys.exit("Unknown error")
|
||||
|
||||
# Add error cases to the vector
|
||||
s += init_cases(
|
||||
data["error_test_cases"],
|
||||
lambda case: "{ %s, %s, { %s }, %s },"
|
||||
% (
|
||||
init_indices(case["key_indices"]),
|
||||
init_indices(case["tweak_indices"]),
|
||||
init_is_xonly(case),
|
||||
comment_to_error(case),
|
||||
),
|
||||
)
|
||||
|
||||
s += finish_init()
|
||||
|
||||
# nonce gen vectors
|
||||
with open(sys.argv[1] + "/nonce_gen_vectors.json", "r") as f:
|
||||
data = json.load(f)
|
||||
|
||||
# The MuSig2 implementation only allows messages of length 32
|
||||
data["test_cases"] = list(
|
||||
filter(lambda c: c["msg"] is None or len(c["msg"]) == 64, data["test_cases"])
|
||||
)
|
||||
|
||||
num_tests = len(data["test_cases"])
|
||||
|
||||
s += """
|
||||
struct musig_nonce_gen_test_case {
|
||||
unsigned char rand_[32];
|
||||
int has_sk;
|
||||
unsigned char sk[32];
|
||||
unsigned char pk[33];
|
||||
int has_aggpk;
|
||||
unsigned char aggpk[32];
|
||||
int has_msg;
|
||||
unsigned char msg[32];
|
||||
int has_extra_in;
|
||||
unsigned char extra_in[32];
|
||||
unsigned char expected_secnonce[97];
|
||||
unsigned char expected_pubnonce[66];
|
||||
};
|
||||
"""
|
||||
|
||||
s += (
|
||||
"""
|
||||
struct musig_nonce_gen_vector {
|
||||
struct musig_nonce_gen_test_case test_case[%d];
|
||||
};
|
||||
"""
|
||||
% num_tests
|
||||
)
|
||||
|
||||
s += create_init("nonce_gen")
|
||||
|
||||
def init_array_maybe(array):
|
||||
return "%d , { %s }" % (
|
||||
0 if array is None else 1,
|
||||
hexstr_to_intarray(array) if array is not None else 0,
|
||||
)
|
||||
|
||||
s += init_cases(
|
||||
data["test_cases"],
|
||||
lambda case: "{ { %s }, %s, { %s }, %s, %s, %s, { %s }, { %s } },"
|
||||
% (
|
||||
hexstr_to_intarray(case["rand_"]),
|
||||
init_array_maybe(case["sk"]),
|
||||
hexstr_to_intarray(case["pk"]),
|
||||
init_array_maybe(case["aggpk"]),
|
||||
init_array_maybe(case["msg"]),
|
||||
init_array_maybe(case["extra_in"]),
|
||||
hexstr_to_intarray(case["expected_secnonce"]),
|
||||
hexstr_to_intarray(case["expected_pubnonce"]),
|
||||
),
|
||||
)
|
||||
|
||||
s += finish_init()
|
||||
|
||||
# nonce agg vectors
|
||||
with open(sys.argv[1] + "/nonce_agg_vectors.json", "r") as f:
|
||||
data = json.load(f)
|
||||
|
||||
num_pnonces = len(data["pnonces"])
|
||||
num_valid_cases = len(data["valid_test_cases"])
|
||||
num_error_cases = len(data["error_test_cases"])
|
||||
|
||||
pnonce_indices_len = 2
|
||||
for case in data["valid_test_cases"] + data["error_test_cases"]:
|
||||
assert len(case["pnonce_indices"]) == pnonce_indices_len
|
||||
|
||||
# Add structures for valid and error cases
|
||||
s += """
|
||||
struct musig_nonce_agg_test_case {
|
||||
size_t pnonce_indices[2];
|
||||
/* if valid case */
|
||||
unsigned char expected[66];
|
||||
/* if error case */
|
||||
int invalid_nonce_idx;
|
||||
};
|
||||
"""
|
||||
# Add structure for entire vector
|
||||
s += """
|
||||
struct musig_nonce_agg_vector {
|
||||
unsigned char pnonces[%d][66];
|
||||
struct musig_nonce_agg_test_case valid_case[%d];
|
||||
struct musig_nonce_agg_test_case error_case[%d];
|
||||
};
|
||||
""" % (
|
||||
num_pnonces,
|
||||
num_valid_cases,
|
||||
num_error_cases,
|
||||
)
|
||||
|
||||
s += create_init("nonce_agg")
|
||||
s += init_arrays("pnonces")
|
||||
|
||||
for cases in (data["valid_test_cases"], data["error_test_cases"]):
|
||||
s += init_cases(
|
||||
cases,
|
||||
lambda case: "{ { %s }, { %s }, %d },"
|
||||
% (
|
||||
", ".join(map(str, case["pnonce_indices"])),
|
||||
init_optional_expected(case),
|
||||
case["error"]["signer"] if "error" in case else 0,
|
||||
),
|
||||
)
|
||||
s += finish_init()
|
||||
|
||||
# sign/verify vectors
|
||||
with open(sys.argv[1] + "/sign_verify_vectors.json", "r") as f:
|
||||
data = json.load(f)
|
||||
|
||||
# The MuSig2 implementation only allows messages of length 32
|
||||
assert list(filter(lambda x: len(x) == 64, data["msgs"]))[0] == data["msgs"][0]
|
||||
data["msgs"] = [data["msgs"][0]]
|
||||
|
||||
def filter_msg32(k):
|
||||
return list(filter(lambda x: x["msg_index"] == 0, data[k]))
|
||||
|
||||
data["valid_test_cases"] = filter_msg32("valid_test_cases")
|
||||
data["sign_error_test_cases"] = filter_msg32("sign_error_test_cases")
|
||||
data["verify_error_test_cases"] = filter_msg32("verify_error_test_cases")
|
||||
data["verify_fail_test_cases"] = filter_msg32("verify_fail_test_cases")
|
||||
|
||||
num_pubkeys = len(data["pubkeys"])
|
||||
max_pubkeys = max(num_pubkeys, max_pubkeys)
|
||||
num_secnonces = len(data["secnonces"])
|
||||
num_pubnonces = len(data["pnonces"])
|
||||
num_aggnonces = len(data["aggnonces"])
|
||||
num_msgs = len(data["msgs"])
|
||||
num_valid_cases = len(data["valid_test_cases"])
|
||||
num_sign_error_cases = len(data["sign_error_test_cases"])
|
||||
num_verify_fail_cases = len(data["verify_fail_test_cases"])
|
||||
num_verify_error_cases = len(data["verify_error_test_cases"])
|
||||
|
||||
all_cases = (
|
||||
data["valid_test_cases"]
|
||||
+ data["sign_error_test_cases"]
|
||||
+ data["verify_error_test_cases"]
|
||||
+ data["verify_fail_test_cases"]
|
||||
)
|
||||
max_key_indices = max(len(test_case["key_indices"]) for test_case in all_cases)
|
||||
max_nonce_indices = max(
|
||||
len(test_case["nonce_indices"]) if "nonce_indices" in test_case else 0
|
||||
for test_case in all_cases
|
||||
)
|
||||
# Add structures for valid and error cases
|
||||
s += (
|
||||
"""
|
||||
/* Omit pubnonces in the test vectors because our partial signature verification
|
||||
* implementation is able to accept the aggnonce directly. */
|
||||
struct musig_valid_case {
|
||||
size_t key_indices_len;
|
||||
size_t key_indices[%d];
|
||||
size_t aggnonce_index;
|
||||
size_t msg_index;
|
||||
size_t signer_index;
|
||||
unsigned char expected[32];
|
||||
};
|
||||
"""
|
||||
% max_key_indices
|
||||
)
|
||||
|
||||
s += (
|
||||
"""
|
||||
struct musig_sign_error_case {
|
||||
size_t key_indices_len;
|
||||
size_t key_indices[%d];
|
||||
size_t aggnonce_index;
|
||||
size_t msg_index;
|
||||
size_t secnonce_index;
|
||||
enum MUSIG_ERROR error;
|
||||
};
|
||||
"""
|
||||
% max_key_indices
|
||||
)
|
||||
|
||||
s += """
|
||||
struct musig_verify_fail_error_case {
|
||||
unsigned char sig[32];
|
||||
size_t key_indices_len;
|
||||
size_t key_indices[%d];
|
||||
size_t nonce_indices_len;
|
||||
size_t nonce_indices[%d];
|
||||
size_t msg_index;
|
||||
size_t signer_index;
|
||||
enum MUSIG_ERROR error;
|
||||
};
|
||||
""" % (
|
||||
max_key_indices,
|
||||
max_nonce_indices,
|
||||
)
|
||||
|
||||
# Add structure for entire vector
|
||||
s += """
|
||||
struct musig_sign_verify_vector {
|
||||
unsigned char sk[32];
|
||||
unsigned char pubkeys[%d][33];
|
||||
unsigned char secnonces[%d][194];
|
||||
unsigned char pubnonces[%d][194];
|
||||
unsigned char aggnonces[%d][66];
|
||||
unsigned char msgs[%d][32];
|
||||
struct musig_valid_case valid_case[%d];
|
||||
struct musig_sign_error_case sign_error_case[%d];
|
||||
struct musig_verify_fail_error_case verify_fail_case[%d];
|
||||
struct musig_verify_fail_error_case verify_error_case[%d];
|
||||
};
|
||||
""" % (
|
||||
num_pubkeys,
|
||||
num_secnonces,
|
||||
num_pubnonces,
|
||||
num_aggnonces,
|
||||
num_msgs,
|
||||
num_valid_cases,
|
||||
num_sign_error_cases,
|
||||
num_verify_fail_cases,
|
||||
num_verify_error_cases,
|
||||
)
|
||||
|
||||
s += create_init("sign_verify")
|
||||
s += init_array("sk")
|
||||
s += init_arrays("pubkeys")
|
||||
s += init_arrays("secnonces")
|
||||
s += init_arrays("pnonces")
|
||||
s += init_arrays("aggnonces")
|
||||
s += init_arrays("msgs")
|
||||
|
||||
s += init_cases(
|
||||
data["valid_test_cases"],
|
||||
lambda case: "{ %s, %d, %d, %d, { %s }},"
|
||||
% (
|
||||
init_indices(case["key_indices"]),
|
||||
case["aggnonce_index"],
|
||||
case["msg_index"],
|
||||
case["signer_index"],
|
||||
init_optional_expected(case),
|
||||
),
|
||||
)
|
||||
|
||||
def sign_error(case):
|
||||
comment = case["comment"]
|
||||
if "pubkey" in comment or "public key" in comment:
|
||||
return "MUSIG_PUBKEY"
|
||||
elif "Aggregate nonce" in comment:
|
||||
return "MUSIG_AGGNONCE"
|
||||
elif "Secnonce" in comment:
|
||||
return "MUSIG_SECNONCE"
|
||||
else:
|
||||
sys.exit("Unknown sign error")
|
||||
|
||||
s += init_cases(
|
||||
data["sign_error_test_cases"],
|
||||
lambda case: "{ %s, %d, %d, %d, %s },"
|
||||
% (
|
||||
init_indices(case["key_indices"]),
|
||||
case["aggnonce_index"],
|
||||
case["msg_index"],
|
||||
case["secnonce_index"],
|
||||
sign_error(case),
|
||||
),
|
||||
)
|
||||
|
||||
def verify_error(case):
|
||||
comment = case["comment"]
|
||||
if "exceeds" in comment:
|
||||
return "MUSIG_SIG"
|
||||
elif "Wrong signer" in comment or "Wrong signature" in comment:
|
||||
return "MUSIG_SIG_VERIFY"
|
||||
elif "pubnonce" in comment:
|
||||
return "MUSIG_PUBNONCE"
|
||||
elif "pubkey" in comment:
|
||||
return "MUSIG_PUBKEY"
|
||||
else:
|
||||
sys.exit("Unknown verify error")
|
||||
|
||||
for cases in ("verify_fail_test_cases", "verify_error_test_cases"):
|
||||
s += init_cases(
|
||||
data[cases],
|
||||
lambda case: "{ { %s }, %s, %s, %d, %d, %s },"
|
||||
% (
|
||||
hexstr_to_intarray(case["sig"]),
|
||||
init_indices(case["key_indices"]),
|
||||
init_indices(case["nonce_indices"]),
|
||||
case["msg_index"],
|
||||
case["signer_index"],
|
||||
verify_error(case),
|
||||
),
|
||||
)
|
||||
|
||||
s += finish_init()
|
||||
|
||||
# tweak vectors
|
||||
with open(sys.argv[1] + "/tweak_vectors.json", "r") as f:
|
||||
data = json.load(f)
|
||||
|
||||
num_pubkeys = len(data["pubkeys"])
|
||||
max_pubkeys = max(num_pubkeys, max_pubkeys)
|
||||
num_pubnonces = len(data["pnonces"])
|
||||
num_tweaks = len(data["tweaks"])
|
||||
num_valid_cases = len(data["valid_test_cases"])
|
||||
num_error_cases = len(data["error_test_cases"])
|
||||
|
||||
all_cases = data["valid_test_cases"] + data["error_test_cases"]
|
||||
max_key_indices = max(len(test_case["key_indices"]) for test_case in all_cases)
|
||||
max_tweak_indices = max(len(test_case["tweak_indices"]) for test_case in all_cases)
|
||||
max_nonce_indices = max(len(test_case["nonce_indices"]) for test_case in all_cases)
|
||||
# Add structures for valid and error cases
|
||||
s += """
|
||||
struct musig_tweak_case {
|
||||
size_t key_indices_len;
|
||||
size_t key_indices[%d];
|
||||
size_t nonce_indices_len;
|
||||
size_t nonce_indices[%d];
|
||||
size_t tweak_indices_len;
|
||||
size_t tweak_indices[%d];
|
||||
int is_xonly[%d];
|
||||
size_t signer_index;
|
||||
unsigned char expected[32];
|
||||
};
|
||||
""" % (
|
||||
max_key_indices,
|
||||
max_nonce_indices,
|
||||
max_tweak_indices,
|
||||
max_tweak_indices,
|
||||
)
|
||||
|
||||
# Add structure for entire vector
|
||||
s += """
|
||||
struct musig_tweak_vector {
|
||||
unsigned char sk[32];
|
||||
unsigned char secnonce[97];
|
||||
unsigned char aggnonce[66];
|
||||
unsigned char msg[32];
|
||||
unsigned char pubkeys[%d][33];
|
||||
unsigned char pubnonces[%d][194];
|
||||
unsigned char tweaks[%d][32];
|
||||
struct musig_tweak_case valid_case[%d];
|
||||
struct musig_tweak_case error_case[%d];
|
||||
};
|
||||
""" % (
|
||||
num_pubkeys,
|
||||
num_pubnonces,
|
||||
num_tweaks,
|
||||
num_valid_cases,
|
||||
num_error_cases,
|
||||
)
|
||||
s += create_init("tweak")
|
||||
s += init_array("sk")
|
||||
s += init_array("secnonce")
|
||||
s += init_array("aggnonce")
|
||||
s += init_array("msg")
|
||||
s += init_arrays("pubkeys")
|
||||
s += init_arrays("pnonces")
|
||||
s += init_arrays("tweaks")
|
||||
|
||||
s += init_cases(
|
||||
data["valid_test_cases"],
|
||||
lambda case: "{ %s, %s, %s, { %s }, %d, { %s }},"
|
||||
% (
|
||||
init_indices(case["key_indices"]),
|
||||
init_indices(case["nonce_indices"]),
|
||||
init_indices(case["tweak_indices"]),
|
||||
init_is_xonly(case),
|
||||
case["signer_index"],
|
||||
init_optional_expected(case),
|
||||
),
|
||||
)
|
||||
|
||||
s += init_cases(
|
||||
data["error_test_cases"],
|
||||
lambda case: "{ %s, %s, %s, { %s }, %d, { %s }},"
|
||||
% (
|
||||
init_indices(case["key_indices"]),
|
||||
init_indices(case["nonce_indices"]),
|
||||
init_indices(case["tweak_indices"]),
|
||||
init_is_xonly(case),
|
||||
case["signer_index"],
|
||||
init_optional_expected(case),
|
||||
),
|
||||
)
|
||||
|
||||
s += finish_init()
|
||||
|
||||
# sigagg vectors
|
||||
with open(sys.argv[1] + "/sig_agg_vectors.json", "r") as f:
|
||||
data = json.load(f)
|
||||
|
||||
num_pubkeys = len(data["pubkeys"])
|
||||
max_pubkeys = max(num_pubkeys, max_pubkeys)
|
||||
num_tweaks = len(data["tweaks"])
|
||||
num_psigs = len(data["psigs"])
|
||||
num_valid_cases = len(data["valid_test_cases"])
|
||||
num_error_cases = len(data["error_test_cases"])
|
||||
|
||||
all_cases = data["valid_test_cases"] + data["error_test_cases"]
|
||||
max_key_indices = max(len(test_case["key_indices"]) for test_case in all_cases)
|
||||
max_tweak_indices = max(len(test_case["tweak_indices"]) for test_case in all_cases)
|
||||
max_psig_indices = max(len(test_case["psig_indices"]) for test_case in all_cases)
|
||||
|
||||
# Add structures for valid and error cases
|
||||
s += """
|
||||
/* Omit pubnonces in the test vectors because they're only needed for
|
||||
* implementations that do not directly accept an aggnonce. */
|
||||
struct musig_sig_agg_case {
|
||||
size_t key_indices_len;
|
||||
size_t key_indices[%d];
|
||||
size_t tweak_indices_len;
|
||||
size_t tweak_indices[%d];
|
||||
int is_xonly[%d];
|
||||
unsigned char aggnonce[66];
|
||||
size_t psig_indices_len;
|
||||
size_t psig_indices[%d];
|
||||
/* if valid case */
|
||||
unsigned char expected[64];
|
||||
/* if error case */
|
||||
int invalid_sig_idx;
|
||||
};
|
||||
""" % (
|
||||
max_key_indices,
|
||||
max_tweak_indices,
|
||||
max_tweak_indices,
|
||||
max_psig_indices,
|
||||
)
|
||||
|
||||
# Add structure for entire vector
|
||||
s += """
|
||||
struct musig_sig_agg_vector {
|
||||
unsigned char pubkeys[%d][33];
|
||||
unsigned char tweaks[%d][32];
|
||||
unsigned char psigs[%d][32];
|
||||
unsigned char msg[32];
|
||||
struct musig_sig_agg_case valid_case[%d];
|
||||
struct musig_sig_agg_case error_case[%d];
|
||||
};
|
||||
""" % (
|
||||
num_pubkeys,
|
||||
num_tweaks,
|
||||
num_psigs,
|
||||
num_valid_cases,
|
||||
num_error_cases,
|
||||
)
|
||||
|
||||
s += create_init("sig_agg")
|
||||
s += init_arrays("pubkeys")
|
||||
s += init_arrays("tweaks")
|
||||
s += init_arrays("psigs")
|
||||
s += init_array("msg")
|
||||
|
||||
for cases in (data["valid_test_cases"], data["error_test_cases"]):
|
||||
s += init_cases(
|
||||
cases,
|
||||
lambda case: "{ %s, %s, { %s }, { %s }, %s, { %s }, %d },"
|
||||
% (
|
||||
init_indices(case["key_indices"]),
|
||||
init_indices(case["tweak_indices"]),
|
||||
init_is_xonly(case),
|
||||
hexstr_to_intarray(case["aggnonce"]),
|
||||
init_indices(case["psig_indices"]),
|
||||
init_optional_expected(case),
|
||||
case["error"]["signer"] if "error" in case else 0,
|
||||
),
|
||||
)
|
||||
s += finish_init()
|
||||
s += "enum { MUSIG_VECTORS_MAX_PUBKEYS = %d };" % max_pubkeys
|
||||
print(s)
|
||||
Reference in new issue
Block a user