chilldkg: Phase 3 - public participant API and CertEq

Add the public participant-facing ChillDKG API to
include/secp256k1_chilldkg.h and the CertEq sub-protocol, completing
the participant side of the protocol (bip-frost-dkg v0.3.0-dev,
reference pinned at a91896883f85b159415ecf298d5e844879af112d).

New module files:
- certeq.h / certeq_impl.h: CertEq sub-protocol. Participants sign
  pad33("BIP DKG/certeq message") || u32be(i) || eq_input with plain
  BIP0340-tagged Schnorr signatures under their host key
  (certeq_participant_step); verification is per-index against the
  x-only hostpubkeys[i][1:33] exactly as the reference
  (certeq_verify). The coordinator side reuses certeq_verify in
  Phase 4.

Public API (all no-malloc, caller-allocated buffers, outputs zeroed on
failure, secret paths cleared):
- secp256k1_chilldkg_hostpubkey_gen: plain compressed host pubkey
  generation; rejects zero / >= group order seckeys.
- secp256k1_chilldkg_params_hash: validates session params (participant
  and threshold ranges, strictly compressed non-infinity pubkeys, no
  duplicates) and computes TH("BIP DKG/params_hash", u32be(t) ||
  hostpubkeys).
- Message-length helpers so callers can size buffers:
  participant_msg1_len (33t+32n+97), coordinator_msg1_len
  (162n+33(t-1)), participant_msg2_len (64), coordinator_msg2_len
  (64n), recovery_data_len (4+33t+162n).
- secp256k1_chilldkg_participant_step1: full EncPedPop step1 with
  seed=deckey=hostseckey; rejects zero randomness and hostseckeys not
  matching the claimed hostpubkey (input errors, not protocol faults).
- secp256k1_chilldkg_participant_step2: parses and verifies cmsg1 via
  the Phase 2 encpedpop/simplpedpop participant path, computes the
  tweaked secshare/pubshares/threshold pubkey, appends enc_secshares
  to eq_input (matching the reference for recovery consistency), and
  emits the 64-byte CertEq signature.
- secp256k1_chilldkg_participant_finalize: re-verifies all n CertEq
  signatures in the certificate, then outputs the 32-byte secshare,
  33-byte threshold pubkey, n pubshares and the self-delimiting
  recovery data (eq_input || cert).

Blame reporting without exceptions: public enum
secp256k1_chilldkg_fault (OK / FAULTY_COORDINATOR /
FAULTY_PARTICIPANT / FAULTY_PARTICIPANT_OR_COORDINATOR /
UNKNOWN_FAULTY_PARTICIPANT_OR_COORDINATOR / INVALID_INPUT) plus an out
fault_index, mapping the reference's exception taxonomy:
- hostseckey invalid/mismatch -> INVALID_INPUT (HostSeckeyError),
- cmsg1 scalar overflow/parse -> FAULTY_COORDINATOR (MsgParseError),
- pubnonce/commitment/PoP faults -> FAULTY_PARTICIPANT_OR_COORDINATOR(i),
- share-vs-pubshare mismatch -> UNKNOWN with fault_index = UINT32_MAX,
- certificate signature failure -> FAULTY_COORDINATOR (documented
  deviation: fault_index carries the failing signature index as
  diagnostic info; the reference discards it).
Enum-returning functions use a local CHILLDKG_ARG_CHECK that fires the
illegal-argument callback and returns INVALID_INPUT (ARG_CHECK would
return 0 = OK).

Opaque state objects with magic-validated save/load (frost idiom):
participant_state1 (4306 bytes, no secrets) and participant_state2
(21073 bytes, contains the secshare; documented keep-secret/no-copy).
Fixed-size at SECP256K1_CHILLDKG_MAX_PARTICIPANTS = 128.

Also fixes a noverify-build bug: state1_load ran point_load inside
VERIFY_CHECK, which compiles out in noverify builds and left the
commitment uninitialized; now called unconditionally.

tests_impl.h: participant_api_test with full-session reference vectors
(n=3, t=2; coordinator aggregation simulated through the internal
Phase 2 coordinator step and verified byte-identical to the
reference's coordinator_step1): msglen helpers, hostpubkey_gen and
params_hash vectors incl. duplicate/invalid/infinity rejection,
byte-exact pmsg1/cmsg1/CertEq sigs/secshare/thresh_pk/pubshares/
recovery, blame cases (tampered enc_secshare -> UNKNOWN, invalid
pubnonce -> FAULTY_PARTICIPANT_OR_COORDINATOR(1), overflowing
enc_secshare -> FAULTY_COORDINATOR, corrupted cert sig ->
FAULTY_COORDINATOR with fault_index and zeroed outputs), NULL-arg
misuse and bad-magic state rejection.

Verified: make check 3/3 (incl. noverify); CMake ctest 363/363;
make distdir includes all new files.
This commit is contained in:
Kgothatso Ngako
2026-08-31 05:22:53 +02:00
parent 580e4f6502
commit 2a0e14d076
12 changed files with 1135 additions and 49 deletions

View File

@@ -8,4 +8,6 @@ noinst_HEADERS += src/modules/chilldkg/simplpedpop.h
noinst_HEADERS += src/modules/chilldkg/simplpedpop_impl.h
noinst_HEADERS += src/modules/chilldkg/encpedpop.h
noinst_HEADERS += src/modules/chilldkg/encpedpop_impl.h
noinst_HEADERS += src/modules/chilldkg/certeq.h
noinst_HEADERS += src/modules/chilldkg/certeq_impl.h
noinst_HEADERS += src/modules/chilldkg/tests_impl.h

View File

@@ -0,0 +1,36 @@
/***********************************************************************
* Distributed under the MIT software license, see the accompanying *
* file COPYING or https://www.opensource.org/licenses/mit-license.php.*
***********************************************************************/
#ifndef SECP256K1_MODULE_CHILLDKG_CERTEQ_H
#define SECP256K1_MODULE_CHILLDKG_CERTEQ_H
#include "../../../include/secp256k1.h"
#include "util.h"
/* This file contains the internal CertEq equality-check sub-protocol of the
* ChillDKG module, mirroring the certeq_* functions of
* chilldkg_ref/chilldkg.py of the bip-frost-dkg reference implementation.
*
* The CertEq message of participant i is
* pad33("BIP DKG/certeq message") || u32be(i) || eq_input
* signed with plain BIP 340 (tag prefix "BIP0340") under the participant's
* host key. */
/* Produce the CertEq signature of the given participant over eq_input
* (certeq_participant_step). Returns 1 on success and 0 if hostseckey32 is
* invalid. On failure, sig64 is set to zero. */
static int secp256k1_chilldkg_certeq_participant_step(const secp256k1_context *ctx, unsigned char *sig64, const unsigned char *hostseckey32, uint32_t participant_id, const unsigned char *eq_input, size_t eq_input_len, const unsigned char *aux_rand32);
/* Verify all n CertEq signatures of a certificate (64 bytes per participant)
* against the host public keys (certeq_verify). The signature of participant
* i is verified against the x-only encoding of hostpubkeys33[i]; the message
* commits to the full 33-byte key via eq_input, so dropping the sign byte is
* okay (see the reference). Returns 1 if all signatures are valid and 0
* otherwise; in the latter case *fault_index receives the index of the first
* invalid signature. */
static int secp256k1_chilldkg_certeq_verify(const secp256k1_context *ctx, const unsigned char *hostpubkeys33, size_t n, const unsigned char *eq_input, size_t eq_input_len, const unsigned char *cert, uint32_t *fault_index);
#endif

View File

@@ -0,0 +1,58 @@
/***********************************************************************
* Distributed under the MIT software license, see the accompanying *
* file COPYING or https://www.opensource.org/licenses/mit-license.php.*
***********************************************************************/
#ifndef SECP256K1_MODULE_CHILLDKG_CERTEQ_IMPL_H
#define SECP256K1_MODULE_CHILLDKG_CERTEQ_IMPL_H
#include <string.h>
#include "../../../include/secp256k1.h"
#include "util.h"
#include "certeq.h"
#include "../../util.h"
/* The maximum length of an eq_input (and thus of the variable-length part of
* a CertEq message): 4 + 33*t + 33*n + 33*n + 32*n with t, n <=
* SECP256K1_CHILLDKG_MAX_PARTICIPANTS. */
#define SECP256K1_CHILLDKG_MAX_EQ_INPUT_LEN (4 + 131 * SECP256K1_CHILLDKG_MAX_PARTICIPANTS)
/* certeq_message: pad33("BIP DKG/certeq message") || u32be(participant_id) ||
* x. msg must hold 37 + x_len bytes. */
static void secp256k1_chilldkg_certeq_message(unsigned char *msg, const unsigned char *x, size_t x_len, uint32_t participant_id) {
secp256k1_chilldkg_pad33(msg, "BIP DKG/certeq message");
secp256k1_write_be32(msg + 33, participant_id);
memcpy(msg + 37, x, x_len);
}
static int secp256k1_chilldkg_certeq_participant_step(const secp256k1_context *ctx, unsigned char *sig64, const unsigned char *hostseckey32, uint32_t participant_id, const unsigned char *eq_input, size_t eq_input_len, const unsigned char *aux_rand32) {
unsigned char msg[37 + SECP256K1_CHILLDKG_MAX_EQ_INPUT_LEN];
int ret;
VERIFY_CHECK(eq_input_len <= SECP256K1_CHILLDKG_MAX_EQ_INPUT_LEN);
secp256k1_chilldkg_certeq_message(msg, eq_input, eq_input_len, participant_id);
ret = secp256k1_chilldkg_schnorrsig_sign(ctx, sig64, msg, 37 + eq_input_len, hostseckey32, aux_rand32, "BIP0340");
secp256k1_memclear_explicit(msg, sizeof(msg));
return ret;
}
static int secp256k1_chilldkg_certeq_verify(const secp256k1_context *ctx, const unsigned char *hostpubkeys33, size_t n, const unsigned char *eq_input, size_t eq_input_len, const unsigned char *cert, uint32_t *fault_index) {
unsigned char msg[37 + SECP256K1_CHILLDKG_MAX_EQ_INPUT_LEN];
size_t i;
VERIFY_CHECK(eq_input_len <= SECP256K1_CHILLDKG_MAX_EQ_INPUT_LEN);
for (i = 0; i < n; i++) {
secp256k1_chilldkg_certeq_message(msg, eq_input, eq_input_len, (uint32_t)i);
/* Dropping the sign byte from hostpubkeys33[i] is okay because the
* message commits to the full host public key. */
if (!secp256k1_chilldkg_schnorrsig_verify(ctx, cert + 64 * i, msg, 37 + eq_input_len, hostpubkeys33 + 33 * i + 1, "BIP0340")) {
*fault_index = (uint32_t)i;
return 0;
}
}
return 1;
}
#endif

View File

@@ -61,7 +61,7 @@ static int secp256k1_chilldkg_encpedpop_encrypt_multi(const secp256k1_context *c
* pubnonce (receiver-side ordering in the pad hash). deckey32 is our host
* secret key and enckey33 our host public key.
*
* Returns SECP256K1_CHILLDKG_SUCCESS on success,
* Returns SECP256K1_CHILLDKG_OK on success,
* SECP256K1_CHILLDKG_FAULTY_PARTICIPANT_OR_COORDINATOR (with *fault_index) if
* some sender's pubnonce is invalid, and SECP256K1_CHILLDKG_INVALID_INPUT if
* deckey32 is invalid. On failure, all pads are cleared. */
@@ -101,7 +101,7 @@ static int secp256k1_chilldkg_encpedpop_participant_step1(const secp256k1_contex
* output and the EncPedPop eq_input (4 + 33*t + 33*n + 33*n bytes).
*
* cmsg must be exactly 97*n + 33*(t-1) + 33*n bytes. Returns
* SECP256K1_CHILLDKG_SUCCESS on success; otherwise a fault code (with
* SECP256K1_CHILLDKG_OK on success; otherwise a fault code (with
* *fault_index set when applicable): FAULTY_COORDINATOR if cmsg is malformed
* or the pubnonce echo is wrong, FAULTY_PARTICIPANT_OR_COORDINATOR(i) if
* sender i's pubnonce is invalid or their pop/commitment is bad, and
@@ -117,7 +117,7 @@ static secp256k1_chilldkg_fault secp256k1_chilldkg_encpedpop_participant_step2(c
* the n summed encrypted secshares. (In pure EncPedPop the coordinator would
* send enc_secshares[i] to participant i only; ChillDKG broadcasts them.)
*
* Returns SECP256K1_CHILLDKG_SUCCESS on success and
* Returns SECP256K1_CHILLDKG_OK on success and
* SECP256K1_CHILLDKG_FAULTY_PARTICIPANT (with *fault_index) if a participant
* message is malformed (invalid commitment, or an encrypted share that
* overflows the group order). */

View File

@@ -125,7 +125,7 @@ static secp256k1_chilldkg_fault secp256k1_chilldkg_encpedpop_decaps_multi(const
}
}
secp256k1_scalar_clear(&deckey);
return SECP256K1_CHILLDKG_SUCCESS;
return SECP256K1_CHILLDKG_OK;
}
static secp256k1_chilldkg_fault secp256k1_chilldkg_encpedpop_decrypt_sum(const secp256k1_context *ctx, secp256k1_scalar *out, uint32_t *fault_index, const unsigned char *deckey32, const unsigned char *enckey33, const unsigned char *pubnonces33, const unsigned char *enc_context, size_t enc_context_len, uint32_t participant_id, const secp256k1_scalar *sum_ciphertexts, size_t n) {
@@ -135,7 +135,7 @@ static secp256k1_chilldkg_fault secp256k1_chilldkg_encpedpop_decrypt_sum(const s
VERIFY_CHECK(n <= SECP256K1_CHILLDKG_MAX_PARTICIPANTS);
fault = secp256k1_chilldkg_encpedpop_decaps_multi(ctx, pads, fault_index, deckey32, enckey33, pubnonces33, enc_context, enc_context_len, participant_id, n);
if (fault == SECP256K1_CHILLDKG_SUCCESS) {
if (fault == SECP256K1_CHILLDKG_OK) {
secp256k1_scalar_set_int(out, 0);
for (i = 0; i < n; i++) {
secp256k1_scalar_add(out, out, &pads[i]);
@@ -277,7 +277,7 @@ static secp256k1_chilldkg_fault secp256k1_chilldkg_encpedpop_participant_step2(c
/* secshare = enc_secshare - sum of the pads (decrypt_sum). */
secp256k1_chilldkg_encpedpop_serialize_enc_context(enc_context, t, &state->enckeys33[0][0], n);
fault = secp256k1_chilldkg_encpedpop_decaps_multi(ctx, pads, fault_index, deckey32, state->enckeys33[participant_id], pubnonces, enc_context, 4 + 33 * (size_t)n, participant_id, n);
if (fault != SECP256K1_CHILLDKG_SUCCESS) {
if (fault != SECP256K1_CHILLDKG_OK) {
for (i = 0; i < n; i++) {
secp256k1_scalar_clear(&pads[i]);
}
@@ -297,14 +297,14 @@ static secp256k1_chilldkg_fault secp256k1_chilldkg_encpedpop_participant_step2(c
for (i = 0; i < n; i++) {
secp256k1_scalar_clear(&pads[i]);
}
if (fault != SECP256K1_CHILLDKG_SUCCESS) {
if (fault != SECP256K1_CHILLDKG_OK) {
return fault;
}
/* eq_input += enckeys || pubnonces */
memcpy(eq_input + 4 + 33 * t, &state->enckeys33[0][0], 33 * (size_t)n);
memcpy(eq_input + 4 + 33 * t + 33 * (size_t)n, pubnonces, 33 * (size_t)n);
return SECP256K1_CHILLDKG_SUCCESS;
return SECP256K1_CHILLDKG_OK;
}
static secp256k1_chilldkg_fault secp256k1_chilldkg_encpedpop_coordinator_step(const secp256k1_context *ctx, unsigned char *cmsg, secp256k1_chilldkg_simplpedpop_dkg_output *dkg_output, unsigned char *eq_input, secp256k1_scalar *enc_secshares, uint32_t *fault_index, const unsigned char *const *pmsgs, uint32_t t, const unsigned char *enckeys33, size_t n) {
@@ -365,14 +365,14 @@ static secp256k1_chilldkg_fault secp256k1_chilldkg_encpedpop_coordinator_step(co
secp256k1_scalar_clear(&share);
fault = secp256k1_chilldkg_simplpedpop_coordinator_step(ctx, cmsg, dkg_output, eq_input, fault_index, pmsgs, t, (uint32_t)n);
if (fault != SECP256K1_CHILLDKG_SUCCESS) {
if (fault != SECP256K1_CHILLDKG_OK) {
return fault;
}
/* eq_input += enckeys || pubnonces */
memcpy(eq_input + 4 + 33 * t, enckeys33, 33 * n);
memcpy(eq_input + 4 + 33 * t + 33 * n, cmsg + simpl_cmsg_len, 33 * n);
return SECP256K1_CHILLDKG_SUCCESS;
return SECP256K1_CHILLDKG_OK;
}
#endif

View File

@@ -6,11 +6,440 @@
#ifndef SECP256K1_MODULE_CHILLDKG_MAIN_H
#define SECP256K1_MODULE_CHILLDKG_MAIN_H
#include <string.h>
#include "../../../include/secp256k1_chilldkg.h"
#include "util_impl.h"
#include "vss_impl.h"
#include "simplpedpop_impl.h"
#include "encpedpop_impl.h"
#include "certeq_impl.h"
/* ARG_CHECK returns 0, which would read as SECP256K1_CHILLDKG_OK in the
* enum-returning public functions of this module. Use this variant there. */
#define SECP256K1_CHILLDKG_ARG_CHECK(cond) do { \
if (EXPECT(!(cond), 0)) { \
secp256k1_callback_call(&ctx->illegal_callback, #cond); \
return SECP256K1_CHILLDKG_INVALID_INPUT; \
} \
} while(0)
/* Validate the session parameters (params_validate in chilldkg_ref): it must
* hold that 1 <= t <= n <= SECP256K1_CHILLDKG_MAX_PARTICIPANTS (the reference
* allows n up to 2^32 - 1), all host public keys must be valid compressed
* public keys (the infinity encoding is not a valid public key), and there
* must be no duplicates. */
static int secp256k1_chilldkg_params_validate(const unsigned char *hostpubkeys33, size_t n, uint32_t t) {
size_t i, j;
if (t < 1 || n < 1 || t > n || n > SECP256K1_CHILLDKG_MAX_PARTICIPANTS) {
return 0;
}
for (i = 0; i < n; i++) {
secp256k1_ge hostpubkey;
if (!secp256k1_chilldkg_point_load(&hostpubkey, hostpubkeys33 + 33 * i)
|| secp256k1_ge_is_infinity(&hostpubkey)) {
/* InvalidHostPubkeyError(i) in the reference. */
return 0;
}
}
for (i = 0; i < n; i++) {
for (j = i + 1; j < n; j++) {
if (secp256k1_memcmp_var(hostpubkeys33 + 33 * i, hostpubkeys33 + 33 * j, 33) == 0) {
/* DuplicateHostPubkeyError in the reference. */
return 0;
}
}
}
return 1;
}
static const unsigned char secp256k1_chilldkg_participant_state1_magic[4] = { 0x3f, 0x2c, 0x9e, 0x51 };
static const unsigned char secp256k1_chilldkg_participant_state2_magic[4] = { 0x7a, 0xd1, 0x44, 0x0b };
/* A state1 object consists of
* - 4 byte magic set during initialization to allow detecting an
* uninitialized object
* - 4 byte threshold t, 4 byte participant count n, 4 byte participant_id
* (big-endian)
* - 33 byte commitment to the secret (compressed encoding with infinity)
* - 33 byte pubnonce
* - 33*SECP256K1_CHILLDKG_MAX_PARTICIPANTS byte host public keys (only the
* first n entries are meaningful)
* The state contains no secret key material. */
static void secp256k1_chilldkg_participant_state1_save(secp256k1_chilldkg_participant_state1 *state, const secp256k1_chilldkg_encpedpop_participant_state *state_i) {
unsigned char *ptr = state->data;
memcpy(ptr, secp256k1_chilldkg_participant_state1_magic, 4);
ptr += 4;
secp256k1_write_be32(ptr, state_i->simpl_state.t);
ptr += 4;
secp256k1_write_be32(ptr, state_i->simpl_state.n);
ptr += 4;
secp256k1_write_be32(ptr, state_i->simpl_state.participant_id);
ptr += 4;
secp256k1_chilldkg_point_save(ptr, &state_i->simpl_state.com_to_secret);
ptr += 33;
memcpy(ptr, state_i->pubnonce33, 33);
ptr += 33;
memcpy(ptr, &state_i->enckeys33[0][0], 33 * SECP256K1_CHILLDKG_MAX_PARTICIPANTS);
VERIFY_CHECK(ptr + 33 * SECP256K1_CHILLDKG_MAX_PARTICIPANTS == state->data + sizeof(state->data));
}
static int secp256k1_chilldkg_participant_state1_load(const secp256k1_context *ctx, secp256k1_chilldkg_encpedpop_participant_state *state_i, const secp256k1_chilldkg_participant_state1 *state) {
const unsigned char *ptr = state->data;
ARG_CHECK(secp256k1_memcmp_var(ptr, secp256k1_chilldkg_participant_state1_magic, 4) == 0);
ptr += 4;
state_i->simpl_state.t = secp256k1_read_be32(ptr);
ptr += 4;
state_i->simpl_state.n = secp256k1_read_be32(ptr);
ptr += 4;
state_i->simpl_state.participant_id = secp256k1_read_be32(ptr);
ptr += 4;
/* The remaining contents were written by state1_save. */
VERIFY_CHECK(state_i->simpl_state.t >= 1
&& state_i->simpl_state.t <= state_i->simpl_state.n
&& state_i->simpl_state.n <= SECP256K1_CHILLDKG_MAX_PARTICIPANTS
&& state_i->simpl_state.participant_id < state_i->simpl_state.n);
/* This load always succeeds; call it unconditionally (it must not sit
* inside VERIFY_CHECK, which is compiled out in noverify builds). */
if (!secp256k1_chilldkg_point_load(&state_i->simpl_state.com_to_secret, ptr)) {
VERIFY_CHECK(0);
secp256k1_ge_set_infinity(&state_i->simpl_state.com_to_secret);
}
ptr += 33;
memcpy(state_i->pubnonce33, ptr, 33);
ptr += 33;
memcpy(&state_i->enckeys33[0][0], ptr, 33 * SECP256K1_CHILLDKG_MAX_PARTICIPANTS);
return 1;
}
typedef struct {
uint32_t t;
uint32_t n;
/* Only the first 4 + 33*t + 98*n bytes are meaningful. */
unsigned char eq_input[SECP256K1_CHILLDKG_MAX_EQ_INPUT_LEN];
secp256k1_chilldkg_simplpedpop_dkg_output dkg_output;
} secp256k1_chilldkg_participant_state2_internal;
/* A state2 object consists of
* - 4 byte magic
* - 4 byte threshold t, 4 byte participant count n (big-endian)
* - eq_input (fixed-capacity buffer)
* - 32 byte (tweaked) secret share
* - 33 byte threshold public key
* - 33*SECP256K1_CHILLDKG_MAX_PARTICIPANTS byte public shares (only the first
* n entries are meaningful)
* The state contains the secret share and must be kept secret. */
static void secp256k1_chilldkg_participant_state2_save(secp256k1_chilldkg_participant_state2 *state, const secp256k1_chilldkg_participant_state2_internal *state_i) {
unsigned char *ptr = state->data;
memcpy(ptr, secp256k1_chilldkg_participant_state2_magic, 4);
ptr += 4;
secp256k1_write_be32(ptr, state_i->t);
ptr += 4;
secp256k1_write_be32(ptr, state_i->n);
ptr += 4;
memcpy(ptr, state_i->eq_input, sizeof(state_i->eq_input));
ptr += sizeof(state_i->eq_input);
memcpy(ptr, state_i->dkg_output.secshare32, 32);
ptr += 32;
memcpy(ptr, state_i->dkg_output.thresh_pk33, 33);
ptr += 33;
memcpy(ptr, &state_i->dkg_output.pubshares33[0][0], 33 * SECP256K1_CHILLDKG_MAX_PARTICIPANTS);
VERIFY_CHECK(ptr + 33 * SECP256K1_CHILLDKG_MAX_PARTICIPANTS == state->data + sizeof(state->data));
}
static int secp256k1_chilldkg_participant_state2_load(const secp256k1_context *ctx, secp256k1_chilldkg_participant_state2_internal *state_i, const secp256k1_chilldkg_participant_state2 *state) {
const unsigned char *ptr = state->data;
ARG_CHECK(secp256k1_memcmp_var(ptr, secp256k1_chilldkg_participant_state2_magic, 4) == 0);
ptr += 4;
state_i->t = secp256k1_read_be32(ptr);
ptr += 4;
state_i->n = secp256k1_read_be32(ptr);
ptr += 4;
/* The remaining contents were written by state2_save. */
VERIFY_CHECK(state_i->t >= 1
&& state_i->t <= state_i->n
&& state_i->n <= SECP256K1_CHILLDKG_MAX_PARTICIPANTS);
memcpy(state_i->eq_input, ptr, sizeof(state_i->eq_input));
ptr += sizeof(state_i->eq_input);
memcpy(state_i->dkg_output.secshare32, ptr, 32);
ptr += 32;
memcpy(state_i->dkg_output.thresh_pk33, ptr, 33);
ptr += 33;
memcpy(&state_i->dkg_output.pubshares33[0][0], ptr, 33 * SECP256K1_CHILLDKG_MAX_PARTICIPANTS);
return 1;
}
int secp256k1_chilldkg_hostpubkey_gen(const secp256k1_context *ctx, unsigned char *hostpubkey33, const unsigned char *hostseckey32) {
secp256k1_scalar seckey;
secp256k1_gej pubkeyj;
secp256k1_ge pubkey;
int overflow;
int ret;
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
ARG_CHECK(hostpubkey33 != NULL);
ARG_CHECK(hostseckey32 != NULL);
memset(hostpubkey33, 0, 33);
secp256k1_scalar_set_b32(&seckey, hostseckey32, &overflow);
ret = !overflow & !secp256k1_scalar_is_zero(&seckey);
/* Branching on the validity of the host secret key is fine: whether the
* caller's key is in range 1..n-1 is not secret. */
secp256k1_declassify(ctx, &ret, sizeof(ret));
if (ret) {
/* pubkey_gen_plain in the reference (IndividualPubkey of BIP 327). */
secp256k1_ecmult_gen_gej(&ctx->ecmult_gen_ctx, &pubkeyj, &seckey);
secp256k1_ge_set_gej(&pubkey, &pubkeyj);
secp256k1_chilldkg_point_save(hostpubkey33, &pubkey);
secp256k1_gej_clear(&pubkeyj);
}
secp256k1_scalar_clear(&seckey);
return ret;
}
int secp256k1_chilldkg_params_hash(const secp256k1_context *ctx, unsigned char *hash32, const unsigned char *hostpubkeys33, size_t n_participants, uint32_t threshold) {
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(hash32 != NULL);
ARG_CHECK(hostpubkeys33 != NULL);
memset(hash32, 0, 32);
if (!secp256k1_chilldkg_params_validate(hostpubkeys33, n_participants, threshold)) {
return 0;
}
secp256k1_chilldkg_params_hash_internal(secp256k1_get_hash_context(ctx), hash32, hostpubkeys33, n_participants, threshold);
return 1;
}
size_t secp256k1_chilldkg_participant_msg1_len(size_t n_participants, uint32_t threshold) {
if (threshold < 1 || n_participants < 1 || threshold > n_participants || n_participants > SECP256K1_CHILLDKG_MAX_PARTICIPANTS) {
return 0;
}
return 33 * (size_t)threshold + 32 * n_participants + 97;
}
size_t secp256k1_chilldkg_coordinator_msg1_len(size_t n_participants, uint32_t threshold) {
if (threshold < 1 || n_participants < 1 || threshold > n_participants || n_participants > SECP256K1_CHILLDKG_MAX_PARTICIPANTS) {
return 0;
}
return 162 * n_participants + 33 * (size_t)(threshold - 1);
}
size_t secp256k1_chilldkg_participant_msg2_len(void) {
return 64;
}
size_t secp256k1_chilldkg_coordinator_msg2_len(size_t n_participants) {
if (n_participants < 1 || n_participants > SECP256K1_CHILLDKG_MAX_PARTICIPANTS) {
return 0;
}
return 64 * n_participants;
}
size_t secp256k1_chilldkg_recovery_data_len(size_t n_participants, uint32_t threshold) {
if (threshold < 1 || n_participants < 1 || threshold > n_participants || n_participants > SECP256K1_CHILLDKG_MAX_PARTICIPANTS) {
return 0;
}
return 4 + 33 * (size_t)threshold + 162 * n_participants;
}
int secp256k1_chilldkg_participant_step1(const secp256k1_context *ctx, secp256k1_chilldkg_participant_state1 *state1, unsigned char *pmsg1, const unsigned char *hostseckey32, const unsigned char *hostpubkeys33, size_t n_participants, uint32_t threshold, const unsigned char *random32) {
secp256k1_chilldkg_encpedpop_participant_state enc_state;
unsigned char hostpubkey33[33];
uint32_t participant_id = 0;
size_t pmsg1_len;
size_t i;
int found = 0;
int ret = 0;
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
ARG_CHECK(state1 != NULL);
ARG_CHECK(pmsg1 != NULL);
ARG_CHECK(hostseckey32 != NULL);
ARG_CHECK(hostpubkeys33 != NULL);
ARG_CHECK(random32 != NULL);
memset(state1->data, 0, sizeof(state1->data));
pmsg1_len = secp256k1_chilldkg_participant_msg1_len(n_participants, threshold);
/* params_validate, then hostpubkey_gen (HostSeckeyError if invalid). */
if (secp256k1_chilldkg_params_validate(hostpubkeys33, n_participants, threshold)
&& secp256k1_chilldkg_hostpubkey_gen(ctx, hostpubkey33, hostseckey32)) {
/* hostpubkeys.index(hostpubkey); the params validation rules out
* duplicates, so there is at most one match. */
for (i = 0; i < n_participants; i++) {
if (secp256k1_memcmp_var(hostpubkeys33 + 33 * i, hostpubkey33, 33) == 0) {
participant_id = (uint32_t)i;
found = 1;
break;
}
}
/* HostSeckeyError if there is no match; RandomnessError if the
* randomness is all zeroes (guards against a malfunctioning random
* number generator). */
if (found && !secp256k1_is_zero_array(random32, 32)) {
/* In EncPedPop, both the seed and the deckey are the host secret
* key, and the enckeys are the host public keys (see
* chilldkg_ref/chilldkg.py participant_step1). */
ret = secp256k1_chilldkg_encpedpop_participant_step1(ctx, &enc_state, pmsg1, hostseckey32, hostseckey32, hostpubkeys33, threshold, participant_id, random32, n_participants);
}
}
if (ret) {
secp256k1_chilldkg_participant_state1_save(state1, &enc_state);
} else {
memset(state1->data, 0, sizeof(state1->data));
if (pmsg1_len > 0) {
memset(pmsg1, 0, pmsg1_len);
}
}
return ret;
}
secp256k1_chilldkg_fault secp256k1_chilldkg_participant_step2(const secp256k1_context *ctx, secp256k1_chilldkg_participant_state2 *state2, unsigned char *sig64, uint32_t *fault_index, const secp256k1_chilldkg_participant_state1 *state1, const unsigned char *hostseckey32, const unsigned char *cmsg1, const unsigned char *aux_rand32) {
secp256k1_chilldkg_encpedpop_participant_state enc_state;
secp256k1_chilldkg_participant_state2_internal state2i;
unsigned char hostpubkey33[33];
secp256k1_scalar enc_secshares[SECP256K1_CHILLDKG_MAX_PARTICIPANTS];
secp256k1_chilldkg_fault fault = SECP256K1_CHILLDKG_INVALID_INPUT;
size_t n, t, enc_cmsg_len, eq_input_len;
uint32_t participant_id;
size_t i;
int overflow;
VERIFY_CHECK(ctx != NULL);
SECP256K1_CHILLDKG_ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
SECP256K1_CHILLDKG_ARG_CHECK(state2 != NULL);
SECP256K1_CHILLDKG_ARG_CHECK(sig64 != NULL);
SECP256K1_CHILLDKG_ARG_CHECK(fault_index != NULL);
SECP256K1_CHILLDKG_ARG_CHECK(state1 != NULL);
SECP256K1_CHILLDKG_ARG_CHECK(hostseckey32 != NULL);
SECP256K1_CHILLDKG_ARG_CHECK(cmsg1 != NULL);
SECP256K1_CHILLDKG_ARG_CHECK(aux_rand32 != NULL);
memset(&state2i, 0, sizeof(state2i));
memset(state2->data, 0, sizeof(state2->data));
memset(sig64, 0, 64);
*fault_index = UINT32_MAX;
if (!secp256k1_chilldkg_participant_state1_load(ctx, &enc_state, state1)) {
return SECP256K1_CHILLDKG_INVALID_INPUT;
}
t = enc_state.simpl_state.t;
n = enc_state.simpl_state.n;
participant_id = enc_state.simpl_state.participant_id;
/* HostSeckeyError if the host secret key is invalid or does not match the
* one used in participant_step1. */
if (!secp256k1_chilldkg_hostpubkey_gen(ctx, hostpubkey33, hostseckey32)
|| secp256k1_memcmp_var(hostpubkey33, enc_state.enckeys33[participant_id], 33) != 0) {
return SECP256K1_CHILLDKG_INVALID_INPUT;
}
/* CoordinatorMsg1.from_bytes: cmsg1 = enc_cmsg || enc_secshares, where
* enc_cmsg has 97*n + 33*(t-1) + 33*n bytes. The encrypted shares are
* parsed checked; an overflow blames the coordinator (MsgParseError ->
* FaultyCoordinatorError in the reference). */
enc_cmsg_len = 97 * n + 33 * (t - 1) + 33 * n;
for (i = 0; i < n; i++) {
secp256k1_scalar_set_b32(&enc_secshares[i], cmsg1 + enc_cmsg_len + 32 * i, &overflow);
if (overflow) {
fault = SECP256K1_CHILLDKG_FAULTY_COORDINATOR;
goto cleanup;
}
}
eq_input_len = 4 + 33 * t + 66 * n;
fault = secp256k1_chilldkg_encpedpop_participant_step2(ctx, &state2i.dkg_output, state2i.eq_input, fault_index, &enc_state, hostseckey32, cmsg1, enc_cmsg_len, &enc_secshares[participant_id]);
if (fault != SECP256K1_CHILLDKG_OK) {
goto cleanup;
}
/* Include the enc_secshares in eq_input to ensure that participants agree
* on all shares, which in turn ensures that they have the right recovery
* data. */
for (i = 0; i < n; i++) {
secp256k1_scalar_get_b32(state2i.eq_input + eq_input_len + 32 * i, &enc_secshares[i]);
}
eq_input_len += 32 * n;
state2i.t = (uint32_t)t;
state2i.n = (uint32_t)n;
/* pmsg2 = certeq_participant_step(hostseckey, participant_id, eq_input, aux_rand) */
if (!secp256k1_chilldkg_certeq_participant_step(ctx, sig64, hostseckey32, participant_id, state2i.eq_input, eq_input_len, aux_rand32)) {
/* Unreachable: the host secret key was validated above. */
fault = SECP256K1_CHILLDKG_INVALID_INPUT;
memset(sig64, 0, 64);
goto cleanup;
}
secp256k1_chilldkg_participant_state2_save(state2, &state2i);
cleanup:
for (i = 0; i < n; i++) {
secp256k1_scalar_clear(&enc_secshares[i]);
}
/* state2i contains the secret share. */
secp256k1_memclear_explicit(&state2i, sizeof(state2i));
return fault;
}
secp256k1_chilldkg_fault secp256k1_chilldkg_participant_finalize(const secp256k1_context *ctx, unsigned char *secshare32, unsigned char *thresh_pk33, unsigned char *pubshares33, unsigned char *recovery, uint32_t *fault_index, const secp256k1_chilldkg_participant_state2 *state2, const unsigned char *cmsg2) {
secp256k1_chilldkg_participant_state2_internal state2i;
size_t n, t, eq_input_len;
VERIFY_CHECK(ctx != NULL);
SECP256K1_CHILLDKG_ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
SECP256K1_CHILLDKG_ARG_CHECK(secshare32 != NULL);
SECP256K1_CHILLDKG_ARG_CHECK(thresh_pk33 != NULL);
SECP256K1_CHILLDKG_ARG_CHECK(pubshares33 != NULL);
SECP256K1_CHILLDKG_ARG_CHECK(recovery != NULL);
SECP256K1_CHILLDKG_ARG_CHECK(fault_index != NULL);
SECP256K1_CHILLDKG_ARG_CHECK(state2 != NULL);
SECP256K1_CHILLDKG_ARG_CHECK(cmsg2 != NULL);
memset(secshare32, 0, 32);
memset(thresh_pk33, 0, 33);
*fault_index = UINT32_MAX;
/* On a state load failure (uninitialized state), only secshare32 and
* thresh_pk33 are zeroed: the sizes of pubshares33 and recovery depend on
* the state contents. */
if (!secp256k1_chilldkg_participant_state2_load(ctx, &state2i, state2)) {
return SECP256K1_CHILLDKG_INVALID_INPUT;
}
t = state2i.t;
n = state2i.n;
eq_input_len = 4 + 33 * t + 98 * n;
memset(pubshares33, 0, 33 * n);
memset(recovery, 0, 4 + 33 * t + 162 * n);
/* certeq_verify: the host public keys are part of eq_input (at offset
* 4 + 33*t). An invalid signature in the certificate blames the
* coordinator; fault_index receives the index of the first invalid
* signature as diagnostic information. */
if (!secp256k1_chilldkg_certeq_verify(ctx, state2i.eq_input + 4 + 33 * t, n, state2i.eq_input, eq_input_len, cmsg2, fault_index)) {
secp256k1_memclear_explicit(&state2i, sizeof(state2i));
return SECP256K1_CHILLDKG_FAULTY_COORDINATOR;
}
memcpy(secshare32, state2i.dkg_output.secshare32, 32);
memcpy(thresh_pk33, state2i.dkg_output.thresh_pk33, 33);
memcpy(pubshares33, &state2i.dkg_output.pubshares33[0][0], 33 * n);
/* recovery data = eq_input || cert */
memcpy(recovery, state2i.eq_input, eq_input_len);
memcpy(recovery + eq_input_len, cmsg2, 64 * n);
/* state2i contains the secret share. */
secp256k1_memclear_explicit(&state2i, sizeof(state2i));
return SECP256K1_CHILLDKG_OK;
}
#endif

View File

@@ -86,7 +86,7 @@ static void secp256k1_chilldkg_simplpedpop_participant_step2_prepare_secshare(se
* verification). Outputs the DKG output and the SimplPedPop contribution to
* eq_input (4 + 33*t bytes).
*
* cmsg must be exactly 97*n + 33*(t-1) bytes. Returns SECP256K1_CHILLDKG_SUCCESS
* cmsg must be exactly 97*n + 33*(t-1) bytes. Returns SECP256K1_CHILLDKG_OK
* on success; otherwise a fault code (with *fault_index set when applicable):
* - FAULTY_COORDINATOR if cmsg is malformed or the echo of our own
* commitment to the secret is wrong;
@@ -109,7 +109,7 @@ static void secp256k1_chilldkg_simplpedpop_assemble_sum_coms(secp256k1_ge *sum_c
* eq_input (4 + 33*t bytes). The pops are NOT verified here (participants
* verify them in step 2).
*
* Outputs cmsg (97*n + 33*(t-1) bytes). Returns SECP256K1_CHILLDKG_SUCCESS on
* Outputs cmsg (97*n + 33*(t-1) bytes). Returns SECP256K1_CHILLDKG_OK on
* success and SECP256K1_CHILLDKG_FAULTY_PARTICIPANT (with *fault_index) if a
* participant message is malformed. */
static secp256k1_chilldkg_fault secp256k1_chilldkg_simplpedpop_coordinator_step(const secp256k1_context *ctx, unsigned char *cmsg, secp256k1_chilldkg_simplpedpop_dkg_output *dkg_output, unsigned char *eq_input, uint32_t *fault_index, const unsigned char *const *pmsgs, uint32_t t, uint32_t n);

View File

@@ -241,7 +241,7 @@ static secp256k1_chilldkg_fault secp256k1_chilldkg_simplpedpop_participant_step2
secp256k1_scalar_clear(&tweak);
secp256k1_scalar_clear(&secshare_tweaked);
return SECP256K1_CHILLDKG_SUCCESS;
return SECP256K1_CHILLDKG_OK;
}
static void secp256k1_chilldkg_simplpedpop_assemble_sum_coms(secp256k1_ge *sum_coms, const secp256k1_ge *coms_to_secrets, const secp256k1_ge *sum_coms_to_nonconst_terms, size_t n, size_t t) {
@@ -350,7 +350,7 @@ static secp256k1_chilldkg_fault secp256k1_chilldkg_simplpedpop_coordinator_step(
}
secp256k1_scalar_clear(&tweak);
return SECP256K1_CHILLDKG_SUCCESS;
return SECP256K1_CHILLDKG_OK;
}
#endif

View File

@@ -201,6 +201,54 @@ static const unsigned char vec_enc_pubshares[3][33] = {
/* encpedpop n = 3, t = 2 */
/* all python-side sanity checks passed */
static const unsigned char vec3_hostseckeys[3][32] = {
{ 0x63, 0xf1, 0xca, 0xd7, 0x7b, 0xa9, 0x64, 0x63, 0x48, 0x03, 0xa3, 0x11, 0x03, 0x0f, 0x5c, 0x74, 0x30, 0x5b, 0x9c, 0x06, 0x24, 0x13, 0xc3, 0xbe, 0xc0, 0xe1, 0xbc, 0xf4, 0x39, 0xb3, 0x35, 0xcf },
{ 0xba, 0x8b, 0x85, 0xb8, 0xd3, 0x0d, 0xcf, 0x07, 0x87, 0xc6, 0x5b, 0x7c, 0x6a, 0x46, 0x08, 0xef, 0xc7, 0x25, 0x35, 0x52, 0x8d, 0xc5, 0x4f, 0xf1, 0xa7, 0x13, 0x52, 0x32, 0x6b, 0x51, 0x71, 0xb1 },
{ 0x73, 0xed, 0x74, 0x96, 0x68, 0x50, 0xa1, 0x21, 0x61, 0x59, 0xd6, 0xe1, 0xf8, 0x07, 0xcc, 0xfb, 0xfa, 0x89, 0x3d, 0xc8, 0xa9, 0x07, 0xfe, 0xfd, 0xe2, 0x34, 0x01, 0xd0, 0xd2, 0xe9, 0xa3, 0xa8 },
};
static const unsigned char vec3_hostpubkeys[3][33] = {
{ 0x02, 0x2d, 0x9b, 0xc8, 0xa8, 0x44, 0x57, 0xc7, 0x51, 0x00, 0x1f, 0x71, 0x4a, 0xd9, 0xac, 0x72, 0x82, 0x8a, 0x01, 0xe5, 0x31, 0xbe, 0x63, 0xe7, 0xfc, 0x3c, 0x4d, 0x37, 0x0c, 0x2f, 0xec, 0xd8, 0x43 },
{ 0x03, 0x34, 0xbe, 0xe4, 0x54, 0x9f, 0x86, 0x95, 0xd8, 0x96, 0xa3, 0xf0, 0x60, 0x40, 0xe0, 0x26, 0x3c, 0x41, 0x90, 0x54, 0xf3, 0xb8, 0xc7, 0x83, 0xa7, 0x7a, 0x77, 0x02, 0x82, 0x86, 0x14, 0x97, 0x3a },
{ 0x02, 0x85, 0xe6, 0xb7, 0xa7, 0xfb, 0x26, 0x5f, 0x38, 0xb9, 0xad, 0xc3, 0x9e, 0x51, 0xd4, 0xac, 0x2d, 0xfb, 0x0f, 0x31, 0xa6, 0xf1, 0x8e, 0x7f, 0xf8, 0xae, 0x2f, 0x3c, 0xa7, 0xbc, 0xaa, 0x7d, 0xcf },
};
static const unsigned char vec3_params_hash[32] = { 0x40, 0x6a, 0x61, 0xe7, 0xd3, 0x91, 0x7e, 0xc0, 0x8d, 0x10, 0x51, 0x38, 0x62, 0xab, 0xbb, 0x40, 0x42, 0x14, 0x39, 0x7c, 0x81, 0x5b, 0x32, 0x60, 0x87, 0xb8, 0x0b, 0x90, 0x31, 0xee, 0xf6, 0xbb };
static const unsigned char vec3_randoms[3][32] = {
{ 0x47, 0x1f, 0x0e, 0xfc, 0x32, 0x6d, 0xaa, 0x68, 0xbb, 0x76, 0x70, 0x31, 0x88, 0x3a, 0xff, 0x5a, 0xcd, 0xc8, 0xca, 0xad, 0x58, 0x5b, 0x0f, 0x5d, 0x13, 0x52, 0xf7, 0x54, 0x70, 0x6c, 0x23, 0xcc },
{ 0xb6, 0x2d, 0x69, 0xb3, 0x7f, 0x3b, 0x20, 0xbd, 0x57, 0x7a, 0xf0, 0x05, 0x7d, 0xd5, 0x11, 0x52, 0xe2, 0xea, 0x90, 0x1c, 0xe1, 0x0f, 0xe9, 0x6c, 0x25, 0x68, 0x55, 0x1d, 0xda, 0xfc, 0xa1, 0x9d },
{ 0xda, 0x99, 0x81, 0x45, 0xd2, 0x74, 0x0f, 0x62, 0xbe, 0x66, 0x57, 0x5b, 0x5f, 0xc1, 0xfd, 0x8d, 0xf4, 0x42, 0x53, 0x72, 0xbb, 0x59, 0x5a, 0x5b, 0xe5, 0x55, 0x17, 0xb9, 0xfe, 0xac, 0x3e, 0x21 },
};
static const unsigned char vec3_aux_rands[3][32] = {
{ 0x43, 0xbb, 0x36, 0xca, 0xf9, 0x05, 0x46, 0x37, 0xdf, 0x41, 0x06, 0x33, 0x10, 0x0c, 0x83, 0x4c, 0x96, 0x5f, 0x6b, 0x88, 0xe9, 0x4c, 0x54, 0x7a, 0x88, 0x0d, 0xce, 0x85, 0x7a, 0xd9, 0x58, 0x66 },
{ 0x01, 0x96, 0xeb, 0x2f, 0x75, 0x84, 0x78, 0x28, 0x2d, 0x0a, 0x8f, 0x73, 0x55, 0x90, 0x22, 0x8c, 0x2d, 0x26, 0xef, 0xfe, 0x9e, 0xad, 0xf8, 0x4b, 0xf2, 0xfe, 0x5d, 0xa0, 0x94, 0x3e, 0x64, 0x03 },
{ 0x4a, 0xee, 0x4b, 0xc4, 0x92, 0x1b, 0xf2, 0x6e, 0xf1, 0xe2, 0x25, 0x75, 0x1b, 0x4f, 0x5f, 0x3d, 0x5f, 0x9a, 0x16, 0x08, 0x04, 0x45, 0x18, 0x5c, 0x98, 0xb5, 0xbf, 0xb1, 0x70, 0xa7, 0x0a, 0x0d },
};
static const unsigned char vec3_pmsgs1[3][259] = {
{ 0x03, 0x13, 0xa8, 0x3d, 0xe5, 0x5f, 0xc3, 0xef, 0x04, 0xe3, 0xd5, 0x0b, 0xe3, 0xe5, 0xae, 0x6f, 0x7d, 0xc8, 0x17, 0x5c, 0x03, 0x61, 0xa2, 0x87, 0xe3, 0x05, 0xac, 0x24, 0xb7, 0xdb, 0x90, 0x5b, 0xa0, 0x02, 0x8e, 0x23, 0x9d, 0x76, 0x52, 0xf4, 0xe2, 0x7b, 0x8f, 0xf7, 0x3b, 0xce, 0xe1, 0xd5, 0xb4, 0xea, 0xf3, 0xc4, 0x1b, 0xef, 0x9d, 0xfc, 0xae, 0x10, 0x53, 0x51, 0xc3, 0x5d, 0xce, 0x73, 0x15, 0xaf, 0x5a, 0x61, 0xbc, 0x3f, 0x93, 0x88, 0x46, 0xad, 0x6e, 0x75, 0x60, 0x32, 0xaf, 0x07, 0x13, 0x50, 0xa5, 0x7f, 0x4d, 0xe5, 0xcc, 0x19, 0x2c, 0xb8, 0x94, 0xc9, 0x05, 0x7e, 0xd1, 0x98, 0x92, 0x80, 0xf4, 0xba, 0x10, 0x30, 0xe5, 0x6a, 0x11, 0xe9, 0xfa, 0xf6, 0x6c, 0xe9, 0x15, 0xcc, 0x29, 0x19, 0xb6, 0x5c, 0x91, 0x3c, 0xbf, 0xcf, 0xf1, 0x92, 0x88, 0xf6, 0xd5, 0x6f, 0x0a, 0x19, 0xb9, 0x06, 0x03, 0x14, 0x18, 0x10, 0xaf, 0x9a, 0x1b, 0x7c, 0x34, 0x27, 0x0a, 0x83, 0xaa, 0x52, 0x62, 0xc2, 0xbe, 0x22, 0x4a, 0x4c, 0x6b, 0xde, 0x0d, 0xc4, 0xdf, 0xda, 0xd1, 0xc2, 0x7f, 0x09, 0x3b, 0x9b, 0x38, 0xea, 0x7b, 0xb3, 0xd7, 0x0c, 0xfc, 0x81, 0x31, 0xe7, 0x7c, 0x63, 0x93, 0xad, 0x2a, 0x28, 0xd7, 0x8b, 0x5a, 0xda, 0x3b, 0xc4, 0x18, 0x5a, 0xcb, 0x64, 0x49, 0x5a, 0x4d, 0xfe, 0xde, 0xd9, 0x82, 0xd7, 0xcc, 0x01, 0x2a, 0x67, 0x40, 0x5b, 0x09, 0xe8, 0x61, 0xbd, 0xa2, 0x72, 0xb8, 0x23, 0x1f, 0xa6, 0xaa, 0xe5, 0xee, 0xdd, 0x01, 0x35, 0xa4, 0x13, 0x5d, 0x08, 0xa5, 0xc5, 0x5c, 0xd9, 0xd4, 0xe1, 0x0e, 0x48, 0x6f, 0x44, 0x6c, 0x29, 0x72, 0xe2, 0x25, 0x06, 0x25, 0x37, 0x0e, 0x28, 0x3f, 0x69, 0x26, 0xc2, 0xc1, 0x79, 0x50, 0xad, 0x0a, 0x0b, 0x20, 0xc1, 0xce, 0x41, 0xe1, 0xff, 0x66 },
{ 0x03, 0x98, 0xf0, 0x88, 0x65, 0x27, 0x59, 0x6b, 0x2c, 0x49, 0xf6, 0xa3, 0xf6, 0x58, 0x46, 0x52, 0x76, 0xdf, 0x6c, 0xd5, 0xc6, 0x68, 0xe2, 0xdf, 0x88, 0x6b, 0xe6, 0x15, 0x81, 0x60, 0x5e, 0x10, 0xa7, 0x02, 0x8c, 0x88, 0x50, 0xb8, 0x9e, 0xe9, 0xb1, 0xe9, 0x89, 0x8a, 0xb2, 0x61, 0x91, 0xb4, 0xf4, 0xc2, 0x9e, 0x9b, 0x7b, 0x0f, 0x71, 0x27, 0x05, 0xe3, 0xd9, 0x54, 0xb0, 0xaf, 0xa1, 0x40, 0x3c, 0x6c, 0xc5, 0xe6, 0x8a, 0xcd, 0xb0, 0xaf, 0x07, 0x62, 0xb1, 0x3f, 0x24, 0xeb, 0x34, 0xd0, 0x39, 0x72, 0xd5, 0x34, 0xcc, 0x72, 0x02, 0x0c, 0x10, 0x19, 0x44, 0x00, 0xd0, 0x21, 0x3f, 0x0d, 0x7f, 0x1e, 0x0d, 0x08, 0xaa, 0xd3, 0xec, 0x21, 0xff, 0x9b, 0xc7, 0xcb, 0x04, 0x97, 0x79, 0x27, 0xd7, 0xc6, 0xe6, 0xbb, 0x4f, 0x0d, 0x88, 0x7a, 0x70, 0x4a, 0x7d, 0xbd, 0x65, 0x8f, 0xa3, 0xc3, 0xc9, 0x2d, 0x03, 0x77, 0xb8, 0xeb, 0x7f, 0x3c, 0xf3, 0x08, 0x9a, 0xeb, 0x55, 0x77, 0xeb, 0xb3, 0x7a, 0xf4, 0x84, 0x25, 0x91, 0xcf, 0x6c, 0x97, 0x42, 0xa4, 0x8e, 0xc6, 0x56, 0xdb, 0xb8, 0xf2, 0x50, 0xf7, 0x09, 0xaf, 0xa6, 0x65, 0x5c, 0xee, 0xee, 0x61, 0x74, 0x28, 0x40, 0xb7, 0xf1, 0xaf, 0x76, 0x0d, 0xf2, 0xf6, 0xcc, 0x6d, 0x4e, 0xe0, 0x5a, 0x9a, 0xd1, 0xfc, 0x7b, 0x5d, 0x86, 0x5c, 0x42, 0xc0, 0x52, 0xd5, 0xb6, 0x97, 0xb0, 0x91, 0xdb, 0xf8, 0xde, 0x6d, 0x2d, 0x81, 0xab, 0xff, 0xb5, 0xf0, 0xcb, 0x75, 0xbd, 0x86, 0xe3, 0x86, 0x04, 0x15, 0x68, 0x05, 0x5e, 0xe2, 0xb4, 0x8f, 0x7c, 0xfc, 0x3d, 0x91, 0x54, 0x76, 0x91, 0x11, 0x18, 0x25, 0x84, 0xf4, 0xf9, 0xdf, 0xad, 0x29, 0xc6, 0x2b, 0x57, 0xc1, 0xf1, 0xe4, 0xa5, 0x40, 0xc0, 0x7f, 0xcb, 0x18, 0x65, 0x07, 0x5c, 0xb2, 0x14, 0x6c, 0x2b },
{ 0x02, 0xc0, 0xa6, 0xde, 0xcb, 0x4a, 0xd7, 0x34, 0xe9, 0xec, 0x43, 0xbd, 0x50, 0xf3, 0xa6, 0xd6, 0xe8, 0xad, 0xa8, 0x38, 0xe8, 0x44, 0x40, 0x0b, 0xfe, 0x14, 0x7e, 0x46, 0xc8, 0xa1, 0x61, 0x5a, 0x2e, 0x03, 0xc8, 0x14, 0xb5, 0xe1, 0x99, 0xff, 0x42, 0x88, 0x52, 0x78, 0x52, 0x96, 0xc4, 0x57, 0x06, 0x65, 0xde, 0x04, 0x58, 0x15, 0x99, 0xc3, 0xb0, 0xcd, 0x24, 0x7e, 0x67, 0x0c, 0x15, 0x51, 0x01, 0xc2, 0x7b, 0x09, 0xdd, 0x3d, 0x86, 0x90, 0xba, 0x4c, 0x1d, 0x65, 0x33, 0x07, 0x50, 0x7a, 0x2c, 0xb3, 0x06, 0x9e, 0xab, 0xbd, 0xe2, 0xb6, 0xe2, 0x4f, 0x0d, 0x79, 0xb4, 0x0a, 0xb4, 0x90, 0xae, 0x89, 0x48, 0xcc, 0x90, 0x5a, 0x4d, 0xc5, 0xc2, 0xe0, 0x59, 0xda, 0x08, 0x33, 0x1b, 0x82, 0x40, 0x51, 0xfa, 0x0b, 0x07, 0x93, 0xe3, 0x6d, 0x96, 0xaf, 0xeb, 0xf6, 0xfc, 0xef, 0x85, 0x1b, 0x21, 0x94, 0x03, 0xfc, 0x22, 0xdc, 0xf0, 0xed, 0xd5, 0x9e, 0xdf, 0x52, 0xab, 0x06, 0xeb, 0xce, 0xba, 0x18, 0x33, 0x7d, 0x00, 0xec, 0x2e, 0xf5, 0x57, 0xcd, 0x23, 0x70, 0xe4, 0x82, 0x07, 0x2b, 0x51, 0x6d, 0x37, 0x6b, 0xc6, 0x34, 0x69, 0xb1, 0x4e, 0x16, 0xc0, 0x90, 0xdf, 0x4a, 0xc9, 0x3b, 0x3d, 0xbc, 0xee, 0xee, 0x29, 0x96, 0x3e, 0xa4, 0x87, 0x2b, 0x9a, 0xb2, 0x59, 0x4f, 0x81, 0x45, 0xaf, 0xfc, 0x3a, 0xfe, 0x8b, 0xfb, 0x90, 0x3d, 0x9b, 0x91, 0xde, 0x14, 0x0f, 0xee, 0xfb, 0x7a, 0x30, 0x67, 0x6d, 0x0d, 0x0f, 0x0e, 0xe7, 0xd9, 0x1b, 0xb8, 0x99, 0xcf, 0xe6, 0xf9, 0x2c, 0x74, 0x1f, 0xb5, 0xb8, 0x20, 0xab, 0x89, 0x01, 0x21, 0x83, 0xf7, 0x65, 0x7e, 0x27, 0x60, 0xf6, 0xa6, 0x73, 0xfa, 0xef, 0x33, 0x72, 0x77, 0xd3, 0xdc, 0x12, 0xc2, 0xe3, 0x87, 0xa9, 0xd4, 0x57, 0x80, 0x89, 0xab, 0xa2 },
};
static const unsigned char vec3_cmsg1[519] = { 0x03, 0x13, 0xa8, 0x3d, 0xe5, 0x5f, 0xc3, 0xef, 0x04, 0xe3, 0xd5, 0x0b, 0xe3, 0xe5, 0xae, 0x6f, 0x7d, 0xc8, 0x17, 0x5c, 0x03, 0x61, 0xa2, 0x87, 0xe3, 0x05, 0xac, 0x24, 0xb7, 0xdb, 0x90, 0x5b, 0xa0, 0x03, 0x98, 0xf0, 0x88, 0x65, 0x27, 0x59, 0x6b, 0x2c, 0x49, 0xf6, 0xa3, 0xf6, 0x58, 0x46, 0x52, 0x76, 0xdf, 0x6c, 0xd5, 0xc6, 0x68, 0xe2, 0xdf, 0x88, 0x6b, 0xe6, 0x15, 0x81, 0x60, 0x5e, 0x10, 0xa7, 0x02, 0xc0, 0xa6, 0xde, 0xcb, 0x4a, 0xd7, 0x34, 0xe9, 0xec, 0x43, 0xbd, 0x50, 0xf3, 0xa6, 0xd6, 0xe8, 0xad, 0xa8, 0x38, 0xe8, 0x44, 0x40, 0x0b, 0xfe, 0x14, 0x7e, 0x46, 0xc8, 0xa1, 0x61, 0x5a, 0x2e, 0x03, 0xe5, 0x85, 0xdb, 0x07, 0xe5, 0x5b, 0xe4, 0x78, 0x39, 0x99, 0xeb, 0xae, 0x7a, 0x14, 0xe0, 0x02, 0x32, 0x42, 0x98, 0x8f, 0x93, 0x8d, 0x44, 0x71, 0x9b, 0x3a, 0x52, 0xae, 0x63, 0xed, 0x0b, 0x6d, 0x5a, 0x61, 0xbc, 0x3f, 0x93, 0x88, 0x46, 0xad, 0x6e, 0x75, 0x60, 0x32, 0xaf, 0x07, 0x13, 0x50, 0xa5, 0x7f, 0x4d, 0xe5, 0xcc, 0x19, 0x2c, 0xb8, 0x94, 0xc9, 0x05, 0x7e, 0xd1, 0x98, 0x92, 0x80, 0xf4, 0xba, 0x10, 0x30, 0xe5, 0x6a, 0x11, 0xe9, 0xfa, 0xf6, 0x6c, 0xe9, 0x15, 0xcc, 0x29, 0x19, 0xb6, 0x5c, 0x91, 0x3c, 0xbf, 0xcf, 0xf1, 0x92, 0x88, 0xf6, 0xd5, 0x6f, 0x0a, 0x19, 0xb9, 0x06, 0xc5, 0xe6, 0x8a, 0xcd, 0xb0, 0xaf, 0x07, 0x62, 0xb1, 0x3f, 0x24, 0xeb, 0x34, 0xd0, 0x39, 0x72, 0xd5, 0x34, 0xcc, 0x72, 0x02, 0x0c, 0x10, 0x19, 0x44, 0x00, 0xd0, 0x21, 0x3f, 0x0d, 0x7f, 0x1e, 0x0d, 0x08, 0xaa, 0xd3, 0xec, 0x21, 0xff, 0x9b, 0xc7, 0xcb, 0x04, 0x97, 0x79, 0x27, 0xd7, 0xc6, 0xe6, 0xbb, 0x4f, 0x0d, 0x88, 0x7a, 0x70, 0x4a, 0x7d, 0xbd, 0x65, 0x8f, 0xa3, 0xc3, 0xc9, 0x2d, 0x7b, 0x09, 0xdd, 0x3d, 0x86, 0x90, 0xba, 0x4c, 0x1d, 0x65, 0x33, 0x07, 0x50, 0x7a, 0x2c, 0xb3, 0x06, 0x9e, 0xab, 0xbd, 0xe2, 0xb6, 0xe2, 0x4f, 0x0d, 0x79, 0xb4, 0x0a, 0xb4, 0x90, 0xae, 0x89, 0x48, 0xcc, 0x90, 0x5a, 0x4d, 0xc5, 0xc2, 0xe0, 0x59, 0xda, 0x08, 0x33, 0x1b, 0x82, 0x40, 0x51, 0xfa, 0x0b, 0x07, 0x93, 0xe3, 0x6d, 0x96, 0xaf, 0xeb, 0xf6, 0xfc, 0xef, 0x85, 0x1b, 0x21, 0x94, 0x03, 0x14, 0x18, 0x10, 0xaf, 0x9a, 0x1b, 0x7c, 0x34, 0x27, 0x0a, 0x83, 0xaa, 0x52, 0x62, 0xc2, 0xbe, 0x22, 0x4a, 0x4c, 0x6b, 0xde, 0x0d, 0xc4, 0xdf, 0xda, 0xd1, 0xc2, 0x7f, 0x09, 0x3b, 0x9b, 0x38, 0x03, 0x77, 0xb8, 0xeb, 0x7f, 0x3c, 0xf3, 0x08, 0x9a, 0xeb, 0x55, 0x77, 0xeb, 0xb3, 0x7a, 0xf4, 0x84, 0x25, 0x91, 0xcf, 0x6c, 0x97, 0x42, 0xa4, 0x8e, 0xc6, 0x56, 0xdb, 0xb8, 0xf2, 0x50, 0xf7, 0x09, 0x03, 0xfc, 0x22, 0xdc, 0xf0, 0xed, 0xd5, 0x9e, 0xdf, 0x52, 0xab, 0x06, 0xeb, 0xce, 0xba, 0x18, 0x33, 0x7d, 0x00, 0xec, 0x2e, 0xf5, 0x57, 0xcd, 0x23, 0x70, 0xe4, 0x82, 0x07, 0x2b, 0x51, 0x6d, 0x37, 0x05, 0xe8, 0x4d, 0x9d, 0xad, 0x38, 0xf9, 0x66, 0xa0, 0x9c, 0x66, 0x4e, 0x97, 0xdd, 0xf3, 0xbb, 0xfa, 0xf3, 0x23, 0xfb, 0xea, 0x68, 0xe0, 0xc0, 0x93, 0x79, 0x4a, 0x3c, 0x00, 0x65, 0x13, 0x8c, 0xac, 0x0e, 0x94, 0x6b, 0x36, 0xb7, 0xe5, 0xc6, 0x69, 0x9f, 0x2e, 0x49, 0xec, 0x9e, 0x7b, 0x5a, 0xb4, 0x19, 0xc1, 0xec, 0xdd, 0x8f, 0xc3, 0x2e, 0x68, 0xfe, 0x27, 0x6d, 0x28, 0x8d, 0x09, 0x47, 0x93, 0x0e, 0x48, 0x01, 0x77, 0x08, 0x46, 0x5d, 0x55, 0x46, 0x46, 0xc9, 0x07, 0x48, 0x4e, 0x87, 0xa3, 0xdc, 0x42, 0x53, 0xe6, 0xdb, 0x4f, 0x7c, 0xeb, 0x5d, 0x3e, 0xf5, 0xa4, 0x49, 0xd5, 0xf2 };
static const unsigned char vec3_pmsgs2[3][64] = {
{ 0x09, 0x1e, 0xab, 0x60, 0x71, 0x94, 0x54, 0x81, 0xfb, 0x97, 0xd0, 0x1c, 0x93, 0xdc, 0x2d, 0xd2, 0xcb, 0xea, 0xbc, 0xa1, 0xb0, 0x9a, 0xd6, 0x65, 0x85, 0xa4, 0x25, 0x84, 0xf2, 0x8d, 0xeb, 0xb3, 0x2c, 0x20, 0x4e, 0xc5, 0xe0, 0x5d, 0xaf, 0xd5, 0xc9, 0xf2, 0x65, 0xce, 0x1a, 0x2b, 0xa4, 0x50, 0x1b, 0x4a, 0x1b, 0x9a, 0x0e, 0x97, 0xc7, 0xf4, 0xb3, 0x4f, 0x86, 0x23, 0x31, 0x85, 0x54, 0xe5 },
{ 0x4f, 0x38, 0xc9, 0x07, 0xc0, 0x20, 0x29, 0xb9, 0x8d, 0x8b, 0x65, 0xa1, 0xc1, 0xbb, 0x24, 0x89, 0x64, 0x38, 0xc0, 0x71, 0xef, 0xd6, 0xa4, 0x90, 0x96, 0x4e, 0x9d, 0x83, 0x23, 0x8a, 0x98, 0xf0, 0xd3, 0x06, 0xf3, 0xc9, 0x4c, 0xa1, 0x50, 0x07, 0x57, 0x89, 0xc6, 0xfc, 0xe8, 0xb8, 0x02, 0xf2, 0x36, 0xfd, 0xcd, 0xf5, 0x8e, 0x5a, 0x1c, 0x65, 0x21, 0x4d, 0xa2, 0x7f, 0xed, 0x3e, 0xa6, 0xed },
{ 0x8b, 0x92, 0x3f, 0xae, 0x45, 0x7c, 0xb0, 0x5c, 0x36, 0x9f, 0x7d, 0x9d, 0x84, 0x28, 0xa2, 0x2c, 0x63, 0x0a, 0x62, 0xeb, 0x6b, 0x51, 0x89, 0x11, 0x54, 0x6a, 0x96, 0xf7, 0x45, 0x88, 0xc1, 0x4a, 0xbd, 0x63, 0xa7, 0xfa, 0x16, 0x9e, 0x17, 0xd9, 0xc5, 0x97, 0x4b, 0x0b, 0xe6, 0xc5, 0x92, 0xfe, 0x48, 0x6b, 0xec, 0xfc, 0xfd, 0xea, 0x76, 0xa3, 0xfa, 0x92, 0xa7, 0x2f, 0xba, 0x1e, 0x7b, 0xef },
};
static const unsigned char vec3_cmsg2[192] = { 0x09, 0x1e, 0xab, 0x60, 0x71, 0x94, 0x54, 0x81, 0xfb, 0x97, 0xd0, 0x1c, 0x93, 0xdc, 0x2d, 0xd2, 0xcb, 0xea, 0xbc, 0xa1, 0xb0, 0x9a, 0xd6, 0x65, 0x85, 0xa4, 0x25, 0x84, 0xf2, 0x8d, 0xeb, 0xb3, 0x2c, 0x20, 0x4e, 0xc5, 0xe0, 0x5d, 0xaf, 0xd5, 0xc9, 0xf2, 0x65, 0xce, 0x1a, 0x2b, 0xa4, 0x50, 0x1b, 0x4a, 0x1b, 0x9a, 0x0e, 0x97, 0xc7, 0xf4, 0xb3, 0x4f, 0x86, 0x23, 0x31, 0x85, 0x54, 0xe5, 0x4f, 0x38, 0xc9, 0x07, 0xc0, 0x20, 0x29, 0xb9, 0x8d, 0x8b, 0x65, 0xa1, 0xc1, 0xbb, 0x24, 0x89, 0x64, 0x38, 0xc0, 0x71, 0xef, 0xd6, 0xa4, 0x90, 0x96, 0x4e, 0x9d, 0x83, 0x23, 0x8a, 0x98, 0xf0, 0xd3, 0x06, 0xf3, 0xc9, 0x4c, 0xa1, 0x50, 0x07, 0x57, 0x89, 0xc6, 0xfc, 0xe8, 0xb8, 0x02, 0xf2, 0x36, 0xfd, 0xcd, 0xf5, 0x8e, 0x5a, 0x1c, 0x65, 0x21, 0x4d, 0xa2, 0x7f, 0xed, 0x3e, 0xa6, 0xed, 0x8b, 0x92, 0x3f, 0xae, 0x45, 0x7c, 0xb0, 0x5c, 0x36, 0x9f, 0x7d, 0x9d, 0x84, 0x28, 0xa2, 0x2c, 0x63, 0x0a, 0x62, 0xeb, 0x6b, 0x51, 0x89, 0x11, 0x54, 0x6a, 0x96, 0xf7, 0x45, 0x88, 0xc1, 0x4a, 0xbd, 0x63, 0xa7, 0xfa, 0x16, 0x9e, 0x17, 0xd9, 0xc5, 0x97, 0x4b, 0x0b, 0xe6, 0xc5, 0x92, 0xfe, 0x48, 0x6b, 0xec, 0xfc, 0xfd, 0xea, 0x76, 0xa3, 0xfa, 0x92, 0xa7, 0x2f, 0xba, 0x1e, 0x7b, 0xef };
static const unsigned char vec3_secshares[3][32] = {
{ 0x38, 0xf8, 0x44, 0xd4, 0x6d, 0xf1, 0xf9, 0x51, 0x56, 0x40, 0x60, 0x09, 0xb7, 0x04, 0x11, 0xcf, 0x88, 0x02, 0x4c, 0xa6, 0xd6, 0x4a, 0x37, 0x06, 0x9f, 0x44, 0x97, 0x10, 0x7d, 0x3d, 0x47, 0x33 },
{ 0x01, 0x1d, 0x1f, 0x86, 0x77, 0xaf, 0x70, 0x71, 0xcf, 0x89, 0xf6, 0x3f, 0x94, 0xad, 0xf7, 0x7c, 0xfa, 0xe7, 0xca, 0xb3, 0x28, 0xd4, 0x4d, 0x5e, 0x24, 0x29, 0x5e, 0x06, 0x77, 0xda, 0x3b, 0xfe },
{ 0xc9, 0x41, 0xfa, 0x38, 0x81, 0x6c, 0xe7, 0x92, 0x48, 0xd3, 0x8c, 0x75, 0x72, 0x57, 0xdd, 0x29, 0x28, 0x7c, 0x25, 0xa6, 0x2a, 0xa7, 0x03, 0xf1, 0x68, 0xe0, 0x83, 0x89, 0x42, 0xad, 0x72, 0x0a },
};
static const unsigned char vec3_thresh_pk[33] = { 0x03, 0x07, 0xf3, 0xc6, 0x29, 0x69, 0xf9, 0xee, 0xaf, 0xb9, 0xf3, 0xd6, 0x25, 0xcb, 0xa6, 0xe8, 0x09, 0x00, 0x17, 0x04, 0xc0, 0x92, 0xd7, 0xca, 0xac, 0x49, 0x6f, 0xd3, 0xed, 0x07, 0x35, 0x23, 0x2c };
static const unsigned char vec3_pubshares[3][33] = {
{ 0x02, 0x2a, 0x8d, 0x1c, 0x2a, 0x1c, 0x95, 0x97, 0x89, 0xdf, 0xa2, 0xb5, 0xc4, 0xc2, 0x97, 0xdb, 0xc9, 0x09, 0x6b, 0xb7, 0xd7, 0x20, 0x1f, 0xe6, 0x67, 0x38, 0x13, 0xae, 0xff, 0x9f, 0xbc, 0x58, 0xba },
{ 0x03, 0x1a, 0x40, 0x20, 0x79, 0x6b, 0x0c, 0x5f, 0x0e, 0x4a, 0x67, 0xd2, 0x74, 0x8f, 0xd2, 0xea, 0x93, 0x65, 0x2c, 0xae, 0x41, 0x0f, 0xf0, 0x2d, 0x45, 0x79, 0x8a, 0x44, 0xb4, 0x50, 0x8f, 0xd5, 0x4d },
{ 0x02, 0xea, 0xad, 0x03, 0x65, 0xfe, 0x7e, 0xa9, 0xea, 0xd9, 0x30, 0x44, 0xb9, 0x68, 0xf6, 0xe7, 0x18, 0x51, 0xe5, 0xac, 0xd3, 0x80, 0x92, 0x11, 0xd8, 0x4d, 0xe2, 0xd7, 0x3a, 0xf6, 0xf9, 0x4a, 0xee },
};
static const unsigned char vec3_recovery[556] = { 0x00, 0x00, 0x00, 0x02, 0x03, 0xdd, 0xe3, 0x46, 0x0b, 0x06, 0x56, 0x58, 0xf6, 0x85, 0x78, 0x85, 0xee, 0x42, 0xa8, 0xfa, 0x9c, 0x53, 0xd6, 0x3d, 0x75, 0x02, 0x16, 0x65, 0x03, 0xf1, 0xb6, 0x6a, 0x56, 0xb9, 0x76, 0xa2, 0x70, 0x03, 0xe5, 0x85, 0xdb, 0x07, 0xe5, 0x5b, 0xe4, 0x78, 0x39, 0x99, 0xeb, 0xae, 0x7a, 0x14, 0xe0, 0x02, 0x32, 0x42, 0x98, 0x8f, 0x93, 0x8d, 0x44, 0x71, 0x9b, 0x3a, 0x52, 0xae, 0x63, 0xed, 0x0b, 0x6d, 0x02, 0x2d, 0x9b, 0xc8, 0xa8, 0x44, 0x57, 0xc7, 0x51, 0x00, 0x1f, 0x71, 0x4a, 0xd9, 0xac, 0x72, 0x82, 0x8a, 0x01, 0xe5, 0x31, 0xbe, 0x63, 0xe7, 0xfc, 0x3c, 0x4d, 0x37, 0x0c, 0x2f, 0xec, 0xd8, 0x43, 0x03, 0x34, 0xbe, 0xe4, 0x54, 0x9f, 0x86, 0x95, 0xd8, 0x96, 0xa3, 0xf0, 0x60, 0x40, 0xe0, 0x26, 0x3c, 0x41, 0x90, 0x54, 0xf3, 0xb8, 0xc7, 0x83, 0xa7, 0x7a, 0x77, 0x02, 0x82, 0x86, 0x14, 0x97, 0x3a, 0x02, 0x85, 0xe6, 0xb7, 0xa7, 0xfb, 0x26, 0x5f, 0x38, 0xb9, 0xad, 0xc3, 0x9e, 0x51, 0xd4, 0xac, 0x2d, 0xfb, 0x0f, 0x31, 0xa6, 0xf1, 0x8e, 0x7f, 0xf8, 0xae, 0x2f, 0x3c, 0xa7, 0xbc, 0xaa, 0x7d, 0xcf, 0x03, 0x14, 0x18, 0x10, 0xaf, 0x9a, 0x1b, 0x7c, 0x34, 0x27, 0x0a, 0x83, 0xaa, 0x52, 0x62, 0xc2, 0xbe, 0x22, 0x4a, 0x4c, 0x6b, 0xde, 0x0d, 0xc4, 0xdf, 0xda, 0xd1, 0xc2, 0x7f, 0x09, 0x3b, 0x9b, 0x38, 0x03, 0x77, 0xb8, 0xeb, 0x7f, 0x3c, 0xf3, 0x08, 0x9a, 0xeb, 0x55, 0x77, 0xeb, 0xb3, 0x7a, 0xf4, 0x84, 0x25, 0x91, 0xcf, 0x6c, 0x97, 0x42, 0xa4, 0x8e, 0xc6, 0x56, 0xdb, 0xb8, 0xf2, 0x50, 0xf7, 0x09, 0x03, 0xfc, 0x22, 0xdc, 0xf0, 0xed, 0xd5, 0x9e, 0xdf, 0x52, 0xab, 0x06, 0xeb, 0xce, 0xba, 0x18, 0x33, 0x7d, 0x00, 0xec, 0x2e, 0xf5, 0x57, 0xcd, 0x23, 0x70, 0xe4, 0x82, 0x07, 0x2b, 0x51, 0x6d, 0x37, 0x05, 0xe8, 0x4d, 0x9d, 0xad, 0x38, 0xf9, 0x66, 0xa0, 0x9c, 0x66, 0x4e, 0x97, 0xdd, 0xf3, 0xbb, 0xfa, 0xf3, 0x23, 0xfb, 0xea, 0x68, 0xe0, 0xc0, 0x93, 0x79, 0x4a, 0x3c, 0x00, 0x65, 0x13, 0x8c, 0xac, 0x0e, 0x94, 0x6b, 0x36, 0xb7, 0xe5, 0xc6, 0x69, 0x9f, 0x2e, 0x49, 0xec, 0x9e, 0x7b, 0x5a, 0xb4, 0x19, 0xc1, 0xec, 0xdd, 0x8f, 0xc3, 0x2e, 0x68, 0xfe, 0x27, 0x6d, 0x28, 0x8d, 0x09, 0x47, 0x93, 0x0e, 0x48, 0x01, 0x77, 0x08, 0x46, 0x5d, 0x55, 0x46, 0x46, 0xc9, 0x07, 0x48, 0x4e, 0x87, 0xa3, 0xdc, 0x42, 0x53, 0xe6, 0xdb, 0x4f, 0x7c, 0xeb, 0x5d, 0x3e, 0xf5, 0xa4, 0x49, 0xd5, 0xf2, 0x09, 0x1e, 0xab, 0x60, 0x71, 0x94, 0x54, 0x81, 0xfb, 0x97, 0xd0, 0x1c, 0x93, 0xdc, 0x2d, 0xd2, 0xcb, 0xea, 0xbc, 0xa1, 0xb0, 0x9a, 0xd6, 0x65, 0x85, 0xa4, 0x25, 0x84, 0xf2, 0x8d, 0xeb, 0xb3, 0x2c, 0x20, 0x4e, 0xc5, 0xe0, 0x5d, 0xaf, 0xd5, 0xc9, 0xf2, 0x65, 0xce, 0x1a, 0x2b, 0xa4, 0x50, 0x1b, 0x4a, 0x1b, 0x9a, 0x0e, 0x97, 0xc7, 0xf4, 0xb3, 0x4f, 0x86, 0x23, 0x31, 0x85, 0x54, 0xe5, 0x4f, 0x38, 0xc9, 0x07, 0xc0, 0x20, 0x29, 0xb9, 0x8d, 0x8b, 0x65, 0xa1, 0xc1, 0xbb, 0x24, 0x89, 0x64, 0x38, 0xc0, 0x71, 0xef, 0xd6, 0xa4, 0x90, 0x96, 0x4e, 0x9d, 0x83, 0x23, 0x8a, 0x98, 0xf0, 0xd3, 0x06, 0xf3, 0xc9, 0x4c, 0xa1, 0x50, 0x07, 0x57, 0x89, 0xc6, 0xfc, 0xe8, 0xb8, 0x02, 0xf2, 0x36, 0xfd, 0xcd, 0xf5, 0x8e, 0x5a, 0x1c, 0x65, 0x21, 0x4d, 0xa2, 0x7f, 0xed, 0x3e, 0xa6, 0xed, 0x8b, 0x92, 0x3f, 0xae, 0x45, 0x7c, 0xb0, 0x5c, 0x36, 0x9f, 0x7d, 0x9d, 0x84, 0x28, 0xa2, 0x2c, 0x63, 0x0a, 0x62, 0xeb, 0x6b, 0x51, 0x89, 0x11, 0x54, 0x6a, 0x96, 0xf7, 0x45, 0x88, 0xc1, 0x4a, 0xbd, 0x63, 0xa7, 0xfa, 0x16, 0x9e, 0x17, 0xd9, 0xc5, 0x97, 0x4b, 0x0b, 0xe6, 0xc5, 0x92, 0xfe, 0x48, 0x6b, 0xec, 0xfc, 0xfd, 0xea, 0x76, 0xa3, 0xfa, 0x92, 0xa7, 0x2f, 0xba, 0x1e, 0x7b, 0xef };
/* chilldkg session n = 3, t = 2 */
/* all python-side sanity checks passed */
/* The group order n and (2^256 - 1) mod n, for scalar parsing edge cases. */
static const unsigned char vec_scalar_order_n[32] = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
@@ -265,11 +313,11 @@ static void chilldkg_params_hash_test(void) {
unsigned char out[32];
/* TH("BIP DKG/params_hash", u32be(2) || hostpubkeys) */
secp256k1_chilldkg_params_hash(hash_ctx, out, &vec_params_hash_hostpubkeys[0][0], 3, 2);
secp256k1_chilldkg_params_hash_internal(hash_ctx, out, &vec_params_hash_hostpubkeys[0][0], 3, 2);
CHECK(secp256k1_memcmp_var(out, vec_params_hash, 32) == 0);
/* t is part of the hash input. */
secp256k1_chilldkg_params_hash(hash_ctx, out, &vec_params_hash_hostpubkeys[0][0], 3, 3);
secp256k1_chilldkg_params_hash_internal(hash_ctx, out, &vec_params_hash_hostpubkeys[0][0], 3, 3);
CHECK(secp256k1_memcmp_var(out, vec_params_hash, 32) != 0);
}
@@ -638,7 +686,7 @@ static void chilldkg_simplpedpop_test(void) {
/* coordinator_step: cmsg, eq_input and the DKG output are byte-exact. */
fault = secp256k1_chilldkg_simplpedpop_coordinator_step(CTX, cmsg, &coord_dkg, eq_input, &fault_index, pmsg_ptrs, t, n);
CHECK(fault == SECP256K1_CHILLDKG_SUCCESS);
CHECK(fault == SECP256K1_CHILLDKG_OK);
CHECK(secp256k1_memcmp_var(cmsg, vec_simpl_cmsg, cmsg_len) == 0);
CHECK(secp256k1_memcmp_var(eq_input, vec_simpl_eq_input, eq_len) == 0);
CHECK(secp256k1_memcmp_var(coord_dkg.thresh_pk33, vec_simpl_thresh_pk, 33) == 0);
@@ -659,7 +707,7 @@ static void chilldkg_simplpedpop_test(void) {
secp256k1_scalar_get_b32(buf32, &secshare);
CHECK(secp256k1_memcmp_var(buf32, vec_simpl_untweaked_secshares[i], 32) == 0);
fault = secp256k1_chilldkg_simplpedpop_participant_step2(CTX, &dkg_outputs[i], eq_input_p, &fault_index, &states[i], cmsg, cmsg_len, &secshare);
CHECK(fault == SECP256K1_CHILLDKG_SUCCESS);
CHECK(fault == SECP256K1_CHILLDKG_OK);
CHECK(secp256k1_memcmp_var(eq_input_p, vec_simpl_eq_input, eq_len) == 0);
CHECK(secp256k1_memcmp_var(dkg_outputs[i].secshare32, vec_simpl_secshares[i], 32) == 0);
CHECK(secp256k1_memcmp_var(dkg_outputs[i].thresh_pk33, vec_simpl_thresh_pk, 33) == 0);
@@ -819,7 +867,7 @@ static void chilldkg_encpedpop_test(void) {
CHECK(secp256k1_chilldkg_encpedpop_encaps_multi(CTX, pads_send, &scalar_tmp, vec_enc_pubnonces[0], vec_enc_hostseckeys[0], &vec_enc_hostpubkeys[0][0], enc_context, sizeof(enc_context), 0, n) == 1);
for (i = 0; i < n; i++) {
fault = secp256k1_chilldkg_encpedpop_decaps_multi(CTX, pads_recv, &fault_index, vec_enc_hostseckeys[i], vec_enc_hostpubkeys[i], &vec_enc_pubnonces[0][0], enc_context, sizeof(enc_context), i, n);
CHECK(fault == SECP256K1_CHILLDKG_SUCCESS);
CHECK(fault == SECP256K1_CHILLDKG_OK);
CHECK(secp256k1_scalar_eq(&pads_send[i], &pads_recv[0]));
}
/* decaps_multi rejects an invalid own host secret key. */
@@ -849,7 +897,7 @@ static void chilldkg_encpedpop_test(void) {
}
for (j = 0; j < n; j++) {
fault = secp256k1_chilldkg_encpedpop_decrypt_sum(CTX, &scalar_tmp, &fault_index, vec_enc_hostseckeys[j], vec_enc_hostpubkeys[j], &vec_enc_pubnonces[0][0], enc_context, sizeof(enc_context), j, &enc_secshares[j], n);
CHECK(fault == SECP256K1_CHILLDKG_SUCCESS);
CHECK(fault == SECP256K1_CHILLDKG_OK);
secp256k1_scalar_set_int(&scalar_tmp2, 0);
for (i = 0; i < n; i++) {
secp256k1_scalar p;
@@ -862,7 +910,7 @@ static void chilldkg_encpedpop_test(void) {
/* coordinator_step: cmsg, eq_input, summed encrypted shares and the DKG
* output are byte-exact. */
fault = secp256k1_chilldkg_encpedpop_coordinator_step(CTX, cmsg, &coord_dkg, eq_input, enc_secshares, &fault_index, pmsg_ptrs, t, &vec_enc_hostpubkeys[0][0], n);
CHECK(fault == SECP256K1_CHILLDKG_SUCCESS);
CHECK(fault == SECP256K1_CHILLDKG_OK);
CHECK(secp256k1_memcmp_var(cmsg, vec_enc_cmsg, cmsg_len) == 0);
CHECK(secp256k1_memcmp_var(eq_input, vec_enc_eq_input, eq_len) == 0);
for (i = 0; i < n; i++) {
@@ -878,7 +926,7 @@ static void chilldkg_encpedpop_test(void) {
for (i = 0; i < n; i++) {
unsigned char eq_input_p[268];
fault = secp256k1_chilldkg_encpedpop_participant_step2(CTX, &dkg_output, eq_input_p, &fault_index, &states[i], vec_enc_hostseckeys[i], cmsg, cmsg_len, &enc_secshares[i]);
CHECK(fault == SECP256K1_CHILLDKG_SUCCESS);
CHECK(fault == SECP256K1_CHILLDKG_OK);
CHECK(secp256k1_memcmp_var(eq_input_p, vec_enc_eq_input, eq_len) == 0);
CHECK(secp256k1_memcmp_var(dkg_output.secshare32, vec_enc_out_secshares[i], 32) == 0);
CHECK(secp256k1_memcmp_var(dkg_output.thresh_pk33, vec_enc_thresh_pk, 33) == 0);
@@ -958,6 +1006,262 @@ static void chilldkg_encpedpop_test(void) {
}
}
static void chilldkg_participant_api_test(void) {
const size_t n = 3;
const uint32_t t = 2;
const size_t pmsg1_len = 33 * 2 + 32 * 3 + 97; /* 259 */
const size_t cmsg1_len = 162 * 3 + 33 * 1; /* 519 */
const size_t enc_cmsg_len = 97 * 3 + 33 * 1 + 33 * 3; /* 423 */
const size_t recovery_len = 4 + 33 * 2 + 162 * 3; /* 556 */
secp256k1_chilldkg_participant_state1 state1[3];
secp256k1_chilldkg_participant_state2 state2[3];
secp256k1_chilldkg_simplpedpop_dkg_output coord_dkg;
secp256k1_scalar enc_secshares[3];
unsigned char pmsg1[3][259];
unsigned char sig64[3][64];
unsigned char cmsg1[519];
unsigned char cmsg1_bad[519];
unsigned char cmsg2[192];
unsigned char cmsg2_bad[192];
unsigned char hostpubkeys33[3 * 33];
unsigned char pubshares33[3 * 33];
unsigned char recovery[556];
unsigned char eq_input[268];
unsigned char buf33[33];
unsigned char buf32[32];
unsigned char secshare32[32];
unsigned char zero32[32] = { 0 };
const unsigned char *pmsg1_ptrs[3];
secp256k1_chilldkg_fault fault;
uint32_t fault_index = 0;
size_t i, j;
for (i = 0; i < n; i++) {
pmsg1_ptrs[i] = pmsg1[i];
memcpy(hostpubkeys33 + 33 * i, vec3_hostpubkeys[i], 33);
}
/* Message length helpers. */
CHECK(secp256k1_chilldkg_participant_msg1_len(n, t) == pmsg1_len);
CHECK(secp256k1_chilldkg_coordinator_msg1_len(n, t) == cmsg1_len);
CHECK(secp256k1_chilldkg_participant_msg2_len() == 64);
CHECK(secp256k1_chilldkg_coordinator_msg2_len(n) == sizeof(cmsg2));
CHECK(secp256k1_chilldkg_recovery_data_len(n, t) == recovery_len);
CHECK(secp256k1_chilldkg_participant_msg1_len(0, 1) == 0);
CHECK(secp256k1_chilldkg_participant_msg1_len(n, 0) == 0);
CHECK(secp256k1_chilldkg_participant_msg1_len(n, (uint32_t)(n + 1)) == 0);
CHECK(secp256k1_chilldkg_participant_msg1_len(SECP256K1_CHILLDKG_MAX_PARTICIPANTS + 1, 1) == 0);
CHECK(secp256k1_chilldkg_coordinator_msg1_len(n, 0) == 0);
CHECK(secp256k1_chilldkg_coordinator_msg2_len(0) == 0);
CHECK(secp256k1_chilldkg_recovery_data_len(n, (uint32_t)(n + 1)) == 0);
/* hostpubkey_gen: byte-exact against the reference; invalid host secret
* keys are rejected and zero the output. */
for (i = 0; i < n; i++) {
CHECK(secp256k1_chilldkg_hostpubkey_gen(CTX, buf33, vec3_hostseckeys[i]) == 1);
CHECK(secp256k1_memcmp_var(buf33, vec3_hostpubkeys[i], 33) == 0);
}
CHECK(secp256k1_chilldkg_hostpubkey_gen(CTX, buf33, zero32) == 0);
CHECK(secp256k1_is_zero_array(buf33, 33));
CHECK(secp256k1_chilldkg_hostpubkey_gen(CTX, buf33, vec_scalar_order_n) == 0);
CHECK(secp256k1_is_zero_array(buf33, 33));
/* params_hash: byte-exact against the reference; invalid parameters
* (duplicate or invalid host public keys, t or n out of range) are
* rejected and zero the output. */
CHECK(secp256k1_chilldkg_params_hash(CTX, buf32, hostpubkeys33, n, t) == 1);
CHECK(secp256k1_memcmp_var(buf32, vec3_params_hash, 32) == 0);
{
unsigned char bad_hostpubkeys[3 * 33];
memcpy(bad_hostpubkeys, hostpubkeys33, sizeof(bad_hostpubkeys));
memcpy(bad_hostpubkeys + 33, hostpubkeys33, 33); /* duplicate */
CHECK(secp256k1_chilldkg_params_hash(CTX, buf32, bad_hostpubkeys, n, t) == 0);
CHECK(secp256k1_is_zero_array(buf32, 32));
memcpy(bad_hostpubkeys, hostpubkeys33, sizeof(bad_hostpubkeys));
memset(bad_hostpubkeys + 33, 0xff, 33); /* invalid public key */
CHECK(secp256k1_chilldkg_params_hash(CTX, buf32, bad_hostpubkeys, n, t) == 0);
memcpy(bad_hostpubkeys, hostpubkeys33, sizeof(bad_hostpubkeys));
memset(bad_hostpubkeys + 33, 0, 33); /* infinity is not a valid public key */
CHECK(secp256k1_chilldkg_params_hash(CTX, buf32, bad_hostpubkeys, n, t) == 0);
}
CHECK(secp256k1_chilldkg_params_hash(CTX, buf32, hostpubkeys33, n, 0) == 0);
CHECK(secp256k1_chilldkg_params_hash(CTX, buf32, hostpubkeys33, n, (uint32_t)(n + 1)) == 0);
CHECK(secp256k1_chilldkg_params_hash(CTX, buf32, hostpubkeys33, SECP256K1_CHILLDKG_MAX_PARTICIPANTS + 1, t) == 0);
/* participant_step1: byte-exact messages against the reference. */
for (i = 0; i < n; i++) {
CHECK(secp256k1_chilldkg_participant_step1(CTX, &state1[i], pmsg1[i], vec3_hostseckeys[i], hostpubkeys33, n, t, vec3_randoms[i]) == 1);
CHECK(secp256k1_memcmp_var(pmsg1[i], vec3_pmsgs1[i], pmsg1_len) == 0);
}
/* participant_step1 rejects invalid input and zeroes its outputs. */
{
secp256k1_chilldkg_participant_state1 tmp_state1;
unsigned char tmp_pmsg1[259];
unsigned char other_seckey[32];
memset(other_seckey, 0, 32);
other_seckey[31] = 1; /* valid key, but not in hostpubkeys33 */
/* All-zero randomness (RandomnessError in the reference). */
CHECK(secp256k1_chilldkg_participant_step1(CTX, &tmp_state1, tmp_pmsg1, vec3_hostseckeys[0], hostpubkeys33, n, t, zero32) == 0);
CHECK(secp256k1_is_zero_array(tmp_state1.data, sizeof(tmp_state1.data)));
CHECK(secp256k1_is_zero_array(tmp_pmsg1, sizeof(tmp_pmsg1)));
/* Host secret key not in the session (HostSeckeyError). */
CHECK(secp256k1_chilldkg_participant_step1(CTX, &tmp_state1, tmp_pmsg1, other_seckey, hostpubkeys33, n, t, vec3_randoms[0]) == 0);
/* Invalid host secret key. */
CHECK(secp256k1_chilldkg_participant_step1(CTX, &tmp_state1, tmp_pmsg1, zero32, hostpubkeys33, n, t, vec3_randoms[0]) == 0);
/* Invalid session parameters. */
CHECK(secp256k1_chilldkg_participant_step1(CTX, &tmp_state1, tmp_pmsg1, vec3_hostseckeys[0], hostpubkeys33, n, 0, vec3_randoms[0]) == 0);
CHECK(secp256k1_chilldkg_participant_step1(CTX, &tmp_state1, tmp_pmsg1, vec3_hostseckeys[0], hostpubkeys33, n, (uint32_t)(n + 1), vec3_randoms[0]) == 0);
}
/* Simulate the coordinator with the internal EncPedPop coordinator step:
* cmsg1 = enc_cmsg || enc_secshares. The result is byte-exact against the
* reference coordinator_step1. */
fault = secp256k1_chilldkg_encpedpop_coordinator_step(CTX, cmsg1, &coord_dkg, eq_input, enc_secshares, &fault_index, pmsg1_ptrs, t, hostpubkeys33, n);
CHECK(fault == SECP256K1_CHILLDKG_OK);
for (i = 0; i < n; i++) {
secp256k1_scalar_get_b32(cmsg1 + enc_cmsg_len + 32 * i, &enc_secshares[i]);
}
CHECK(secp256k1_memcmp_var(cmsg1, vec3_cmsg1, cmsg1_len) == 0);
/* participant_step2: byte-exact CertEq signatures against the reference. */
for (i = 0; i < n; i++) {
fault = secp256k1_chilldkg_participant_step2(CTX, &state2[i], sig64[i], &fault_index, &state1[i], vec3_hostseckeys[i], cmsg1, vec3_aux_rands[i]);
CHECK(fault == SECP256K1_CHILLDKG_OK);
CHECK(secp256k1_memcmp_var(sig64[i], vec3_pmsgs2[i], 64) == 0);
}
/* The certificate is the concatenation of the pmsg2 signatures. */
for (i = 0; i < n; i++) {
memcpy(cmsg2 + 64 * i, sig64[i], 64);
}
CHECK(secp256k1_memcmp_var(cmsg2, vec3_cmsg2, sizeof(cmsg2)) == 0);
/* participant_finalize: byte-exact outputs against the reference. */
for (i = 0; i < n; i++) {
fault = secp256k1_chilldkg_participant_finalize(CTX, secshare32, buf33, pubshares33, recovery, &fault_index, &state2[i], cmsg2);
CHECK(fault == SECP256K1_CHILLDKG_OK);
CHECK(secp256k1_memcmp_var(secshare32, vec3_secshares[i], 32) == 0);
CHECK(secp256k1_memcmp_var(buf33, vec3_thresh_pk, 33) == 0);
for (j = 0; j < n; j++) {
CHECK(secp256k1_memcmp_var(pubshares33 + 33 * j, vec3_pubshares[j], 33) == 0);
}
CHECK(secp256k1_memcmp_var(recovery, vec3_recovery, recovery_len) == 0);
}
/* participant_step2 with a host secret key that does not match the one
* used in step 1 is an input error (HostSeckeyError in the reference). */
{
unsigned char tmp_sig64[64];
fault = secp256k1_chilldkg_participant_step2(CTX, &state2[0], tmp_sig64, &fault_index, &state1[0], vec3_hostseckeys[1], cmsg1, vec3_aux_rands[0]);
CHECK(fault == SECP256K1_CHILLDKG_INVALID_INPUT);
CHECK(secp256k1_is_zero_array(tmp_sig64, 64));
}
/* participant_step2 blames faults correctly. */
{
secp256k1_chilldkg_participant_state1 fresh_state1;
unsigned char tmp_sig64[64];
/* A tampered encrypted secshare: the decrypted share does not match
* the pubshare; the fault cannot be attributed without the
* investigation procedure. */
CHECK(secp256k1_chilldkg_participant_step1(CTX, &fresh_state1, pmsg1[0], vec3_hostseckeys[0], hostpubkeys33, n, t, vec3_randoms[0]) == 1);
memcpy(cmsg1_bad, cmsg1, cmsg1_len);
cmsg1_bad[enc_cmsg_len + 31] ^= 1;
fault_index = 0;
fault = secp256k1_chilldkg_participant_step2(CTX, &state2[0], tmp_sig64, &fault_index, &fresh_state1, vec3_hostseckeys[0], cmsg1_bad, vec3_aux_rands[0]);
CHECK(fault == SECP256K1_CHILLDKG_UNKNOWN_FAULTY_PARTICIPANT_OR_COORDINATOR);
CHECK(fault_index == UINT32_MAX);
/* An invalid pubnonce of participant 1 blames that participant or the
* coordinator. */
CHECK(secp256k1_chilldkg_participant_step1(CTX, &fresh_state1, pmsg1[0], vec3_hostseckeys[0], hostpubkeys33, n, t, vec3_randoms[0]) == 1);
memcpy(cmsg1_bad, cmsg1, cmsg1_len);
memset(cmsg1_bad + 324 + 33, 0xff, 33);
fault_index = 0;
fault = secp256k1_chilldkg_participant_step2(CTX, &state2[0], tmp_sig64, &fault_index, &fresh_state1, vec3_hostseckeys[0], cmsg1_bad, vec3_aux_rands[0]);
CHECK(fault == SECP256K1_CHILLDKG_FAULTY_PARTICIPANT_OR_COORDINATOR);
CHECK(fault_index == 1);
/* An encrypted secshare that overflows the group order blames the
* coordinator. */
CHECK(secp256k1_chilldkg_participant_step1(CTX, &fresh_state1, pmsg1[0], vec3_hostseckeys[0], hostpubkeys33, n, t, vec3_randoms[0]) == 1);
memcpy(cmsg1_bad, cmsg1, cmsg1_len);
memset(cmsg1_bad + enc_cmsg_len, 0xff, 32);
fault = secp256k1_chilldkg_participant_step2(CTX, &state2[0], tmp_sig64, &fault_index, &fresh_state1, vec3_hostseckeys[0], cmsg1_bad, vec3_aux_rands[0]);
CHECK(fault == SECP256K1_CHILLDKG_FAULTY_COORDINATOR);
}
/* participant_finalize: an invalid signature in the certificate blames
* the coordinator and zeroes the outputs. */
{
secp256k1_chilldkg_participant_state1 fresh_state1;
secp256k1_chilldkg_participant_state2 fresh_state2;
unsigned char tmp_sig64[64];
CHECK(secp256k1_chilldkg_participant_step1(CTX, &fresh_state1, pmsg1[0], vec3_hostseckeys[0], hostpubkeys33, n, t, vec3_randoms[0]) == 1);
fault = secp256k1_chilldkg_participant_step2(CTX, &fresh_state2, tmp_sig64, &fault_index, &fresh_state1, vec3_hostseckeys[0], cmsg1, vec3_aux_rands[0]);
CHECK(fault == SECP256K1_CHILLDKG_OK);
memcpy(cmsg2_bad, cmsg2, sizeof(cmsg2));
cmsg2_bad[64 + 10] ^= 1; /* corrupt the signature of participant 1 */
fault_index = 0;
fault = secp256k1_chilldkg_participant_finalize(CTX, secshare32, buf33, pubshares33, recovery, &fault_index, &fresh_state2, cmsg2_bad);
CHECK(fault == SECP256K1_CHILLDKG_FAULTY_COORDINATOR);
CHECK(fault_index == 1);
CHECK(secp256k1_is_zero_array(secshare32, 32));
CHECK(secp256k1_is_zero_array(buf33, 33));
CHECK(secp256k1_is_zero_array(pubshares33, 33 * n));
CHECK(secp256k1_is_zero_array(recovery, recovery_len));
}
/* API misuse. */
CHECK_ILLEGAL(CTX, secp256k1_chilldkg_hostpubkey_gen(CTX, NULL, vec3_hostseckeys[0]));
CHECK_ILLEGAL(CTX, secp256k1_chilldkg_hostpubkey_gen(CTX, buf33, NULL));
CHECK_ILLEGAL(CTX, secp256k1_chilldkg_params_hash(CTX, NULL, hostpubkeys33, n, t));
CHECK_ILLEGAL(CTX, secp256k1_chilldkg_params_hash(CTX, buf32, NULL, n, t));
CHECK_ILLEGAL(CTX, secp256k1_chilldkg_participant_step1(CTX, NULL, pmsg1[0], vec3_hostseckeys[0], hostpubkeys33, n, t, vec3_randoms[0]));
CHECK_ILLEGAL(CTX, secp256k1_chilldkg_participant_step1(CTX, &state1[0], NULL, vec3_hostseckeys[0], hostpubkeys33, n, t, vec3_randoms[0]));
CHECK_ILLEGAL(CTX, secp256k1_chilldkg_participant_step1(CTX, &state1[0], pmsg1[0], NULL, hostpubkeys33, n, t, vec3_randoms[0]));
CHECK_ILLEGAL(CTX, secp256k1_chilldkg_participant_step1(CTX, &state1[0], pmsg1[0], vec3_hostseckeys[0], NULL, n, t, vec3_randoms[0]));
CHECK_ILLEGAL(CTX, secp256k1_chilldkg_participant_step1(CTX, &state1[0], pmsg1[0], vec3_hostseckeys[0], hostpubkeys33, n, t, NULL));
/* API misuse of the enum-returning functions: the illegal-argument
* callback fires exactly once and the function returns INVALID_INPUT. */
CHECK_ILLEGAL_VOID(CTX, CHECK(secp256k1_chilldkg_participant_step2(CTX, NULL, sig64[0], &fault_index, &state1[0], vec3_hostseckeys[0], cmsg1, vec3_aux_rands[0]) == SECP256K1_CHILLDKG_INVALID_INPUT));
CHECK_ILLEGAL_VOID(CTX, CHECK(secp256k1_chilldkg_participant_step2(CTX, &state2[0], NULL, &fault_index, &state1[0], vec3_hostseckeys[0], cmsg1, vec3_aux_rands[0]) == SECP256K1_CHILLDKG_INVALID_INPUT));
CHECK_ILLEGAL_VOID(CTX, CHECK(secp256k1_chilldkg_participant_step2(CTX, &state2[0], sig64[0], NULL, &state1[0], vec3_hostseckeys[0], cmsg1, vec3_aux_rands[0]) == SECP256K1_CHILLDKG_INVALID_INPUT));
CHECK_ILLEGAL_VOID(CTX, CHECK(secp256k1_chilldkg_participant_step2(CTX, &state2[0], sig64[0], &fault_index, NULL, vec3_hostseckeys[0], cmsg1, vec3_aux_rands[0]) == SECP256K1_CHILLDKG_INVALID_INPUT));
CHECK_ILLEGAL_VOID(CTX, CHECK(secp256k1_chilldkg_participant_step2(CTX, &state2[0], sig64[0], &fault_index, &state1[0], NULL, cmsg1, vec3_aux_rands[0]) == SECP256K1_CHILLDKG_INVALID_INPUT));
CHECK_ILLEGAL_VOID(CTX, CHECK(secp256k1_chilldkg_participant_step2(CTX, &state2[0], sig64[0], &fault_index, &state1[0], vec3_hostseckeys[0], NULL, vec3_aux_rands[0]) == SECP256K1_CHILLDKG_INVALID_INPUT));
CHECK_ILLEGAL_VOID(CTX, CHECK(secp256k1_chilldkg_participant_step2(CTX, &state2[0], sig64[0], &fault_index, &state1[0], vec3_hostseckeys[0], cmsg1, NULL) == SECP256K1_CHILLDKG_INVALID_INPUT));
CHECK_ILLEGAL_VOID(CTX, CHECK(secp256k1_chilldkg_participant_finalize(CTX, NULL, buf33, pubshares33, recovery, &fault_index, &state2[0], cmsg2) == SECP256K1_CHILLDKG_INVALID_INPUT));
CHECK_ILLEGAL_VOID(CTX, CHECK(secp256k1_chilldkg_participant_finalize(CTX, secshare32, NULL, pubshares33, recovery, &fault_index, &state2[0], cmsg2) == SECP256K1_CHILLDKG_INVALID_INPUT));
CHECK_ILLEGAL_VOID(CTX, CHECK(secp256k1_chilldkg_participant_finalize(CTX, secshare32, buf33, NULL, recovery, &fault_index, &state2[0], cmsg2) == SECP256K1_CHILLDKG_INVALID_INPUT));
CHECK_ILLEGAL_VOID(CTX, CHECK(secp256k1_chilldkg_participant_finalize(CTX, secshare32, buf33, pubshares33, NULL, &fault_index, &state2[0], cmsg2) == SECP256K1_CHILLDKG_INVALID_INPUT));
CHECK_ILLEGAL_VOID(CTX, CHECK(secp256k1_chilldkg_participant_finalize(CTX, secshare32, buf33, pubshares33, recovery, NULL, &state2[0], cmsg2) == SECP256K1_CHILLDKG_INVALID_INPUT));
CHECK_ILLEGAL_VOID(CTX, CHECK(secp256k1_chilldkg_participant_finalize(CTX, secshare32, buf33, pubshares33, recovery, &fault_index, NULL, cmsg2) == SECP256K1_CHILLDKG_INVALID_INPUT));
CHECK_ILLEGAL_VOID(CTX, CHECK(secp256k1_chilldkg_participant_finalize(CTX, secshare32, buf33, pubshares33, recovery, &fault_index, &state2[0], NULL) == SECP256K1_CHILLDKG_INVALID_INPUT));
/* States with a bad magic (uninitialized or corrupted) are rejected. */
{
secp256k1_chilldkg_participant_state1 bad_state1;
secp256k1_chilldkg_participant_state2 bad_state2;
unsigned char tmp_sig64[64];
memset(&bad_state1, 0, sizeof(bad_state1));
memset(&bad_state2, 0, sizeof(bad_state2));
CHECK_ILLEGAL_VOID(CTX, CHECK(secp256k1_chilldkg_participant_step2(CTX, &state2[0], tmp_sig64, &fault_index, &bad_state1, vec3_hostseckeys[0], cmsg1, vec3_aux_rands[0]) == SECP256K1_CHILLDKG_INVALID_INPUT));
CHECK_ILLEGAL_VOID(CTX, CHECK(secp256k1_chilldkg_participant_finalize(CTX, secshare32, buf33, pubshares33, recovery, &fault_index, &bad_state2, cmsg2) == SECP256K1_CHILLDKG_INVALID_INPUT));
CHECK(secp256k1_chilldkg_participant_step1(CTX, &bad_state1, pmsg1[0], vec3_hostseckeys[0], hostpubkeys33, n, t, vec3_randoms[0]) == 1);
bad_state1.data[0] ^= 1;
CHECK_ILLEGAL_VOID(CTX, CHECK(secp256k1_chilldkg_participant_step2(CTX, &state2[0], tmp_sig64, &fault_index, &bad_state1, vec3_hostseckeys[0], cmsg1, vec3_aux_rands[0]) == SECP256K1_CHILLDKG_INVALID_INPUT));
}
for (i = 0; i < n; i++) {
secp256k1_scalar_clear(&enc_secshares[i]);
}
}
static const struct tf_test_entry tests_chilldkg[] = {
CASE1(chilldkg_tagged_hashes_test),
CASE1(chilldkg_params_hash_test),
@@ -968,6 +1272,7 @@ static const struct tf_test_entry tests_chilldkg[] = {
CASE1(chilldkg_vss_test),
CASE1(chilldkg_simplpedpop_test),
CASE1(chilldkg_encpedpop_test),
CASE1(chilldkg_participant_api_test),
};
#endif

View File

@@ -7,36 +7,16 @@
#define SECP256K1_MODULE_CHILLDKG_UTIL_H
#include "../../../include/secp256k1.h"
#include "../../../include/secp256k1_chilldkg.h"
#include "../../group.h"
#include "../../hash.h"
#include "../../scalar.h"
/* Maximum number of participants (and thus the maximum threshold) supported by
* this module. The state objects of the module are fixed-size and do not use
* dynamic allocation, so a compile-time cap is required. This matches the
* frost module's convention; the public header will expose the same constant
* in a later phase. */
#define SECP256K1_CHILLDKG_MAX_PARTICIPANTS 128
/* Return codes of the internal SimplPedPop/EncPedPop functions, mirroring the
* exception taxonomy of chilldkg_ref/util.py. The public API will expose its
* own enum in a later phase.
*
* For SECP256K1_CHILLDKG_FAULTY_PARTICIPANT and
* SECP256K1_CHILLDKG_FAULTY_PARTICIPANT_OR_COORDINATOR, the fault_index output
* of the failing function receives the index of the (suspected) faulty
* participant. For SECP256K1_CHILLDKG_UNKNOWN_FAULTY_PARTICIPANT_OR_COORDINATOR
* the faulty party cannot be identified without the investigation procedure
* (a later phase); fault_index is set to UINT32_MAX. */
typedef enum {
SECP256K1_CHILLDKG_SUCCESS = 0,
SECP256K1_CHILLDKG_FAULTY_COORDINATOR,
SECP256K1_CHILLDKG_FAULTY_PARTICIPANT,
SECP256K1_CHILLDKG_FAULTY_PARTICIPANT_OR_COORDINATOR,
SECP256K1_CHILLDKG_UNKNOWN_FAULTY_PARTICIPANT_OR_COORDINATOR,
SECP256K1_CHILLDKG_INVALID_INPUT
} secp256k1_chilldkg_fault;
/* The public header defines SECP256K1_CHILLDKG_MAX_PARTICIPANTS and the fault
* enum secp256k1_chilldkg_fault (SECP256K1_CHILLDKG_OK, ...), which the
* internal SimplPedPop/EncPedPop functions use directly as their return
* codes. */
/* This file contains the internal primitives of the ChillDKG module that
* mirror chilldkg_ref/util.py, secp256k1lab/bip340.py and secp256k1lab/ecdh.py
@@ -120,6 +100,6 @@ static void secp256k1_chilldkg_encpedpop_self_pad(const secp256k1_hash_ctx *hash
/* Compute the session parameters hash (chilldkg.py `params_hash`):
* out32 = TH("BIP DKG/params_hash", u32be(t) || hostpubkeys[0] || ... || hostpubkeys[n-1])
* where hostpubkeys33 is an array of n 33-byte compressed host public keys. */
static void secp256k1_chilldkg_params_hash(const secp256k1_hash_ctx *hash_ctx, unsigned char *out32, const unsigned char *hostpubkeys33, size_t n, uint32_t t);
static void secp256k1_chilldkg_params_hash_internal(const secp256k1_hash_ctx *hash_ctx, unsigned char *out32, const unsigned char *hostpubkeys33, size_t n, uint32_t t);
#endif

View File

@@ -371,7 +371,7 @@ static void secp256k1_chilldkg_encpedpop_self_pad(const secp256k1_hash_ctx *hash
secp256k1_memclear_explicit(hash32, sizeof(hash32));
}
static void secp256k1_chilldkg_params_hash(const secp256k1_hash_ctx *hash_ctx, unsigned char *out32, const unsigned char *hostpubkeys33, size_t n, uint32_t t) {
static void secp256k1_chilldkg_params_hash_internal(const secp256k1_hash_ctx *hash_ctx, unsigned char *out32, const unsigned char *hostpubkeys33, size_t n, uint32_t t) {
unsigned char buf[4];
secp256k1_sha256 sha;