10#include "barretenberg/serialize/msgpack.hpp"
12#include "proof_of_possession.hpp"
15namespace crypto::schnorr {
28template <
typename G1,
typename HashRegNon,
typename HashSig = Blake2sHasher>
class multisig {
33 static_assert(!std::is_same_v<HashRegNon, HashSig>);
36 using Fq =
typename G1::coordinate_field;
37 using Fr =
typename G1::subgroup_field;
38 using affine_element =
typename G1::affine_element;
39 using element =
typename G1::element;
52 typedef uint8_t
const* in_buf;
53 typedef uint8_t
const* vec_in_buf;
54 typedef uint8_t* out_buf;
55 typedef uint8_t** vec_out_buf;
57 affine_element public_key = G1::affine_point_at_infinity;
62 MSGPACK_FIELDS(public_key, proof_of_possession);
67 : public_key(account.public_key)
68 , proof_of_possession(account)
73 : public_key(public_key)
74 , proof_of_possession(proof_of_possession)
79 typedef uint8_t
const* in_buf;
80 typedef uint8_t* out_buf;
89 typedef uint8_t
const* in_buf;
90 typedef uint8_t
const* vec_in_buf;
91 typedef uint8_t* out_buf;
92 typedef uint8_t** vec_out_buf;
103 return ((R < other.R) || ((R == other.R) && S < (other.S)));
106 bool operator==(
const RoundOnePublicOutput& other)
const {
return (R == other.R) && (S == other.S); }
109 using RoundTwoPublicOutput = Fr;
119 static bool valid_round1_nonces(
const std::vector<RoundOnePublicOutput>& round1_public_outputs)
121 for (
size_t i = 0; i < round1_public_outputs.size(); ++i) {
122 auto& [R_user, S_user] = round1_public_outputs[i];
123 if (!R_user.on_curve() || R_user.is_point_at_infinity()) {
124 info(
"Round 1 commitments contains invalid R at index ", i);
127 if (!S_user.on_curve() || S_user.is_point_at_infinity()) {
128 info(
"Round 1 commitments contains invalid S at index ", i);
132 if (
auto duplicated = duplicated_indices(round1_public_outputs); duplicated.size() > 0) {
133 info(
"Round 1 commitments contains duplicate values at indices ", duplicated);
154 static Fr generate_nonce_challenge(
const std::string& message,
156 const std::vector<RoundOnePublicOutput>& round_1_nonces)
159 const std::string domain_separator_nonce(
"h_nonce");
163 std::vector<uint8_t> nonce_challenge_buffer;
166 domain_separator_nonce.begin(), domain_separator_nonce.end(), std::back_inserter(nonce_challenge_buffer));
169 serialize::write(nonce_challenge_buffer, G1::affine_one);
172 serialize::write(nonce_challenge_buffer, aggregate_pubkey);
179 const std::string m_start =
"m_start";
180 std::copy(m_start.begin(), m_start.end(), std::back_inserter(nonce_challenge_buffer));
182 write(nonce_challenge_buffer,
static_cast<uint32_t
>(message.size()));
184 std::copy(message.begin(), message.end(), std::back_inserter(nonce_challenge_buffer));
186 const std::string m_end =
"m_end";
187 std::copy(m_end.begin(), m_end.end(), std::back_inserter(nonce_challenge_buffer));
190 for (
const auto& nonce : round_1_nonces) {
191 serialize::write(nonce_challenge_buffer, nonce.R);
192 serialize::write(nonce_challenge_buffer, nonce.S);
196 auto nonce_challenge_raw = HashRegNon::hash(nonce_challenge_buffer);
198 Fr nonce_challenge = Fr::serialize_from_buffer(&nonce_challenge_raw[0]);
199 return nonce_challenge;
210 static affine_element construct_multisig_nonce(
const Fr& a,
const std::vector<RoundOnePublicOutput>& round_1_nonces)
212 element R_sum = round_1_nonces[0].R;
213 element S_sum = round_1_nonces[0].S;
214 for (
size_t i = 1; i < round_1_nonces.size(); ++i) {
215 const auto& [R, S] = round_1_nonces[i];
232 template <
typename T>
static std::vector<size_t> duplicated_indices(
const std::vector<T>& input)
234 const size_t num_inputs = input.size();
236 std::vector<size_t> indices(num_inputs);
237 std::iota(indices.begin(), indices.end(), 0);
241 std::sort(indices.begin(), indices.end(), [&](
size_t a,
size_t b) { return input[a] < input[b]; });
244 std::vector<size_t> duplicates;
245 for (
size_t i = 1; i < num_inputs; ++i) {
246 const size_t idx1 = indices[i - 1];
247 const size_t idx2 = indices[i];
248 if (input[idx1] == input[idx2]) {
249 duplicates.push_back(idx1);
250 duplicates.push_back(idx2);
266 const std::vector<MultiSigPublicKey>& signer_pubkeys)
268 std::vector<affine_element> points;
269 for (
const auto& [public_key, proof_of_possession] : signer_pubkeys) {
270 points.push_back(public_key);
273 if (
auto duplicated = duplicated_indices(points); duplicated.size() > 0) {
274 info(
"Duplicated public keys at indices ", duplicated);
278 element aggregate_pubkey_jac = G1::point_at_infinity;
279 for (
size_t i = 0; i < signer_pubkeys.size(); ++i) {
280 const auto& [public_key, proof_of_possession] = signer_pubkeys[i];
281 if (!public_key.on_curve() || public_key.is_point_at_infinity()) {
282 info(
"Multisig signer pubkey not a valid point at index ", i);
285 if (!proof_of_possession.verify(public_key)) {
286 info(
"Multisig proof of posession invalid at index ", i);
289 aggregate_pubkey_jac += public_key;
295 affine_element aggregate_pubkey(aggregate_pubkey_jac);
296 if (aggregate_pubkey.is_point_at_infinity()) {
297 info(
"Multisig aggregate public key is invalid");
300 return aggregate_pubkey;
315 Fr r_user = Fr::random_element();
317 affine_element R_user = G1::one * r_user;
321 Fr s_user = Fr::random_element();
323 affine_element S_user = G1::one * s_user;
327 return { pubOut, privOut };
343 const std::string& message,
346 const std::vector<MultiSigPublicKey>& signer_pubkeys,
347 const std::vector<RoundOnePublicOutput>& round_1_nonces)
349 const size_t num_signers = signer_pubkeys.size();
350 if (round_1_nonces.size() != num_signers) {
351 info(
"Multisig mismatch round_1_nonces and signers");
356 if (!valid_round1_nonces(round_1_nonces)) {
362 if (!aggregate_pubkey.has_value()) {
368 Fr a = generate_nonce_challenge(message, *aggregate_pubkey, round_1_nonces);
371 affine_element R = construct_multisig_nonce(a, round_1_nonces);
374 auto e_buf = generate_schnorr_challenge<HashSig, G1>(message, *aggregate_pubkey, R);
375 Fr e = Fr::serialize_from_buffer(&e_buf[0]);
378 Fr z = signer_round_1_private_output.r + signer_round_1_private_output.s * a - signer.private_key * e;
395 const std::string& message,
396 const std::vector<MultiSigPublicKey>& signer_pubkeys,
397 const std::vector<RoundOnePublicOutput>& round_1_nonces,
398 const std::vector<RoundTwoPublicOutput>& round_2_signature_shares)
400 const size_t num_signers = signer_pubkeys.size();
401 if (round_1_nonces.size() != num_signers) {
402 info(
"Invalid number of round1 messages");
405 if (round_2_signature_shares.size() != num_signers) {
406 info(
"Invalid number of round2 messages");
409 if (!valid_round1_nonces(round_1_nonces)) {
415 if (!aggregate_pubkey.has_value()) {
421 Fr a = generate_nonce_challenge(message, *aggregate_pubkey, round_1_nonces);
424 affine_element R = construct_multisig_nonce(a, round_1_nonces);
426 auto e_buf = generate_schnorr_challenge<HashSig, G1>(message, *aggregate_pubkey, R);
430 std::copy(e_buf.begin(), e_buf.end(), sig.e.begin());
433 for (
auto& z : round_2_signature_shares) {
437 Fr::serialize_to_buffer(s, &sig.s[0]);
440 if (!verify_signature<HashSig, Fq, Fr, G1>(message, *aggregate_pubkey, sig)) {
Definition: affine_element.hpp:11
Implements the SpeedyMuSig protocol; a secure 2-round interactive multisignature scheme whose signatu...
Definition: multisig.hpp:28
static std::optional< RoundTwoPublicOutput > construct_signature_round_2(const std::string &message, const key_pair &signer, const RoundOnePrivateOutput &signer_round_1_private_output, const std::vector< MultiSigPublicKey > &signer_pubkeys, const std::vector< RoundOnePublicOutput > &round_1_nonces)
Second round of SpeedyMuSig. Given the signer pubkeys and the output of round 1, round 2 has each sig...
Definition: multisig.hpp:342
static std::optional< affine_element > validate_and_combine_signer_pubkeys(const std::vector< MultiSigPublicKey > &signer_pubkeys)
Computes the sum of all signer pubkeys. Output is the public key of the public-facing schnorr multisi...
Definition: multisig.hpp:265
static std::pair< RoundOnePublicOutput, RoundOnePrivateOutput > construct_signature_round_1()
First round of SpeedyMuSig. Signers generate random nonce keypairs R = {r, [R]}, S = {s,...
Definition: multisig.hpp:311
static std::optional< signature > combine_signatures(const std::string &message, const std::vector< MultiSigPublicKey > &signer_pubkeys, const std::vector< RoundOnePublicOutput > &round_1_nonces, const std::vector< RoundTwoPublicOutput > &round_2_signature_shares)
the final step in the SpeedyMuSig multisig scheme. Can be computed by an untrusted 3rd party....
Definition: multisig.hpp:394
A proof of possession is a Schnorr proof of knowledge of a secret key corresponding to a given public...
Definition: proof_of_possession.hpp:18
Definition: schnorr.hpp:17
MultiSigPublicKey wraps a signer's public key g1::affine_element along with a proof of posession: a s...
Definition: multisig.hpp:51
Definition: multisig.hpp:78
Definition: multisig.hpp:88
Definition: schnorr.hpp:25