The last of the review findings, plus the comment and structure fixes it
listed.
The example's repair run used the pre-enrollment participant count.
Two blocks earlier the example teaches that every participant must
update its record of n from 3 to 4 after an enrollment, and the
signing session duly uses N_PARTICIPANTS_AFTER. Then enroll() -- which
hard-coded N_PARTICIPANTS -- ran the repair at n = 3. It worked only
because the Lagrange math never involves n and every party in this
single-process demo passed the same stale value.
In a real post-enrollment repair it would not. n is bound into the
parameters hash, so helpers feeding their updated n = 4 into
shares_gen while the requester feeds n = 3 abort round 1.2 with no
visible cause. enroll() now takes n_participants as a parameter, the
repair passes N_PARTICIPANTS_AFTER, and both the function's contract
comment and the repair call site say why. The repaired share is still
byte-identical to the original, which is the point: n changes the
hash, not the arithmetic.
The example leaked secrets on its failure paths.
enroll() erased the delta and sigma buffers only on success; four
early returns left them live. sign_and_verify() returned from three
places without erasing already-generated secnonces. Both now route
every exit through a cleanup block. This example is otherwise more
careful about erasure than its siblings, so the asymmetry was exactly
what a reader copying it would carry into production -- on the fault
paths where hygiene matters most.
The double-wipe of session_secrand is gone with it: shares_gen and
nonce_gen both wipe the seed on every path, and doing it again read
as uncertainty about the contract. The comment now states the
contract instead. The fill_random failure path does erase, since
nothing else has touched the buffer there.
The example's mismatch message asserted a cause it cannot know.
It printed "Helper %u disagrees about the enrollment parameters" for
what may equally be a corrupted share, per the previous commit's
finding. It now says the helper "contributed a share this helper
cannot use", with a comment noting that share_agg does not
distinguish the two causes so neither can the message.
Comment and structure fixes, all noted in the review:
- The vector generator claimed case 4 was "the only case whose DERIVED
public share has odd Y". It is not -- cases 1, 2 and 4 are odd and
case 3 is even. The comment existed to justify a coverage choice and
misinformed; both parity comments now describe the set accurately and
say they document it rather than constrain it. Regenerating vectors.h
still reproduces it byte for byte.
- The secp256k1_frost_sort_ids declaration in frost/session.h no longer
duplicates the definition's doc comment, which was two copies to keep
in sync. It says what the function is for and points at the
definition for the contract.
- The t >= 2 rationale was stated in full in three places. The impl now
states the conclusion and names frost_enrollment.md as the single
place to edit if the policy moves.
- The ctime_tests comments read ambiguously ("the parameters hash is
public, the delta values are not" against a header calling deltas
secret), and computing direct_hash without asserting anything invited
a "forgotten assertion" reading. Both are now explicit.
- The example moves next to frost_example in Makefile.am rather than
after iceberg, matching the FROST-stack grouping used in
configure.ac, ci.sh, ci.yml and README.
- frost_enrollment.md now distinguishes what is unstable (the C API)
from what is frozen (the wire-visible encodings), which the two
statements together previously left easy to conflate.
Not fixed, deliberately, and now recorded where the tree can see it: the
plan called for a CHANGELOG.md entry. That file states in its first two
lines that it is upstream libsecp256k1's changelog and not this fork's,
and none of frost, chilldkg, iceberg or prefractal has an entry. Adding
the first one is a decision about all five modules, not this one. The
README link is the fork's actual convention for announcing a module and
is in place.
Verification: autotools builds warning-free and `make check` is 12/12
including the example; ctime_tests is clean under valgrind; `make dist`
carries all nine frost_enrollment files; CMake with examples builds
warning-free and ctest is 542/542; the example source is clean under
gcc -std=c89 -pedantic -Wall -Wextra; regenerating vectors.h reproduces
it byte for byte.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
897 lines
40 KiB
C
897 lines
40 KiB
C
/***********************************************************************
|
|
* Copyright (c) 2020 Gregory Maxwell *
|
|
* Distributed under the MIT software license, see the accompanying *
|
|
* file COPYING or https://www.opensource.org/licenses/mit-license.php.*
|
|
***********************************************************************/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "../include/secp256k1.h"
|
|
#include "assumptions.h"
|
|
#include "checkmem.h"
|
|
|
|
#if !SECP256K1_CHECKMEM_ENABLED
|
|
# error "This tool cannot be compiled without memory-checking interface (valgrind or msan)"
|
|
#endif
|
|
|
|
#ifdef ENABLE_MODULE_ECDH
|
|
# include "../include/secp256k1_ecdh.h"
|
|
#endif
|
|
|
|
#ifdef ENABLE_MODULE_RECOVERY
|
|
# include "../include/secp256k1_recovery.h"
|
|
#endif
|
|
|
|
#ifdef ENABLE_MODULE_EXTRAKEYS
|
|
# include "../include/secp256k1_extrakeys.h"
|
|
#endif
|
|
|
|
#ifdef ENABLE_MODULE_SCHNORRSIG
|
|
#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
|
|
|
|
#ifdef ENABLE_MODULE_ECDSA_S2C
|
|
#include "../include/secp256k1_ecdsa_s2c.h"
|
|
#endif
|
|
|
|
#ifdef ENABLE_MODULE_ECDSA_ADAPTOR
|
|
#include "../include/secp256k1_ecdsa_adaptor.h"
|
|
#endif
|
|
|
|
#if defined(__GNUC__)
|
|
# pragma GCC diagnostic push
|
|
# pragma GCC diagnostic warning "-Wunused-function"
|
|
#endif
|
|
|
|
#ifdef ENABLE_MODULE_FROST
|
|
#include "../include/secp256k1_frost.h"
|
|
#endif
|
|
|
|
#ifdef ENABLE_MODULE_CHILLDKG
|
|
#include "../include/secp256k1_chilldkg.h"
|
|
#endif
|
|
|
|
#ifdef ENABLE_MODULE_PREFRACTAL
|
|
#include "../include/secp256k1_prefractal.h"
|
|
#endif
|
|
|
|
#ifdef ENABLE_MODULE_FROST_ENROLLMENT
|
|
#include "../include/secp256k1_frost_enrollment.h"
|
|
#endif
|
|
|
|
#ifdef ENABLE_MODULE_ICEBERG
|
|
#include "../include/secp256k1_iceberg.h"
|
|
#include "../include/secp256k1_iceberg_dealer.h"
|
|
#endif
|
|
|
|
static void run_tests(secp256k1_context *ctx, unsigned char *key);
|
|
|
|
int main(void) {
|
|
secp256k1_context* ctx;
|
|
unsigned char key[32];
|
|
int ret, i;
|
|
|
|
if (!SECP256K1_CHECKMEM_RUNNING()) {
|
|
fprintf(stderr, "This test can only usefully be run inside valgrind because it was not compiled under msan.\n");
|
|
fprintf(stderr, "Usage: valgrind ./ctime_tests (or with Autotools: libtool --mode=execute valgrind ./ctime_tests)\n");
|
|
return EXIT_FAILURE;
|
|
}
|
|
ctx = secp256k1_context_create(SECP256K1_CONTEXT_DECLASSIFY);
|
|
/** In theory, testing with a single secret input should be sufficient:
|
|
* If control flow depended on secrets the tool would generate an error.
|
|
*/
|
|
for (i = 0; i < 32; i++) {
|
|
key[i] = i + 65;
|
|
}
|
|
|
|
run_tests(ctx, key);
|
|
|
|
/* Test context randomisation. Do this last because it leaves the context
|
|
* tainted. */
|
|
SECP256K1_CHECKMEM_UNDEFINE(key, 32);
|
|
ret = secp256k1_context_randomize(ctx, key);
|
|
SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
|
|
CHECK(ret);
|
|
|
|
secp256k1_context_destroy(ctx);
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
static void run_tests(secp256k1_context *ctx, unsigned char *key) {
|
|
secp256k1_ecdsa_signature signature;
|
|
secp256k1_pubkey pubkey;
|
|
size_t siglen = 74;
|
|
size_t outputlen = 33;
|
|
int i;
|
|
int ret;
|
|
unsigned char msg[32];
|
|
unsigned char sig[74];
|
|
unsigned char spubkey[33];
|
|
#ifdef ENABLE_MODULE_RECOVERY
|
|
secp256k1_ecdsa_recoverable_signature recoverable_signature;
|
|
int recid;
|
|
#endif
|
|
#ifdef ENABLE_MODULE_EXTRAKEYS
|
|
secp256k1_keypair keypair;
|
|
#endif
|
|
#ifdef ENABLE_MODULE_ELLSWIFT
|
|
unsigned char ellswift[64];
|
|
static const unsigned char prefix[64] = {'t', 'e', 's', 't'};
|
|
#endif
|
|
|
|
for (i = 0; i < 32; i++) {
|
|
msg[i] = i + 1;
|
|
}
|
|
|
|
/* Test keygen. */
|
|
SECP256K1_CHECKMEM_UNDEFINE(key, 32);
|
|
ret = secp256k1_ec_pubkey_create(ctx, &pubkey, key);
|
|
SECP256K1_CHECKMEM_DEFINE(&pubkey, sizeof(secp256k1_pubkey));
|
|
SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
|
|
CHECK(ret);
|
|
CHECK(secp256k1_ec_pubkey_serialize(ctx, spubkey, &outputlen, &pubkey, SECP256K1_EC_COMPRESSED) == 1);
|
|
|
|
/* Test signing. */
|
|
SECP256K1_CHECKMEM_UNDEFINE(key, 32);
|
|
ret = secp256k1_ecdsa_sign(ctx, &signature, msg, key, NULL, NULL);
|
|
SECP256K1_CHECKMEM_DEFINE(&signature, sizeof(secp256k1_ecdsa_signature));
|
|
SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
|
|
CHECK(ret);
|
|
CHECK(secp256k1_ecdsa_signature_serialize_der(ctx, sig, &siglen, &signature));
|
|
|
|
#ifdef ENABLE_MODULE_ECDH
|
|
/* Test ECDH. */
|
|
SECP256K1_CHECKMEM_UNDEFINE(key, 32);
|
|
ret = secp256k1_ecdh(ctx, msg, &pubkey, key, NULL, NULL);
|
|
SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
|
|
CHECK(ret == 1);
|
|
#endif
|
|
|
|
#ifdef ENABLE_MODULE_RECOVERY
|
|
/* Test signing a recoverable signature. */
|
|
SECP256K1_CHECKMEM_UNDEFINE(key, 32);
|
|
ret = secp256k1_ecdsa_sign_recoverable(ctx, &recoverable_signature, msg, key, NULL, NULL);
|
|
SECP256K1_CHECKMEM_DEFINE(&recoverable_signature, sizeof(recoverable_signature));
|
|
SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
|
|
CHECK(ret);
|
|
CHECK(secp256k1_ecdsa_recoverable_signature_serialize_compact(ctx, sig, &recid, &recoverable_signature));
|
|
CHECK(recid >= 0 && recid <= 3);
|
|
#endif
|
|
|
|
SECP256K1_CHECKMEM_UNDEFINE(key, 32);
|
|
ret = secp256k1_ec_seckey_verify(ctx, key);
|
|
SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
|
|
CHECK(ret == 1);
|
|
|
|
SECP256K1_CHECKMEM_UNDEFINE(key, 32);
|
|
ret = secp256k1_ec_seckey_negate(ctx, key);
|
|
SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
|
|
CHECK(ret == 1);
|
|
|
|
SECP256K1_CHECKMEM_UNDEFINE(key, 32);
|
|
SECP256K1_CHECKMEM_UNDEFINE(msg, 32);
|
|
ret = secp256k1_ec_seckey_tweak_add(ctx, key, msg);
|
|
SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
|
|
CHECK(ret == 1);
|
|
|
|
SECP256K1_CHECKMEM_UNDEFINE(key, 32);
|
|
SECP256K1_CHECKMEM_UNDEFINE(msg, 32);
|
|
ret = secp256k1_ec_seckey_tweak_mul(ctx, key, msg);
|
|
SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
|
|
CHECK(ret == 1);
|
|
|
|
/* Test keypair_create and keypair_xonly_tweak_add. */
|
|
#ifdef ENABLE_MODULE_EXTRAKEYS
|
|
SECP256K1_CHECKMEM_UNDEFINE(key, 32);
|
|
ret = secp256k1_keypair_create(ctx, &keypair, key);
|
|
SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
|
|
CHECK(ret == 1);
|
|
|
|
/* The tweak is not treated as a secret in keypair_tweak_add */
|
|
SECP256K1_CHECKMEM_DEFINE(msg, 32);
|
|
ret = secp256k1_keypair_xonly_tweak_add(ctx, &keypair, msg);
|
|
SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
|
|
CHECK(ret == 1);
|
|
|
|
SECP256K1_CHECKMEM_UNDEFINE(key, 32);
|
|
SECP256K1_CHECKMEM_UNDEFINE(&keypair, sizeof(keypair));
|
|
ret = secp256k1_keypair_sec(ctx, key, &keypair);
|
|
SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
|
|
CHECK(ret == 1);
|
|
#endif
|
|
|
|
#ifdef ENABLE_MODULE_SCHNORRSIG
|
|
SECP256K1_CHECKMEM_UNDEFINE(key, 32);
|
|
ret = secp256k1_keypair_create(ctx, &keypair, key);
|
|
SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
|
|
CHECK(ret == 1);
|
|
ret = secp256k1_schnorrsig_sign32(ctx, sig, msg, &keypair, NULL);
|
|
SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
|
|
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;
|
|
const secp256k1_musig_partial_sig *partial_sig_ptr[1];
|
|
unsigned char extra_input[32];
|
|
unsigned char sec_adaptor[32];
|
|
secp256k1_pubkey adaptor;
|
|
unsigned char pre_sig[64];
|
|
int nonce_parity;
|
|
|
|
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;
|
|
memcpy(sec_adaptor, key, sizeof(sec_adaptor));
|
|
sec_adaptor[0] = extra_input[0] + 3;
|
|
partial_sig_ptr[0] = &partial_sig;
|
|
|
|
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));
|
|
CHECK(secp256k1_ec_pubkey_create(ctx, &adaptor, sec_adaptor));
|
|
|
|
SECP256K1_CHECKMEM_UNDEFINE(key, 32);
|
|
SECP256K1_CHECKMEM_UNDEFINE(session_secrand, sizeof(session_secrand));
|
|
SECP256K1_CHECKMEM_UNDEFINE(extra_input, sizeof(extra_input));
|
|
SECP256K1_CHECKMEM_UNDEFINE(sec_adaptor, sizeof(sec_adaptor));
|
|
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, &adaptor) == 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);
|
|
|
|
SECP256K1_CHECKMEM_DEFINE(&partial_sig, sizeof(partial_sig));
|
|
CHECK(secp256k1_musig_partial_sig_agg(ctx, pre_sig, &session, partial_sig_ptr, 1));
|
|
SECP256K1_CHECKMEM_DEFINE(pre_sig, sizeof(pre_sig));
|
|
|
|
CHECK(secp256k1_musig_nonce_parity(ctx, &nonce_parity, &session));
|
|
ret = secp256k1_musig_adapt(ctx, sig, pre_sig, sec_adaptor, nonce_parity);
|
|
SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
|
|
CHECK(ret == 1);
|
|
ret = secp256k1_musig_extract_adaptor(ctx, sec_adaptor, sig, pre_sig, nonce_parity);
|
|
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);
|
|
SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
|
|
CHECK(ret == 1);
|
|
|
|
SECP256K1_CHECKMEM_UNDEFINE(key, 32);
|
|
ret = secp256k1_ellswift_create(ctx, ellswift, key, ellswift);
|
|
SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
|
|
CHECK(ret == 1);
|
|
|
|
for (i = 0; i < 2; i++) {
|
|
SECP256K1_CHECKMEM_UNDEFINE(key, 32);
|
|
SECP256K1_CHECKMEM_DEFINE(&ellswift, sizeof(ellswift));
|
|
ret = secp256k1_ellswift_xdh(ctx, msg, ellswift, ellswift, key, i, secp256k1_ellswift_xdh_hash_function_bip324, NULL);
|
|
SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
|
|
CHECK(ret == 1);
|
|
|
|
SECP256K1_CHECKMEM_UNDEFINE(key, 32);
|
|
SECP256K1_CHECKMEM_DEFINE(&ellswift, sizeof(ellswift));
|
|
ret = secp256k1_ellswift_xdh(ctx, msg, ellswift, ellswift, key, i, secp256k1_ellswift_xdh_hash_function_prefix, (void *)prefix);
|
|
SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
|
|
CHECK(ret == 1);
|
|
}
|
|
#endif
|
|
|
|
#ifdef ENABLE_MODULE_ECDSA_S2C
|
|
{
|
|
unsigned char s2c_data[32] = {0};
|
|
unsigned char s2c_data_comm[32] = {0};
|
|
secp256k1_ecdsa_s2c_opening s2c_opening;
|
|
|
|
SECP256K1_CHECKMEM_UNDEFINE(key, 32);
|
|
SECP256K1_CHECKMEM_UNDEFINE(s2c_data, 32);
|
|
ret = secp256k1_ecdsa_s2c_sign(ctx, &signature, &s2c_opening, msg, key, s2c_data);
|
|
SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
|
|
CHECK(ret == 1);
|
|
|
|
SECP256K1_CHECKMEM_UNDEFINE(s2c_data, 32);
|
|
ret = secp256k1_ecdsa_anti_exfil_host_commit(ctx, s2c_data_comm, s2c_data);
|
|
SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
|
|
CHECK(ret == 1);
|
|
|
|
SECP256K1_CHECKMEM_UNDEFINE(key, 32);
|
|
SECP256K1_CHECKMEM_UNDEFINE(s2c_data, 32);
|
|
ret = secp256k1_ecdsa_anti_exfil_signer_commit(ctx, &s2c_opening, msg, key, s2c_data);
|
|
SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
|
|
CHECK(ret == 1);
|
|
}
|
|
#endif
|
|
|
|
#ifdef ENABLE_MODULE_ECDSA_ADAPTOR
|
|
{
|
|
unsigned char adaptor_sig[162];
|
|
unsigned char deckey[32];
|
|
unsigned char expected_deckey[32];
|
|
secp256k1_pubkey enckey;
|
|
|
|
for (i = 0; i < 32; i++) {
|
|
deckey[i] = i + 2;
|
|
}
|
|
|
|
ret = secp256k1_ec_pubkey_create(ctx, &enckey, deckey);
|
|
CHECK(ret == 1);
|
|
|
|
SECP256K1_CHECKMEM_UNDEFINE(key, 32);
|
|
ret = secp256k1_ecdsa_adaptor_encrypt(ctx, adaptor_sig, key, &enckey, msg, NULL, NULL);
|
|
SECP256K1_CHECKMEM_DEFINE(adaptor_sig, sizeof(adaptor_sig));
|
|
SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
|
|
CHECK(ret == 1);
|
|
|
|
SECP256K1_CHECKMEM_UNDEFINE(deckey, 32);
|
|
ret = secp256k1_ecdsa_adaptor_decrypt(ctx, &signature, deckey, adaptor_sig);
|
|
SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
|
|
CHECK(ret == 1);
|
|
|
|
SECP256K1_CHECKMEM_UNDEFINE(&signature, 32);
|
|
ret = secp256k1_ecdsa_adaptor_recover(ctx, expected_deckey, &signature, adaptor_sig, &enckey);
|
|
SECP256K1_CHECKMEM_DEFINE(expected_deckey, sizeof(expected_deckey));
|
|
SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
|
|
CHECK(ret == 1);
|
|
|
|
SECP256K1_CHECKMEM_DEFINE(deckey, sizeof(deckey));
|
|
ret = secp256k1_memcmp_var(deckey, expected_deckey, sizeof(expected_deckey));
|
|
SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
|
|
CHECK(ret == 0);
|
|
}
|
|
#endif
|
|
|
|
#ifdef ENABLE_MODULE_FROST
|
|
{
|
|
unsigned char thresh_seckey[32];
|
|
unsigned char secshares[2 * 32];
|
|
secp256k1_pubkey thresh_pk;
|
|
secp256k1_pubkey pubshares[2];
|
|
uint32_t frost_ids[2] = { 0, 1 };
|
|
secp256k1_frost_tweak_cache cache;
|
|
secp256k1_frost_secnonce secnonce[2];
|
|
secp256k1_frost_pubnonce pubnonce[2];
|
|
const secp256k1_frost_pubnonce *pubnonce_ptrs[2];
|
|
secp256k1_frost_aggnonce aggnonce;
|
|
secp256k1_frost_session session;
|
|
secp256k1_frost_partial_sig partial_sig[2];
|
|
const secp256k1_frost_partial_sig *partial_sig_ptrs[2];
|
|
unsigned char session_secrand[2][32];
|
|
|
|
pubnonce_ptrs[0] = &pubnonce[0];
|
|
pubnonce_ptrs[1] = &pubnonce[1];
|
|
partial_sig_ptrs[0] = &partial_sig[0];
|
|
partial_sig_ptrs[1] = &partial_sig[1];
|
|
|
|
/* All public inputs are derived from defined memory. key is reused as
|
|
* the base of the (secret) threshold key and session randomness. */
|
|
SECP256K1_CHECKMEM_DEFINE(key, 32);
|
|
memcpy(thresh_seckey, key, sizeof(thresh_seckey));
|
|
thresh_seckey[0] = thresh_seckey[0] + 1;
|
|
memcpy(session_secrand[0], key, 32);
|
|
session_secrand[0][0] = session_secrand[0][0] + 2;
|
|
memcpy(session_secrand[1], key, 32);
|
|
session_secrand[1][0] = session_secrand[1][0] + 3;
|
|
|
|
/* Test frost_trusted_dealer_keygen. The threshold secret key and the
|
|
* resulting secret shares are secret; the threshold public key and
|
|
* the public shares are public. */
|
|
SECP256K1_CHECKMEM_UNDEFINE(thresh_seckey, sizeof(thresh_seckey));
|
|
ret = secp256k1_frost_trusted_dealer_keygen(ctx, secshares, &thresh_pk, pubshares, 2, 2, thresh_seckey);
|
|
SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
|
|
CHECK(ret == 1);
|
|
SECP256K1_CHECKMEM_DEFINE(&thresh_pk, sizeof(thresh_pk));
|
|
SECP256K1_CHECKMEM_DEFINE(pubshares, sizeof(pubshares));
|
|
|
|
/* The session setup uses only public inputs. */
|
|
CHECK(secp256k1_frost_tweak_cache_init(ctx, &cache, &thresh_pk) == 1);
|
|
/* Make sure that previous tests don't undefine msg. It's not used as a secret here. */
|
|
SECP256K1_CHECKMEM_DEFINE(msg, sizeof(msg));
|
|
|
|
/* Test frost_nonce_gen. The session randomness and the secret share
|
|
* are secret; the pubnonce is public. The secnonce stays secret. */
|
|
for (i = 0; i < 2; i++) {
|
|
SECP256K1_CHECKMEM_UNDEFINE(session_secrand[i], 32);
|
|
SECP256K1_CHECKMEM_UNDEFINE(&secshares[32 * i], 32);
|
|
ret = secp256k1_frost_nonce_gen(ctx, &secnonce[i], &pubnonce[i], session_secrand[i], &secshares[32 * i], &pubshares[i], NULL, msg, 32, NULL, 0);
|
|
SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
|
|
CHECK(ret == 1);
|
|
SECP256K1_CHECKMEM_DEFINE(&pubnonce[i], sizeof(pubnonce[i]));
|
|
}
|
|
|
|
CHECK(secp256k1_frost_nonce_agg(ctx, &aggnonce, NULL, pubnonce_ptrs, 2) == 1);
|
|
CHECK(secp256k1_frost_session_init(ctx, &session, &aggnonce, frost_ids, pubshares, 2, 2, 2, &cache, msg, 32) == 1);
|
|
|
|
/* Test frost_sign. The secret share is secret (the secnonce is
|
|
* tainted through the tainted session randomness; its magic bytes
|
|
* must remain defined for the validity check). The partial signature
|
|
* is public. */
|
|
for (i = 0; i < 2; i++) {
|
|
SECP256K1_CHECKMEM_UNDEFINE(&secshares[32 * i], 32);
|
|
ret = secp256k1_frost_sign(ctx, &partial_sig[i], &secnonce[i], &secshares[32 * i], &session, frost_ids, pubshares, 2, frost_ids[i]);
|
|
SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
|
|
CHECK(ret == 1);
|
|
SECP256K1_CHECKMEM_DEFINE(&partial_sig[i], sizeof(partial_sig[i]));
|
|
}
|
|
CHECK(secp256k1_frost_partial_sig_agg(ctx, sig, NULL, &session, partial_sig_ptrs, 2) == 1);
|
|
|
|
/* Test frost_deterministic_sign. The secret share is secret; the
|
|
* pubnonce and the partial signature are public. */
|
|
for (i = 0; i < 2; i++) {
|
|
secp256k1_frost_pubnonce det_pubnonce;
|
|
secp256k1_frost_partial_sig det_partial_sig;
|
|
|
|
SECP256K1_CHECKMEM_UNDEFINE(&secshares[32 * i], 32);
|
|
ret = secp256k1_frost_deterministic_sign(ctx, &det_partial_sig, &det_pubnonce, &secshares[32 * i], frost_ids[i], &aggnonce, frost_ids, pubshares, 2, 2, 2, &cache, msg, 32, NULL);
|
|
SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
|
|
CHECK(ret == 1);
|
|
SECP256K1_CHECKMEM_DEFINE(&det_pubnonce, sizeof(det_pubnonce));
|
|
SECP256K1_CHECKMEM_DEFINE(&det_partial_sig, sizeof(det_partial_sig));
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef ENABLE_MODULE_CHILLDKG
|
|
{
|
|
/* Full ChillDKG session with n = 2, t = 2 (pmsg1: 227 bytes, cmsg1:
|
|
* 357 bytes, cert: 128 bytes, recovery data: 394 bytes). */
|
|
unsigned char hostseckeys[2][32];
|
|
unsigned char hostpubkeys[2 * 33];
|
|
unsigned char dkg_random[2][32];
|
|
unsigned char aux_rands[2][32];
|
|
secp256k1_chilldkg_participant_state1 state1[2];
|
|
secp256k1_chilldkg_participant_state2 state2[2];
|
|
secp256k1_chilldkg_coordinator_state cstate;
|
|
unsigned char pmsgs1[2][227];
|
|
const unsigned char *pmsgs1_ptrs[2];
|
|
unsigned char cmsg1[357];
|
|
unsigned char pmsgs2[2][64];
|
|
const unsigned char *pmsgs2_ptrs[2];
|
|
unsigned char cmsg2[128];
|
|
unsigned char secshare32[32];
|
|
unsigned char thresh_pk33[33];
|
|
unsigned char pubshares33[2 * 33];
|
|
unsigned char recovery[394];
|
|
unsigned char rec_secshare32[32];
|
|
unsigned char rec_thresh_pk33[33];
|
|
unsigned char rec_pubshares33[33 * SECP256K1_CHILLDKG_MAX_PARTICIPANTS];
|
|
unsigned char rec_hostpubkeys33[33 * SECP256K1_CHILLDKG_MAX_PARTICIPANTS];
|
|
unsigned char ack_sig[64];
|
|
size_t rec_n;
|
|
uint32_t rec_t;
|
|
uint32_t fault_index;
|
|
secp256k1_chilldkg_fault fault;
|
|
|
|
pmsgs1_ptrs[0] = pmsgs1[0];
|
|
pmsgs1_ptrs[1] = pmsgs1[1];
|
|
pmsgs2_ptrs[0] = pmsgs2[0];
|
|
pmsgs2_ptrs[1] = pmsgs2[1];
|
|
|
|
/* All public inputs are derived from defined memory. key is reused as
|
|
* the base of the (secret) host keys, session randomness and
|
|
* auxiliary randomness. */
|
|
SECP256K1_CHECKMEM_DEFINE(key, 32);
|
|
for (i = 0; i < 2; i++) {
|
|
memcpy(hostseckeys[i], key, 32);
|
|
hostseckeys[i][0] = hostseckeys[i][0] + 4 + i;
|
|
memcpy(dkg_random[i], key, 32);
|
|
dkg_random[i][0] = dkg_random[i][0] + 6 + i;
|
|
memcpy(aux_rands[i], key, 32);
|
|
aux_rands[i][0] = aux_rands[i][0] + 8 + i;
|
|
}
|
|
|
|
/* Test chilldkg_hostpubkey_gen. The host secret keys are secret; the
|
|
* host public keys are public. */
|
|
for (i = 0; i < 2; i++) {
|
|
SECP256K1_CHECKMEM_UNDEFINE(hostseckeys[i], 32);
|
|
ret = secp256k1_chilldkg_hostpubkey_gen(ctx, &hostpubkeys[33 * i], hostseckeys[i]);
|
|
SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
|
|
CHECK(ret == 1);
|
|
SECP256K1_CHECKMEM_DEFINE(&hostpubkeys[33 * i], 33);
|
|
}
|
|
|
|
/* Test chilldkg_participant_step1. The host secret key and the
|
|
* session randomness are secret; pmsg1 and the state1 object (which
|
|
* contains no secrets) are public. */
|
|
for (i = 0; i < 2; i++) {
|
|
SECP256K1_CHECKMEM_UNDEFINE(hostseckeys[i], 32);
|
|
SECP256K1_CHECKMEM_UNDEFINE(dkg_random[i], 32);
|
|
ret = secp256k1_chilldkg_participant_step1(ctx, &state1[i], pmsgs1[i], hostseckeys[i], hostpubkeys, 2, 2, dkg_random[i]);
|
|
SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
|
|
CHECK(ret == 1);
|
|
SECP256K1_CHECKMEM_DEFINE(pmsgs1[i], sizeof(pmsgs1[i]));
|
|
SECP256K1_CHECKMEM_DEFINE(&state1[i], sizeof(state1[i]));
|
|
}
|
|
|
|
/* The coordinator's steps use only public inputs. */
|
|
fault = secp256k1_chilldkg_coordinator_step1(ctx, &cstate, cmsg1, &fault_index, pmsgs1_ptrs, hostpubkeys, 2, 2);
|
|
SECP256K1_CHECKMEM_DEFINE(&fault, sizeof(fault));
|
|
CHECK(fault == SECP256K1_CHILLDKG_OK);
|
|
|
|
/* Test chilldkg_participant_step2. The host secret key and the aux
|
|
* randomness are secret; the CertEq signature is public. The state2
|
|
* object contains the secret share and stays secret. */
|
|
for (i = 0; i < 2; i++) {
|
|
SECP256K1_CHECKMEM_UNDEFINE(hostseckeys[i], 32);
|
|
SECP256K1_CHECKMEM_UNDEFINE(aux_rands[i], 32);
|
|
fault = secp256k1_chilldkg_participant_step2(ctx, &state2[i], pmsgs2[i], &fault_index, NULL, &state1[i], hostseckeys[i], cmsg1, aux_rands[i]);
|
|
SECP256K1_CHECKMEM_DEFINE(&fault, sizeof(fault));
|
|
CHECK(fault == SECP256K1_CHILLDKG_OK);
|
|
SECP256K1_CHECKMEM_DEFINE(pmsgs2[i], sizeof(pmsgs2[i]));
|
|
}
|
|
|
|
fault = secp256k1_chilldkg_coordinator_finalize(ctx, cmsg2, thresh_pk33, pubshares33, recovery, &fault_index, &cstate, pmsgs2_ptrs);
|
|
SECP256K1_CHECKMEM_DEFINE(&fault, sizeof(fault));
|
|
CHECK(fault == SECP256K1_CHILLDKG_OK);
|
|
|
|
/* Test chilldkg_participant_finalize. The state2 input and the
|
|
* secshare output are secret; the threshold public key, the public
|
|
* shares and the recovery data are public. */
|
|
fault = secp256k1_chilldkg_participant_finalize(ctx, secshare32, thresh_pk33, pubshares33, recovery, &fault_index, &state2[0], cmsg2);
|
|
SECP256K1_CHECKMEM_DEFINE(&fault, sizeof(fault));
|
|
CHECK(fault == SECP256K1_CHILLDKG_OK);
|
|
SECP256K1_CHECKMEM_DEFINE(thresh_pk33, sizeof(thresh_pk33));
|
|
SECP256K1_CHECKMEM_DEFINE(pubshares33, sizeof(pubshares33));
|
|
SECP256K1_CHECKMEM_DEFINE(recovery, sizeof(recovery));
|
|
|
|
/* Test chilldkg_participant_recover. The host secret key and the
|
|
* recovered secret share are secret; the recovery data and the
|
|
* remaining outputs are public. */
|
|
SECP256K1_CHECKMEM_UNDEFINE(hostseckeys[0], 32);
|
|
fault = secp256k1_chilldkg_participant_recover(ctx, rec_secshare32, rec_thresh_pk33, rec_pubshares33, rec_hostpubkeys33, &rec_n, &rec_t, &fault_index, hostseckeys[0], recovery, sizeof(recovery));
|
|
SECP256K1_CHECKMEM_DEFINE(&fault, sizeof(fault));
|
|
CHECK(fault == SECP256K1_CHILLDKG_OK);
|
|
SECP256K1_CHECKMEM_DEFINE(rec_thresh_pk33, sizeof(rec_thresh_pk33));
|
|
SECP256K1_CHECKMEM_DEFINE(rec_pubshares33, 2 * 33);
|
|
SECP256K1_CHECKMEM_DEFINE(rec_hostpubkeys33, 2 * 33);
|
|
SECP256K1_CHECKMEM_DEFINE(&rec_n, sizeof(rec_n));
|
|
SECP256K1_CHECKMEM_DEFINE(&rec_t, sizeof(rec_t));
|
|
CHECK(rec_n == 2 && rec_t == 2);
|
|
|
|
/* Test chilldkg_recovery_ack_sign. The host secret key and the aux
|
|
* randomness are secret; the acknowledgment signature is public. */
|
|
SECP256K1_CHECKMEM_UNDEFINE(hostseckeys[0], 32);
|
|
SECP256K1_CHECKMEM_UNDEFINE(aux_rands[0], 32);
|
|
ret = secp256k1_chilldkg_recovery_ack_sign(ctx, ack_sig, hostseckeys[0], hostpubkeys, 2, 2, recovery, sizeof(recovery), aux_rands[0]);
|
|
SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
|
|
CHECK(ret == 1);
|
|
SECP256K1_CHECKMEM_DEFINE(ack_sig, sizeof(ack_sig));
|
|
}
|
|
#endif
|
|
|
|
#ifdef ENABLE_MODULE_PREFRACTAL
|
|
{
|
|
/* A 2-of-2 nested group with one stock musig cosigner, taken as far as
|
|
* one signature share. Secret here is the threshold key, the secret
|
|
* shares derived from it, and the session randomness the nonces come
|
|
* from. Not secret: the identifiers, the public shares, the threshold
|
|
* public key, both aggregate nonces, the outer keyagg cache, b_frost,
|
|
* and the resulting partial signature. */
|
|
unsigned char thresh_seckey[32];
|
|
unsigned char secshares[2 * 32];
|
|
unsigned char session_secrand[2][32];
|
|
unsigned char cosigner_seckey[32];
|
|
secp256k1_pubkey thresh_pk, pubshares[2], cosigner_pk;
|
|
const secp256k1_pubkey *outer_pubkeys[2];
|
|
uint32_t pf_ids[2] = { 0, 1 };
|
|
secp256k1_frost_tweak_cache pf_cache;
|
|
secp256k1_frost_secnonce pf_secnonce[2];
|
|
secp256k1_frost_pubnonce pf_pubnonce[2];
|
|
const secp256k1_frost_pubnonce *pf_pubnonce_ptrs[2];
|
|
secp256k1_frost_aggnonce pf_aggnonce;
|
|
secp256k1_frost_partial_sig pf_partial_sig;
|
|
secp256k1_xonly_pubkey outer_xonly;
|
|
secp256k1_musig_keyagg_cache outer_cache;
|
|
secp256k1_musig_pubnonce group_pubnonce, cosigner_pubnonce;
|
|
const secp256k1_musig_pubnonce *just_cosigner[1];
|
|
secp256k1_musig_secnonce cosigner_secnonce;
|
|
secp256k1_musig_aggnonce cosigner_aggnonce;
|
|
unsigned char cosigner_secrand[32];
|
|
|
|
pf_pubnonce_ptrs[0] = &pf_pubnonce[0];
|
|
pf_pubnonce_ptrs[1] = &pf_pubnonce[1];
|
|
|
|
SECP256K1_CHECKMEM_DEFINE(key, 32);
|
|
memcpy(thresh_seckey, key, sizeof(thresh_seckey));
|
|
thresh_seckey[0] = thresh_seckey[0] + 4;
|
|
memcpy(cosigner_seckey, key, sizeof(cosigner_seckey));
|
|
cosigner_seckey[0] = cosigner_seckey[0] + 5;
|
|
memcpy(session_secrand[0], key, 32);
|
|
session_secrand[0][0] = session_secrand[0][0] + 6;
|
|
memcpy(session_secrand[1], key, 32);
|
|
session_secrand[1][0] = session_secrand[1][0] + 7;
|
|
memcpy(cosigner_secrand, key, 32);
|
|
cosigner_secrand[0] = cosigner_secrand[0] + 8;
|
|
SECP256K1_CHECKMEM_DEFINE(msg, sizeof(msg));
|
|
|
|
SECP256K1_CHECKMEM_UNDEFINE(thresh_seckey, sizeof(thresh_seckey));
|
|
ret = secp256k1_frost_trusted_dealer_keygen(ctx, secshares, &thresh_pk, pubshares, 2, 2, thresh_seckey);
|
|
SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
|
|
CHECK(ret == 1);
|
|
SECP256K1_CHECKMEM_DEFINE(&thresh_pk, sizeof(thresh_pk));
|
|
SECP256K1_CHECKMEM_DEFINE(pubshares, sizeof(pubshares));
|
|
CHECK(secp256k1_frost_tweak_cache_init(ctx, &pf_cache, &thresh_pk) == 1);
|
|
|
|
/* The outer aggregation and the cosigner's round one are entirely
|
|
* public as far as this module is concerned. */
|
|
SECP256K1_CHECKMEM_UNDEFINE(cosigner_seckey, sizeof(cosigner_seckey));
|
|
ret = secp256k1_ec_pubkey_create(ctx, &cosigner_pk, cosigner_seckey);
|
|
SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
|
|
CHECK(ret == 1);
|
|
SECP256K1_CHECKMEM_DEFINE(&cosigner_pk, sizeof(cosigner_pk));
|
|
outer_pubkeys[0] = &thresh_pk;
|
|
outer_pubkeys[1] = &cosigner_pk;
|
|
CHECK(secp256k1_musig_pubkey_agg(ctx, &outer_xonly, &outer_cache, outer_pubkeys, 2) == 1);
|
|
|
|
SECP256K1_CHECKMEM_UNDEFINE(cosigner_secrand, sizeof(cosigner_secrand));
|
|
ret = secp256k1_musig_nonce_gen(ctx, &cosigner_secnonce, &cosigner_pubnonce, cosigner_secrand, cosigner_seckey, &cosigner_pk, msg, &outer_cache, NULL);
|
|
SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
|
|
CHECK(ret == 1);
|
|
SECP256K1_CHECKMEM_DEFINE(&cosigner_pubnonce, sizeof(cosigner_pubnonce));
|
|
just_cosigner[0] = &cosigner_pubnonce;
|
|
CHECK(secp256k1_musig_nonce_agg(ctx, &cosigner_aggnonce, just_cosigner, 1) == 1);
|
|
|
|
/* Group round one. msg is NULL: the wire nonce is published before the
|
|
* message is known. */
|
|
for (i = 0; i < 2; i++) {
|
|
SECP256K1_CHECKMEM_UNDEFINE(session_secrand[i], 32);
|
|
SECP256K1_CHECKMEM_UNDEFINE(&secshares[32 * i], 32);
|
|
ret = secp256k1_frost_nonce_gen(ctx, &pf_secnonce[i], &pf_pubnonce[i], session_secrand[i], &secshares[32 * i], &pubshares[i], NULL, NULL, 0, NULL, 0);
|
|
SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
|
|
CHECK(ret == 1);
|
|
SECP256K1_CHECKMEM_DEFINE(&pf_pubnonce[i], sizeof(pf_pubnonce[i]));
|
|
}
|
|
/* Aggregation is over published nonces only, so it is public. */
|
|
CHECK(secp256k1_prefractal_nonce_agg(ctx, &group_pubnonce, &pf_aggnonce, pf_pubnonce_ptrs, pf_ids, 2, &thresh_pk) == 1);
|
|
|
|
/* The share and the secnonce are secret; the partial signature is the
|
|
* public output. */
|
|
ret = secp256k1_prefractal_sign(ctx, &pf_partial_sig, &pf_secnonce[0], &secshares[0], pf_ids[0], pf_ids, pubshares, 2, &pf_aggnonce, &thresh_pk, &pf_cache, &outer_cache, &cosigner_aggnonce, msg);
|
|
SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
|
|
CHECK(ret == 1);
|
|
SECP256K1_CHECKMEM_DEFINE(&pf_partial_sig, sizeof(pf_partial_sig));
|
|
}
|
|
#endif
|
|
|
|
#ifdef ENABLE_MODULE_FROST_ENROLLMENT
|
|
{
|
|
/* A 2-of-3 group enrolling a fourth participant, taken through all
|
|
* three rounds. Secret here is the threshold key, the secret shares
|
|
* derived from it, the session randomness the split comes from, and
|
|
* every delta and sigma value on the wire -- those are additive shares
|
|
* of real secret shares. Not secret: the identifiers, the public
|
|
* shares, the threshold public key, the parameters hashes, and the
|
|
* public share derived for the new participant. */
|
|
unsigned char thresh_seckey[32];
|
|
unsigned char secshares[3 * 32];
|
|
unsigned char session_secrand[2][32];
|
|
unsigned char shares[2][2 * 32];
|
|
unsigned char all_shares[2 * 32];
|
|
unsigned char received_hashes[2 * 32];
|
|
unsigned char params_hashes[2][32];
|
|
unsigned char sigmas[2 * 32];
|
|
unsigned char new_secshare[32];
|
|
unsigned char direct_hash[32];
|
|
secp256k1_pubkey thresh_pk, pubshares[3], new_pubshare;
|
|
uint32_t fe_ids[2] = { 0, 1 };
|
|
uint32_t mismatch_id;
|
|
int j;
|
|
|
|
SECP256K1_CHECKMEM_DEFINE(key, 32);
|
|
memcpy(thresh_seckey, key, sizeof(thresh_seckey));
|
|
thresh_seckey[0] = thresh_seckey[0] + 9;
|
|
memcpy(session_secrand[0], key, 32);
|
|
session_secrand[0][0] = session_secrand[0][0] + 10;
|
|
memcpy(session_secrand[1], key, 32);
|
|
session_secrand[1][0] = session_secrand[1][0] + 11;
|
|
|
|
SECP256K1_CHECKMEM_UNDEFINE(thresh_seckey, sizeof(thresh_seckey));
|
|
ret = secp256k1_frost_trusted_dealer_keygen(ctx, secshares, &thresh_pk, pubshares, 3, 2, thresh_seckey);
|
|
SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
|
|
CHECK(ret == 1);
|
|
SECP256K1_CHECKMEM_DEFINE(&thresh_pk, sizeof(thresh_pk));
|
|
SECP256K1_CHECKMEM_DEFINE(pubshares, sizeof(pubshares));
|
|
|
|
/* The parameters hash and the public share at the target identifier
|
|
* are functions of public data alone, so these calls take no secret
|
|
* input. Their outputs are not compared against anything here: this
|
|
* is a constant-time harness, and correctness is tests_impl.h's job. */
|
|
CHECK(secp256k1_frost_enrollment_params_hash(ctx, direct_hash, &thresh_pk, fe_ids, 2, 3, 3, 2) == 1);
|
|
CHECK(secp256k1_frost_enrollment_pubshare_derive(ctx, &new_pubshare, pubshares, fe_ids, 2, 3, 3, 2) == 1);
|
|
|
|
/* Round 1.1. The seed and the secret share are secret; the parameters
|
|
* hash is public and is marked so, while the delta values stay
|
|
* secret and are carried into round 1.2 undefined. */
|
|
for (i = 0; i < 2; i++) {
|
|
SECP256K1_CHECKMEM_UNDEFINE(session_secrand[i], 32);
|
|
SECP256K1_CHECKMEM_UNDEFINE(&secshares[32 * i], 32);
|
|
ret = secp256k1_frost_enrollment_shares_gen(ctx, shares[i], params_hashes[i], session_secrand[i], &secshares[32 * i], &thresh_pk, fe_ids, 2, fe_ids[i], 3, 3, 2);
|
|
SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
|
|
CHECK(ret == 1);
|
|
SECP256K1_CHECKMEM_DEFINE(params_hashes[i], 32);
|
|
}
|
|
|
|
/* Round 1.2, for both helpers. The delta values stay secret through
|
|
* the sum; only the return value is examined. */
|
|
for (j = 0; j < 2; j++) {
|
|
memset(received_hashes, 0, sizeof(received_hashes));
|
|
for (i = 0; i < 2; i++) {
|
|
memcpy(&all_shares[32 * i], &shares[i][32 * j], 32);
|
|
if (i != j) {
|
|
memcpy(&received_hashes[32 * i], params_hashes[i], 32);
|
|
}
|
|
}
|
|
ret = secp256k1_frost_enrollment_share_agg(ctx, &sigmas[32 * j], &mismatch_id, all_shares, received_hashes, &thresh_pk, fe_ids, 2, fe_ids[j], 3, 3, 2);
|
|
SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
|
|
CHECK(ret == 1);
|
|
}
|
|
|
|
/* Round 2, with both optional checks on. The resulting share is
|
|
* secret; the verification against the expected public share is the
|
|
* one place a secret-derived point is deliberately declassified. */
|
|
ret = secp256k1_frost_enrollment_secshare_gen(ctx, new_secshare, sigmas, &thresh_pk, fe_ids, 2, 3, 3, 2, params_hashes[0], &new_pubshare);
|
|
SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
|
|
CHECK(ret == 1);
|
|
}
|
|
#endif
|
|
|
|
#ifdef ENABLE_MODULE_ICEBERG
|
|
{
|
|
/* A 3-of-5 group, dealt from `key` and taken as far as one signature
|
|
* share. Secret here is the dealer's root seed and everything the
|
|
* module derives from it: the per-subset seeds, the key share, and the
|
|
* two nonce shares. Not secret: participant indices, the group and
|
|
* threshold, every Lagrange weight, the commitments, the session label,
|
|
* and both nonce coefficients. */
|
|
enum { ICEBERG_N = 5, ICEBERG_T = 3, ICEBERG_MU = 2 * ICEBERG_T - 1 };
|
|
secp256k1_iceberg_share shares[ICEBERG_N];
|
|
secp256k1_iceberg_share *share_ptr[ICEBERG_N];
|
|
secp256k1_iceberg_share_cache share_cache;
|
|
secp256k1_iceberg_pubshare pubshares[ICEBERG_N];
|
|
const secp256k1_iceberg_pubshare *pubshare_ptr[ICEBERG_N];
|
|
secp256k1_iceberg_pubnonce nonces[ICEBERG_N];
|
|
const secp256k1_iceberg_pubnonce *nonce_ptr[ICEBERG_N];
|
|
secp256k1_iceberg_aggnonce iceberg_aggnonce;
|
|
secp256k1_iceberg_partial_sig iceberg_psig;
|
|
secp256k1_musig_pubnonce group_pubnonce, cosigner_pubnonce;
|
|
const secp256k1_musig_pubnonce *cosigner_ptr[1];
|
|
secp256k1_musig_secnonce cosigner_secnonce;
|
|
secp256k1_musig_aggnonce cosigner_aggnonce;
|
|
secp256k1_musig_keyagg_cache iceberg_cache;
|
|
secp256k1_pubkey group_pk, cosigner_pk;
|
|
const secp256k1_pubkey *iceberg_pk_ptr[2];
|
|
unsigned char share_bytes[SECP256K1_ICEBERG_SHARE_MAX_LEN];
|
|
unsigned char sid[32], cosigner_secrand[32];
|
|
size_t share_len;
|
|
int party;
|
|
|
|
for (party = 0; party < ICEBERG_N; party++) {
|
|
share_ptr[party] = &shares[party];
|
|
pubshare_ptr[party] = &pubshares[party];
|
|
nonce_ptr[party] = &nonces[party];
|
|
}
|
|
SECP256K1_CHECKMEM_DEFINE(key, 32);
|
|
/* The cosigner needs randomness distinct from the dealer's root seed;
|
|
* any perturbation of `key` will do. */
|
|
memcpy(cosigner_secrand, key, sizeof(cosigner_secrand));
|
|
cosigner_secrand[0] = cosigner_secrand[0] + 3;
|
|
CHECK(secp256k1_keypair_create(ctx, &keypair, key));
|
|
CHECK(secp256k1_keypair_pub(ctx, &cosigner_pk, &keypair));
|
|
|
|
SECP256K1_CHECKMEM_UNDEFINE(key, 32);
|
|
ret = secp256k1_iceberg_shares_gen(ctx, share_ptr, ICEBERG_N, ICEBERG_T, key);
|
|
SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
|
|
CHECK(ret == 1);
|
|
|
|
/* Storing and restoring a share moves seed material through a buffer,
|
|
* which is where a length or an offset computed from it would show. */
|
|
share_len = sizeof(share_bytes);
|
|
ret = secp256k1_iceberg_share_serialize(ctx, share_bytes, &share_len, &shares[0]);
|
|
SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
|
|
CHECK(ret == 1);
|
|
ret = secp256k1_iceberg_share_parse(ctx, &shares[0], share_bytes, share_len);
|
|
SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
|
|
CHECK(ret == 1);
|
|
|
|
ret = secp256k1_iceberg_share_cache_create(ctx, &share_cache, &shares[0]);
|
|
SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
|
|
CHECK(ret == 1);
|
|
/* The header says the cache holds no secret, and this is the line that
|
|
* makes that a result rather than a claim. Every byte of it must be
|
|
* defined: the weights come from the group size, the threshold and the
|
|
* participant index, all of which are public, and none of them from a
|
|
* seed. Declassifying is not the same test: it would say the value may be
|
|
* published, where this says nothing secret reached it. */
|
|
SECP256K1_CHECKMEM_CHECK(&share_cache, sizeof(share_cache));
|
|
|
|
for (party = 0; party < ICEBERG_N; party++) {
|
|
ret = secp256k1_iceberg_pubshare_gen(ctx, &pubshares[party], &shares[party],
|
|
party == 0 ? &share_cache : NULL);
|
|
SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
|
|
CHECK(ret == 1);
|
|
}
|
|
|
|
/* A commitment is public, and so is everything built from one. */
|
|
SECP256K1_CHECKMEM_DEFINE(pubshares, sizeof(pubshares));
|
|
CHECK(secp256k1_iceberg_pubkey_agg(ctx, &group_pk, pubshare_ptr, ICEBERG_MU, ICEBERG_N, ICEBERG_T) == 1);
|
|
|
|
iceberg_pk_ptr[0] = &group_pk;
|
|
iceberg_pk_ptr[1] = &cosigner_pk;
|
|
CHECK(secp256k1_musig_pubkey_agg(ctx, NULL, &iceberg_cache, iceberg_pk_ptr, 2));
|
|
SECP256K1_CHECKMEM_DEFINE(msg, sizeof(msg));
|
|
SECP256K1_CHECKMEM_UNDEFINE(cosigner_secrand, sizeof(cosigner_secrand));
|
|
ret = secp256k1_musig_nonce_gen(ctx, &cosigner_secnonce, &cosigner_pubnonce,
|
|
cosigner_secrand, NULL, &cosigner_pk, msg,
|
|
&iceberg_cache, NULL);
|
|
SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
|
|
CHECK(ret == 1);
|
|
SECP256K1_CHECKMEM_DEFINE(&cosigner_pubnonce, sizeof(cosigner_pubnonce));
|
|
cosigner_ptr[0] = &cosigner_pubnonce;
|
|
CHECK(secp256k1_musig_nonce_agg(ctx, &cosigner_aggnonce, cosigner_ptr, 1));
|
|
memset(sid, 0x7e, sizeof(sid)); /* public: the label is the caller's to choose */
|
|
|
|
for (party = 0; party < ICEBERG_MU; party++) {
|
|
ret = secp256k1_iceberg_nonce_gen(ctx, &nonces[party], &shares[party],
|
|
party == 0 ? &share_cache : NULL, sid);
|
|
SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
|
|
CHECK(ret == 1);
|
|
}
|
|
SECP256K1_CHECKMEM_DEFINE(nonces, sizeof(nonces));
|
|
CHECK(secp256k1_iceberg_nonce_agg(ctx, &group_pubnonce, &iceberg_aggnonce,
|
|
nonce_ptr, ICEBERG_MU, ICEBERG_N, ICEBERG_T, &group_pk) == 1);
|
|
|
|
ret = secp256k1_iceberg_partial_sign(ctx, &iceberg_psig, &shares[0], &share_cache,
|
|
sid, nonce_ptr, ICEBERG_MU, &group_pk,
|
|
&iceberg_cache, msg, &cosigner_aggnonce);
|
|
SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
|
|
CHECK(ret == 1);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#if defined(__GNUC__)
|
|
# pragma GCC diagnostic pop
|
|
#endif
|