From d48a1579cfce42740f2a060bcdb9d0e49b7e88c9 Mon Sep 17 00:00:00 2001 From: Kgothatso Ngako Date: Mon, 31 Aug 2026 06:09:03 +0200 Subject: [PATCH] chilldkg: Phase 5 - recovery, recovery acks, investigation Complete the ChillDKG protocol surface with the recovery and blame- attribution procedures (bip-frost-dkg v0.3.0-dev, reference commit a91896883f85b159415ecf298d5e844879af112d). Recovery: - secp256k1_chilldkg_participant_recover / _coordinator_recover: parse the self-delimiting recovery layout u32be(t) || sum_coms(33t) || hostpubkeys(33n) || pubnonces(33n) || enc_secshares(32n, checked) || cert(64n), deriving n = (len-4-33t)/162 exactly as the reference's deserialize_recovery_data; re-verify the certificate, recompute the TapTweak and the receiver's ECDH/self pads from (hostseckey, pubnonces, enc_context), recompute the tweaked secshare, and sanity-check secshare*G == pubshares[own]. Also return hostpubkeys, n and t so callers can re-derive session params. RecoveryDataError / HostSeckeyError / params failures map to INVALID_INPUT (no index, as in the reference); an invalid pubnonce during decrypt passes through as FAULTY_PARTICIPANT_OR_COORDINATOR(i), matching the reference leaking that exception from recover(). Recovery acks: - secp256k1_chilldkg_recovery_ack_sign / _acks_verify: BIP-340 (standard BIP0340 tags) over pad33("BIP DKG/recovery acknowledgment") || u32be(i) || recovery_data. Verification failure maps to FAULTY_PARTICIPANT(i) (InvalidRecoveryAckError subclasses FaultyParticipantError in the reference). Investigation: - secp256k1_chilldkg_coordinator_investigate: builds one 65n-byte per-participant message (per-dealer encrypted partial secshares (32n) + partial pubshares (33n)) per call; the reference returns all n at once -- equivalent, the caller iterates. - secp256k1_chilldkg_participant_investigate: the reference's three-step blame attribution -- sum-of-pubshares check -> FAULTY_COORDINATOR; sum-of-secshares check -> FAULTY_COORDINATOR (covers the reference's SecshareSumError translation); per-dealer decrypted share vs commitment -> FAULTY_PARTICIPANT_OR_COORDINATOR(i) (or FAULTY_COORDINATOR for the own index); all-consistent -> INVALID_INPUT (the reference's uncaught RuntimeError). - Investigation data is transported via a new opaque, secret-bearing secp256k1_chilldkg_participant_inv_data object (4205 bytes, magic-validated save/load, secret-cleared) filled by participant_step2 on the UNKNOWN_FAULT paths. This amends the Phase 3 participant_step2 signature with a nullable inv_data out-param -- required because recomputing inside participant_investigate would duplicate step2's decrypt/verify logic. - New length helper secp256k1_chilldkg_investigation_msg_len (65n). tests_impl.h: chilldkg_recovery_test (recover roundtrips byte-exact vs the session outputs and reference vectors, tampered/truncated/ over-long recovery data, unknown/invalid hostseckey, ack sign byte-exact + verify with wrong-index and tampered-ack blame, params/recovery mismatch rejects, misuse) and chilldkg_investigate_test (two end-to-end public-API scenarios generated from the reference: dealer corrupting a participant's encrypted share, and coordinator tampering with an enc_secshare; cmsg1/cinv/inv_data byte-exact, blame codes and indices matching the reference's exception type and index; malformed cinv -> FAULTY_COORDINATOR; malformed pmsg1 -> FAULTY_PARTICIPANT(j); misuse). The all-consistent investigate path is not constructible without discrete logs and matches the reference's unreachable RuntimeError. Verified: make check 3/3 (incl. noverify); CMake ctest 369/369; make distdir clean. --- include/secp256k1_chilldkg.h | 270 ++++++++++++- src/modules/chilldkg/encpedpop.h | 28 +- src/modules/chilldkg/encpedpop_impl.h | 69 +++- src/modules/chilldkg/main_impl.h | 481 +++++++++++++++++++++++- src/modules/chilldkg/simplpedpop.h | 29 +- src/modules/chilldkg/simplpedpop_impl.h | 92 ++++- src/modules/chilldkg/tests_impl.h | 410 ++++++++++++++++++-- 7 files changed, 1334 insertions(+), 45 deletions(-) diff --git a/include/secp256k1_chilldkg.h b/include/secp256k1_chilldkg.h index 57ac76a4..de25bd86 100644 --- a/include/secp256k1_chilldkg.h +++ b/include/secp256k1_chilldkg.h @@ -112,6 +112,21 @@ typedef struct secp256k1_chilldkg_participant_state2 { unsigned char data[12 + 4 + 131 * SECP256K1_CHILLDKG_MAX_PARTICIPANTS + 32 + 33 + 33 * SECP256K1_CHILLDKG_MAX_PARTICIPANTS]; } secp256k1_chilldkg_participant_state2; +/** Opaque data structure that holds the investigation data output by + * secp256k1_chilldkg_participant_step2 when it returns + * SECP256K1_CHILLDKG_UNKNOWN_FAULTY_PARTICIPANT_OR_COORDINATOR, to be passed + * to secp256k1_chilldkg_participant_investigate together with the + * coordinator's investigation message. + * + * This structure contains secret data (the untweaked secret share and the + * decryption pads); it MUST be kept secret. + * + * Guaranteed to be 4205 bytes in size. + */ +typedef struct secp256k1_chilldkg_participant_inv_data { + unsigned char data[12 + 32 + 33 + 32 + 32 * SECP256K1_CHILLDKG_MAX_PARTICIPANTS]; +} secp256k1_chilldkg_participant_inv_data; + /** Opaque data structure that holds the coordinator's session state after * secp256k1_chilldkg_coordinator_step1, to be passed to * secp256k1_chilldkg_coordinator_finalize (it must not be reused). @@ -261,6 +276,12 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_chilldkg_participant_st * fault_index: pointer to a uint32 that receives the identifier of * the (suspected) faulty participant where applicable, * and UINT32_MAX otherwise + * inv_data: pointer to a participant_inv_data object that + * receives the investigation data if (and only if) the + * return value is + * SECP256K1_CHILLDKG_UNKNOWN_FAULTY_PARTICIPANT_OR_COORDINATOR, + * or NULL if the caller does not intend to run + * secp256k1_chilldkg_participant_investigate * In: state1: pointer to the state1 object output by * secp256k1_chilldkg_participant_step1 * hostseckey32: pointer to the 32-byte host secret key (must be the @@ -276,11 +297,12 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT secp256k1_chilldkg_fault secp256k1_ch secp256k1_chilldkg_participant_state2 *state2, unsigned char *sig64, uint32_t *fault_index, + secp256k1_chilldkg_participant_inv_data *inv_data, const secp256k1_chilldkg_participant_state1 *state1, const unsigned char *hostseckey32, const unsigned char *cmsg1, const unsigned char *aux_rand32 -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6) SECP256K1_ARG_NONNULL(7) SECP256K1_ARG_NONNULL(8); +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(6) SECP256K1_ARG_NONNULL(7) SECP256K1_ARG_NONNULL(8) SECP256K1_ARG_NONNULL(9); /** Perform a participant's final step of a ChillDKG session. * @@ -400,6 +422,252 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT secp256k1_chilldkg_fault secp256k1_ch const unsigned char *const *pmsgs2 ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6) SECP256K1_ARG_NONNULL(7) SECP256K1_ARG_NONNULL(8); +/** Length of a coordinator investigation message (cinv): 65*n bytes (n + * encrypted partial secret shares of 32 bytes each, followed by n partial + * public shares of 33 bytes each). Returns 0 if n is out of range. */ +SECP256K1_API size_t secp256k1_chilldkg_investigation_msg_len( + size_t n_participants +); + +/** Recover the DKG output of a participant from recovery data. + * + * This function serves two purposes: recovering from a failure of + * secp256k1_chilldkg_participant_finalize after obtaining the recovery data + * from another participant or the coordinator, and reproducing the DKG + * outputs on a new device (e.g., after data loss). + * + * The recovery data (see secp256k1_chilldkg_recovery_data_len) is + * self-delimiting: the number of participants n and the threshold t are + * derived from its length and contents. + * + * Returns: SECP256K1_CHILLDKG_OK on success, + * SECP256K1_CHILLDKG_INVALID_INPUT if the recovery data is invalid + * (this includes an invalid certificate) or if the host secret key + * is invalid or does not match any host public key in the recovery + * data (RecoveryDataError and HostSeckeyError in the reference + * implementation), or + * SECP256K1_CHILLDKG_FAULTY_PARTICIPANT_OR_COORDINATOR (with + * fault_index set) if a pubnonce in the recovery data is invalid. + * On failure, secshare32 and thresh_pk33 are set to zero and + * *n_participants_out and *threshold_out are set to 0; pubshares33 + * and hostpubkeys33_out are set to zero whenever n could be + * determined. + * Args: ctx: pointer to a context object + * Out: secshare32: pointer to a 32-byte array to store the + * (tweaked) secret share + * thresh_pk33: pointer to a 33-byte array to store the + * threshold public key (compressed serialization) + * pubshares33: pointer to an array capable of holding + * 33*SECP256K1_CHILLDKG_MAX_PARTICIPANTS bytes + * to store the public shares (33 bytes each; the + * first n entries are filled) + * hostpubkeys33_out: pointer to an array capable of holding + * 33*SECP256K1_CHILLDKG_MAX_PARTICIPANTS bytes + * to store the host public keys of the recovered + * session (the first n entries are filled) + * n_participants_out: pointer to a size_t to store the number of + * participants n of the recovered session + * threshold_out: pointer to a uint32 to store the threshold t + * of the recovered session + * fault_index: pointer to a uint32 that receives the identifier + * of the suspected participant where applicable, + * and UINT32_MAX otherwise + * In: hostseckey32: pointer to the 32-byte host secret key + * recovery: pointer to the recovery data + * recovery_len: length of the recovery data + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT secp256k1_chilldkg_fault secp256k1_chilldkg_participant_recover( + const secp256k1_context *ctx, + unsigned char *secshare32, + unsigned char *thresh_pk33, + unsigned char *pubshares33, + unsigned char *hostpubkeys33_out, + size_t *n_participants_out, + uint32_t *threshold_out, + uint32_t *fault_index, + const unsigned char *hostseckey32, + const unsigned char *recovery, + size_t recovery_len +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6) SECP256K1_ARG_NONNULL(7) SECP256K1_ARG_NONNULL(8) SECP256K1_ARG_NONNULL(9) SECP256K1_ARG_NONNULL(10); + +/** Recover the DKG output of the coordinator from recovery data. + * + * Like secp256k1_chilldkg_participant_recover, but for the coordinator, who + * has no secret share. + * + * Returns: SECP256K1_CHILLDKG_OK on success, + * SECP256K1_CHILLDKG_INVALID_INPUT if the recovery data is invalid. + * On failure, thresh_pk33 is set to zero and *n_participants_out + * and *threshold_out are set to 0; pubshares33 and + * hostpubkeys33_out are set to zero whenever n could be determined. + * Args: ctx: pointer to a context object + * Out: thresh_pk33: pointer to a 33-byte array to store the + * threshold public key (compressed serialization) + * pubshares33: pointer to an array capable of holding + * 33*SECP256K1_CHILLDKG_MAX_PARTICIPANTS bytes + * to store the public shares (the first n + * entries are filled) + * hostpubkeys33_out: pointer to an array capable of holding + * 33*SECP256K1_CHILLDKG_MAX_PARTICIPANTS bytes + * to store the host public keys of the recovered + * session (the first n entries are filled) + * n_participants_out: pointer to a size_t to store n + * threshold_out: pointer to a uint32 to store t + * In: recovery: pointer to the recovery data + * recovery_len: length of the recovery data + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT secp256k1_chilldkg_fault secp256k1_chilldkg_coordinator_recover( + const secp256k1_context *ctx, + unsigned char *thresh_pk33, + unsigned char *pubshares33, + unsigned char *hostpubkeys33_out, + size_t *n_participants_out, + uint32_t *threshold_out, + const unsigned char *recovery, + size_t recovery_len +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6) SECP256K1_ARG_NONNULL(7); + +/** Sign recovery data to create a recovery acknowledgment. + * + * The acknowledgment signature (64 bytes) is a BIP 340 signature over + * pad33("BIP DKG/recovery acknowledgment") || u32be(participant_id) || + * recovery_data under the participant's host key. Acks can be collected in + * an optional acknowledgment round to confirm that all participants have + * received the recovery data. + * + * Returns: 1 on success, 0 on invalid input (invalid host secret key, host + * secret key not matching any host public key, invalid session + * parameters, or recovery data that is invalid or does not match + * the session parameters). On failure, sig64 is set to zero. + * Args: ctx: pointer to a context object + * Out: sig64: pointer to a 64-byte array to store the + * acknowledgment signature + * In: hostseckey32: pointer to the 32-byte host secret key + * hostpubkeys33: pointer to an array of n host public keys (33 bytes + * each) + * n_participants: total number of participants n + * threshold: threshold t + * recovery: pointer to the recovery data + * recovery_len: length of the recovery data + * aux_rand32: pointer to 32 bytes of auxiliary randomness (see + * BIP 340) + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_chilldkg_recovery_ack_sign( + const secp256k1_context *ctx, + unsigned char *sig64, + const unsigned char *hostseckey32, + const unsigned char *hostpubkeys33, + size_t n_participants, + uint32_t threshold, + const unsigned char *recovery, + size_t recovery_len, + const unsigned char *aux_rand32 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(7) SECP256K1_ARG_NONNULL(9); + +/** Verify recovery acknowledgment signatures from all participants. + * + * Note that a failure of this function does NOT mean the DKG failed + * (reaching this point implies the DKG itself was successful). It only means + * it cannot be confirmed that all participants have a copy of the recovery + * data. + * + * Returns: SECP256K1_CHILLDKG_OK if all n acknowledgment signatures are + * valid, SECP256K1_CHILLDKG_INVALID_INPUT on invalid session + * parameters or recovery data, or SECP256K1_CHILLDKG_FAULTY_PARTICIPANT + * (with fault_index set to the signer) if an acknowledgment + * signature is invalid (InvalidRecoveryAckError in the reference + * implementation). + * Args: ctx: pointer to a context object + * Out: fault_index: pointer to a uint32 that receives the identifier of + * the participant whose signature is invalid, where + * applicable, and UINT32_MAX otherwise + * In: hostpubkeys33: pointer to an array of n host public keys (33 bytes + * each) + * n_participants: total number of participants n + * threshold: threshold t + * recovery: pointer to the recovery data + * recovery_len: length of the recovery data + * ack_sigs64: array of n pointers to the acknowledgment + * signatures (64 bytes each), in the same order as + * hostpubkeys33 + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT secp256k1_chilldkg_fault secp256k1_chilldkg_recovery_acks_verify( + const secp256k1_context *ctx, + uint32_t *fault_index, + const unsigned char *hostpubkeys33, + size_t n_participants, + uint32_t threshold, + const unsigned char *recovery, + size_t recovery_len, + const unsigned char *const *ack_sigs64 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(6) SECP256K1_ARG_NONNULL(8); + +/** Generate the investigation message for a single participant. + * + * The investigation message (65*n bytes, see + * secp256k1_chilldkg_investigation_msg_len) allows the given participant to + * investigate who is to blame for a failed ChillDKG session (see + * secp256k1_chilldkg_participant_investigate). The message contains no + * confidential information and can be safely broadcast. (Unlike the + * reference implementation, which computes the investigation messages for + * all participants at once, this function computes one message per call.) + * + * Returns: SECP256K1_CHILLDKG_OK on success, + * SECP256K1_CHILLDKG_INVALID_INPUT on invalid session parameters or + * participant_id >= n, or SECP256K1_CHILLDKG_FAULTY_PARTICIPANT + * (with fault_index set to the sender) if a participant message is + * malformed. On failure, cinv is set to zero. + * Args: ctx: pointer to a context object + * Out: cinv: pointer to a 65*n byte array to store the + * investigation message for the given participant + * fault_index: pointer to a uint32 (see above) + * In: pmsgs1: array of n pointers to the participants' first + * messages (33*t + 32*n + 97 bytes each) + * hostpubkeys33: pointer to an array of n host public keys (33 bytes + * each) + * n_participants: total number of participants n + * threshold: threshold t + * participant_id: the participant the investigation message is for + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT secp256k1_chilldkg_fault secp256k1_chilldkg_coordinator_investigate( + const secp256k1_context *ctx, + unsigned char *cinv, + uint32_t *fault_index, + const unsigned char *const *pmsgs1, + const unsigned char *hostpubkeys33, + size_t n_participants, + uint32_t threshold, + uint32_t participant_id +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); + +/** Investigate who is to blame for a failed ChillDKG session. + * + * This function can be called when secp256k1_chilldkg_participant_step2 + * returned SECP256K1_CHILLDKG_UNKNOWN_FAULTY_PARTICIPANT_OR_COORDINATOR. It + * narrows down the suspected faulty parties by analyzing the investigation + * message provided by the coordinator. + * + * Returns: SECP256K1_CHILLDKG_FAULTY_COORDINATOR if the coordinator is + * faulty, SECP256K1_CHILLDKG_FAULTY_PARTICIPANT_OR_COORDINATOR + * (with fault_index set to the suspected participant) if another + * participant or the coordinator is faulty, or + * SECP256K1_CHILLDKG_INVALID_INPUT if all inputs are consistent + * (i.e., the function was called even though no fault occurred). + * Args: ctx: pointer to a context object + * Out: fault_index: pointer to a uint32 (see above) + * In: inv_data: pointer to the participant_inv_data object output by + * secp256k1_chilldkg_participant_step2 + * cinv: pointer to the coordinator's investigation message + * for this participant (65*n bytes, see + * secp256k1_chilldkg_investigation_msg_len) + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT secp256k1_chilldkg_fault secp256k1_chilldkg_participant_investigate( + const secp256k1_context *ctx, + uint32_t *fault_index, + const secp256k1_chilldkg_participant_inv_data *inv_data, + const unsigned char *cinv +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + #ifdef __cplusplus } #endif diff --git a/src/modules/chilldkg/encpedpop.h b/src/modules/chilldkg/encpedpop.h index 37f8aaf7..e9aaa62b 100644 --- a/src/modules/chilldkg/encpedpop.h +++ b/src/modules/chilldkg/encpedpop.h @@ -95,6 +95,16 @@ typedef struct { * public keys) or on negligible-probability derivation failures. */ static int secp256k1_chilldkg_encpedpop_participant_step1(const secp256k1_context *ctx, secp256k1_chilldkg_encpedpop_participant_state *state, unsigned char *pmsg, const unsigned char *seed32, const unsigned char *deckey32, const unsigned char *enckeys33, uint32_t t, uint32_t participant_id, const unsigned char *random32, size_t n); +/* Investigation data captured when participant_step2 fails with + * SECP256K1_CHILLDKG_UNKNOWN_FAULTY_PARTICIPANT_OR_COORDINATOR + * (encpedpop.py ParticipantInvestigationData). Contains secrets (the + * untweaked secshare and the pads); clear after use. */ +typedef struct { + secp256k1_chilldkg_simplpedpop_participant_inv_data simpl; + secp256k1_scalar enc_secshare; + secp256k1_scalar pads[SECP256K1_CHILLDKG_MAX_PARTICIPANTS]; +} secp256k1_chilldkg_encpedpop_participant_inv_data; + /* Participant step 2 (encpedpop.py participant_step2): verify that the * coordinator echoed our pubnonce, decrypt our secshare from the summed * encrypted secshare, and run SimplPedPop participant_step2. Outputs the DKG @@ -106,8 +116,22 @@ static int secp256k1_chilldkg_encpedpop_participant_step1(const secp256k1_contex * 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 * UNKNOWN_FAULTY_PARTICIPANT_OR_COORDINATOR if the decrypted secshare does - * not match the pubshare. */ -static secp256k1_chilldkg_fault secp256k1_chilldkg_encpedpop_participant_step2(const secp256k1_context *ctx, secp256k1_chilldkg_simplpedpop_dkg_output *dkg_output, unsigned char *eq_input, uint32_t *fault_index, const secp256k1_chilldkg_encpedpop_participant_state *state, const unsigned char *deckey32, const unsigned char *cmsg, size_t cmsg_len, const secp256k1_scalar *enc_secshare); + * not match the pubshare; in the latter case, and only in that case, inv_data + * (if not NULL) is filled for + * secp256k1_chilldkg_encpedpop_participant_investigate. */ +static secp256k1_chilldkg_fault secp256k1_chilldkg_encpedpop_participant_step2(const secp256k1_context *ctx, secp256k1_chilldkg_simplpedpop_dkg_output *dkg_output, unsigned char *eq_input, uint32_t *fault_index, secp256k1_chilldkg_encpedpop_participant_inv_data *inv_data, const secp256k1_chilldkg_encpedpop_participant_state *state, const unsigned char *deckey32, const unsigned char *cmsg, size_t cmsg_len, const secp256k1_scalar *enc_secshare); + +/* Determine the faulty party after an + * UNKNOWN_FAULTY_PARTICIPANT_OR_COORDINATOR fault of participant_step2 + * (encpedpop.py participant_investigate). cinv is the coordinator's + * investigation message for this participant (65*n bytes: n encrypted partial + * secshares || n partial pubshares). Returns FAULTY_COORDINATOR if the + * investigation message is malformed or if the coordinator is to blame, + * FAULTY_PARTICIPANT_OR_COORDINATOR(i) (with *fault_index = i) if participant + * i is to blame, and SECP256K1_CHILLDKG_INVALID_INPUT if all inputs are + * consistent (i.e., the function was called even though no fault occurred; + * the reference raises a RuntimeError in this case). */ +static secp256k1_chilldkg_fault secp256k1_chilldkg_encpedpop_participant_investigate(const secp256k1_context *ctx, uint32_t *fault_index, const secp256k1_chilldkg_encpedpop_participant_inv_data *inv_data, const unsigned char *cinv, size_t cinv_len); /* Coordinator step (encpedpop.py coordinator_step): parse the n participant * messages (each 33*t + 64 + 33 + 32*n bytes), aggregate SimplPedPop, and sum diff --git a/src/modules/chilldkg/encpedpop_impl.h b/src/modules/chilldkg/encpedpop_impl.h index d9bd6bb3..46ca623a 100644 --- a/src/modules/chilldkg/encpedpop_impl.h +++ b/src/modules/chilldkg/encpedpop_impl.h @@ -240,7 +240,7 @@ static int secp256k1_chilldkg_encpedpop_participant_step1(const secp256k1_contex return ret; } -static secp256k1_chilldkg_fault secp256k1_chilldkg_encpedpop_participant_step2(const secp256k1_context *ctx, secp256k1_chilldkg_simplpedpop_dkg_output *dkg_output, unsigned char *eq_input, uint32_t *fault_index, const secp256k1_chilldkg_encpedpop_participant_state *state, const unsigned char *deckey32, const unsigned char *cmsg, size_t cmsg_len, const secp256k1_scalar *enc_secshare) { +static secp256k1_chilldkg_fault secp256k1_chilldkg_encpedpop_participant_step2(const secp256k1_context *ctx, secp256k1_chilldkg_simplpedpop_dkg_output *dkg_output, unsigned char *eq_input, uint32_t *fault_index, secp256k1_chilldkg_encpedpop_participant_inv_data *inv_data, const secp256k1_chilldkg_encpedpop_participant_state *state, const unsigned char *deckey32, const unsigned char *cmsg, size_t cmsg_len, const secp256k1_scalar *enc_secshare) { const uint32_t t = state->simpl_state.t; const uint32_t n = state->simpl_state.n; const uint32_t participant_id = state->simpl_state.participant_id; @@ -290,7 +290,17 @@ static secp256k1_chilldkg_fault secp256k1_chilldkg_encpedpop_participant_step2(c secp256k1_scalar_negate(&secshare, &padsum); secp256k1_scalar_add(&secshare, &secshare, enc_secshare); - fault = secp256k1_chilldkg_simplpedpop_participant_step2(ctx, dkg_output, eq_input, fault_index, &state->simpl_state, cmsg, simpl_cmsg_len, &secshare); + fault = secp256k1_chilldkg_simplpedpop_participant_step2(ctx, dkg_output, eq_input, fault_index, inv_data != NULL ? &inv_data->simpl : NULL, &state->simpl_state, cmsg, simpl_cmsg_len, &secshare); + + /* On an UNKNOWN fault, capture the encrypted secshare and the pads so + * that the investigation procedure can decrypt the partial secshares + * (inv_data->simpl was filled by the SimplPedPop participant_step2). */ + if (fault == SECP256K1_CHILLDKG_UNKNOWN_FAULTY_PARTICIPANT_OR_COORDINATOR && inv_data != NULL) { + inv_data->enc_secshare = *enc_secshare; + for (i = 0; i < n; i++) { + inv_data->pads[i] = pads[i]; + } + } secp256k1_scalar_clear(&secshare); secp256k1_scalar_clear(&padsum); @@ -375,4 +385,59 @@ static secp256k1_chilldkg_fault secp256k1_chilldkg_encpedpop_coordinator_step(co return SECP256K1_CHILLDKG_OK; } +static secp256k1_chilldkg_fault secp256k1_chilldkg_encpedpop_participant_investigate(const secp256k1_context *ctx, uint32_t *fault_index, const secp256k1_chilldkg_encpedpop_participant_inv_data *inv_data, const unsigned char *cinv, size_t cinv_len) { + const uint32_t n = inv_data->simpl.n; + secp256k1_ge partial_pubshares[SECP256K1_CHILLDKG_MAX_PARTICIPANTS]; + secp256k1_scalar enc_partial_secshares[SECP256K1_CHILLDKG_MAX_PARTICIPANTS]; + secp256k1_scalar partial_secshares[SECP256K1_CHILLDKG_MAX_PARTICIPANTS]; + secp256k1_chilldkg_fault fault; + uint32_t i; + int overflow; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + ARG_CHECK(fault_index != NULL); + ARG_CHECK(inv_data != NULL); + ARG_CHECK(cinv != NULL); + /* inv_data was written by participant_step2. */ + VERIFY_CHECK(n >= 1 && n <= SECP256K1_CHILLDKG_MAX_PARTICIPANTS); + + /* CoordinatorInvestigationMsg.from_bytes: enc_partial_secshares (32*n) || + * partial_pubshares (33*n). Parse errors blame the coordinator + * (MsgParseError -> FaultyCoordinatorError in the reference). */ + if (cinv_len != 65 * (size_t)n) { + return SECP256K1_CHILLDKG_FAULTY_COORDINATOR; + } + for (i = 0; i < n; i++) { + secp256k1_scalar_set_b32(&enc_partial_secshares[i], cinv + 32 * i, &overflow); + if (overflow) { + fault = SECP256K1_CHILLDKG_FAULTY_COORDINATOR; + goto cleanup; + } + } + for (i = 0; i < n; i++) { + if (!secp256k1_chilldkg_point_load(&partial_pubshares[i], cinv + 32 * n + 33 * i)) { + fault = SECP256K1_CHILLDKG_FAULTY_COORDINATOR; + goto cleanup; + } + } + + /* Decrypt the partial secshares addressed to us. */ + for (i = 0; i < n; i++) { + secp256k1_scalar pad_neg; + secp256k1_scalar_negate(&pad_neg, &inv_data->pads[i]); + secp256k1_scalar_add(&partial_secshares[i], &enc_partial_secshares[i], &pad_neg); + secp256k1_scalar_clear(&pad_neg); + } + + fault = secp256k1_chilldkg_simplpedpop_participant_investigate(ctx, fault_index, &inv_data->simpl, partial_pubshares, partial_secshares); + +cleanup: + for (i = 0; i < n; i++) { + secp256k1_scalar_clear(&enc_partial_secshares[i]); + secp256k1_scalar_clear(&partial_secshares[i]); + } + return fault; +} + #endif diff --git a/src/modules/chilldkg/main_impl.h b/src/modules/chilldkg/main_impl.h index c2b4a44a..9075d91b 100644 --- a/src/modules/chilldkg/main_impl.h +++ b/src/modules/chilldkg/main_impl.h @@ -58,6 +58,70 @@ static int secp256k1_chilldkg_params_validate(const unsigned char *hostpubkeys33 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 }; static const unsigned char secp256k1_chilldkg_coordinator_state_magic[4] = { 0x1b, 0x8e, 0x63, 0xa7 }; +static const unsigned char secp256k1_chilldkg_participant_inv_data_magic[4] = { 0x62, 0x4a, 0xc5, 0x90 }; + +/* An inv_data object consists of + * - 4 byte magic + * - 4 byte participant count n, 4 byte participant_id (big-endian) + * - 32 byte untweaked secshare + * - 33 byte untweaked pubshare (compressed encoding with infinity) + * - 32 byte encrypted secshare + * - 32*SECP256K1_CHILLDKG_MAX_PARTICIPANTS byte pads (only the first n + * entries are meaningful) + * The inv data contains secrets (the untweaked secshare and the pads). */ +static void secp256k1_chilldkg_participant_inv_data_save(secp256k1_chilldkg_participant_inv_data *inv_data, const secp256k1_chilldkg_encpedpop_participant_inv_data *inv_data_i) { + unsigned char *ptr = inv_data->data; + size_t i; + + memcpy(ptr, secp256k1_chilldkg_participant_inv_data_magic, 4); + ptr += 4; + secp256k1_write_be32(ptr, inv_data_i->simpl.n); + ptr += 4; + secp256k1_write_be32(ptr, inv_data_i->simpl.participant_id); + ptr += 4; + secp256k1_scalar_get_b32(ptr, &inv_data_i->simpl.secshare); + ptr += 32; + secp256k1_chilldkg_point_save(ptr, &inv_data_i->simpl.pubshare); + ptr += 33; + secp256k1_scalar_get_b32(ptr, &inv_data_i->enc_secshare); + ptr += 32; + for (i = 0; i < SECP256K1_CHILLDKG_MAX_PARTICIPANTS; i++) { + secp256k1_scalar_get_b32(ptr + 32 * i, &inv_data_i->pads[i]); + } + ptr += 32 * SECP256K1_CHILLDKG_MAX_PARTICIPANTS; + VERIFY_CHECK(ptr == inv_data->data + sizeof(inv_data->data)); +} + +static int secp256k1_chilldkg_participant_inv_data_load(const secp256k1_context *ctx, secp256k1_chilldkg_encpedpop_participant_inv_data *inv_data_i, const secp256k1_chilldkg_participant_inv_data *inv_data) { + const unsigned char *ptr = inv_data->data; + size_t i; + + ARG_CHECK(secp256k1_memcmp_var(ptr, secp256k1_chilldkg_participant_inv_data_magic, 4) == 0); + ptr += 4; + inv_data_i->simpl.n = secp256k1_read_be32(ptr); + ptr += 4; + inv_data_i->simpl.participant_id = secp256k1_read_be32(ptr); + ptr += 4; + /* The remaining contents were written by inv_data_save. */ + VERIFY_CHECK(inv_data_i->simpl.n >= 1 + && inv_data_i->simpl.n <= SECP256K1_CHILLDKG_MAX_PARTICIPANTS + && inv_data_i->simpl.participant_id < inv_data_i->simpl.n); + secp256k1_scalar_set_b32(&inv_data_i->simpl.secshare, ptr, NULL); + ptr += 32; + /* 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(&inv_data_i->simpl.pubshare, ptr)) { + VERIFY_CHECK(0); + secp256k1_ge_set_infinity(&inv_data_i->simpl.pubshare); + } + ptr += 33; + secp256k1_scalar_set_b32(&inv_data_i->enc_secshare, ptr, NULL); + ptr += 32; + for (i = 0; i < SECP256K1_CHILLDKG_MAX_PARTICIPANTS; i++) { + secp256k1_scalar_set_b32(&inv_data_i->pads[i], ptr + 32 * i, NULL); + } + return 1; +} /* A state1 object consists of * - 4 byte magic set during initialization to allow detecting an @@ -304,8 +368,9 @@ int secp256k1_chilldkg_participant_step1(const secp256k1_context *ctx, secp256k1 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_fault secp256k1_chilldkg_participant_step2(const secp256k1_context *ctx, secp256k1_chilldkg_participant_state2 *state2, unsigned char *sig64, uint32_t *fault_index, secp256k1_chilldkg_participant_inv_data *inv_data, 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_encpedpop_participant_inv_data inv_data_i; secp256k1_chilldkg_participant_state2_internal state2i; unsigned char hostpubkey33[33]; secp256k1_scalar enc_secshares[SECP256K1_CHILLDKG_MAX_PARTICIPANTS]; @@ -326,9 +391,13 @@ secp256k1_chilldkg_fault secp256k1_chilldkg_participant_step2(const secp256k1_co SECP256K1_CHILLDKG_ARG_CHECK(aux_rand32 != NULL); memset(&state2i, 0, sizeof(state2i)); + memset(&inv_data_i, 0, sizeof(inv_data_i)); memset(state2->data, 0, sizeof(state2->data)); memset(sig64, 0, 64); *fault_index = UINT32_MAX; + if (inv_data != NULL) { + memset(inv_data->data, 0, sizeof(inv_data->data)); + } if (!secp256k1_chilldkg_participant_state1_load(ctx, &enc_state, state1)) { return SECP256K1_CHILLDKG_INVALID_INPUT; @@ -358,8 +427,11 @@ secp256k1_chilldkg_fault secp256k1_chilldkg_participant_step2(const secp256k1_co } 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]); + fault = secp256k1_chilldkg_encpedpop_participant_step2(ctx, &state2i.dkg_output, state2i.eq_input, fault_index, inv_data != NULL ? &inv_data_i : NULL, &enc_state, hostseckey32, cmsg1, enc_cmsg_len, &enc_secshares[participant_id]); if (fault != SECP256K1_CHILLDKG_OK) { + if (fault == SECP256K1_CHILLDKG_UNKNOWN_FAULTY_PARTICIPANT_OR_COORDINATOR && inv_data != NULL) { + secp256k1_chilldkg_participant_inv_data_save(inv_data, &inv_data_i); + } goto cleanup; } @@ -386,8 +458,9 @@ cleanup: for (i = 0; i < n; i++) { secp256k1_scalar_clear(&enc_secshares[i]); } - /* state2i contains the secret share. */ + /* state2i and inv_data_i contain the secret share / decryption pads. */ secp256k1_memclear_explicit(&state2i, sizeof(state2i)); + secp256k1_memclear_explicit(&inv_data_i, sizeof(inv_data_i)); return fault; } @@ -614,4 +687,406 @@ secp256k1_chilldkg_fault secp256k1_chilldkg_coordinator_finalize(const secp256k1 return SECP256K1_CHILLDKG_OK; } +size_t secp256k1_chilldkg_investigation_msg_len(size_t n_participants) { + if (n_participants < 1 || n_participants > SECP256K1_CHILLDKG_MAX_PARTICIPANTS) { + return 0; + } + return 65 * n_participants; +} + +/* Parse the self-delimiting recovery data (deserialize_recovery_data in + * chilldkg_ref/chilldkg.py): + * u32be(t) || sum_coms (33*t) || hostpubkeys (33*n) || pubnonces (33*n) || + * enc_secshares (32*n) || cert (64*n) + * where n = (recovery_len - 4 - 33*t) / 162. sum_coms may contain the point + * at infinity; the enc_secshares are parsed checked. The hostpubkeys33, + * pubnonces33 and cert outputs point into the recovery buffer. Returns 1 on + * success and 0 on invalid recovery data (RecoveryDataError in the + * reference). */ +static int secp256k1_chilldkg_deserialize_recovery_data(uint32_t *t_out, size_t *n_out, secp256k1_ge *sum_coms, secp256k1_scalar *enc_secshares, const unsigned char **hostpubkeys33, const unsigned char **pubnonces33, const unsigned char **cert, const unsigned char *recovery, size_t recovery_len) { + size_t t, n, rest_len, i; + const unsigned char *ptr; + int overflow; + + if (recovery_len < 4) { + return 0; + } + t = secp256k1_read_be32(recovery); + rest_len = recovery_len - 4; + if (rest_len < 33 * t || (rest_len - 33 * t) % 162 != 0) { + return 0; + } + n = (rest_len - 33 * t) / 162; + if (n > SECP256K1_CHILLDKG_MAX_PARTICIPANTS) { + return 0; + } + ptr = recovery + 4; + for (i = 0; i < t; i++) { + if (!secp256k1_chilldkg_point_load(&sum_coms[i], ptr + 33 * i)) { + return 0; + } + } + ptr += 33 * t; + *hostpubkeys33 = ptr; + ptr += 33 * n; + *pubnonces33 = ptr; + ptr += 33 * n; + for (i = 0; i < n; i++) { + secp256k1_scalar_set_b32(&enc_secshares[i], ptr + 32 * i, &overflow); + if (overflow) { + return 0; + } + } + ptr += 32 * n; + *cert = ptr; + *t_out = (uint32_t)t; + *n_out = n; + return 1; +} + +/* Shared implementation of participant_recover and coordinator_recover + * (recover in chilldkg_ref/chilldkg.py). hostseckey32 is NULL for the + * coordinator. secshare32 is NULL for the coordinator. */ +static secp256k1_chilldkg_fault secp256k1_chilldkg_recover(const secp256k1_context *ctx, unsigned char *secshare32, unsigned char *thresh_pk33, unsigned char *pubshares33, unsigned char *hostpubkeys33_out, size_t *n_participants_out, uint32_t *threshold_out, uint32_t *fault_index, const unsigned char *hostseckey32, const unsigned char *recovery, size_t recovery_len) { + secp256k1_ge sum_coms[SECP256K1_CHILLDKG_MAX_PARTICIPANTS]; + secp256k1_ge sum_coms_tweaked[SECP256K1_CHILLDKG_MAX_PARTICIPANTS]; + secp256k1_ge pubtweak; + secp256k1_ge pubshare; + secp256k1_gej pubsharej; + secp256k1_scalar enc_secshares[SECP256K1_CHILLDKG_MAX_PARTICIPANTS]; + secp256k1_scalar tweak; + secp256k1_scalar secshare; + unsigned char enc_context[4 + 33 * SECP256K1_CHILLDKG_MAX_PARTICIPANTS]; + unsigned char hostpubkey33[33]; + const unsigned char *hostpubkeys33 = NULL; + const unsigned char *pubnonces33 = NULL; + const unsigned char *cert = NULL; + secp256k1_chilldkg_fault fault = SECP256K1_CHILLDKG_INVALID_INPUT; + uint32_t participant_id = 0; + uint32_t t = 0; + size_t n = 0; + size_t i; + int found = 0; + + if (secshare32 != NULL) { + memset(secshare32, 0, 32); + } + memset(thresh_pk33, 0, 33); + *n_participants_out = 0; + *threshold_out = 0; + if (fault_index != NULL) { + *fault_index = UINT32_MAX; + } + + /* Deserialize; invalid recovery data is a RecoveryDataError in the + * reference. */ + if (!secp256k1_chilldkg_deserialize_recovery_data(&t, &n, sum_coms, enc_secshares, &hostpubkeys33, &pubnonces33, &cert, recovery, recovery_len)) { + goto cleanup; + } + /* params_validate; invalid session parameters in the recovery data are a + * RecoveryDataError in the reference. */ + if (!secp256k1_chilldkg_params_validate(hostpubkeys33, n, t)) { + goto cleanup; + } + memset(pubshares33, 0, 33 * n); + memset(hostpubkeys33_out, 0, 33 * n); + + /* Verify the certificate over eq_input (the recovery data without the + * cert). An invalid certificate is a RecoveryDataError in the reference + * (the index of the invalid signature is not reported there). */ + if (!secp256k1_chilldkg_certeq_verify(ctx, hostpubkeys33, n, recovery, recovery_len - 64 * n, cert, fault_index)) { + if (fault_index != NULL) { + *fault_index = UINT32_MAX; + } + goto cleanup; + } + + /* HostSeckeyError if the host secret key is invalid or does not match any + * host public key in the recovery data. */ + if (hostseckey32 != NULL) { + if (!secp256k1_chilldkg_hostpubkey_gen(ctx, hostpubkey33, hostseckey32)) { + goto cleanup; + } + for (i = 0; i < n; i++) { + if (secp256k1_memcmp_var(hostpubkeys33 + 33 * i, hostpubkey33, 33) == 0) { + participant_id = (uint32_t)i; + found = 1; + break; + } + } + if (!found) { + goto cleanup; + } + } + + /* Recompute the tweaked threshold public key and pubshares. A failure of + * invalid_taproot_commit (infinity commitment to the secret, or tweak + * hash overflow) is not caught by the reference (it would raise a + * non-protocol error); report it as invalid recovery data. */ + if (!secp256k1_chilldkg_vss_invalid_taproot_commit(ctx, sum_coms_tweaked, &tweak, &pubtweak, sum_coms, t)) { + goto cleanup; + } + secp256k1_chilldkg_point_save(thresh_pk33, &sum_coms_tweaked[0]); + for (i = 0; i < n; i++) { + secp256k1_chilldkg_vss_pubshare(&pubsharej, sum_coms_tweaked, t, (uint32_t)i); + secp256k1_ge_set_gej_var(&pubshare, &pubsharej); + secp256k1_chilldkg_point_save(pubshares33 + 33 * i, &pubshare); + } + memcpy(hostpubkeys33_out, hostpubkeys33, 33 * n); + *n_participants_out = n; + *threshold_out = t; + + if (hostseckey32 != NULL) { + /* Decrypt our secshare and apply the tweak. */ + secp256k1_chilldkg_encpedpop_serialize_enc_context(enc_context, t, hostpubkeys33, n); + fault = secp256k1_chilldkg_encpedpop_decrypt_sum(ctx, &secshare, fault_index, hostseckey32, hostpubkeys33 + 33 * participant_id, pubnonces33, enc_context, 4 + 33 * n, participant_id, &enc_secshares[participant_id], n); + if (fault != SECP256K1_CHILLDKG_OK) { + memset(thresh_pk33, 0, 33); + memset(pubshares33, 0, 33 * n); + memset(hostpubkeys33_out, 0, 33 * n); + *n_participants_out = 0; + *threshold_out = 0; + goto cleanup; + } + secp256k1_scalar_add(&secshare, &secshare, &tweak); + /* This is just a sanity check (assert in the reference): the cert is + * valid, so an equivalent check was done during the actual session. */ + if (!secp256k1_chilldkg_point_load(&pubshare, pubshares33 + 33 * participant_id)) { + VERIFY_CHECK(0); + } + if (!secp256k1_chilldkg_vss_verify_secshare(ctx, &secshare, &pubshare)) { + VERIFY_CHECK(0); + } + secp256k1_scalar_get_b32(secshare32, &secshare); + } + fault = SECP256K1_CHILLDKG_OK; + +cleanup: + secp256k1_scalar_clear(&tweak); + secp256k1_scalar_clear(&secshare); + for (i = 0; i < SECP256K1_CHILLDKG_MAX_PARTICIPANTS; i++) { + secp256k1_scalar_clear(&enc_secshares[i]); + } + return fault; +} + +secp256k1_chilldkg_fault secp256k1_chilldkg_participant_recover(const secp256k1_context *ctx, unsigned char *secshare32, unsigned char *thresh_pk33, unsigned char *pubshares33, unsigned char *hostpubkeys33_out, size_t *n_participants_out, uint32_t *threshold_out, uint32_t *fault_index, const unsigned char *hostseckey32, const unsigned char *recovery, size_t recovery_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(hostpubkeys33_out != NULL); + SECP256K1_CHILLDKG_ARG_CHECK(n_participants_out != NULL); + SECP256K1_CHILLDKG_ARG_CHECK(threshold_out != NULL); + SECP256K1_CHILLDKG_ARG_CHECK(fault_index != NULL); + SECP256K1_CHILLDKG_ARG_CHECK(hostseckey32 != NULL); + SECP256K1_CHILLDKG_ARG_CHECK(recovery != NULL); + + return secp256k1_chilldkg_recover(ctx, secshare32, thresh_pk33, pubshares33, hostpubkeys33_out, n_participants_out, threshold_out, fault_index, hostseckey32, recovery, recovery_len); +} + +secp256k1_chilldkg_fault secp256k1_chilldkg_coordinator_recover(const secp256k1_context *ctx, unsigned char *thresh_pk33, unsigned char *pubshares33, unsigned char *hostpubkeys33_out, size_t *n_participants_out, uint32_t *threshold_out, const unsigned char *recovery, size_t recovery_len) { + VERIFY_CHECK(ctx != NULL); + SECP256K1_CHILLDKG_ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + SECP256K1_CHILLDKG_ARG_CHECK(thresh_pk33 != NULL); + SECP256K1_CHILLDKG_ARG_CHECK(pubshares33 != NULL); + SECP256K1_CHILLDKG_ARG_CHECK(hostpubkeys33_out != NULL); + SECP256K1_CHILLDKG_ARG_CHECK(n_participants_out != NULL); + SECP256K1_CHILLDKG_ARG_CHECK(threshold_out != NULL); + SECP256K1_CHILLDKG_ARG_CHECK(recovery != NULL); + + return secp256k1_chilldkg_recover(ctx, NULL, thresh_pk33, pubshares33, hostpubkeys33_out, n_participants_out, threshold_out, NULL, NULL, recovery, recovery_len); +} + +/* recovery_ack_message: pad33("BIP DKG/recovery acknowledgment") || + * u32be(participant_id) || recovery_data. msg must hold 37 + recovery_len + * bytes. */ +static void secp256k1_chilldkg_recovery_ack_message(unsigned char *msg, const unsigned char *recovery, size_t recovery_len, uint32_t participant_id) { + secp256k1_chilldkg_pad33(msg, "BIP DKG/recovery acknowledgment"); + secp256k1_write_be32(msg + 33, participant_id); + memcpy(msg + 37, recovery, recovery_len); +} + +/* The maximum length of recovery data: 4 + 33*t + 162*n with t, n <= + * SECP256K1_CHILLDKG_MAX_PARTICIPANTS. */ +#define SECP256K1_CHILLDKG_MAX_RECOVERY_LEN (4 + 195 * SECP256K1_CHILLDKG_MAX_PARTICIPANTS) + +/* Check that the recovery data parses and matches the given session + * parameters (part of participant_recovery_ack_sign and + * participant_recovery_acks_verify in the reference). Returns 1 on match, 0 + * otherwise. */ +static int secp256k1_chilldkg_recovery_matches_params(const unsigned char *recovery, size_t recovery_len, const unsigned char *hostpubkeys33, size_t n, uint32_t t) { + secp256k1_ge sum_coms[SECP256K1_CHILLDKG_MAX_PARTICIPANTS]; + secp256k1_scalar enc_secshares[SECP256K1_CHILLDKG_MAX_PARTICIPANTS]; + const unsigned char *hostpubkeys33_rec = NULL; + const unsigned char *pubnonces33 = NULL; + const unsigned char *cert = NULL; + uint32_t t_rec = 0; + size_t n_rec = 0; + size_t i; + int ret; + + ret = secp256k1_chilldkg_deserialize_recovery_data(&t_rec, &n_rec, sum_coms, enc_secshares, &hostpubkeys33_rec, &pubnonces33, &cert, recovery, recovery_len) + && t_rec == t + && n_rec == n + && secp256k1_memcmp_var(hostpubkeys33_rec, hostpubkeys33, 33 * n) == 0; + for (i = 0; i < SECP256K1_CHILLDKG_MAX_PARTICIPANTS; i++) { + secp256k1_scalar_clear(&enc_secshares[i]); + } + return ret; +} + +int secp256k1_chilldkg_recovery_ack_sign(const secp256k1_context *ctx, unsigned char *sig64, const unsigned char *hostseckey32, const unsigned char *hostpubkeys33, size_t n_participants, uint32_t threshold, const unsigned char *recovery, size_t recovery_len, const unsigned char *aux_rand32) { + unsigned char msg[37 + SECP256K1_CHILLDKG_MAX_RECOVERY_LEN]; + unsigned char hostpubkey33[33]; + uint32_t participant_id = 0; + 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(sig64 != NULL); + ARG_CHECK(hostseckey32 != NULL); + ARG_CHECK(hostpubkeys33 != NULL); + ARG_CHECK(recovery != NULL); + ARG_CHECK(aux_rand32 != NULL); + + memset(sig64, 0, 64); + if (recovery_len > SECP256K1_CHILLDKG_MAX_RECOVERY_LEN) { + return 0; + } + if (secp256k1_chilldkg_params_validate(hostpubkeys33, n_participants, threshold) + && secp256k1_chilldkg_hostpubkey_gen(ctx, hostpubkey33, hostseckey32)) { + 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; + } + } + /* RecoveryDataError if the recovery data is invalid or does not match + * the provided session parameters. */ + if (found && secp256k1_chilldkg_recovery_matches_params(recovery, recovery_len, hostpubkeys33, n_participants, threshold)) { + secp256k1_chilldkg_recovery_ack_message(msg, recovery, recovery_len, participant_id); + ret = secp256k1_chilldkg_schnorrsig_sign(ctx, sig64, msg, 37 + recovery_len, hostseckey32, aux_rand32, "BIP0340"); + } + } + secp256k1_memclear_explicit(msg, sizeof(msg)); + return ret; +} + +secp256k1_chilldkg_fault secp256k1_chilldkg_recovery_acks_verify(const secp256k1_context *ctx, uint32_t *fault_index, const unsigned char *hostpubkeys33, size_t n_participants, uint32_t threshold, const unsigned char *recovery, size_t recovery_len, const unsigned char *const *ack_sigs64) { + unsigned char msg[37 + SECP256K1_CHILLDKG_MAX_RECOVERY_LEN]; + size_t i; + + VERIFY_CHECK(ctx != NULL); + SECP256K1_CHILLDKG_ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + SECP256K1_CHILLDKG_ARG_CHECK(fault_index != NULL); + SECP256K1_CHILLDKG_ARG_CHECK(hostpubkeys33 != NULL); + SECP256K1_CHILLDKG_ARG_CHECK(recovery != NULL); + SECP256K1_CHILLDKG_ARG_CHECK(ack_sigs64 != NULL); + + *fault_index = UINT32_MAX; + if (recovery_len > SECP256K1_CHILLDKG_MAX_RECOVERY_LEN + || !secp256k1_chilldkg_params_validate(hostpubkeys33, n_participants, threshold) + || !secp256k1_chilldkg_recovery_matches_params(recovery, recovery_len, hostpubkeys33, n_participants, threshold)) { + return SECP256K1_CHILLDKG_INVALID_INPUT; + } + + for (i = 0; i < n_participants; i++) { + secp256k1_chilldkg_recovery_ack_message(msg, recovery, recovery_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, ack_sigs64[i], msg, 37 + recovery_len, hostpubkeys33 + 33 * i + 1, "BIP0340")) { + /* InvalidRecoveryAckError(i) in the reference (a + * FaultyParticipantError). */ + *fault_index = (uint32_t)i; + return SECP256K1_CHILLDKG_FAULTY_PARTICIPANT; + } + } + return SECP256K1_CHILLDKG_OK; +} + +secp256k1_chilldkg_fault secp256k1_chilldkg_coordinator_investigate(const secp256k1_context *ctx, unsigned char *cinv, uint32_t *fault_index, const unsigned char *const *pmsgs1, const unsigned char *hostpubkeys33, size_t n_participants, uint32_t threshold, uint32_t participant_id) { + secp256k1_ge com[SECP256K1_CHILLDKG_MAX_PARTICIPANTS]; + secp256k1_ge pubshare; + secp256k1_gej pubsharej; + secp256k1_scalar share; + size_t cinv_len; + size_t i; + size_t j; + int overflow; + + VERIFY_CHECK(ctx != NULL); + SECP256K1_CHILLDKG_ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + SECP256K1_CHILLDKG_ARG_CHECK(cinv != NULL); + SECP256K1_CHILLDKG_ARG_CHECK(fault_index != NULL); + SECP256K1_CHILLDKG_ARG_CHECK(pmsgs1 != NULL); + SECP256K1_CHILLDKG_ARG_CHECK(hostpubkeys33 != NULL); + + *fault_index = UINT32_MAX; + cinv_len = secp256k1_chilldkg_investigation_msg_len(n_participants); + if (!secp256k1_chilldkg_params_validate(hostpubkeys33, n_participants, threshold) + || participant_id >= n_participants) { + if (cinv_len > 0) { + memset(cinv, 0, cinv_len); + } + return SECP256K1_CHILLDKG_INVALID_INPUT; + } + memset(cinv, 0, cinv_len); + + /* For the given participant, collect the encrypted partial secshare and + * the partial pubshare of every dealer. Every participant message is + * parsed fully (ParticipantMsg1.from_bytes in the reference); a malformed + * message blames its sender. The pubnonces are not parsed (they are plain + * byte strings in the reference). */ + for (j = 0; j < n_participants; j++) { + const unsigned char *pmsg1 = pmsgs1[j]; + for (i = 0; i < threshold; i++) { + if (!secp256k1_chilldkg_point_load(&com[i], pmsg1 + 33 * i)) { + *fault_index = (uint32_t)j; + return SECP256K1_CHILLDKG_FAULTY_PARTICIPANT; + } + } + for (i = 0; i < n_participants; i++) { + secp256k1_scalar_set_b32(&share, pmsg1 + 33 * threshold + 97 + 32 * i, &overflow); + if (overflow) { + secp256k1_scalar_clear(&share); + *fault_index = (uint32_t)j; + return SECP256K1_CHILLDKG_FAULTY_PARTICIPANT; + } + if (i == participant_id) { + secp256k1_scalar_get_b32(cinv + 32 * j, &share); + } + } + secp256k1_scalar_clear(&share); + secp256k1_chilldkg_vss_pubshare(&pubsharej, com, threshold, participant_id); + secp256k1_ge_set_gej_var(&pubshare, &pubsharej); + secp256k1_chilldkg_point_save(cinv + 32 * n_participants + 33 * j, &pubshare); + } + return SECP256K1_CHILLDKG_OK; +} + +secp256k1_chilldkg_fault secp256k1_chilldkg_participant_investigate(const secp256k1_context *ctx, uint32_t *fault_index, const secp256k1_chilldkg_participant_inv_data *inv_data, const unsigned char *cinv) { + secp256k1_chilldkg_encpedpop_participant_inv_data inv_data_i; + secp256k1_chilldkg_fault fault; + + VERIFY_CHECK(ctx != NULL); + SECP256K1_CHILLDKG_ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + SECP256K1_CHILLDKG_ARG_CHECK(fault_index != NULL); + SECP256K1_CHILLDKG_ARG_CHECK(inv_data != NULL); + SECP256K1_CHILLDKG_ARG_CHECK(cinv != NULL); + + *fault_index = UINT32_MAX; + if (!secp256k1_chilldkg_participant_inv_data_load(ctx, &inv_data_i, inv_data)) { + return SECP256K1_CHILLDKG_INVALID_INPUT; + } + fault = secp256k1_chilldkg_encpedpop_participant_investigate(ctx, fault_index, &inv_data_i, cinv, 65 * (size_t)inv_data_i.simpl.n); + /* inv_data_i contains the untweaked secshare and the pads. */ + secp256k1_memclear_explicit(&inv_data_i, sizeof(inv_data_i)); + return fault; +} + #endif diff --git a/src/modules/chilldkg/simplpedpop.h b/src/modules/chilldkg/simplpedpop.h index 13aac08d..c5d2872f 100644 --- a/src/modules/chilldkg/simplpedpop.h +++ b/src/modules/chilldkg/simplpedpop.h @@ -78,6 +78,17 @@ static int secp256k1_chilldkg_simplpedpop_participant_step1(const secp256k1_cont * (simplpedpop.py participant_step2_prepare_secshare). */ static void secp256k1_chilldkg_simplpedpop_participant_step2_prepare_secshare(secp256k1_scalar *out, const secp256k1_scalar *partial_secshares, size_t n); +/* Investigation data captured when participant_step2 fails with + * SECP256K1_CHILLDKG_UNKNOWN_FAULTY_PARTICIPANT_OR_COORDINATOR + * (simplpedpop.py ParticipantInvestigationData). Contains secrets (the + * untweaked secshare); clear after use. */ +typedef struct { + uint32_t n; + uint32_t participant_id; + secp256k1_scalar secshare; /* untweaked */ + secp256k1_ge pubshare; /* untweaked */ +} secp256k1_chilldkg_simplpedpop_participant_inv_data; + /* Participant step 2 (simplpedpop.py participant_step2): verify the * coordinator message, i.e., check that our own commitment to the secret was * echoed correctly, verify the pops of all other participants against the @@ -93,9 +104,21 @@ static void secp256k1_chilldkg_simplpedpop_participant_step2_prepare_secshare(se * - FAULTY_PARTICIPANT_OR_COORDINATOR(i) if participant i sent the point at * infinity as commitment to their secret or an invalid pop; * - UNKNOWN_FAULTY_PARTICIPANT_OR_COORDINATOR if the tweaked secshare does - * not match the tweaked pubshare (the investigation procedure of a later - * phase can attribute the fault). */ -static secp256k1_chilldkg_fault secp256k1_chilldkg_simplpedpop_participant_step2(const secp256k1_context *ctx, secp256k1_chilldkg_simplpedpop_dkg_output *dkg_output, unsigned char *eq_input, uint32_t *fault_index, const secp256k1_chilldkg_simplpedpop_participant_state *state, const unsigned char *cmsg, size_t cmsg_len, const secp256k1_scalar *secshare); + * not match the tweaked pubshare; in this case, and only in this case, + * inv_data (if not NULL) is filled for + * secp256k1_chilldkg_simplpedpop_participant_investigate. */ +static secp256k1_chilldkg_fault secp256k1_chilldkg_simplpedpop_participant_step2(const secp256k1_context *ctx, secp256k1_chilldkg_simplpedpop_dkg_output *dkg_output, unsigned char *eq_input, uint32_t *fault_index, secp256k1_chilldkg_simplpedpop_participant_inv_data *inv_data, const secp256k1_chilldkg_simplpedpop_participant_state *state, const unsigned char *cmsg, size_t cmsg_len, const secp256k1_scalar *secshare); + +/* Determine the faulty party after an + * UNKNOWN_FAULTY_PARTICIPANT_OR_COORDINATOR fault of participant_step2 + * (simplpedpop.py participant_investigate): check that the n partial + * pubshares sum to the pubshare, that the n partial secshares sum to the + * secshare, and that every partial secshare matches its partial pubshare. + * Returns FAULTY_COORDINATOR, or FAULTY_PARTICIPANT_OR_COORDINATOR(i) with + * *fault_index = i. If all inputs are consistent (i.e., the function was + * called even though no fault occurred), returns SECP256K1_CHILLDKG_INVALID_INPUT + * (the reference raises a RuntimeError in this case). */ +static secp256k1_chilldkg_fault secp256k1_chilldkg_simplpedpop_participant_investigate(const secp256k1_context *ctx, uint32_t *fault_index, const secp256k1_chilldkg_simplpedpop_participant_inv_data *inv_data, const secp256k1_ge *partial_pubshares, const secp256k1_scalar *partial_secshares); /* Assemble the summed VSS commitment (simplpedpop.py assemble_sum_coms): * sum_coms[0] = sum of the n coms_to_secrets, sum_coms[j] = diff --git a/src/modules/chilldkg/simplpedpop_impl.h b/src/modules/chilldkg/simplpedpop_impl.h index b627914e..15e9bd38 100644 --- a/src/modules/chilldkg/simplpedpop_impl.h +++ b/src/modules/chilldkg/simplpedpop_impl.h @@ -117,7 +117,7 @@ static void secp256k1_chilldkg_simplpedpop_participant_step2_prepare_secshare(se } } -static secp256k1_chilldkg_fault secp256k1_chilldkg_simplpedpop_participant_step2(const secp256k1_context *ctx, secp256k1_chilldkg_simplpedpop_dkg_output *dkg_output, unsigned char *eq_input, uint32_t *fault_index, const secp256k1_chilldkg_simplpedpop_participant_state *state, const unsigned char *cmsg, size_t cmsg_len, const secp256k1_scalar *secshare) { +static secp256k1_chilldkg_fault secp256k1_chilldkg_simplpedpop_participant_step2(const secp256k1_context *ctx, secp256k1_chilldkg_simplpedpop_dkg_output *dkg_output, unsigned char *eq_input, uint32_t *fault_index, secp256k1_chilldkg_simplpedpop_participant_inv_data *inv_data, const secp256k1_chilldkg_simplpedpop_participant_state *state, const unsigned char *cmsg, size_t cmsg_len, const secp256k1_scalar *secshare) { const uint32_t t = state->t; const uint32_t n = state->n; const uint32_t participant_id = state->participant_id; @@ -197,8 +197,16 @@ static secp256k1_chilldkg_fault secp256k1_chilldkg_simplpedpop_participant_step2 /* Unreachable in practice: sum_coms[0] can only be the point at * infinity if some pop is invalid (checked above), and a tweak hash * overflow has negligible probability. The reference raises a - * non-protocol error here. */ + * non-protocol error here. Fill inv_data with the untweaked values so + * that the investigation procedure can still run. */ *fault_index = UINT32_MAX; + if (inv_data != NULL) { + inv_data->n = n; + inv_data->participant_id = participant_id; + inv_data->secshare = *secshare; + secp256k1_chilldkg_vss_pubshare(&pubsharej, sum_coms, t, participant_id); + secp256k1_ge_set_gej_var(&inv_data->pubshare, &pubsharej); + } return SECP256K1_CHILLDKG_UNKNOWN_FAULTY_PARTICIPANT_OR_COORDINATOR; } @@ -209,9 +217,21 @@ static secp256k1_chilldkg_fault secp256k1_chilldkg_simplpedpop_participant_step2 secp256k1_ge_set_gej_var(&pubshare, &pubsharej); secp256k1_scalar_add(&secshare_tweaked, secshare, &tweak); if (!secp256k1_chilldkg_vss_verify_secshare(ctx, &secshare_tweaked, &pubshare)) { - /* "Received invalid secshare"; the investigation procedure (a later - * phase) can determine the faulty party. */ + /* "Received invalid secshare"; capture the investigation data so + * that the investigation procedure can determine the faulty party. */ *fault_index = UINT32_MAX; + if (inv_data != NULL) { + secp256k1_ge pubtweak_neg; + inv_data->n = n; + inv_data->participant_id = participant_id; + /* The investigation works on the untweaked values. */ + inv_data->secshare = *secshare; + /* pubshare = pubshare_tweaked - pubtweak (gej_add_ge_var handles + * a pubtweak at infinity, which occurs iff the tweak is zero). */ + secp256k1_ge_neg(&pubtweak_neg, &pubtweak); + secp256k1_gej_add_ge_var(&pubsharej, &pubsharej, &pubtweak_neg, NULL); + secp256k1_ge_set_gej_var(&inv_data->pubshare, &pubsharej); + } secp256k1_scalar_clear(&tweak); secp256k1_scalar_clear(&secshare_tweaked); return SECP256K1_CHILLDKG_UNKNOWN_FAULTY_PARTICIPANT_OR_COORDINATOR; @@ -353,4 +373,68 @@ static secp256k1_chilldkg_fault secp256k1_chilldkg_simplpedpop_coordinator_step( return SECP256K1_CHILLDKG_OK; } +static secp256k1_chilldkg_fault secp256k1_chilldkg_simplpedpop_participant_investigate(const secp256k1_context *ctx, uint32_t *fault_index, const secp256k1_chilldkg_simplpedpop_participant_inv_data *inv_data, const secp256k1_ge *partial_pubshares, const secp256k1_scalar *partial_secshares) { + const uint32_t n = inv_data->n; + const uint32_t participant_id = inv_data->participant_id; + secp256k1_gej sumj; + secp256k1_ge sum; + secp256k1_scalar secshare_sum; + unsigned char buf33[33]; + unsigned char buf33b[33]; + uint32_t i; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + + /* If the sum of the partial pubshares is not equal to the pubshare, the + * coordinator is faulty. */ + secp256k1_gej_set_infinity(&sumj); + for (i = 0; i < n; i++) { + secp256k1_gej termj; + secp256k1_gej_set_ge(&termj, &partial_pubshares[i]); + secp256k1_gej_add_var(&sumj, &sumj, &termj, NULL); + } + secp256k1_ge_set_gej_var(&sum, &sumj); + secp256k1_chilldkg_point_save(buf33, &sum); + secp256k1_chilldkg_point_save(buf33b, &inv_data->pubshare); + if (secp256k1_memcmp_var(buf33, buf33b, 33) != 0) { + return SECP256K1_CHILLDKG_FAULTY_COORDINATOR; + } + + /* If the sum of the partial secshares is not equal to the secshare, the + * coordinator is faulty (since the encryption is additively homomorphic, + * this means that the sum of the *encrypted* partial secshares in the + * investigation message was not equal to the encrypted secshare; the + * reference signals this case with SecshareSumError, which the EncPedPop + * layer translates to FaultyCoordinatorError). */ + secp256k1_scalar_set_int(&secshare_sum, 0); + for (i = 0; i < n; i++) { + secp256k1_scalar_add(&secshare_sum, &secshare_sum, &partial_secshares[i]); + } + if (!secp256k1_scalar_eq(&secshare_sum, &inv_data->secshare)) { + secp256k1_scalar_clear(&secshare_sum); + return SECP256K1_CHILLDKG_FAULTY_COORDINATOR; + } + secp256k1_scalar_clear(&secshare_sum); + + /* If some partial secshare does not match its partial pubshare, the + * corresponding participant or the coordinator is faulty; for our own + * index, only the coordinator can be at fault ("Coordinator fiddled with + * the share from me to myself"). */ + for (i = 0; i < n; i++) { + if (!secp256k1_chilldkg_vss_verify_secshare(ctx, &partial_secshares[i], &partial_pubshares[i])) { + if (i != participant_id) { + *fault_index = i; + return SECP256K1_CHILLDKG_FAULTY_PARTICIPANT_OR_COORDINATOR; + } + return SECP256K1_CHILLDKG_FAULTY_COORDINATOR; + } + } + + /* We now know that the secshare matches the pubshare. This means that + * this function was called even though no fault occurred (the reference + * raises a RuntimeError in this case). */ + return SECP256K1_CHILLDKG_INVALID_INPUT; +} + #endif diff --git a/src/modules/chilldkg/tests_impl.h b/src/modules/chilldkg/tests_impl.h index 2c4b0394..e7048a6e 100644 --- a/src/modules/chilldkg/tests_impl.h +++ b/src/modules/chilldkg/tests_impl.h @@ -249,6 +249,77 @@ static const unsigned char vec3_recovery[556] = { 0x00, 0x00, 0x00, 0x02, 0x03, /* chilldkg session n = 3, t = 2 */ /* all python-side sanity checks passed */ +static const unsigned char vec5_hostseckeys[3][32] = { + { 0x12, 0x83, 0xa7, 0x64, 0x03, 0x38, 0xef, 0x46, 0x92, 0x13, 0x25, 0xa8, 0x3e, 0xae, 0x85, 0x94, 0x58, 0x52, 0x0a, 0x93, 0x5a, 0x17, 0x9d, 0xe9, 0x1c, 0xba, 0x39, 0xd8, 0xb6, 0xf9, 0x3d, 0xbf }, + { 0xd2, 0xd5, 0x5c, 0xf8, 0x10, 0xd0, 0x93, 0xcc, 0xfb, 0xff, 0xdd, 0x89, 0x65, 0x13, 0x59, 0x79, 0xa7, 0x3f, 0x5f, 0x70, 0x06, 0x20, 0x3e, 0x0f, 0x1f, 0x57, 0x20, 0x90, 0x1a, 0x0d, 0x3e, 0xf2 }, + { 0x70, 0x86, 0x80, 0x07, 0x06, 0x69, 0xf8, 0x6b, 0x8f, 0x14, 0x24, 0xb3, 0x93, 0xdd, 0x03, 0x03, 0xd6, 0x63, 0xa6, 0xa9, 0x1f, 0x85, 0x2d, 0x03, 0x34, 0xae, 0x64, 0xac, 0xdd, 0x17, 0x72, 0x34 }, +}; +static const unsigned char vec5_hostpubkeys[3][33] = { + { 0x03, 0x4f, 0x68, 0x91, 0xb1, 0xd2, 0x7f, 0x67, 0xd3, 0xbb, 0x8a, 0xd4, 0x80, 0x83, 0x1a, 0x3d, 0x80, 0x7f, 0x58, 0xed, 0x42, 0x21, 0x6c, 0x94, 0x9b, 0xd0, 0xc7, 0x8a, 0x51, 0x9c, 0xbe, 0x76, 0xe0 }, + { 0x02, 0xfb, 0x7e, 0x20, 0xfb, 0xa2, 0x87, 0xee, 0x46, 0xb9, 0x53, 0x62, 0x32, 0xf6, 0x9c, 0x31, 0xa9, 0x0c, 0x66, 0xea, 0xf1, 0xfd, 0x67, 0x18, 0xdd, 0xdc, 0x1f, 0x10, 0xaa, 0x15, 0xf8, 0xa3, 0x4f }, + { 0x03, 0x59, 0x5a, 0x19, 0x3f, 0xe6, 0xe8, 0xf1, 0xf4, 0x36, 0x00, 0x79, 0x4e, 0x19, 0x39, 0xaa, 0x29, 0x04, 0x1f, 0x3b, 0xc1, 0xd3, 0x29, 0x49, 0x1e, 0x88, 0x38, 0x32, 0x19, 0x74, 0x8c, 0x2f, 0xa5 }, +}; +static const unsigned char vec5_randoms[3][32] = { + { 0xa8, 0xfa, 0xcd, 0xc0, 0x2b, 0x1f, 0xcf, 0x2a, 0x0f, 0x0e, 0xd0, 0x22, 0xe5, 0x59, 0x53, 0xb2, 0xf6, 0xf7, 0x96, 0x9e, 0x49, 0x55, 0x96, 0xde, 0xa9, 0x2c, 0xd4, 0x3d, 0xed, 0x48, 0x3a, 0x24 }, + { 0xe9, 0xfb, 0x8f, 0x6b, 0x5e, 0x9a, 0x21, 0xc2, 0x16, 0x58, 0xbd, 0x75, 0x7e, 0x66, 0xda, 0x5f, 0x5c, 0x8f, 0xe6, 0x6b, 0x39, 0xf3, 0xca, 0x82, 0xf0, 0xd9, 0xe7, 0x5e, 0x49, 0x00, 0xde, 0xbd }, + { 0xed, 0xf0, 0x7d, 0x54, 0x83, 0x08, 0xea, 0xf8, 0x85, 0x83, 0x38, 0x0b, 0xb2, 0xf4, 0xf0, 0x5d, 0x42, 0x4a, 0x7d, 0x2f, 0x6d, 0xed, 0x43, 0x36, 0xd7, 0x7e, 0xc4, 0x63, 0x59, 0x72, 0x37, 0x5f }, +}; +static const unsigned char vec5_aux_rands[3][32] = { + { 0xb7, 0x35, 0xae, 0xe7, 0x29, 0x77, 0x3e, 0x4f, 0xdd, 0xc1, 0x89, 0x15, 0xb7, 0xb1, 0x93, 0x5f, 0x86, 0x7c, 0xe1, 0x08, 0xa0, 0x50, 0x64, 0x27, 0x9a, 0x8f, 0x8d, 0x0d, 0x0e, 0xc1, 0xe8, 0x2f }, + { 0x3f, 0xf6, 0x7d, 0xab, 0x0f, 0x97, 0xf4, 0x79, 0x28, 0xf3, 0x63, 0x67, 0xb8, 0x6f, 0xde, 0x1c, 0x16, 0xe7, 0x13, 0xf9, 0x25, 0x0f, 0xb6, 0xd9, 0x19, 0x44, 0xbf, 0x53, 0xc7, 0xfd, 0x6f, 0xfb }, + { 0x01, 0xda, 0x0f, 0xd4, 0xa7, 0xff, 0xc9, 0x7a, 0x15, 0x1e, 0x01, 0xfa, 0x07, 0x54, 0x53, 0xf0, 0x83, 0x5e, 0x25, 0x40, 0x63, 0x01, 0x5f, 0xcd, 0xac, 0xc7, 0x31, 0xb9, 0xdc, 0x6e, 0x24, 0x15 }, +}; +static const unsigned char vec5_ack_aux_rands[3][32] = { + { 0xd1, 0x29, 0xa1, 0x50, 0x10, 0xb1, 0xba, 0x89, 0x99, 0x00, 0x1b, 0x3c, 0xb8, 0x35, 0x8e, 0x2c, 0x48, 0xb7, 0x87, 0x83, 0xb8, 0xdf, 0x4d, 0x94, 0xb4, 0x3e, 0x88, 0xa8, 0x97, 0xb6, 0x3a, 0xd6 }, + { 0xc5, 0x26, 0xf1, 0xa4, 0xa1, 0x3d, 0xf9, 0xea, 0x33, 0x1e, 0x05, 0x48, 0x81, 0x79, 0x25, 0xfb, 0x3a, 0x1b, 0x82, 0x56, 0xb5, 0x8f, 0x93, 0xab, 0x22, 0xbf, 0x45, 0x1c, 0x70, 0x4e, 0x28, 0x89 }, + { 0x39, 0x1c, 0xc5, 0x83, 0x75, 0x88, 0x23, 0x92, 0x8e, 0xd7, 0x87, 0xb5, 0x81, 0x9c, 0x40, 0x1e, 0x91, 0xe5, 0xa1, 0xe7, 0x5d, 0x5d, 0x4d, 0x42, 0x2f, 0xd3, 0xf7, 0x0d, 0x11, 0x6b, 0x2a, 0xae }, +}; +static const unsigned char vec5_pmsgs1[3][259] = { + { 0x02, 0xa9, 0xf7, 0xa6, 0x54, 0x1e, 0x12, 0x22, 0x46, 0xfd, 0x57, 0xee, 0xdc, 0x6e, 0x3a, 0x6a, 0x12, 0x48, 0x05, 0x4f, 0x02, 0x2b, 0x7c, 0x01, 0xe2, 0xa4, 0x36, 0x4c, 0x09, 0x20, 0xda, 0x93, 0x4b, 0x03, 0xcd, 0xba, 0xe2, 0x7d, 0xcc, 0x62, 0x90, 0x56, 0x8d, 0x21, 0xb6, 0x35, 0xa7, 0xbb, 0xd2, 0x93, 0x28, 0x78, 0xd7, 0x34, 0x55, 0x74, 0xa0, 0x52, 0xd6, 0x9e, 0xa1, 0x43, 0xe9, 0x8b, 0xc2, 0x12, 0xf5, 0xb1, 0x94, 0x34, 0xb9, 0x81, 0x06, 0x4d, 0x17, 0xa1, 0x6a, 0x1d, 0x81, 0x58, 0x4c, 0xf0, 0x55, 0xdf, 0x32, 0x62, 0x08, 0xd8, 0x29, 0x1e, 0xf2, 0xa6, 0xa3, 0xdb, 0x52, 0x98, 0xb8, 0x0f, 0xe6, 0x49, 0x74, 0xe1, 0x9f, 0x90, 0x70, 0x66, 0x09, 0x6b, 0xa9, 0xd5, 0x05, 0xd6, 0xb2, 0x36, 0x7a, 0xf2, 0x9b, 0xbb, 0x0e, 0x66, 0xed, 0x22, 0xd5, 0xdf, 0x21, 0xba, 0x1e, 0x51, 0x77, 0x40, 0x03, 0x07, 0xd9, 0x3f, 0x10, 0xca, 0xd5, 0x95, 0x92, 0xf3, 0x83, 0xc1, 0xd0, 0x0e, 0xb3, 0xb2, 0xf0, 0xea, 0xcb, 0x67, 0x79, 0xd7, 0xc1, 0x70, 0x0a, 0x42, 0x74, 0x77, 0x4d, 0xfd, 0x00, 0xab, 0x72, 0xbc, 0x05, 0xad, 0x21, 0x38, 0x77, 0xd9, 0x5b, 0x57, 0xb5, 0xa2, 0x0d, 0x49, 0x36, 0x53, 0x58, 0xfe, 0x61, 0x5e, 0x3c, 0x35, 0xb2, 0x8a, 0x6e, 0x7b, 0xdc, 0xc2, 0x44, 0x4c, 0x36, 0xeb, 0x06, 0xeb, 0x3b, 0xea, 0xd3, 0x1e, 0xb1, 0x6b, 0xe9, 0x70, 0x40, 0xd8, 0xeb, 0x68, 0x18, 0x84, 0x62, 0x82, 0xde, 0x65, 0x73, 0x5f, 0x24, 0x71, 0x9f, 0xd2, 0x68, 0xaf, 0x79, 0xdb, 0x1a, 0xa9, 0x8f, 0xe1, 0xab, 0xf1, 0x0b, 0xbb, 0x29, 0xf0, 0xf7, 0x33, 0xc4, 0x8e, 0x6f, 0xa6, 0x16, 0x94, 0x58, 0x75, 0x2a, 0x39, 0x8d, 0xfa, 0x5c, 0x37, 0x8d, 0x66, 0xb2, 0xb4, 0xfd, 0xe6, 0x5b, 0x27, 0xec }, + { 0x03, 0x1a, 0x5c, 0xb4, 0x0b, 0x67, 0xdb, 0xbc, 0xc4, 0x8b, 0x03, 0xcc, 0x2c, 0xc5, 0x54, 0x52, 0x0c, 0x01, 0x72, 0x57, 0x55, 0xd5, 0x2a, 0x0d, 0xec, 0x5d, 0xb1, 0xe0, 0xe1, 0xad, 0x00, 0x45, 0x59, 0x02, 0x9b, 0xac, 0x46, 0x00, 0xbb, 0xa5, 0xa5, 0x43, 0x08, 0x81, 0xc5, 0xac, 0x25, 0x1c, 0x8a, 0x01, 0xc7, 0x2d, 0x04, 0x69, 0x0e, 0x89, 0xeb, 0x6c, 0xb8, 0xe3, 0xdb, 0x4c, 0x60, 0x9d, 0x12, 0xf1, 0x1d, 0xf4, 0x21, 0xdd, 0xdf, 0x41, 0xc4, 0x1c, 0xae, 0x3a, 0x03, 0xd6, 0xd6, 0x7a, 0x25, 0x48, 0xcb, 0x36, 0x8b, 0xfa, 0x59, 0xb8, 0xfe, 0x6a, 0x2a, 0x39, 0x1b, 0x30, 0x60, 0xd0, 0x2e, 0x8b, 0x2d, 0xab, 0x0a, 0x99, 0x90, 0x31, 0xb9, 0x0f, 0x62, 0xa1, 0xb0, 0x1a, 0x3f, 0x3e, 0x43, 0xf6, 0xce, 0x94, 0x73, 0xcc, 0x88, 0xda, 0x76, 0x91, 0x66, 0x98, 0xa2, 0xd2, 0x4f, 0xdf, 0x81, 0xcb, 0x03, 0x7f, 0xc5, 0xc6, 0x73, 0xb0, 0xe6, 0xfb, 0x74, 0x98, 0xce, 0x1a, 0x86, 0xd7, 0xf7, 0xe8, 0x05, 0xf4, 0x65, 0xe6, 0x8b, 0xca, 0xe2, 0x95, 0x10, 0xdd, 0xdd, 0xec, 0xb6, 0x34, 0x2f, 0xae, 0x12, 0x10, 0x87, 0xc2, 0xea, 0x8c, 0x60, 0xeb, 0xc6, 0x3d, 0x40, 0x37, 0x38, 0xcf, 0x60, 0x3e, 0xfd, 0x7b, 0xc8, 0x31, 0x10, 0xfa, 0x29, 0x61, 0x5b, 0x93, 0x6e, 0x85, 0xa0, 0xd3, 0x5c, 0xe0, 0xa1, 0xa2, 0xf0, 0xff, 0xfb, 0x07, 0x2f, 0x48, 0xdb, 0x25, 0x84, 0x4d, 0xd0, 0x5c, 0xeb, 0xc0, 0xba, 0x8c, 0x51, 0x3d, 0x06, 0x69, 0x28, 0x42, 0xe6, 0x40, 0x4a, 0xfe, 0x20, 0x0d, 0x37, 0xc4, 0xe1, 0x46, 0x4f, 0x8e, 0x55, 0xc0, 0xd5, 0x13, 0x65, 0xf2, 0x99, 0xa2, 0x78, 0x3e, 0xd4, 0xc9, 0x47, 0x5d, 0x4d, 0x45, 0xbd, 0x5c, 0xd7, 0xe8, 0xd2, 0x82, 0xc2, 0x4a, 0xe5, 0xd1, 0x5d, 0xe9, 0x4b }, + { 0x02, 0x54, 0xc4, 0xc5, 0x71, 0x04, 0x3b, 0x01, 0x22, 0xe0, 0x1a, 0x79, 0x37, 0x1e, 0x0f, 0xc2, 0x6b, 0x66, 0x4e, 0xbc, 0xbb, 0x4e, 0x18, 0xe5, 0x1a, 0xa9, 0x67, 0x21, 0x18, 0x23, 0x0b, 0x86, 0xef, 0x02, 0xd3, 0x52, 0xd3, 0x63, 0x83, 0x81, 0xad, 0x26, 0x0d, 0x5d, 0xc0, 0x99, 0xcf, 0x80, 0xd4, 0xd7, 0x5f, 0x33, 0x3e, 0x15, 0xe3, 0xff, 0xce, 0xf6, 0x01, 0xe8, 0x66, 0x20, 0xb3, 0x15, 0x84, 0x18, 0xa7, 0x13, 0x00, 0x3b, 0x39, 0x2d, 0x2d, 0x35, 0xa5, 0x7c, 0x5d, 0x3c, 0x96, 0x92, 0xb8, 0xd4, 0x43, 0x94, 0xca, 0xf7, 0x71, 0x60, 0x5c, 0x6f, 0x5b, 0x99, 0x33, 0xcb, 0x39, 0xf8, 0xae, 0xf5, 0xbc, 0x92, 0x09, 0x3f, 0x03, 0xd0, 0x5c, 0x4a, 0x16, 0xff, 0x0a, 0xc3, 0x0b, 0x34, 0x78, 0xe7, 0xea, 0xfb, 0x48, 0xbc, 0x38, 0xbb, 0x18, 0x20, 0xf0, 0x8f, 0x10, 0x63, 0x3f, 0x42, 0xf4, 0xbd, 0x02, 0x6d, 0xda, 0xf4, 0x4d, 0x4a, 0x66, 0xfc, 0xf7, 0x7b, 0xc2, 0x81, 0x44, 0xa9, 0x62, 0xb9, 0x5b, 0xc9, 0x09, 0x9d, 0x87, 0x1e, 0x4d, 0xbe, 0x5f, 0x0a, 0x94, 0x78, 0xa5, 0x24, 0xaf, 0xb7, 0x90, 0xc8, 0x1e, 0xcf, 0x74, 0xdc, 0xd5, 0xb4, 0x7d, 0x16, 0xf1, 0xad, 0x68, 0xbc, 0xdb, 0x0c, 0xd5, 0x6e, 0x07, 0x49, 0xbf, 0xcc, 0x15, 0x62, 0x12, 0xd1, 0xa1, 0x91, 0x71, 0x37, 0x39, 0x98, 0x1d, 0x59, 0xdc, 0x42, 0x24, 0x9d, 0x7d, 0x04, 0x7f, 0xa4, 0x12, 0xc2, 0x4d, 0x87, 0x0b, 0x62, 0x1e, 0x81, 0x48, 0x68, 0x27, 0xfd, 0xde, 0x94, 0x34, 0x2e, 0xf3, 0x17, 0x78, 0x37, 0x4c, 0x90, 0xff, 0x71, 0x7f, 0x6a, 0xf2, 0xad, 0x9d, 0x84, 0x6e, 0x5d, 0x39, 0x36, 0xec, 0x80, 0xf1, 0xba, 0xa4, 0x1d, 0x5b, 0x09, 0xc6, 0xd5, 0x50, 0xeb, 0x45, 0x7c, 0x21, 0x7e, 0x86, 0xff, 0xf8, 0x15, 0x6c }, +}; +static const unsigned char vec5_cmsg1[519] = { 0x02, 0xa9, 0xf7, 0xa6, 0x54, 0x1e, 0x12, 0x22, 0x46, 0xfd, 0x57, 0xee, 0xdc, 0x6e, 0x3a, 0x6a, 0x12, 0x48, 0x05, 0x4f, 0x02, 0x2b, 0x7c, 0x01, 0xe2, 0xa4, 0x36, 0x4c, 0x09, 0x20, 0xda, 0x93, 0x4b, 0x03, 0x1a, 0x5c, 0xb4, 0x0b, 0x67, 0xdb, 0xbc, 0xc4, 0x8b, 0x03, 0xcc, 0x2c, 0xc5, 0x54, 0x52, 0x0c, 0x01, 0x72, 0x57, 0x55, 0xd5, 0x2a, 0x0d, 0xec, 0x5d, 0xb1, 0xe0, 0xe1, 0xad, 0x00, 0x45, 0x59, 0x02, 0x54, 0xc4, 0xc5, 0x71, 0x04, 0x3b, 0x01, 0x22, 0xe0, 0x1a, 0x79, 0x37, 0x1e, 0x0f, 0xc2, 0x6b, 0x66, 0x4e, 0xbc, 0xbb, 0x4e, 0x18, 0xe5, 0x1a, 0xa9, 0x67, 0x21, 0x18, 0x23, 0x0b, 0x86, 0xef, 0x02, 0x67, 0x0c, 0x35, 0xb2, 0xae, 0x50, 0x90, 0xff, 0xe0, 0x32, 0x36, 0xee, 0x4f, 0x86, 0xf9, 0x31, 0xac, 0x79, 0x80, 0x64, 0xfb, 0x9d, 0x9c, 0xd1, 0x95, 0x4b, 0xf0, 0x5f, 0xc1, 0x38, 0x38, 0x69, 0xf5, 0xb1, 0x94, 0x34, 0xb9, 0x81, 0x06, 0x4d, 0x17, 0xa1, 0x6a, 0x1d, 0x81, 0x58, 0x4c, 0xf0, 0x55, 0xdf, 0x32, 0x62, 0x08, 0xd8, 0x29, 0x1e, 0xf2, 0xa6, 0xa3, 0xdb, 0x52, 0x98, 0xb8, 0x0f, 0xe6, 0x49, 0x74, 0xe1, 0x9f, 0x90, 0x70, 0x66, 0x09, 0x6b, 0xa9, 0xd5, 0x05, 0xd6, 0xb2, 0x36, 0x7a, 0xf2, 0x9b, 0xbb, 0x0e, 0x66, 0xed, 0x22, 0xd5, 0xdf, 0x21, 0xba, 0x1e, 0x51, 0x77, 0x40, 0x1d, 0xf4, 0x21, 0xdd, 0xdf, 0x41, 0xc4, 0x1c, 0xae, 0x3a, 0x03, 0xd6, 0xd6, 0x7a, 0x25, 0x48, 0xcb, 0x36, 0x8b, 0xfa, 0x59, 0xb8, 0xfe, 0x6a, 0x2a, 0x39, 0x1b, 0x30, 0x60, 0xd0, 0x2e, 0x8b, 0x2d, 0xab, 0x0a, 0x99, 0x90, 0x31, 0xb9, 0x0f, 0x62, 0xa1, 0xb0, 0x1a, 0x3f, 0x3e, 0x43, 0xf6, 0xce, 0x94, 0x73, 0xcc, 0x88, 0xda, 0x76, 0x91, 0x66, 0x98, 0xa2, 0xd2, 0x4f, 0xdf, 0x81, 0xcb, 0xa7, 0x13, 0x00, 0x3b, 0x39, 0x2d, 0x2d, 0x35, 0xa5, 0x7c, 0x5d, 0x3c, 0x96, 0x92, 0xb8, 0xd4, 0x43, 0x94, 0xca, 0xf7, 0x71, 0x60, 0x5c, 0x6f, 0x5b, 0x99, 0x33, 0xcb, 0x39, 0xf8, 0xae, 0xf5, 0xbc, 0x92, 0x09, 0x3f, 0x03, 0xd0, 0x5c, 0x4a, 0x16, 0xff, 0x0a, 0xc3, 0x0b, 0x34, 0x78, 0xe7, 0xea, 0xfb, 0x48, 0xbc, 0x38, 0xbb, 0x18, 0x20, 0xf0, 0x8f, 0x10, 0x63, 0x3f, 0x42, 0xf4, 0xbd, 0x03, 0x07, 0xd9, 0x3f, 0x10, 0xca, 0xd5, 0x95, 0x92, 0xf3, 0x83, 0xc1, 0xd0, 0x0e, 0xb3, 0xb2, 0xf0, 0xea, 0xcb, 0x67, 0x79, 0xd7, 0xc1, 0x70, 0x0a, 0x42, 0x74, 0x77, 0x4d, 0xfd, 0x00, 0xab, 0x72, 0x03, 0x7f, 0xc5, 0xc6, 0x73, 0xb0, 0xe6, 0xfb, 0x74, 0x98, 0xce, 0x1a, 0x86, 0xd7, 0xf7, 0xe8, 0x05, 0xf4, 0x65, 0xe6, 0x8b, 0xca, 0xe2, 0x95, 0x10, 0xdd, 0xdd, 0xec, 0xb6, 0x34, 0x2f, 0xae, 0x12, 0x02, 0x6d, 0xda, 0xf4, 0x4d, 0x4a, 0x66, 0xfc, 0xf7, 0x7b, 0xc2, 0x81, 0x44, 0xa9, 0x62, 0xb9, 0x5b, 0xc9, 0x09, 0x9d, 0x87, 0x1e, 0x4d, 0xbe, 0x5f, 0x0a, 0x94, 0x78, 0xa5, 0x24, 0xaf, 0xb7, 0x90, 0x94, 0xac, 0x3f, 0x80, 0xa1, 0xae, 0x79, 0x9e, 0xab, 0xe7, 0x86, 0xae, 0xd5, 0x71, 0x9f, 0x2d, 0x2d, 0x81, 0xfc, 0x26, 0x4c, 0xa8, 0xad, 0xa1, 0x21, 0x1a, 0x7a, 0xc9, 0x86, 0x97, 0x22, 0x83, 0xe8, 0x09, 0x2c, 0xf2, 0xc3, 0x5d, 0xb9, 0x44, 0x39, 0xd7, 0xe9, 0x09, 0x4c, 0x0f, 0xa7, 0x3c, 0xd5, 0xc9, 0x2d, 0xbb, 0x16, 0xe2, 0xa8, 0x7e, 0x81, 0xd4, 0x66, 0x85, 0x4f, 0x68, 0xbe, 0x2e, 0x99, 0x7a, 0xea, 0x54, 0x29, 0x9c, 0x88, 0xcb, 0x83, 0x97, 0x67, 0xd4, 0x65, 0xdd, 0x18, 0x45, 0x35, 0x23, 0xac, 0x2b, 0x7d, 0x3c, 0x6b, 0x69, 0xa5, 0xc4, 0x1f, 0xdd, 0xe7, 0x7a, 0xe5, 0x62 }; +static const unsigned char vec5_pmsgs2[3][64] = { + { 0x37, 0x24, 0xa1, 0x76, 0xf2, 0x82, 0xd3, 0x37, 0xac, 0x82, 0x26, 0xd2, 0xe7, 0x1c, 0x92, 0xff, 0x87, 0x65, 0x9c, 0x7f, 0x98, 0x17, 0x4e, 0x54, 0xd3, 0x43, 0x6f, 0x1a, 0x83, 0xda, 0xc4, 0x14, 0xbf, 0xbb, 0x69, 0x94, 0x7a, 0xc2, 0x3c, 0xd4, 0x42, 0x3a, 0x0d, 0xdd, 0x4a, 0x22, 0x0c, 0xbb, 0x17, 0x63, 0xfa, 0xbb, 0x3f, 0x54, 0xd0, 0xdc, 0xcc, 0x4d, 0x44, 0xb3, 0xa3, 0x23, 0x83, 0x3b }, + { 0x5e, 0xbc, 0xe2, 0x1d, 0x9a, 0xbe, 0x87, 0x88, 0x67, 0x56, 0xbd, 0x19, 0x66, 0x0c, 0x3a, 0x3c, 0x02, 0x36, 0x82, 0x4a, 0xb9, 0xc3, 0x3e, 0x3b, 0x5c, 0xb4, 0xa8, 0x3b, 0x06, 0xa1, 0x20, 0xff, 0x11, 0x7f, 0x2b, 0xf8, 0x18, 0x3b, 0x79, 0xe2, 0x46, 0x61, 0x1b, 0xd2, 0x3b, 0x22, 0x7a, 0x8c, 0x7b, 0xbb, 0xcb, 0xe7, 0xbd, 0xf8, 0x2a, 0x05, 0xf2, 0x34, 0xfa, 0x66, 0x26, 0xd3, 0x02, 0x20 }, + { 0x30, 0x42, 0x47, 0x3e, 0x0b, 0x02, 0x24, 0xf9, 0x9a, 0xc5, 0x1a, 0xe8, 0x12, 0xdb, 0x5f, 0x99, 0x70, 0x78, 0x96, 0x72, 0x41, 0x75, 0x99, 0x2e, 0xf1, 0xdf, 0x5d, 0xdf, 0xee, 0x41, 0xc5, 0x75, 0x99, 0xa6, 0xb4, 0x3f, 0xd3, 0x86, 0xdc, 0xb7, 0xc4, 0x01, 0x21, 0xe2, 0xe9, 0x59, 0x34, 0xa8, 0xe3, 0x1d, 0x79, 0xb7, 0xa9, 0xda, 0xd4, 0x46, 0xea, 0x9a, 0x72, 0x7f, 0xb6, 0xe9, 0x26, 0x17 }, +}; +static const unsigned char vec5_cmsg2[192] = { 0x37, 0x24, 0xa1, 0x76, 0xf2, 0x82, 0xd3, 0x37, 0xac, 0x82, 0x26, 0xd2, 0xe7, 0x1c, 0x92, 0xff, 0x87, 0x65, 0x9c, 0x7f, 0x98, 0x17, 0x4e, 0x54, 0xd3, 0x43, 0x6f, 0x1a, 0x83, 0xda, 0xc4, 0x14, 0xbf, 0xbb, 0x69, 0x94, 0x7a, 0xc2, 0x3c, 0xd4, 0x42, 0x3a, 0x0d, 0xdd, 0x4a, 0x22, 0x0c, 0xbb, 0x17, 0x63, 0xfa, 0xbb, 0x3f, 0x54, 0xd0, 0xdc, 0xcc, 0x4d, 0x44, 0xb3, 0xa3, 0x23, 0x83, 0x3b, 0x5e, 0xbc, 0xe2, 0x1d, 0x9a, 0xbe, 0x87, 0x88, 0x67, 0x56, 0xbd, 0x19, 0x66, 0x0c, 0x3a, 0x3c, 0x02, 0x36, 0x82, 0x4a, 0xb9, 0xc3, 0x3e, 0x3b, 0x5c, 0xb4, 0xa8, 0x3b, 0x06, 0xa1, 0x20, 0xff, 0x11, 0x7f, 0x2b, 0xf8, 0x18, 0x3b, 0x79, 0xe2, 0x46, 0x61, 0x1b, 0xd2, 0x3b, 0x22, 0x7a, 0x8c, 0x7b, 0xbb, 0xcb, 0xe7, 0xbd, 0xf8, 0x2a, 0x05, 0xf2, 0x34, 0xfa, 0x66, 0x26, 0xd3, 0x02, 0x20, 0x30, 0x42, 0x47, 0x3e, 0x0b, 0x02, 0x24, 0xf9, 0x9a, 0xc5, 0x1a, 0xe8, 0x12, 0xdb, 0x5f, 0x99, 0x70, 0x78, 0x96, 0x72, 0x41, 0x75, 0x99, 0x2e, 0xf1, 0xdf, 0x5d, 0xdf, 0xee, 0x41, 0xc5, 0x75, 0x99, 0xa6, 0xb4, 0x3f, 0xd3, 0x86, 0xdc, 0xb7, 0xc4, 0x01, 0x21, 0xe2, 0xe9, 0x59, 0x34, 0xa8, 0xe3, 0x1d, 0x79, 0xb7, 0xa9, 0xda, 0xd4, 0x46, 0xea, 0x9a, 0x72, 0x7f, 0xb6, 0xe9, 0x26, 0x17 }; +static const unsigned char vec5_secshares[3][32] = { + { 0x1a, 0xba, 0xaa, 0x2d, 0x25, 0xd0, 0x5b, 0x3f, 0x9b, 0xdc, 0x54, 0xc4, 0x51, 0xe9, 0x90, 0xec, 0xf0, 0x4b, 0x22, 0x49, 0xf1, 0xbc, 0x61, 0x5e, 0xa8, 0x7b, 0xf7, 0x31, 0x62, 0xcb, 0xf5, 0xce }, + { 0x35, 0xe1, 0x3c, 0x52, 0xfa, 0x2f, 0x39, 0x4c, 0x6a, 0x9d, 0x9b, 0xcd, 0x1b, 0x96, 0xd2, 0x95, 0xff, 0xbb, 0x85, 0x95, 0x62, 0x56, 0x02, 0xdf, 0x1a, 0x44, 0xd6, 0x7f, 0xf7, 0x25, 0x4f, 0x1f }, + { 0x51, 0x07, 0xce, 0x78, 0xce, 0x8e, 0x17, 0x59, 0x39, 0x5e, 0xe2, 0xd5, 0xe5, 0x44, 0x14, 0x3f, 0x0f, 0x2b, 0xe8, 0xe0, 0xd2, 0xef, 0xa4, 0x5f, 0x8c, 0x0d, 0xb5, 0xce, 0x8b, 0x7e, 0xa8, 0x70 }, +}; +static const unsigned char vec5_thresh_pk[33] = { 0x02, 0xd6, 0x31, 0x69, 0x44, 0xf7, 0x8c, 0x21, 0x4a, 0xac, 0x9f, 0x93, 0x32, 0x70, 0xe7, 0xa6, 0xad, 0xa9, 0x5e, 0xd0, 0xe0, 0x03, 0x6a, 0xab, 0x78, 0x16, 0x1b, 0xbb, 0xbe, 0x25, 0xa5, 0xee, 0xb5 }; +static const unsigned char vec5_pubshares[3][33] = { + { 0x02, 0x9d, 0x60, 0x50, 0x8f, 0x5e, 0xd6, 0x93, 0x19, 0x8f, 0x17, 0x6d, 0x2e, 0x19, 0x99, 0x3e, 0x3a, 0x17, 0x0f, 0xb9, 0x83, 0xc8, 0x95, 0x97, 0x94, 0xe9, 0x8b, 0x4e, 0x75, 0x95, 0xf7, 0x99, 0x5f }, + { 0x03, 0x81, 0xff, 0x24, 0x16, 0x43, 0xe5, 0x5b, 0x70, 0xc0, 0xfc, 0x01, 0x65, 0xcc, 0x16, 0x80, 0x6e, 0x23, 0x76, 0xf2, 0x6b, 0xf4, 0xf2, 0xc2, 0x58, 0x72, 0x98, 0x69, 0xb2, 0x89, 0x46, 0x2a, 0x69 }, + { 0x02, 0x6e, 0x74, 0xfa, 0x97, 0x9d, 0x46, 0xe9, 0xe4, 0x71, 0x23, 0x66, 0x8a, 0x55, 0x85, 0x09, 0x21, 0x67, 0x3b, 0x37, 0x41, 0x6e, 0xcf, 0x95, 0x34, 0x98, 0xca, 0x19, 0x0a, 0xe6, 0xce, 0xd3, 0xa1 }, +}; +static const unsigned char vec5_recovery[556] = { 0x00, 0x00, 0x00, 0x02, 0x03, 0x3b, 0x49, 0x45, 0x46, 0x83, 0x43, 0x3e, 0xc5, 0x06, 0x9a, 0x38, 0xaf, 0x81, 0x12, 0xc3, 0xe8, 0x77, 0x51, 0x2a, 0xfe, 0xf3, 0x58, 0xfa, 0x5b, 0xb7, 0x2a, 0xb7, 0x6e, 0x26, 0x7e, 0x8f, 0x8a, 0x02, 0x67, 0x0c, 0x35, 0xb2, 0xae, 0x50, 0x90, 0xff, 0xe0, 0x32, 0x36, 0xee, 0x4f, 0x86, 0xf9, 0x31, 0xac, 0x79, 0x80, 0x64, 0xfb, 0x9d, 0x9c, 0xd1, 0x95, 0x4b, 0xf0, 0x5f, 0xc1, 0x38, 0x38, 0x69, 0x03, 0x4f, 0x68, 0x91, 0xb1, 0xd2, 0x7f, 0x67, 0xd3, 0xbb, 0x8a, 0xd4, 0x80, 0x83, 0x1a, 0x3d, 0x80, 0x7f, 0x58, 0xed, 0x42, 0x21, 0x6c, 0x94, 0x9b, 0xd0, 0xc7, 0x8a, 0x51, 0x9c, 0xbe, 0x76, 0xe0, 0x02, 0xfb, 0x7e, 0x20, 0xfb, 0xa2, 0x87, 0xee, 0x46, 0xb9, 0x53, 0x62, 0x32, 0xf6, 0x9c, 0x31, 0xa9, 0x0c, 0x66, 0xea, 0xf1, 0xfd, 0x67, 0x18, 0xdd, 0xdc, 0x1f, 0x10, 0xaa, 0x15, 0xf8, 0xa3, 0x4f, 0x03, 0x59, 0x5a, 0x19, 0x3f, 0xe6, 0xe8, 0xf1, 0xf4, 0x36, 0x00, 0x79, 0x4e, 0x19, 0x39, 0xaa, 0x29, 0x04, 0x1f, 0x3b, 0xc1, 0xd3, 0x29, 0x49, 0x1e, 0x88, 0x38, 0x32, 0x19, 0x74, 0x8c, 0x2f, 0xa5, 0x03, 0x07, 0xd9, 0x3f, 0x10, 0xca, 0xd5, 0x95, 0x92, 0xf3, 0x83, 0xc1, 0xd0, 0x0e, 0xb3, 0xb2, 0xf0, 0xea, 0xcb, 0x67, 0x79, 0xd7, 0xc1, 0x70, 0x0a, 0x42, 0x74, 0x77, 0x4d, 0xfd, 0x00, 0xab, 0x72, 0x03, 0x7f, 0xc5, 0xc6, 0x73, 0xb0, 0xe6, 0xfb, 0x74, 0x98, 0xce, 0x1a, 0x86, 0xd7, 0xf7, 0xe8, 0x05, 0xf4, 0x65, 0xe6, 0x8b, 0xca, 0xe2, 0x95, 0x10, 0xdd, 0xdd, 0xec, 0xb6, 0x34, 0x2f, 0xae, 0x12, 0x02, 0x6d, 0xda, 0xf4, 0x4d, 0x4a, 0x66, 0xfc, 0xf7, 0x7b, 0xc2, 0x81, 0x44, 0xa9, 0x62, 0xb9, 0x5b, 0xc9, 0x09, 0x9d, 0x87, 0x1e, 0x4d, 0xbe, 0x5f, 0x0a, 0x94, 0x78, 0xa5, 0x24, 0xaf, 0xb7, 0x90, 0x94, 0xac, 0x3f, 0x80, 0xa1, 0xae, 0x79, 0x9e, 0xab, 0xe7, 0x86, 0xae, 0xd5, 0x71, 0x9f, 0x2d, 0x2d, 0x81, 0xfc, 0x26, 0x4c, 0xa8, 0xad, 0xa1, 0x21, 0x1a, 0x7a, 0xc9, 0x86, 0x97, 0x22, 0x83, 0xe8, 0x09, 0x2c, 0xf2, 0xc3, 0x5d, 0xb9, 0x44, 0x39, 0xd7, 0xe9, 0x09, 0x4c, 0x0f, 0xa7, 0x3c, 0xd5, 0xc9, 0x2d, 0xbb, 0x16, 0xe2, 0xa8, 0x7e, 0x81, 0xd4, 0x66, 0x85, 0x4f, 0x68, 0xbe, 0x2e, 0x99, 0x7a, 0xea, 0x54, 0x29, 0x9c, 0x88, 0xcb, 0x83, 0x97, 0x67, 0xd4, 0x65, 0xdd, 0x18, 0x45, 0x35, 0x23, 0xac, 0x2b, 0x7d, 0x3c, 0x6b, 0x69, 0xa5, 0xc4, 0x1f, 0xdd, 0xe7, 0x7a, 0xe5, 0x62, 0x37, 0x24, 0xa1, 0x76, 0xf2, 0x82, 0xd3, 0x37, 0xac, 0x82, 0x26, 0xd2, 0xe7, 0x1c, 0x92, 0xff, 0x87, 0x65, 0x9c, 0x7f, 0x98, 0x17, 0x4e, 0x54, 0xd3, 0x43, 0x6f, 0x1a, 0x83, 0xda, 0xc4, 0x14, 0xbf, 0xbb, 0x69, 0x94, 0x7a, 0xc2, 0x3c, 0xd4, 0x42, 0x3a, 0x0d, 0xdd, 0x4a, 0x22, 0x0c, 0xbb, 0x17, 0x63, 0xfa, 0xbb, 0x3f, 0x54, 0xd0, 0xdc, 0xcc, 0x4d, 0x44, 0xb3, 0xa3, 0x23, 0x83, 0x3b, 0x5e, 0xbc, 0xe2, 0x1d, 0x9a, 0xbe, 0x87, 0x88, 0x67, 0x56, 0xbd, 0x19, 0x66, 0x0c, 0x3a, 0x3c, 0x02, 0x36, 0x82, 0x4a, 0xb9, 0xc3, 0x3e, 0x3b, 0x5c, 0xb4, 0xa8, 0x3b, 0x06, 0xa1, 0x20, 0xff, 0x11, 0x7f, 0x2b, 0xf8, 0x18, 0x3b, 0x79, 0xe2, 0x46, 0x61, 0x1b, 0xd2, 0x3b, 0x22, 0x7a, 0x8c, 0x7b, 0xbb, 0xcb, 0xe7, 0xbd, 0xf8, 0x2a, 0x05, 0xf2, 0x34, 0xfa, 0x66, 0x26, 0xd3, 0x02, 0x20, 0x30, 0x42, 0x47, 0x3e, 0x0b, 0x02, 0x24, 0xf9, 0x9a, 0xc5, 0x1a, 0xe8, 0x12, 0xdb, 0x5f, 0x99, 0x70, 0x78, 0x96, 0x72, 0x41, 0x75, 0x99, 0x2e, 0xf1, 0xdf, 0x5d, 0xdf, 0xee, 0x41, 0xc5, 0x75, 0x99, 0xa6, 0xb4, 0x3f, 0xd3, 0x86, 0xdc, 0xb7, 0xc4, 0x01, 0x21, 0xe2, 0xe9, 0x59, 0x34, 0xa8, 0xe3, 0x1d, 0x79, 0xb7, 0xa9, 0xda, 0xd4, 0x46, 0xea, 0x9a, 0x72, 0x7f, 0xb6, 0xe9, 0x26, 0x17 }; +static const unsigned char vec5_ack_sigs[3][64] = { + { 0x11, 0xfc, 0x90, 0x7c, 0x69, 0x20, 0x13, 0x7a, 0x51, 0x09, 0xa2, 0x03, 0x75, 0x32, 0x7e, 0x7f, 0x7f, 0x20, 0xf2, 0x82, 0x60, 0xa8, 0xf3, 0x01, 0xcf, 0xbc, 0xfd, 0xbb, 0xa7, 0xea, 0x64, 0xa8, 0xbc, 0xf5, 0xc7, 0x05, 0xfc, 0xa4, 0x81, 0xa9, 0xbb, 0x4e, 0x0b, 0x49, 0x1a, 0x2a, 0x49, 0x0d, 0x9c, 0xc8, 0x6f, 0x6d, 0x23, 0xdc, 0x15, 0x2f, 0xdc, 0xe7, 0x99, 0xa7, 0x8a, 0xe1, 0x4d, 0x1a }, + { 0x44, 0xff, 0xf6, 0x84, 0xf5, 0x8d, 0x9f, 0xc4, 0xad, 0x46, 0xc6, 0x49, 0x33, 0x42, 0x8e, 0x9e, 0xa7, 0xc1, 0x25, 0xc0, 0x28, 0x6d, 0xbd, 0x78, 0xfd, 0x61, 0x30, 0x85, 0x52, 0x8f, 0x40, 0x67, 0x9e, 0x97, 0x55, 0xcc, 0x21, 0x30, 0xf8, 0xa4, 0x3b, 0x11, 0x88, 0xd2, 0xe4, 0x0a, 0xa2, 0x41, 0x4f, 0x60, 0x41, 0x01, 0x7b, 0x4b, 0xc1, 0x88, 0x6c, 0x74, 0x1d, 0xee, 0x85, 0xa7, 0x99, 0xa0 }, + { 0x00, 0x20, 0xeb, 0x92, 0x88, 0x9e, 0x59, 0x81, 0x9e, 0x0d, 0x7b, 0x3f, 0x6b, 0x91, 0x3e, 0xe0, 0x70, 0x33, 0x02, 0x8e, 0xf3, 0xd9, 0x57, 0x66, 0xe8, 0x48, 0xb6, 0xad, 0xe8, 0x2b, 0x22, 0x21, 0xf5, 0x51, 0xa7, 0x4a, 0x64, 0x8e, 0xa6, 0xc1, 0x4e, 0xa7, 0x07, 0x81, 0xb9, 0x16, 0xfb, 0xc9, 0xca, 0x27, 0xac, 0x70, 0xd7, 0xb6, 0x2d, 0x97, 0xaa, 0x95, 0xd5, 0x40, 0x4f, 0x04, 0x56, 0x69 }, +}; +static const unsigned char vec5_inv_pmsg1_bad[259] = { 0x03, 0x1a, 0x5c, 0xb4, 0x0b, 0x67, 0xdb, 0xbc, 0xc4, 0x8b, 0x03, 0xcc, 0x2c, 0xc5, 0x54, 0x52, 0x0c, 0x01, 0x72, 0x57, 0x55, 0xd5, 0x2a, 0x0d, 0xec, 0x5d, 0xb1, 0xe0, 0xe1, 0xad, 0x00, 0x45, 0x59, 0x02, 0x9b, 0xac, 0x46, 0x00, 0xbb, 0xa5, 0xa5, 0x43, 0x08, 0x81, 0xc5, 0xac, 0x25, 0x1c, 0x8a, 0x01, 0xc7, 0x2d, 0x04, 0x69, 0x0e, 0x89, 0xeb, 0x6c, 0xb8, 0xe3, 0xdb, 0x4c, 0x60, 0x9d, 0x12, 0xf1, 0x1d, 0xf4, 0x21, 0xdd, 0xdf, 0x41, 0xc4, 0x1c, 0xae, 0x3a, 0x03, 0xd6, 0xd6, 0x7a, 0x25, 0x48, 0xcb, 0x36, 0x8b, 0xfa, 0x59, 0xb8, 0xfe, 0x6a, 0x2a, 0x39, 0x1b, 0x30, 0x60, 0xd0, 0x2e, 0x8b, 0x2d, 0xab, 0x0a, 0x99, 0x90, 0x31, 0xb9, 0x0f, 0x62, 0xa1, 0xb0, 0x1a, 0x3f, 0x3e, 0x43, 0xf6, 0xce, 0x94, 0x73, 0xcc, 0x88, 0xda, 0x76, 0x91, 0x66, 0x98, 0xa2, 0xd2, 0x4f, 0xdf, 0x81, 0xcb, 0x03, 0x7f, 0xc5, 0xc6, 0x73, 0xb0, 0xe6, 0xfb, 0x74, 0x98, 0xce, 0x1a, 0x86, 0xd7, 0xf7, 0xe8, 0x05, 0xf4, 0x65, 0xe6, 0x8b, 0xca, 0xe2, 0x95, 0x10, 0xdd, 0xdd, 0xec, 0xb6, 0x34, 0x2f, 0xae, 0x12, 0x10, 0x87, 0xc2, 0xea, 0x8c, 0x60, 0xeb, 0xc6, 0x3d, 0x40, 0x37, 0x38, 0xcf, 0x60, 0x3e, 0xfd, 0x7b, 0xc8, 0x31, 0x10, 0xfa, 0x29, 0x61, 0x5b, 0x93, 0x6e, 0x85, 0xa0, 0xd3, 0x5c, 0xe0, 0xa1, 0xa2, 0xf0, 0xff, 0xfb, 0x07, 0x2f, 0x48, 0xdb, 0x25, 0x84, 0x4d, 0xd0, 0x5c, 0xeb, 0xc0, 0xba, 0x8c, 0x51, 0x3d, 0x06, 0x69, 0x28, 0x42, 0xe6, 0x40, 0x4a, 0xfe, 0x20, 0x0d, 0x37, 0xc4, 0xe1, 0x46, 0x4f, 0x8e, 0x55, 0xc0, 0xd5, 0x13, 0x65, 0xf2, 0x99, 0xa2, 0x78, 0x3e, 0xd4, 0xc9, 0x47, 0x5d, 0x4d, 0x45, 0xbd, 0x5c, 0xd7, 0xe8, 0xd2, 0x82, 0xc2, 0x4a, 0xe5, 0xd1, 0x5d, 0xe9, 0x4a }; +static const unsigned char vec5_inv_cmsg1[519] = { 0x02, 0xa9, 0xf7, 0xa6, 0x54, 0x1e, 0x12, 0x22, 0x46, 0xfd, 0x57, 0xee, 0xdc, 0x6e, 0x3a, 0x6a, 0x12, 0x48, 0x05, 0x4f, 0x02, 0x2b, 0x7c, 0x01, 0xe2, 0xa4, 0x36, 0x4c, 0x09, 0x20, 0xda, 0x93, 0x4b, 0x03, 0x1a, 0x5c, 0xb4, 0x0b, 0x67, 0xdb, 0xbc, 0xc4, 0x8b, 0x03, 0xcc, 0x2c, 0xc5, 0x54, 0x52, 0x0c, 0x01, 0x72, 0x57, 0x55, 0xd5, 0x2a, 0x0d, 0xec, 0x5d, 0xb1, 0xe0, 0xe1, 0xad, 0x00, 0x45, 0x59, 0x02, 0x54, 0xc4, 0xc5, 0x71, 0x04, 0x3b, 0x01, 0x22, 0xe0, 0x1a, 0x79, 0x37, 0x1e, 0x0f, 0xc2, 0x6b, 0x66, 0x4e, 0xbc, 0xbb, 0x4e, 0x18, 0xe5, 0x1a, 0xa9, 0x67, 0x21, 0x18, 0x23, 0x0b, 0x86, 0xef, 0x02, 0x67, 0x0c, 0x35, 0xb2, 0xae, 0x50, 0x90, 0xff, 0xe0, 0x32, 0x36, 0xee, 0x4f, 0x86, 0xf9, 0x31, 0xac, 0x79, 0x80, 0x64, 0xfb, 0x9d, 0x9c, 0xd1, 0x95, 0x4b, 0xf0, 0x5f, 0xc1, 0x38, 0x38, 0x69, 0xf5, 0xb1, 0x94, 0x34, 0xb9, 0x81, 0x06, 0x4d, 0x17, 0xa1, 0x6a, 0x1d, 0x81, 0x58, 0x4c, 0xf0, 0x55, 0xdf, 0x32, 0x62, 0x08, 0xd8, 0x29, 0x1e, 0xf2, 0xa6, 0xa3, 0xdb, 0x52, 0x98, 0xb8, 0x0f, 0xe6, 0x49, 0x74, 0xe1, 0x9f, 0x90, 0x70, 0x66, 0x09, 0x6b, 0xa9, 0xd5, 0x05, 0xd6, 0xb2, 0x36, 0x7a, 0xf2, 0x9b, 0xbb, 0x0e, 0x66, 0xed, 0x22, 0xd5, 0xdf, 0x21, 0xba, 0x1e, 0x51, 0x77, 0x40, 0x1d, 0xf4, 0x21, 0xdd, 0xdf, 0x41, 0xc4, 0x1c, 0xae, 0x3a, 0x03, 0xd6, 0xd6, 0x7a, 0x25, 0x48, 0xcb, 0x36, 0x8b, 0xfa, 0x59, 0xb8, 0xfe, 0x6a, 0x2a, 0x39, 0x1b, 0x30, 0x60, 0xd0, 0x2e, 0x8b, 0x2d, 0xab, 0x0a, 0x99, 0x90, 0x31, 0xb9, 0x0f, 0x62, 0xa1, 0xb0, 0x1a, 0x3f, 0x3e, 0x43, 0xf6, 0xce, 0x94, 0x73, 0xcc, 0x88, 0xda, 0x76, 0x91, 0x66, 0x98, 0xa2, 0xd2, 0x4f, 0xdf, 0x81, 0xcb, 0xa7, 0x13, 0x00, 0x3b, 0x39, 0x2d, 0x2d, 0x35, 0xa5, 0x7c, 0x5d, 0x3c, 0x96, 0x92, 0xb8, 0xd4, 0x43, 0x94, 0xca, 0xf7, 0x71, 0x60, 0x5c, 0x6f, 0x5b, 0x99, 0x33, 0xcb, 0x39, 0xf8, 0xae, 0xf5, 0xbc, 0x92, 0x09, 0x3f, 0x03, 0xd0, 0x5c, 0x4a, 0x16, 0xff, 0x0a, 0xc3, 0x0b, 0x34, 0x78, 0xe7, 0xea, 0xfb, 0x48, 0xbc, 0x38, 0xbb, 0x18, 0x20, 0xf0, 0x8f, 0x10, 0x63, 0x3f, 0x42, 0xf4, 0xbd, 0x03, 0x07, 0xd9, 0x3f, 0x10, 0xca, 0xd5, 0x95, 0x92, 0xf3, 0x83, 0xc1, 0xd0, 0x0e, 0xb3, 0xb2, 0xf0, 0xea, 0xcb, 0x67, 0x79, 0xd7, 0xc1, 0x70, 0x0a, 0x42, 0x74, 0x77, 0x4d, 0xfd, 0x00, 0xab, 0x72, 0x03, 0x7f, 0xc5, 0xc6, 0x73, 0xb0, 0xe6, 0xfb, 0x74, 0x98, 0xce, 0x1a, 0x86, 0xd7, 0xf7, 0xe8, 0x05, 0xf4, 0x65, 0xe6, 0x8b, 0xca, 0xe2, 0x95, 0x10, 0xdd, 0xdd, 0xec, 0xb6, 0x34, 0x2f, 0xae, 0x12, 0x02, 0x6d, 0xda, 0xf4, 0x4d, 0x4a, 0x66, 0xfc, 0xf7, 0x7b, 0xc2, 0x81, 0x44, 0xa9, 0x62, 0xb9, 0x5b, 0xc9, 0x09, 0x9d, 0x87, 0x1e, 0x4d, 0xbe, 0x5f, 0x0a, 0x94, 0x78, 0xa5, 0x24, 0xaf, 0xb7, 0x90, 0x94, 0xac, 0x3f, 0x80, 0xa1, 0xae, 0x79, 0x9e, 0xab, 0xe7, 0x86, 0xae, 0xd5, 0x71, 0x9f, 0x2d, 0x2d, 0x81, 0xfc, 0x26, 0x4c, 0xa8, 0xad, 0xa1, 0x21, 0x1a, 0x7a, 0xc9, 0x86, 0x97, 0x22, 0x83, 0xe8, 0x09, 0x2c, 0xf2, 0xc3, 0x5d, 0xb9, 0x44, 0x39, 0xd7, 0xe9, 0x09, 0x4c, 0x0f, 0xa7, 0x3c, 0xd5, 0xc9, 0x2d, 0xbb, 0x16, 0xe2, 0xa8, 0x7e, 0x81, 0xd4, 0x66, 0x85, 0x4f, 0x68, 0xbe, 0x2e, 0x99, 0x7a, 0xea, 0x54, 0x29, 0x9c, 0x88, 0xcb, 0x83, 0x97, 0x67, 0xd4, 0x65, 0xdd, 0x18, 0x45, 0x35, 0x23, 0xac, 0x2b, 0x7d, 0x3c, 0x6b, 0x69, 0xa5, 0xc4, 0x1f, 0xdd, 0xe7, 0x7a, 0xe5, 0x61 }; +static const unsigned char vec5_inv_cinv[195] = { 0xe1, 0xab, 0xf1, 0x0b, 0xbb, 0x29, 0xf0, 0xf7, 0x33, 0xc4, 0x8e, 0x6f, 0xa6, 0x16, 0x94, 0x58, 0x75, 0x2a, 0x39, 0x8d, 0xfa, 0x5c, 0x37, 0x8d, 0x66, 0xb2, 0xb4, 0xfd, 0xe6, 0x5b, 0x27, 0xec, 0x46, 0x4f, 0x8e, 0x55, 0xc0, 0xd5, 0x13, 0x65, 0xf2, 0x99, 0xa2, 0x78, 0x3e, 0xd4, 0xc9, 0x47, 0x5d, 0x4d, 0x45, 0xbd, 0x5c, 0xd7, 0xe8, 0xd2, 0x82, 0xc2, 0x4a, 0xe5, 0xd1, 0x5d, 0xe9, 0x4a, 0x71, 0x7f, 0x6a, 0xf2, 0xad, 0x9d, 0x84, 0x6e, 0x5d, 0x39, 0x36, 0xec, 0x80, 0xf1, 0xba, 0xa4, 0x1d, 0x5b, 0x09, 0xc6, 0xd5, 0x50, 0xeb, 0x45, 0x7c, 0x21, 0x7e, 0x86, 0xff, 0xf8, 0x15, 0x6c, 0x03, 0x46, 0x53, 0x9b, 0xa9, 0x64, 0xd2, 0x4c, 0x64, 0x4b, 0x70, 0x9e, 0xa7, 0x39, 0xc3, 0xc0, 0x2d, 0x6a, 0xa3, 0xce, 0xe8, 0xd2, 0xad, 0x1d, 0x86, 0xff, 0x27, 0x51, 0x8b, 0xf6, 0xd1, 0xa4, 0x42, 0x03, 0x03, 0x35, 0xc1, 0x1b, 0x53, 0x33, 0xf4, 0xb5, 0x2c, 0x35, 0xaf, 0xcb, 0x5c, 0xc7, 0x8d, 0x15, 0x30, 0xdf, 0x92, 0x04, 0xe1, 0x26, 0xe1, 0x5f, 0x4b, 0x33, 0xb0, 0x2a, 0x59, 0x67, 0xf4, 0x80, 0x03, 0x31, 0x29, 0xb0, 0x6b, 0xfb, 0xf6, 0x50, 0xe1, 0x70, 0xd4, 0x90, 0x59, 0xd5, 0xcd, 0x80, 0x10, 0x1b, 0x39, 0xd8, 0x45, 0x04, 0x4f, 0xc3, 0x55, 0xea, 0x24, 0x18, 0x6f, 0x94, 0xe1, 0x62, 0xb2 }; +static const unsigned char vec5_inv_secshare[32] = { 0x40, 0x03, 0x27, 0xf8, 0xd3, 0xb5, 0x90, 0x1a, 0xb1, 0x15, 0x1f, 0x57, 0x34, 0xee, 0x71, 0xea, 0x8c, 0xa6, 0x79, 0x0f, 0xc6, 0xd7, 0x6a, 0xe8, 0x19, 0xc8, 0xb2, 0x46, 0x17, 0x4f, 0xaf, 0xce }; +static const unsigned char vec5_inv_pubshare[33] = { 0x02, 0x08, 0xd9, 0x49, 0xec, 0x74, 0xe5, 0xc0, 0xd9, 0x65, 0x75, 0xb8, 0x17, 0xd9, 0x3b, 0x00, 0xf8, 0xc3, 0xf6, 0x03, 0xde, 0x20, 0x66, 0x00, 0xc9, 0x3c, 0x18, 0xb0, 0x25, 0x4d, 0xc4, 0x47, 0x2e }; +static const unsigned char vec5_inv_enc_secshare[32] = { 0x99, 0x7a, 0xea, 0x54, 0x29, 0x9c, 0x88, 0xcb, 0x83, 0x97, 0x67, 0xd4, 0x65, 0xdd, 0x18, 0x45, 0x35, 0x23, 0xac, 0x2b, 0x7d, 0x3c, 0x6b, 0x69, 0xa5, 0xc4, 0x1f, 0xdd, 0xe7, 0x7a, 0xe5, 0x61 }; +static const unsigned char vec5_inv_pads[3][32] = { + { 0xa5, 0x7a, 0xcd, 0xa1, 0x9b, 0x4a, 0x2b, 0x89, 0xf3, 0x10, 0x6d, 0xd2, 0x77, 0x09, 0x3b, 0x29, 0x42, 0x86, 0x77, 0x22, 0x3b, 0x58, 0x8a, 0xe8, 0xa6, 0xa0, 0x9a, 0xfc, 0x43, 0x14, 0x42, 0x36 }, + { 0x01, 0x8d, 0x94, 0xfa, 0x65, 0x1c, 0x3f, 0xdb, 0x06, 0x08, 0x3a, 0xcf, 0x2f, 0xd5, 0x79, 0x36, 0x53, 0x99, 0x1a, 0x8d, 0x80, 0x2f, 0x6a, 0xfa, 0x7a, 0xb4, 0x3d, 0x9f, 0xd1, 0x45, 0x59, 0xa2 }, + { 0xb2, 0x6f, 0x5f, 0xbf, 0x55, 0x80, 0x8d, 0x4b, 0xd9, 0x69, 0x9f, 0xdb, 0x8a, 0x0f, 0xf1, 0xf9, 0xcd, 0x0c, 0x7e, 0x52, 0xaa, 0x25, 0xaa, 0xda, 0x2a, 0x78, 0xf3, 0x88, 0x8c, 0x07, 0xda, 0xfc }, +}; +/* investigation: dealer 1 sent bad share to participant 2; blame = FaultyParticipantOrCoordinatorError(1) */ +static const unsigned char vec5_inv_cmsg1_coord_bad[519] = { 0x02, 0xa9, 0xf7, 0xa6, 0x54, 0x1e, 0x12, 0x22, 0x46, 0xfd, 0x57, 0xee, 0xdc, 0x6e, 0x3a, 0x6a, 0x12, 0x48, 0x05, 0x4f, 0x02, 0x2b, 0x7c, 0x01, 0xe2, 0xa4, 0x36, 0x4c, 0x09, 0x20, 0xda, 0x93, 0x4b, 0x03, 0x1a, 0x5c, 0xb4, 0x0b, 0x67, 0xdb, 0xbc, 0xc4, 0x8b, 0x03, 0xcc, 0x2c, 0xc5, 0x54, 0x52, 0x0c, 0x01, 0x72, 0x57, 0x55, 0xd5, 0x2a, 0x0d, 0xec, 0x5d, 0xb1, 0xe0, 0xe1, 0xad, 0x00, 0x45, 0x59, 0x02, 0x54, 0xc4, 0xc5, 0x71, 0x04, 0x3b, 0x01, 0x22, 0xe0, 0x1a, 0x79, 0x37, 0x1e, 0x0f, 0xc2, 0x6b, 0x66, 0x4e, 0xbc, 0xbb, 0x4e, 0x18, 0xe5, 0x1a, 0xa9, 0x67, 0x21, 0x18, 0x23, 0x0b, 0x86, 0xef, 0x02, 0x67, 0x0c, 0x35, 0xb2, 0xae, 0x50, 0x90, 0xff, 0xe0, 0x32, 0x36, 0xee, 0x4f, 0x86, 0xf9, 0x31, 0xac, 0x79, 0x80, 0x64, 0xfb, 0x9d, 0x9c, 0xd1, 0x95, 0x4b, 0xf0, 0x5f, 0xc1, 0x38, 0x38, 0x69, 0xf5, 0xb1, 0x94, 0x34, 0xb9, 0x81, 0x06, 0x4d, 0x17, 0xa1, 0x6a, 0x1d, 0x81, 0x58, 0x4c, 0xf0, 0x55, 0xdf, 0x32, 0x62, 0x08, 0xd8, 0x29, 0x1e, 0xf2, 0xa6, 0xa3, 0xdb, 0x52, 0x98, 0xb8, 0x0f, 0xe6, 0x49, 0x74, 0xe1, 0x9f, 0x90, 0x70, 0x66, 0x09, 0x6b, 0xa9, 0xd5, 0x05, 0xd6, 0xb2, 0x36, 0x7a, 0xf2, 0x9b, 0xbb, 0x0e, 0x66, 0xed, 0x22, 0xd5, 0xdf, 0x21, 0xba, 0x1e, 0x51, 0x77, 0x40, 0x1d, 0xf4, 0x21, 0xdd, 0xdf, 0x41, 0xc4, 0x1c, 0xae, 0x3a, 0x03, 0xd6, 0xd6, 0x7a, 0x25, 0x48, 0xcb, 0x36, 0x8b, 0xfa, 0x59, 0xb8, 0xfe, 0x6a, 0x2a, 0x39, 0x1b, 0x30, 0x60, 0xd0, 0x2e, 0x8b, 0x2d, 0xab, 0x0a, 0x99, 0x90, 0x31, 0xb9, 0x0f, 0x62, 0xa1, 0xb0, 0x1a, 0x3f, 0x3e, 0x43, 0xf6, 0xce, 0x94, 0x73, 0xcc, 0x88, 0xda, 0x76, 0x91, 0x66, 0x98, 0xa2, 0xd2, 0x4f, 0xdf, 0x81, 0xcb, 0xa7, 0x13, 0x00, 0x3b, 0x39, 0x2d, 0x2d, 0x35, 0xa5, 0x7c, 0x5d, 0x3c, 0x96, 0x92, 0xb8, 0xd4, 0x43, 0x94, 0xca, 0xf7, 0x71, 0x60, 0x5c, 0x6f, 0x5b, 0x99, 0x33, 0xcb, 0x39, 0xf8, 0xae, 0xf5, 0xbc, 0x92, 0x09, 0x3f, 0x03, 0xd0, 0x5c, 0x4a, 0x16, 0xff, 0x0a, 0xc3, 0x0b, 0x34, 0x78, 0xe7, 0xea, 0xfb, 0x48, 0xbc, 0x38, 0xbb, 0x18, 0x20, 0xf0, 0x8f, 0x10, 0x63, 0x3f, 0x42, 0xf4, 0xbd, 0x03, 0x07, 0xd9, 0x3f, 0x10, 0xca, 0xd5, 0x95, 0x92, 0xf3, 0x83, 0xc1, 0xd0, 0x0e, 0xb3, 0xb2, 0xf0, 0xea, 0xcb, 0x67, 0x79, 0xd7, 0xc1, 0x70, 0x0a, 0x42, 0x74, 0x77, 0x4d, 0xfd, 0x00, 0xab, 0x72, 0x03, 0x7f, 0xc5, 0xc6, 0x73, 0xb0, 0xe6, 0xfb, 0x74, 0x98, 0xce, 0x1a, 0x86, 0xd7, 0xf7, 0xe8, 0x05, 0xf4, 0x65, 0xe6, 0x8b, 0xca, 0xe2, 0x95, 0x10, 0xdd, 0xdd, 0xec, 0xb6, 0x34, 0x2f, 0xae, 0x12, 0x02, 0x6d, 0xda, 0xf4, 0x4d, 0x4a, 0x66, 0xfc, 0xf7, 0x7b, 0xc2, 0x81, 0x44, 0xa9, 0x62, 0xb9, 0x5b, 0xc9, 0x09, 0x9d, 0x87, 0x1e, 0x4d, 0xbe, 0x5f, 0x0a, 0x94, 0x78, 0xa5, 0x24, 0xaf, 0xb7, 0x90, 0x94, 0xac, 0x3f, 0x80, 0xa1, 0xae, 0x79, 0x9e, 0xab, 0xe7, 0x86, 0xae, 0xd5, 0x71, 0x9f, 0x2d, 0x2d, 0x81, 0xfc, 0x26, 0x4c, 0xa8, 0xad, 0xa1, 0x21, 0x1a, 0x7a, 0xc9, 0x86, 0x97, 0x22, 0x83, 0xe8, 0x09, 0x2c, 0xf2, 0xc3, 0x5d, 0xb9, 0x44, 0x39, 0xd7, 0xe9, 0x09, 0x4c, 0x0f, 0xa7, 0x3c, 0xd5, 0xc9, 0x2d, 0xbb, 0x16, 0xe2, 0xa8, 0x7e, 0x81, 0xd4, 0x66, 0x85, 0x4f, 0x68, 0xbe, 0x2e, 0x99, 0x7a, 0xea, 0x54, 0x29, 0x9c, 0x88, 0xcb, 0x83, 0x97, 0x67, 0xd4, 0x65, 0xdd, 0x18, 0x45, 0x35, 0x23, 0xac, 0x2b, 0x7d, 0x3c, 0x6b, 0x69, 0xa5, 0xc4, 0x1f, 0xdd, 0xe7, 0x7a, 0xe5, 0x63 }; +/* investigation: coordinator tampered with enc_secshare of 2; blame = FaultyCoordinatorError */ +/* 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, @@ -706,7 +777,7 @@ static void chilldkg_simplpedpop_test(void) { secp256k1_chilldkg_simplpedpop_participant_step2_prepare_secshare(&secshare, partials, n); 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); + fault = secp256k1_chilldkg_simplpedpop_participant_step2(CTX, &dkg_outputs[i], eq_input_p, &fault_index, NULL, &states[i], cmsg, cmsg_len, &secshare); 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); @@ -735,7 +806,7 @@ static void chilldkg_simplpedpop_test(void) { memcpy(cmsg_bad, cmsg_bad + 33, 33); memcpy(cmsg_bad + 33, swap, 33); } - fault = secp256k1_chilldkg_simplpedpop_participant_step2(CTX, &dkg_outputs[0], eq_input, &fault_index, &states[0], cmsg_bad, cmsg_len, &secshare); + fault = secp256k1_chilldkg_simplpedpop_participant_step2(CTX, &dkg_outputs[0], eq_input, &fault_index, NULL, &states[0], cmsg_bad, cmsg_len, &secshare); CHECK(fault == SECP256K1_CHILLDKG_FAULTY_COORDINATOR); /* A tampered pop blames the corresponding participant (or the @@ -743,7 +814,7 @@ static void chilldkg_simplpedpop_test(void) { memcpy(cmsg_bad, cmsg, cmsg_len); cmsg_bad[33 * 3 + 33 * 1 + 64 * 1] ^= 1; fault_index = 0; - fault = secp256k1_chilldkg_simplpedpop_participant_step2(CTX, &dkg_outputs[0], eq_input, &fault_index, &states[0], cmsg_bad, cmsg_len, &secshare); + fault = secp256k1_chilldkg_simplpedpop_participant_step2(CTX, &dkg_outputs[0], eq_input, &fault_index, NULL, &states[0], cmsg_bad, cmsg_len, &secshare); CHECK(fault == SECP256K1_CHILLDKG_FAULTY_PARTICIPANT_OR_COORDINATOR); CHECK(fault_index == 1); @@ -757,7 +828,7 @@ static void chilldkg_simplpedpop_test(void) { memcpy(cmsg_bad + 33 * 3 + 33 + 2 * 64, swap, 64); } fault_index = 0; - fault = secp256k1_chilldkg_simplpedpop_participant_step2(CTX, &dkg_outputs[0], eq_input, &fault_index, &states[0], cmsg_bad, cmsg_len, &secshare); + fault = secp256k1_chilldkg_simplpedpop_participant_step2(CTX, &dkg_outputs[0], eq_input, &fault_index, NULL, &states[0], cmsg_bad, cmsg_len, &secshare); CHECK(fault == SECP256K1_CHILLDKG_FAULTY_PARTICIPANT_OR_COORDINATOR); CHECK(fault_index == 1); @@ -765,7 +836,7 @@ static void chilldkg_simplpedpop_test(void) { memcpy(cmsg_bad, cmsg, cmsg_len); memset(cmsg_bad + 33 * 2, 0, 33); fault_index = 0; - fault = secp256k1_chilldkg_simplpedpop_participant_step2(CTX, &dkg_outputs[0], eq_input, &fault_index, &states[0], cmsg_bad, cmsg_len, &secshare); + fault = secp256k1_chilldkg_simplpedpop_participant_step2(CTX, &dkg_outputs[0], eq_input, &fault_index, NULL, &states[0], cmsg_bad, cmsg_len, &secshare); CHECK(fault == SECP256K1_CHILLDKG_FAULTY_PARTICIPANT_OR_COORDINATOR); CHECK(fault_index == 2); @@ -776,16 +847,16 @@ static void chilldkg_simplpedpop_test(void) { memcpy(cmsg_bad, cmsg, cmsg_len); cmsg_bad[33 * 3] ^= 1; fault_index = 0; - fault = secp256k1_chilldkg_simplpedpop_participant_step2(CTX, &dkg_outputs[0], eq_input, &fault_index, &states[0], cmsg_bad, cmsg_len, &secshare); + fault = secp256k1_chilldkg_simplpedpop_participant_step2(CTX, &dkg_outputs[0], eq_input, &fault_index, NULL, &states[0], cmsg_bad, cmsg_len, &secshare); CHECK(fault == SECP256K1_CHILLDKG_UNKNOWN_FAULTY_PARTICIPANT_OR_COORDINATOR); CHECK(fault_index == UINT32_MAX); /* A malformed cmsg (wrong length, invalid point) blames the coordinator. */ - fault = secp256k1_chilldkg_simplpedpop_participant_step2(CTX, &dkg_outputs[0], eq_input, &fault_index, &states[0], cmsg, cmsg_len - 1, &secshare); + fault = secp256k1_chilldkg_simplpedpop_participant_step2(CTX, &dkg_outputs[0], eq_input, &fault_index, NULL, &states[0], cmsg, cmsg_len - 1, &secshare); CHECK(fault == SECP256K1_CHILLDKG_FAULTY_COORDINATOR); memcpy(cmsg_bad, cmsg, cmsg_len); cmsg_bad[0] = 0x04; - fault = secp256k1_chilldkg_simplpedpop_participant_step2(CTX, &dkg_outputs[0], eq_input, &fault_index, &states[0], cmsg_bad, cmsg_len, &secshare); + fault = secp256k1_chilldkg_simplpedpop_participant_step2(CTX, &dkg_outputs[0], eq_input, &fault_index, NULL, &states[0], cmsg_bad, cmsg_len, &secshare); CHECK(fault == SECP256K1_CHILLDKG_FAULTY_COORDINATOR); /* The coordinator blames the sender of a malformed participant message. */ @@ -925,7 +996,7 @@ static void chilldkg_encpedpop_test(void) { * byte-exact. */ 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]); + fault = secp256k1_chilldkg_encpedpop_participant_step2(CTX, &dkg_output, eq_input_p, &fault_index, NULL, &states[i], vec_enc_hostseckeys[i], cmsg, cmsg_len, &enc_secshares[i]); 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); @@ -943,7 +1014,7 @@ static void chilldkg_encpedpop_test(void) { /* The coordinator echoes a wrong pubnonce for us. */ memcpy(cmsg_bad, cmsg, cmsg_len); cmsg_bad[simpl_cmsg_len] ^= 1; - fault = secp256k1_chilldkg_encpedpop_participant_step2(CTX, &dkg_output, eq_input, &fault_index, &states[0], vec_enc_hostseckeys[0], cmsg_bad, cmsg_len, &enc_secshares[0]); + fault = secp256k1_chilldkg_encpedpop_participant_step2(CTX, &dkg_output, eq_input, &fault_index, NULL, &states[0], vec_enc_hostseckeys[0], cmsg_bad, cmsg_len, &enc_secshares[0]); CHECK(fault == SECP256K1_CHILLDKG_FAULTY_COORDINATOR); /* An invalid pubnonce of sender 1 (bad encoding, or the infinity @@ -951,13 +1022,13 @@ static void chilldkg_encpedpop_test(void) { memcpy(cmsg_bad, cmsg, cmsg_len); memset(cmsg_bad + simpl_cmsg_len + 33, 0xff, 33); fault_index = 0; - fault = secp256k1_chilldkg_encpedpop_participant_step2(CTX, &dkg_output, eq_input, &fault_index, &states[0], vec_enc_hostseckeys[0], cmsg_bad, cmsg_len, &enc_secshares[0]); + fault = secp256k1_chilldkg_encpedpop_participant_step2(CTX, &dkg_output, eq_input, &fault_index, NULL, &states[0], vec_enc_hostseckeys[0], cmsg_bad, cmsg_len, &enc_secshares[0]); CHECK(fault == SECP256K1_CHILLDKG_FAULTY_PARTICIPANT_OR_COORDINATOR); CHECK(fault_index == 1); memcpy(cmsg_bad, cmsg, cmsg_len); memset(cmsg_bad + simpl_cmsg_len + 33, 0, 33); fault_index = 0; - fault = secp256k1_chilldkg_encpedpop_participant_step2(CTX, &dkg_output, eq_input, &fault_index, &states[0], vec_enc_hostseckeys[0], cmsg_bad, cmsg_len, &enc_secshares[0]); + fault = secp256k1_chilldkg_encpedpop_participant_step2(CTX, &dkg_output, eq_input, &fault_index, NULL, &states[0], vec_enc_hostseckeys[0], cmsg_bad, cmsg_len, &enc_secshares[0]); CHECK(fault == SECP256K1_CHILLDKG_FAULTY_PARTICIPANT_OR_COORDINATOR); CHECK(fault_index == 1); @@ -966,12 +1037,12 @@ static void chilldkg_encpedpop_test(void) { secp256k1_scalar_set_int(&scalar_tmp2, 1); secp256k1_scalar_add(&scalar_tmp, &enc_secshares[0], &scalar_tmp2); fault_index = 0; - fault = secp256k1_chilldkg_encpedpop_participant_step2(CTX, &dkg_output, eq_input, &fault_index, &states[0], vec_enc_hostseckeys[0], cmsg, cmsg_len, &scalar_tmp); + fault = secp256k1_chilldkg_encpedpop_participant_step2(CTX, &dkg_output, eq_input, &fault_index, NULL, &states[0], vec_enc_hostseckeys[0], cmsg, cmsg_len, &scalar_tmp); CHECK(fault == SECP256K1_CHILLDKG_UNKNOWN_FAULTY_PARTICIPANT_OR_COORDINATOR); CHECK(fault_index == UINT32_MAX); /* A malformed cmsg (wrong length) blames the coordinator. */ - fault = secp256k1_chilldkg_encpedpop_participant_step2(CTX, &dkg_output, eq_input, &fault_index, &states[0], vec_enc_hostseckeys[0], cmsg, cmsg_len - 1, &enc_secshares[0]); + fault = secp256k1_chilldkg_encpedpop_participant_step2(CTX, &dkg_output, eq_input, &fault_index, NULL, &states[0], vec_enc_hostseckeys[0], cmsg, cmsg_len - 1, &enc_secshares[0]); CHECK(fault == SECP256K1_CHILLDKG_FAULTY_COORDINATOR); /* The coordinator blames the sender of a malformed participant message: @@ -1127,7 +1198,7 @@ static void chilldkg_participant_api_test(void) { /* 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]); + fault = secp256k1_chilldkg_participant_step2(CTX, &state2[i], sig64[i], &fault_index, NULL, &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); } @@ -1154,7 +1225,7 @@ static void chilldkg_participant_api_test(void) { * 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]); + fault = secp256k1_chilldkg_participant_step2(CTX, &state2[0], tmp_sig64, &fault_index, NULL, &state1[0], vec3_hostseckeys[1], cmsg1, vec3_aux_rands[0]); CHECK(fault == SECP256K1_CHILLDKG_INVALID_INPUT); CHECK(secp256k1_is_zero_array(tmp_sig64, 64)); } @@ -1171,7 +1242,7 @@ static void chilldkg_participant_api_test(void) { 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]); + fault = secp256k1_chilldkg_participant_step2(CTX, &state2[0], tmp_sig64, &fault_index, NULL, &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); @@ -1181,7 +1252,7 @@ static void chilldkg_participant_api_test(void) { 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]); + fault = secp256k1_chilldkg_participant_step2(CTX, &state2[0], tmp_sig64, &fault_index, NULL, &fresh_state1, vec3_hostseckeys[0], cmsg1_bad, vec3_aux_rands[0]); CHECK(fault == SECP256K1_CHILLDKG_FAULTY_PARTICIPANT_OR_COORDINATOR); CHECK(fault_index == 1); @@ -1190,7 +1261,7 @@ static void chilldkg_participant_api_test(void) { 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]); + fault = secp256k1_chilldkg_participant_step2(CTX, &state2[0], tmp_sig64, &fault_index, NULL, &fresh_state1, vec3_hostseckeys[0], cmsg1_bad, vec3_aux_rands[0]); CHECK(fault == SECP256K1_CHILLDKG_FAULTY_COORDINATOR); } @@ -1201,7 +1272,7 @@ static void chilldkg_participant_api_test(void) { 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]); + fault = secp256k1_chilldkg_participant_step2(CTX, &fresh_state2, tmp_sig64, &fault_index, NULL, &fresh_state1, vec3_hostseckeys[0], cmsg1, vec3_aux_rands[0]); CHECK(fault == SECP256K1_CHILLDKG_OK); memcpy(cmsg2_bad, cmsg2, sizeof(cmsg2)); @@ -1228,13 +1299,13 @@ static void chilldkg_participant_api_test(void) { 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_step2(CTX, NULL, sig64[0], &fault_index, 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], NULL, &fault_index, 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], NULL, 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, 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, NULL, &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, NULL, &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, NULL, &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)); @@ -1250,11 +1321,11 @@ static void chilldkg_participant_api_test(void) { 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_step2(CTX, &state2[0], tmp_sig64, &fault_index, NULL, &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)); + CHECK_ILLEGAL_VOID(CTX, CHECK(secp256k1_chilldkg_participant_step2(CTX, &state2[0], tmp_sig64, &fault_index, NULL, &bad_state1, vec3_hostseckeys[0], cmsg1, vec3_aux_rands[0]) == SECP256K1_CHILLDKG_INVALID_INPUT)); } for (i = 0; i < n; i++) { @@ -1301,7 +1372,7 @@ static void chilldkg_coordinator_api_test(void) { CHECK(fault == SECP256K1_CHILLDKG_OK); CHECK(secp256k1_memcmp_var(cmsg1, vec3_cmsg1, cmsg1_len) == 0); for (i = 0; i < n; i++) { - fault = secp256k1_chilldkg_participant_step2(CTX, &p_state2[i], pmsg2[i], &fault_index, &p_state1[i], vec3_hostseckeys[i], cmsg1, vec3_aux_rands[i]); + fault = secp256k1_chilldkg_participant_step2(CTX, &p_state2[i], pmsg2[i], &fault_index, NULL, &p_state1[i], vec3_hostseckeys[i], cmsg1, vec3_aux_rands[i]); CHECK(fault == SECP256K1_CHILLDKG_OK); CHECK(secp256k1_memcmp_var(pmsg2[i], vec3_pmsgs2[i], 64) == 0); } @@ -1410,6 +1481,283 @@ static void chilldkg_coordinator_api_test(void) { } } +static void chilldkg_recovery_test(void) { + const size_t n = 3; + const uint32_t t = 2; + const size_t recovery_len = 4 + 33 * 2 + 162 * 3; /* 556 */ + unsigned char hostpubkeys33[3 * 33]; + unsigned char pubshares33[3 * 33]; + unsigned char hostpubkeys_out[3 * 33]; + unsigned char recovery[556]; + unsigned char recovery_bad[556]; + unsigned char buf33[33]; + unsigned char secshare32[32]; + unsigned char ack_sigs[3][64]; + const unsigned char *ack_sig_ptrs[3]; + size_t n_out; + uint32_t t_out, fault_index; + secp256k1_chilldkg_fault fault; + size_t i, j; + + for (i = 0; i < n; i++) { + memcpy(hostpubkeys33 + 33 * i, vec5_hostpubkeys[i], 33); + ack_sig_ptrs[i] = ack_sigs[i]; + } + + CHECK(secp256k1_chilldkg_investigation_msg_len(n) == 65 * n); + CHECK(secp256k1_chilldkg_investigation_msg_len(0) == 0); + CHECK(secp256k1_chilldkg_investigation_msg_len(SECP256K1_CHILLDKG_MAX_PARTICIPANTS + 1) == 0); + + /* Recovery data taken from the reference vectors directly (the session + * itself is covered by the participant/coordinator API tests). */ + memcpy(recovery, vec5_recovery, recovery_len); + + /* participant_recover: outputs match the session's DKG output. */ + for (i = 0; i < n; i++) { + fault = secp256k1_chilldkg_participant_recover(CTX, secshare32, buf33, pubshares33, hostpubkeys_out, &n_out, &t_out, &fault_index, vec5_hostseckeys[i], recovery, recovery_len); + CHECK(fault == SECP256K1_CHILLDKG_OK); + CHECK(secp256k1_memcmp_var(secshare32, vec5_secshares[i], 32) == 0); + CHECK(secp256k1_memcmp_var(buf33, vec5_thresh_pk, 33) == 0); + for (j = 0; j < n; j++) { + CHECK(secp256k1_memcmp_var(pubshares33 + 33 * j, vec5_pubshares[j], 33) == 0); + CHECK(secp256k1_memcmp_var(hostpubkeys_out + 33 * j, vec5_hostpubkeys[j], 33) == 0); + } + CHECK(n_out == n && t_out == t); + } + + /* coordinator_recover: same outputs, no secret share. */ + fault = secp256k1_chilldkg_coordinator_recover(CTX, buf33, pubshares33, hostpubkeys_out, &n_out, &t_out, recovery, recovery_len); + CHECK(fault == SECP256K1_CHILLDKG_OK); + CHECK(secp256k1_memcmp_var(buf33, vec5_thresh_pk, 33) == 0); + CHECK(n_out == n && t_out == t); + for (j = 0; j < n; j++) { + CHECK(secp256k1_memcmp_var(pubshares33 + 33 * j, vec5_pubshares[j], 33) == 0); + } + + /* Invalid recovery data is an input error (RecoveryDataError in the + * reference) and zeroes the outputs. */ + memcpy(recovery_bad, recovery, recovery_len); + recovery_bad[recovery_len - 1] ^= 1; /* corrupt the last cert signature */ + fault_index = 0; + fault = secp256k1_chilldkg_participant_recover(CTX, secshare32, buf33, pubshares33, hostpubkeys_out, &n_out, &t_out, &fault_index, vec5_hostseckeys[0], recovery_bad, recovery_len); + CHECK(fault == SECP256K1_CHILLDKG_INVALID_INPUT); + CHECK(fault_index == UINT32_MAX); + CHECK(secp256k1_is_zero_array(secshare32, 32)); + CHECK(secp256k1_is_zero_array(buf33, 33)); + CHECK(n_out == 0 && t_out == 0); + /* Truncated and extended recovery data have inconsistent lengths. */ + fault = secp256k1_chilldkg_participant_recover(CTX, secshare32, buf33, pubshares33, hostpubkeys_out, &n_out, &t_out, &fault_index, vec5_hostseckeys[0], recovery, recovery_len - 1); + CHECK(fault == SECP256K1_CHILLDKG_INVALID_INPUT); + fault = secp256k1_chilldkg_participant_recover(CTX, secshare32, buf33, pubshares33, hostpubkeys_out, &n_out, &t_out, &fault_index, vec5_hostseckeys[0], recovery, 3); + CHECK(fault == SECP256K1_CHILLDKG_INVALID_INPUT); + fault = secp256k1_chilldkg_coordinator_recover(CTX, buf33, pubshares33, hostpubkeys_out, &n_out, &t_out, recovery, recovery_len + 1); + CHECK(fault == SECP256K1_CHILLDKG_INVALID_INPUT); + /* A host secret key not in the session is an input error + * (HostSeckeyError in the reference). */ + { + unsigned char other_seckey[32] = { 0 }; + other_seckey[31] = 1; /* valid key, but not in the session */ + fault = secp256k1_chilldkg_participant_recover(CTX, secshare32, buf33, pubshares33, hostpubkeys_out, &n_out, &t_out, &fault_index, other_seckey, recovery, recovery_len); + CHECK(fault == SECP256K1_CHILLDKG_INVALID_INPUT); + } + { + unsigned char zero32[32] = { 0 }; + fault = secp256k1_chilldkg_participant_recover(CTX, secshare32, buf33, pubshares33, hostpubkeys_out, &n_out, &t_out, &fault_index, zero32, recovery, recovery_len); + CHECK(fault == SECP256K1_CHILLDKG_INVALID_INPUT); + } + + /* Recovery acks: byte-exact signatures, and verification succeeds. */ + for (i = 0; i < n; i++) { + CHECK(secp256k1_chilldkg_recovery_ack_sign(CTX, ack_sigs[i], vec5_hostseckeys[i], hostpubkeys33, n, t, recovery, recovery_len, vec5_ack_aux_rands[i]) == 1); + CHECK(secp256k1_memcmp_var(ack_sigs[i], vec5_ack_sigs[i], 64) == 0); + } + fault = secp256k1_chilldkg_recovery_acks_verify(CTX, &fault_index, hostpubkeys33, n, t, recovery, recovery_len, ack_sig_ptrs); + CHECK(fault == SECP256K1_CHILLDKG_OK); + + /* An invalid ack blames its signer (InvalidRecoveryAckError is a + * FaultyParticipantError in the reference). */ + { + unsigned char ack_bad[64]; + const unsigned char *bad_ptrs[3]; + bad_ptrs[0] = ack_sigs[0]; + bad_ptrs[1] = ack_bad; + bad_ptrs[2] = ack_sigs[2]; + memcpy(ack_bad, ack_sigs[1], 64); + ack_bad[7] ^= 1; + fault_index = 0; + fault = secp256k1_chilldkg_recovery_acks_verify(CTX, &fault_index, hostpubkeys33, n, t, recovery, recovery_len, bad_ptrs); + CHECK(fault == SECP256K1_CHILLDKG_FAULTY_PARTICIPANT); + CHECK(fault_index == 1); + /* An ack for the wrong participant index fails verification. */ + bad_ptrs[1] = ack_sigs[2]; + bad_ptrs[2] = ack_sigs[1]; + fault = secp256k1_chilldkg_recovery_acks_verify(CTX, &fault_index, hostpubkeys33, n, t, recovery, recovery_len, bad_ptrs); + CHECK(fault == SECP256K1_CHILLDKG_FAULTY_PARTICIPANT); + CHECK(fault_index == 1); + } + + /* Ack sign/verify with mismatched session parameters or recovery data + * fails (RecoveryDataError in the reference). */ + CHECK(secp256k1_chilldkg_recovery_ack_sign(CTX, ack_sigs[0], vec5_hostseckeys[0], hostpubkeys33, n, 1, recovery, recovery_len, vec5_ack_aux_rands[0]) == 0); + CHECK(secp256k1_is_zero_array(ack_sigs[0], 64)); + memcpy(recovery_bad, recovery, recovery_len); + recovery_bad[4 + 33 * 2] ^= 1; /* corrupt a host public key in eq_input */ + CHECK(secp256k1_chilldkg_recovery_ack_sign(CTX, ack_sigs[0], vec5_hostseckeys[0], hostpubkeys33, n, t, recovery_bad, recovery_len, vec5_ack_aux_rands[0]) == 0); + fault = secp256k1_chilldkg_recovery_acks_verify(CTX, &fault_index, hostpubkeys33, n, t, recovery_bad, recovery_len, ack_sig_ptrs); + CHECK(fault == SECP256K1_CHILLDKG_INVALID_INPUT); + /* A host secret key not in the session cannot sign an ack. */ + { + unsigned char other_seckey[32] = { 0 }; + other_seckey[31] = 1; + CHECK(secp256k1_chilldkg_recovery_ack_sign(CTX, ack_sigs[0], other_seckey, hostpubkeys33, n, t, recovery, recovery_len, vec5_ack_aux_rands[0]) == 0); + } + + /* API misuse. */ + CHECK_ILLEGAL(CTX, secp256k1_chilldkg_recovery_ack_sign(CTX, NULL, vec5_hostseckeys[0], hostpubkeys33, n, t, recovery, recovery_len, vec5_ack_aux_rands[0])); + CHECK_ILLEGAL(CTX, secp256k1_chilldkg_recovery_ack_sign(CTX, ack_sigs[0], NULL, hostpubkeys33, n, t, recovery, recovery_len, vec5_ack_aux_rands[0])); + CHECK_ILLEGAL(CTX, secp256k1_chilldkg_recovery_ack_sign(CTX, ack_sigs[0], vec5_hostseckeys[0], NULL, n, t, recovery, recovery_len, vec5_ack_aux_rands[0])); + CHECK_ILLEGAL(CTX, secp256k1_chilldkg_recovery_ack_sign(CTX, ack_sigs[0], vec5_hostseckeys[0], hostpubkeys33, n, t, NULL, recovery_len, vec5_ack_aux_rands[0])); + CHECK_ILLEGAL(CTX, secp256k1_chilldkg_recovery_ack_sign(CTX, ack_sigs[0], vec5_hostseckeys[0], hostpubkeys33, n, t, recovery, recovery_len, NULL)); + CHECK_ILLEGAL_VOID(CTX, CHECK(secp256k1_chilldkg_participant_recover(CTX, NULL, buf33, pubshares33, hostpubkeys_out, &n_out, &t_out, &fault_index, vec5_hostseckeys[0], recovery, recovery_len) == SECP256K1_CHILLDKG_INVALID_INPUT)); + CHECK_ILLEGAL_VOID(CTX, CHECK(secp256k1_chilldkg_participant_recover(CTX, secshare32, buf33, pubshares33, hostpubkeys_out, &n_out, &t_out, &fault_index, NULL, recovery, recovery_len) == SECP256K1_CHILLDKG_INVALID_INPUT)); + CHECK_ILLEGAL_VOID(CTX, CHECK(secp256k1_chilldkg_participant_recover(CTX, secshare32, buf33, pubshares33, hostpubkeys_out, &n_out, &t_out, &fault_index, vec5_hostseckeys[0], NULL, recovery_len) == SECP256K1_CHILLDKG_INVALID_INPUT)); + CHECK_ILLEGAL_VOID(CTX, CHECK(secp256k1_chilldkg_coordinator_recover(CTX, NULL, pubshares33, hostpubkeys_out, &n_out, &t_out, recovery, recovery_len) == SECP256K1_CHILLDKG_INVALID_INPUT)); + CHECK_ILLEGAL_VOID(CTX, CHECK(secp256k1_chilldkg_recovery_acks_verify(CTX, NULL, hostpubkeys33, n, t, recovery, recovery_len, ack_sig_ptrs) == SECP256K1_CHILLDKG_INVALID_INPUT)); + CHECK_ILLEGAL_VOID(CTX, CHECK(secp256k1_chilldkg_recovery_acks_verify(CTX, &fault_index, NULL, n, t, recovery, recovery_len, ack_sig_ptrs) == SECP256K1_CHILLDKG_INVALID_INPUT)); + CHECK_ILLEGAL_VOID(CTX, CHECK(secp256k1_chilldkg_recovery_acks_verify(CTX, &fault_index, hostpubkeys33, n, t, recovery, recovery_len, NULL) == SECP256K1_CHILLDKG_INVALID_INPUT)); +} + +static void chilldkg_investigate_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 cinv_len = 65 * 3; /* 195 */ + const uint32_t dealer = 1; + const uint32_t victim = 2; + secp256k1_chilldkg_participant_state1 state1; + secp256k1_chilldkg_participant_state2 state2; + secp256k1_chilldkg_participant_inv_data inv_data; + secp256k1_chilldkg_coordinator_state coord_state; + unsigned char pmsg1[3][259]; + const unsigned char *pmsg1_ptrs[3]; + unsigned char cmsg1[519]; + unsigned char cinv[195]; + unsigned char cinv_bad[195]; + unsigned char hostpubkeys33[3 * 33]; + unsigned char sig64[64]; + secp256k1_chilldkg_fault fault; + uint32_t fault_index = 0; + size_t i; + + for (i = 0; i < n; i++) { + pmsg1_ptrs[i] = pmsg1[i]; + memcpy(hostpubkeys33 + 33 * i, vec5_hostpubkeys[i], 33); + } + for (i = 0; i < n; i++) { + CHECK(secp256k1_chilldkg_participant_step1(CTX, &state1, pmsg1[i], vec5_hostseckeys[i], hostpubkeys33, n, t, vec5_randoms[i]) == 1); + CHECK(secp256k1_memcmp_var(pmsg1[i], vec5_pmsgs1[i], pmsg1_len) == 0); + } + + /* Scenario 1: dealer 1 sent a corrupted encrypted share to participant + * 2. */ + memcpy(pmsg1[dealer], vec5_inv_pmsg1_bad, pmsg1_len); + fault = secp256k1_chilldkg_coordinator_step1(CTX, &coord_state, cmsg1, &fault_index, pmsg1_ptrs, hostpubkeys33, n, t); + CHECK(fault == SECP256K1_CHILLDKG_OK); + CHECK(secp256k1_memcmp_var(cmsg1, vec5_inv_cmsg1, cmsg1_len) == 0); + + /* The victim's step2 fails with UNKNOWN and fills the inv data. */ + CHECK(secp256k1_chilldkg_participant_step1(CTX, &state1, pmsg1[victim], vec5_hostseckeys[victim], hostpubkeys33, n, t, vec5_randoms[victim]) == 1); + fault = secp256k1_chilldkg_participant_step2(CTX, &state2, sig64, &fault_index, &inv_data, &state1, vec5_hostseckeys[victim], cmsg1, vec5_aux_rands[victim]); + CHECK(fault == SECP256K1_CHILLDKG_UNKNOWN_FAULTY_PARTICIPANT_OR_COORDINATOR); + CHECK(fault_index == UINT32_MAX); + CHECK(secp256k1_is_zero_array(sig64, 64)); + /* The inv data is byte-exact against the reference's + * ParticipantInvestigationData (layout: magic || n || participant_id || + * secshare || pubshare || enc_secshare || pads). */ + CHECK(secp256k1_read_be32(&inv_data.data[4]) == n); + CHECK(secp256k1_read_be32(&inv_data.data[8]) == victim); + CHECK(secp256k1_memcmp_var(&inv_data.data[12], vec5_inv_secshare, 32) == 0); + CHECK(secp256k1_memcmp_var(&inv_data.data[44], vec5_inv_pubshare, 33) == 0); + CHECK(secp256k1_memcmp_var(&inv_data.data[77], vec5_inv_enc_secshare, 32) == 0); + for (i = 0; i < n; i++) { + CHECK(secp256k1_memcmp_var(&inv_data.data[109 + 32 * i], vec5_inv_pads[i], 32) == 0); + } + + /* The coordinator's investigation message is byte-exact. */ + fault = secp256k1_chilldkg_coordinator_investigate(CTX, cinv, &fault_index, pmsg1_ptrs, hostpubkeys33, n, t, victim); + CHECK(fault == SECP256K1_CHILLDKG_OK); + CHECK(secp256k1_memcmp_var(cinv, vec5_inv_cinv, cinv_len) == 0); + + /* The investigation blames the dealer. */ + fault_index = 0; + fault = secp256k1_chilldkg_participant_investigate(CTX, &fault_index, &inv_data, cinv); + CHECK(fault == SECP256K1_CHILLDKG_FAULTY_PARTICIPANT_OR_COORDINATOR); + CHECK(fault_index == dealer); + + /* A malformed investigation message (invalid point encoding, wrong + * scalar) blames the coordinator. */ + memcpy(cinv_bad, cinv, cinv_len); + cinv_bad[32 * n] = 0x07; /* invalid prefix of the first partial pubshare */ + fault = secp256k1_chilldkg_participant_investigate(CTX, &fault_index, &inv_data, cinv_bad); + CHECK(fault == SECP256K1_CHILLDKG_FAULTY_COORDINATOR); + memcpy(cinv_bad, cinv, cinv_len); + memset(cinv_bad, 0xff, 32); /* overflowing first encrypted partial share */ + fault = secp256k1_chilldkg_participant_investigate(CTX, &fault_index, &inv_data, cinv_bad); + CHECK(fault == SECP256K1_CHILLDKG_FAULTY_COORDINATOR); + + /* Scenario 2: the coordinator tampered with the encrypted secshare of + * participant 2 in cmsg1. */ + for (i = 0; i < n; i++) { + memcpy(pmsg1[i], vec5_pmsgs1[i], pmsg1_len); + } + memcpy(cmsg1, vec5_inv_cmsg1_coord_bad, cmsg1_len); + CHECK(secp256k1_chilldkg_participant_step1(CTX, &state1, pmsg1[victim], vec5_hostseckeys[victim], hostpubkeys33, n, t, vec5_randoms[victim]) == 1); + fault = secp256k1_chilldkg_participant_step2(CTX, &state2, sig64, &fault_index, &inv_data, &state1, vec5_hostseckeys[victim], cmsg1, vec5_aux_rands[victim]); + CHECK(fault == SECP256K1_CHILLDKG_UNKNOWN_FAULTY_PARTICIPANT_OR_COORDINATOR); + fault = secp256k1_chilldkg_coordinator_investigate(CTX, cinv, &fault_index, pmsg1_ptrs, hostpubkeys33, n, t, victim); + CHECK(fault == SECP256K1_CHILLDKG_OK); + fault_index = 0; + fault = secp256k1_chilldkg_participant_investigate(CTX, &fault_index, &inv_data, cinv); + CHECK(fault == SECP256K1_CHILLDKG_FAULTY_COORDINATOR); + CHECK(fault_index == UINT32_MAX); + + /* coordinator_investigate: a malformed pmsg1 blames its sender; an + * out-of-range participant id is an input error. */ + { + unsigned char pmsg1_bad[259]; + const unsigned char *bad_ptrs[3]; + bad_ptrs[0] = pmsg1[0]; + bad_ptrs[1] = pmsg1_bad; + bad_ptrs[2] = pmsg1[2]; + memcpy(pmsg1_bad, pmsg1[1], pmsg1_len); + memset(pmsg1_bad + 33 * 2 + 97, 0xff, 32); /* overflowing share for participant 0 */ + fault_index = 0; + fault = secp256k1_chilldkg_coordinator_investigate(CTX, cinv, &fault_index, bad_ptrs, hostpubkeys33, n, t, victim); + CHECK(fault == SECP256K1_CHILLDKG_FAULTY_PARTICIPANT); + CHECK(fault_index == 1); + fault = secp256k1_chilldkg_coordinator_investigate(CTX, cinv, &fault_index, pmsg1_ptrs, hostpubkeys33, n, t, (uint32_t)n); + CHECK(fault == SECP256K1_CHILLDKG_INVALID_INPUT); + CHECK(secp256k1_is_zero_array(cinv, cinv_len)); + } + + /* A zeroed (or corrupted) inv_data object is rejected. */ + { + secp256k1_chilldkg_participant_inv_data bad_inv_data; + memset(&bad_inv_data, 0, sizeof(bad_inv_data)); + CHECK_ILLEGAL_VOID(CTX, CHECK(secp256k1_chilldkg_participant_investigate(CTX, &fault_index, &bad_inv_data, cinv) == SECP256K1_CHILLDKG_INVALID_INPUT)); + } + + /* API misuse. */ + CHECK_ILLEGAL_VOID(CTX, CHECK(secp256k1_chilldkg_coordinator_investigate(CTX, NULL, &fault_index, pmsg1_ptrs, hostpubkeys33, n, t, victim) == SECP256K1_CHILLDKG_INVALID_INPUT)); + CHECK_ILLEGAL_VOID(CTX, CHECK(secp256k1_chilldkg_coordinator_investigate(CTX, cinv, NULL, pmsg1_ptrs, hostpubkeys33, n, t, victim) == SECP256K1_CHILLDKG_INVALID_INPUT)); + CHECK_ILLEGAL_VOID(CTX, CHECK(secp256k1_chilldkg_coordinator_investigate(CTX, cinv, &fault_index, NULL, hostpubkeys33, n, t, victim) == SECP256K1_CHILLDKG_INVALID_INPUT)); + CHECK_ILLEGAL_VOID(CTX, CHECK(secp256k1_chilldkg_coordinator_investigate(CTX, cinv, &fault_index, pmsg1_ptrs, NULL, n, t, victim) == SECP256K1_CHILLDKG_INVALID_INPUT)); + CHECK_ILLEGAL_VOID(CTX, CHECK(secp256k1_chilldkg_participant_investigate(CTX, NULL, &inv_data, cinv) == SECP256K1_CHILLDKG_INVALID_INPUT)); + CHECK_ILLEGAL_VOID(CTX, CHECK(secp256k1_chilldkg_participant_investigate(CTX, &fault_index, NULL, cinv) == SECP256K1_CHILLDKG_INVALID_INPUT)); + CHECK_ILLEGAL_VOID(CTX, CHECK(secp256k1_chilldkg_participant_investigate(CTX, &fault_index, &inv_data, NULL) == SECP256K1_CHILLDKG_INVALID_INPUT)); +} + static const struct tf_test_entry tests_chilldkg[] = { CASE1(chilldkg_tagged_hashes_test), CASE1(chilldkg_params_hash_test), @@ -1422,6 +1770,8 @@ static const struct tf_test_entry tests_chilldkg[] = { CASE1(chilldkg_encpedpop_test), CASE1(chilldkg_participant_api_test), CASE1(chilldkg_coordinator_api_test), + CASE1(chilldkg_recovery_test), + CASE1(chilldkg_investigate_test), }; #endif