diff --git a/barretenberg/cpp/src/barretenberg/crypto/schnorr/multisig.hpp b/barretenberg/cpp/src/barretenberg/crypto/schnorr/multisig.hpp deleted file mode 100644 index d3623c7934d7..000000000000 --- a/barretenberg/cpp/src/barretenberg/crypto/schnorr/multisig.hpp +++ /dev/null @@ -1,447 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - -#include "barretenberg/serialize/msgpack.hpp" - -#include "proof_of_possession.hpp" -#include "schnorr.hpp" - -namespace bb::crypto { - -/** - * @brief Implements the SpeedyMuSig protocol; a secure 2-round interactive multisignature scheme - * whose signature outputs can be verified by a regular Schnorr verification algorithm. - * - * @tparam G1 The elliptic curve group being used to generate the multisignature - * @tparam HashRegNon Hash function used to model H_reg and H_non. It must be different from H_sig for proper domain - * separation. - * @tparam HashSig Hash function used generate the Fiat-Shamir challenge for the signature (H_sig). - * - * @details SpeedyMuSig paper at https://eprint.iacr.org/2021/1375.pdf - */ -template class schnorr_multisig { - - // ensure that a different hash function is used for signature and proof of possession/nonce. - // we can apply domain separation for HashRegNon but not for HashSig, so this ensures all hash functions - // are modeled as different random oracles. - static_assert(!std::is_same_v); - - public: - using Fq = typename G1::Fq; - using Fr = typename G1::Fr; - using affine_element = typename G1::affine_element; - using element = typename G1::element; - using key_pair = crypto::schnorr_key_pair; - - /** - * @brief MultiSigPublicKey wraps a signer's public key g1::affine_element - * along with a proof of posession: a signature whose message is the public key, - * signed by the corresponding private key. - * - * This is to prevent attacks where an attacker presents a key that they do not know the secret to - * (e.g. the attacker public key is a linear combination of other public keys) - * - */ - struct MultiSigPublicKey { - typedef uint8_t const* in_buf; - typedef uint8_t const* vec_in_buf; - typedef uint8_t* out_buf; - typedef uint8_t** vec_out_buf; - - affine_element public_key = G1::affine_point_at_infinity; - // proof of knowledge of the secret_key for public_key - SchnorrProofOfPossession proof_of_possession; - - // For serialization, update with any new fields - SERIALIZATION_FIELDS(public_key, proof_of_possession); - // restore default constructor to enable deserialization - MultiSigPublicKey() = default; - // create a MultiSigPublicKey with a proof of possession associated with public_key of account - MultiSigPublicKey(const key_pair& account) - : public_key(account.public_key) - , proof_of_possession(account) - {} - // Needed to appease SERIALIZATION_FIELDS - MultiSigPublicKey(const affine_element& public_key, - const SchnorrProofOfPossession& proof_of_possession) - : public_key(public_key) - , proof_of_possession(proof_of_possession) - {} - }; - - struct RoundOnePrivateOutput { - using in_buf = const uint8_t*; - using out_buf = uint8_t*; - - Fr r; - Fr s; - // For serialization, update with any new fields - SERIALIZATION_FIELDS(r, s); - }; - - struct RoundOnePublicOutput { - using in_buf = const uint8_t*; - using vec_in_buf = const uint8_t*; - using out_buf = uint8_t*; - using vec_out_buf = uint8_t**; - - // R = r⋅G - affine_element R; - // S = s⋅G - affine_element S; - // For serialization, update with any new fields - SERIALIZATION_FIELDS(R, S); - // for std::sort - bool operator<(const RoundOnePublicOutput& other) const - { - return ((R < other.R) || ((R == other.R) && S < (other.S))); - } - - bool operator==(const RoundOnePublicOutput& other) const { return (R == other.R) && (S == other.S); } - }; - // corresponds to z = r + as - ex, - using RoundTwoPublicOutput = Fr; - - private: - /** - * @brief given a list of commitments to nonces produced in round 1, we check that all points are valid and that the - * list does not contain duplicates - * - * @param round1_public_outputs a list of pairs of points {(R1,S1), ...., (Rn,Sn)} - * @return bool whether or not the list is valid. - */ - static bool valid_round1_nonces(const std::vector& round1_public_outputs) - { - for (size_t i = 0; i < round1_public_outputs.size(); ++i) { - auto& [R_user, S_user] = round1_public_outputs[i]; - if (!R_user.on_curve() || R_user.is_point_at_infinity()) { - info("Round 1 commitments contains invalid R at index ", i); - return false; - } - if (!S_user.on_curve() || S_user.is_point_at_infinity()) { - info("Round 1 commitments contains invalid S at index ", i); - return false; - } - } - if (auto duplicated = duplicated_indices(round1_public_outputs); duplicated.size() > 0) { - info("Round 1 commitments contains duplicate values at indices ", duplicated); - return false; - } - return true; - } - - /** - * @brief Generates the Fiat-Shamir challenge `a` that is used to create a Schnorr signature nonce group element - * [R], where [R] is a uniformly randomly distributed combination of the signer nonces - * - * N.B. `a` is message and signer dependent and cannot be pre-generated prior to knowing the message being - * signed over - * - * @warning the resulting 'a' suffers from a slight bias as we apply %r on the 256 bit hash output. - * - * @param message - * @param aggregate_pubkey the output of `combine_signer_pubkeys` - * @param round_1_nonces the public outputs of round 1 from all signers - * @return Fr the nonce challenge `a = int(H_non(G, X_agg, "m_start", m.size(), m, "m_end" {(R1, S1), ..., (Rn, - * Sn)})) % r ` where r is the field order - */ - static Fr generate_nonce_challenge(const std::string& message, - const affine_element& aggregate_pubkey, - const std::vector& round_1_nonces) - { - // Domain separation for H_non - const std::string domain_separator_nonce("h_nonce"); - - // compute nonce challenge - // H('domain_separator_nonce', G, X, "m_start", m.size(), m, "m_end", {(R1, S1), ..., (Rn, Sn)}) - std::vector nonce_challenge_buffer; - // write domain separator - std::copy( - domain_separator_nonce.begin(), domain_separator_nonce.end(), std::back_inserter(nonce_challenge_buffer)); - - // write the group generator - write(nonce_challenge_buffer, G1::affine_one); - - // write X - write(nonce_challenge_buffer, aggregate_pubkey); - - // we slightly deviate from the protocol when including 'm', since the length of 'm' is variable - // by writing a prefix and a suffix, we prevent the message from being interpreted as coming from a different - // session. - - // write "m_start" - const std::string m_start = "m_start"; - std::copy(m_start.begin(), m_start.end(), std::back_inserter(nonce_challenge_buffer)); - // write m.size() - write(nonce_challenge_buffer, static_cast(message.size())); - // write message - std::copy(message.begin(), message.end(), std::back_inserter(nonce_challenge_buffer)); - // write "m_end" - const std::string m_end = "m_end"; - std::copy(m_end.begin(), m_end.end(), std::back_inserter(nonce_challenge_buffer)); - - // write {(R1, S1), ..., (Rn, Sn)} - for (const auto& nonce : round_1_nonces) { - write(nonce_challenge_buffer, nonce.R); - write(nonce_challenge_buffer, nonce.S); - } - - // uses the different hash function for proper domain separation - auto nonce_challenge_raw = HashRegNon::hash(nonce_challenge_buffer); - // this results in a slight bias - Fr nonce_challenge = Fr::serialize_from_buffer(&nonce_challenge_raw[0]); - return nonce_challenge; - } - - /** - * @brief Compute the Schnorr signature scheme's nonce group element [R], given each signer's public nonces - * [R_user], [S_user] and the nonce challenge `a` - * - * @param a the nonce challenge - * @param round_1_nonces the public outputs of round 1 from all signers - * @return affine_element Schnorr nonce [R] - */ - static affine_element construct_multisig_nonce(const Fr& a, const std::vector& round_1_nonces) - { - element R_sum = round_1_nonces[0].R; - element S_sum = round_1_nonces[0].S; - for (size_t i = 1; i < round_1_nonces.size(); ++i) { - const auto& [R, S] = round_1_nonces[i]; - R_sum += R; - S_sum += S; - } - affine_element R(R_sum + S_sum * a); - return R; - } - - /** - * @brief Returns a vector of indices of elements in input that are included more than once. - * - * @warning The returned list may include an index more than once. - * - * @tparam T implements operator< - * @param input list of elements possibly containing duplicates - * @return std::vector a list of indices of input which are included more than once - */ - template static std::vector duplicated_indices(const std::vector& input) - { - const size_t num_inputs = input.size(); - // indices = [0,1,..., num_inputs-1] - std::vector indices(num_inputs); - std::iota(indices.begin(), indices.end(), 0); - - // sort indices according to input. - // input[indices[i-1]] <= input[indices[i]] - std::sort(indices.begin(), indices.end(), [&](size_t a, size_t b) { return input[a] < input[b]; }); - - // This loop will include multiple copies of the same index if an element appears more than twice. - std::vector duplicates; - for (size_t i = 1; i < num_inputs; ++i) { - const size_t idx1 = indices[i - 1]; - const size_t idx2 = indices[i]; - if (input[idx1] == input[idx2]) { - duplicates.push_back(idx1); - duplicates.push_back(idx2); - } - } - return duplicates; - } - - public: - /** - * @brief Computes the sum of all signer pubkeys. Output is the public key of the public-facing schnorr multisig - * "signer" - * - * @param signer_pubkeys - * - * @return std::optional the Schnorr aggregate "signer" public key, if all keys are valid. - */ - static std::optional validate_and_combine_signer_pubkeys( - const std::vector& signer_pubkeys) - { - std::vector points; - for (const auto& [public_key, proof_of_possession] : signer_pubkeys) { - points.push_back(public_key); - } - - if (auto duplicated = duplicated_indices(points); duplicated.size() > 0) { - info("Duplicated public keys at indices ", duplicated); - return std::nullopt; - } - - element aggregate_pubkey_jac = G1::point_at_infinity; - for (size_t i = 0; i < signer_pubkeys.size(); ++i) { - const auto& [public_key, proof_of_possession] = signer_pubkeys[i]; - if (!public_key.on_curve() || public_key.is_point_at_infinity()) { - info("Multisig signer pubkey not a valid point at index ", i); - return std::nullopt; - } - if (!proof_of_possession.verify(public_key)) { - info("Multisig proof of posession invalid at index ", i); - return std::nullopt; - } - aggregate_pubkey_jac += public_key; - } - - // This would prevent accidentally creating an aggregate key for the point at inifinity, - // with the trivial secret key. - // While it shouldn't happen, it is a cheap check. - affine_element aggregate_pubkey(aggregate_pubkey_jac); - if (aggregate_pubkey.is_point_at_infinity()) { - info("Multisig aggregate public key is invalid"); - return std::nullopt; - } - return aggregate_pubkey; - } - - /** - * @brief First round of SpeedyMuSig. Signers generate random nonce keypairs R = {r, [R]}, S = {s, [S]} - * - * @param message - * @return RoundOnePublicOutput group elements [R_user], [S_user] - * @return RoundOnePrivateOutput field elements [r_user], [s_user] - * - */ - static std::pair construct_signature_round_1() - { - // r_user ← 𝔽 - Fr r_user = Fr::random_element(); - // R_user ← r_user⋅G - affine_element R_user = G1::one * r_user; - - // s_user ← 𝔽 - Fr s_user = Fr::random_element(); - // S_user ← s_user⋅G - affine_element S_user = G1::one * s_user; - - RoundOnePublicOutput pubOut{ R_user, S_user }; - RoundOnePrivateOutput privOut{ r_user, s_user }; - secure_erase_bytes(&r_user, sizeof(r_user)); - secure_erase_bytes(&s_user, sizeof(s_user)); - return { pubOut, privOut }; - } - - /** - * @brief Second round of SpeedyMuSig. Given the signer pubkeys and the output of round 1, - * round 2 has each signer compute a share of the Schnorr signature scheme's `s` parameter - * - * @param message - * @param signer - * @param signer_round_1_private_output the signer's secreet nonce values r, s - * @param signer_pubkeys - * @param round_1_nonces the output fro round 1 - * @return std::optional signer's share of `s`, if round 2 succeeds - * - */ - static std::optional construct_signature_round_2( - const std::string& message, - const key_pair& signer, - const RoundOnePrivateOutput& signer_round_1_private_output, - const std::vector& signer_pubkeys, - const std::vector& round_1_nonces) - { - const size_t num_signers = signer_pubkeys.size(); - if (round_1_nonces.size() != num_signers) { - info("Multisig mismatch round_1_nonces and signers"); - return std::nullopt; - } - - // check that round_1_nonces does not contain duplicates and that all points are valid - if (!valid_round1_nonces(round_1_nonces)) { - return std::nullopt; - } - - // compute aggregate key X = X_1 + ... + X_n - auto aggregate_pubkey = validate_and_combine_signer_pubkeys(signer_pubkeys); - if (!aggregate_pubkey.has_value()) { - // previous call has failed - return std::nullopt; - } - - // compute nonce challenge H_non(G, X, "m_start", m, "m_end", {(R1, S1), ..., (Rn, Sn)}) - Fr a = generate_nonce_challenge(message, *aggregate_pubkey, round_1_nonces); - - // compute aggregate nonce R = R1 + ... + Rn + S1 * a + ... + Sn * a - affine_element R = construct_multisig_nonce(a, round_1_nonces); - - // Now we have the multisig nonce, compute schnorr challenge e (termed `c` in the speedyMuSig paper) - auto e_buf = schnorr_generate_challenge(message, *aggregate_pubkey, R); - Fr e = Fr::serialize_from_buffer(&e_buf[0]); - - // output of round 2 is z - Fr z = signer_round_1_private_output.r + signer_round_1_private_output.s * a - signer.private_key * e; - return z; - } - - /** - * @brief the final step in the SpeedyMuSig multisig scheme. Can be computed by an untrusted 3rd party. - * Combines the message, signer pubkeys and round1 outputs to compute the Schnorr signature parameter `e`. - * Combines the outputs of round 2 to compose the total Schnorr signature parameter `s` - * - * @param message - * @param signer_pubkeys - * @param round_1_nonces The outputs of round 1 - * @param round_2_signature_shares The outputs of round 2 - * @return signature it's a Schnorr signature! Looks identical to a regular non-multisig Schnorr signature. - * @return std::nullopt if any of the signature shares are invalid - */ - static std::optional combine_signatures( - const std::string& message, - const std::vector& signer_pubkeys, - const std::vector& round_1_nonces, - const std::vector& round_2_signature_shares) - { - const size_t num_signers = signer_pubkeys.size(); - if (round_1_nonces.size() != num_signers) { - info("Invalid number of round1 messages"); - return std::nullopt; - } - if (round_2_signature_shares.size() != num_signers) { - info("Invalid number of round2 messages"); - return std::nullopt; - } - if (!valid_round1_nonces(round_1_nonces)) { - return std::nullopt; - } - - // compute aggregate key X = X_1 + ... + X_n - auto aggregate_pubkey = validate_and_combine_signer_pubkeys(signer_pubkeys); - if (!aggregate_pubkey.has_value()) { - // previous call has failed - return std::nullopt; - } - - // compute nonce challenge H(X, m, {(R1, S1), ..., (Rn, Sn)}) - Fr a = generate_nonce_challenge(message, *aggregate_pubkey, round_1_nonces); - - // compute aggregate nonce R = R1 + ... + Rn + S1 * a + ... + Sn * a - affine_element R = construct_multisig_nonce(a, round_1_nonces); - - auto e_buf = schnorr_generate_challenge(message, *aggregate_pubkey, R); - - schnorr_signature sig; - // copy e as its raw bit representation (without modular reduction) - std::copy(e_buf.begin(), e_buf.end(), sig.e.begin()); - - Fr s = 0; - for (auto& z : round_2_signature_shares) { - s += z; - } - // write s, which will always produce an integer < r - Fr::serialize_to_buffer(s, &sig.s[0]); - - // verify the final signature before returning - if (!schnorr_verify_signature(message, *aggregate_pubkey, sig)) { - return std::nullopt; - } - - return sig; - } -}; -} // namespace bb::crypto diff --git a/barretenberg/cpp/src/barretenberg/crypto/schnorr/multisig.test.cpp b/barretenberg/cpp/src/barretenberg/crypto/schnorr/multisig.test.cpp deleted file mode 100644 index 1ebaa1e0ffe4..000000000000 --- a/barretenberg/cpp/src/barretenberg/crypto/schnorr/multisig.test.cpp +++ /dev/null @@ -1,137 +0,0 @@ -#include "barretenberg/ecc/curves/grumpkin/grumpkin.hpp" -#include - -#include "./multisig.hpp" - -using namespace bb; -using namespace bb::crypto; - -namespace { -template struct MultisigTest : public ::testing::Test { - using G = grumpkin::g1; - using Fr = grumpkin::fr; - using KeyPair = crypto::schnorr_key_pair; - using multisig = crypto::schnorr_multisig; - using multisig_public_key = typename multisig::MultiSigPublicKey; - - static KeyPair generate_account() - { - KeyPair account; - account.private_key = Fr::random_element(); - account.public_key = G::one * account.private_key; - return account; - } - - static std::vector create_signer_pubkeys(const std::vector& accounts) - { - // setup multisig signers - std::vector signer_pubkeys; - for (size_t i = 0; i < accounts.size(); ++i) { - auto& signer = accounts[i]; - signer_pubkeys.push_back(multisig_public_key(signer)); - } - return signer_pubkeys; - } - - static std::optional create_multisig(const std::string& message, - const std::vector& accounts, - const bool tamper_proof_of_possession = false) - { - std::vector round1_pub; - std::vector round1_priv; - std::vector round2; - std::vector signer_pubkeys = create_signer_pubkeys(accounts); - - if (tamper_proof_of_possession) { - signer_pubkeys[0].proof_of_possession.response += 1; - } - const size_t num_signers = accounts.size(); - - for (size_t i = 0; i < num_signers; ++i) { - auto [_round1_pub, _round1_priv] = multisig::construct_signature_round_1(); - round1_pub.push_back(_round1_pub); - round1_priv.push_back(_round1_priv); - } - - for (size_t i = 0; i < num_signers; ++i) { - auto& signer = accounts[i]; - if (auto round2_output = multisig::construct_signature_round_2( - message, signer, round1_priv[i], signer_pubkeys, round1_pub)) { - round2.push_back(*round2_output); - } - } - return multisig::combine_signatures(message, signer_pubkeys, round1_pub, round2); - } -}; - -using HashTypes = ::testing::Types; -} // namespace - -TYPED_TEST_SUITE(MultisigTest, HashTypes); - -TYPED_TEST(MultisigTest, verify_multi_signature_blake2s) -{ - using G = grumpkin::g1; - using Fr = grumpkin::fr; - using Fq = grumpkin::fq; - using KeyPair = crypto::schnorr_key_pair; - using multisig = crypto::schnorr_multisig; - - std::string message = "The quick brown dog jumped over the lazy fox."; - - const size_t num_signers = 5; - - std::vector accounts(num_signers); - for (auto& acct : accounts) { - acct = this->generate_account(); - } - - auto signature = this->create_multisig(message, accounts); - ASSERT_TRUE(signature.has_value()); - - auto pub_key = multisig::validate_and_combine_signer_pubkeys(this->create_signer_pubkeys(accounts)); - ASSERT_TRUE(pub_key.has_value()); - - bool result = crypto::schnorr_verify_signature(message, *pub_key, *signature); - - EXPECT_EQ(result, true); -} - -TYPED_TEST(MultisigTest, multi_signature_fails_if_proof_of_possession_invalid) -{ - using G = grumpkin::g1; - using Fr = grumpkin::fr; - using KeyPair = crypto::schnorr_key_pair; - - std::string message = "The quick brown dog jumped over the lazy fox."; - - const size_t num_signers = 5; - - std::vector accounts(num_signers); - for (auto& acct : accounts) { - acct = this->generate_account(); - } - - auto signature = this->create_multisig(message, accounts, true); - ASSERT_FALSE(signature.has_value()); -} - -TYPED_TEST(MultisigTest, multi_signature_fails_if_duplicates) -{ - using G = grumpkin::g1; - using Fr = grumpkin::fr; - using KeyPair = crypto::schnorr_key_pair; - - std::string message = "The quick brown dog jumped over the lazy fox."; - - const size_t num_signers = 5; - - std::vector accounts(num_signers); - for (auto& acct : accounts) { - acct = this->generate_account(); - } - - accounts[2] = accounts[4]; // :o - auto signature = this->create_multisig(message, accounts); - ASSERT_FALSE(signature.has_value()); -} diff --git a/barretenberg/cpp/src/barretenberg/crypto/schnorr/proof_of_possession.hpp b/barretenberg/cpp/src/barretenberg/crypto/schnorr/proof_of_possession.hpp deleted file mode 100644 index c6ad7b3592e1..000000000000 --- a/barretenberg/cpp/src/barretenberg/crypto/schnorr/proof_of_possession.hpp +++ /dev/null @@ -1,138 +0,0 @@ -#pragma once - -#include - -#include "barretenberg/common/serialize.hpp" -#include "schnorr.hpp" - -namespace bb::crypto { - -/** - * @brief A proof of possession is a Schnorr proof of knowledge of a secret key corresponding to a given public key. - * - * @details This implementation follows the specification detailed in https://eprint.iacr.org/2021/1375.pdf - * - * @tparam G1 group over which the key pair was generated - * @tparam Hash function used to derive the Fiat-Shamir challenge - */ -template struct SchnorrProofOfPossession { - using Fq = typename G1::Fq; - using Fr = typename G1::Fr; - using affine_element = typename G1::affine_element; - using element = typename G1::element; - using key_pair = crypto::schnorr_key_pair; - - // challenge = e = H_reg(pk,pk,R) - std::array challenge; - // response = z = k - e * sk - Fr response = Fr::zero(); - - // restore default constructor to enable deserialization - SchnorrProofOfPossession() = default; - - /** - * @brief Create a new proof of possession for a given account. - * - * @warning Proofs are not deterministic. - * - * @param account a key_pair (secret_key, public_key) - */ - SchnorrProofOfPossession(const key_pair& account) - { - auto secret_key = account.private_key; - auto public_key = account.public_key; - - // Fr::random_element() will call std::random_device, which in turn relies on system calls to generate a string - // of random bits. It is important to ensure that the execution environment will correctly supply system calls - // that give std::random_device access to an entropy source that produces a string of non-deterministic - // uniformly random bits. For example, when compiling into a wasm binary, it is essential that the random_get - // method is overloaded to utilise a suitable entropy source - // (see https://github.com/WebAssembly/WASI/blob/main/phases/snapshot/docs.md) - Fr k = Fr::random_element(); - - affine_element R = G1::one * k; - - auto challenge_bytes = generate_challenge(public_key, R); - std::copy(challenge_bytes.begin(), challenge_bytes.end(), challenge.begin()); - - Fr challenge_fr = Fr::serialize_from_buffer(&challenge_bytes[0]); - response = k - challenge_fr * secret_key; - secure_erase_bytes(&k, sizeof(k)); - secure_erase_bytes(&secret_key, sizeof(secret_key)); - } - - /** - * @brief verifies that an unserialized signature is valid - * - * @param public_key the public key for which this proof is intended - * @return whether the proof is correct - */ - bool verify(const affine_element& public_key) const - { - Fr challenge_fr = Fr::serialize_from_buffer(&challenge[0]); - // this ensures that a default constructed proof is invalid - if (response.is_zero()) - return false; - - if (!public_key.on_curve() || public_key.is_point_at_infinity()) - return false; - - // R = e•pk + z•G - affine_element R = element(public_key) * challenge_fr + G1::one * response; - if (R.is_point_at_infinity()) - return false; - - // recompute the challenge e - auto challenge_computed = generate_challenge(public_key, R); - return std::equal(challenge.begin(), challenge.end(), challenge_computed.begin(), challenge_computed.end()); - } - - private: - /** - * @brief Generate the Fiat-Shamir challenge e = H_reg(G,X,X,R) - * - * @param public_key X = secret_key•G - * @param R the commitment R = k•G - * @return e = H_reg(X,X,R) - */ - static auto generate_challenge(const affine_element& public_key, const affine_element& R) - { - // Domain separation challenges - const std::string domain_separator_pop("h_reg"); - - // buffer containing (domain_sep, G, X, X, R) - std::vector challenge_buf; - - // write domain separator - std::copy(domain_separator_pop.begin(), domain_separator_pop.end(), std::back_inserter(challenge_buf)); - - // write the group generator - write(challenge_buf, G1::affine_one); - - // write X twice as per the spec - write(challenge_buf, public_key); - write(challenge_buf, public_key); - - // write R - write(challenge_buf, R); - - // generate the raw bits of H_reg(X,X,R) - return Hash::hash(challenge_buf); - } -}; - -template -inline void read(B& it, SchnorrProofOfPossession& proof_of_possession) -{ - read(it, proof_of_possession.challenge); - read(it, proof_of_possession.response); -} - -template -inline void write(B& buf, SchnorrProofOfPossession const& proof_of_possession) -{ - write(buf, proof_of_possession.challenge); - write(buf, proof_of_possession.response); -} - -} // namespace bb::crypto diff --git a/barretenberg/cpp/src/barretenberg/crypto/schnorr/proof_of_possession.test.cpp b/barretenberg/cpp/src/barretenberg/crypto/schnorr/proof_of_possession.test.cpp deleted file mode 100644 index b08f122205a1..000000000000 --- a/barretenberg/cpp/src/barretenberg/crypto/schnorr/proof_of_possession.test.cpp +++ /dev/null @@ -1,103 +0,0 @@ - -#include "proof_of_possession.hpp" -#include "barretenberg/ecc/curves/grumpkin/grumpkin.hpp" -#include - -using namespace bb; -using namespace bb::crypto; - -template struct ProofOfPossessionTest : public ::testing::Test { - using G = grumpkin::g1; - using Fr = grumpkin::fr; - using KeyPair = schnorr_key_pair; - - static KeyPair generate_account() - { - KeyPair account; - account.private_key = Fr::random_element(); - account.public_key = G::one * account.private_key; - return account; - } -}; - -using HashTypes = ::testing::Types; -TYPED_TEST_SUITE(ProofOfPossessionTest, HashTypes); - -TYPED_TEST(ProofOfPossessionTest, valid_proof) -{ - using G = grumpkin::g1; - using Hash = TypeParam; - using Proof = SchnorrProofOfPossession; - - const auto account = this->generate_account(); - const auto proof = Proof(account); - EXPECT_TRUE(proof.verify(account.public_key)); -} - -TYPED_TEST(ProofOfPossessionTest, invalid_empty_proof) -{ - using G = grumpkin::g1; - using Hash = TypeParam; - using Proof = SchnorrProofOfPossession; - - const auto account = this->generate_account(); - const auto proof = Proof(); - EXPECT_FALSE(proof.verify(account.public_key)); -} - -TYPED_TEST(ProofOfPossessionTest, fail_with_different_account) -{ - using G = grumpkin::g1; - using Hash = TypeParam; - using Proof = SchnorrProofOfPossession; - - const auto account1 = this->generate_account(); - const auto account2 = this->generate_account(); - auto proof = Proof(account1); - EXPECT_FALSE(proof.verify(account2.public_key)); -} - -TYPED_TEST(ProofOfPossessionTest, fail_zero_challenge) -{ - using G = grumpkin::g1; - using Hash = TypeParam; - using Proof = SchnorrProofOfPossession; - - const auto account = this->generate_account(); - auto proof = Proof(account); - proof.challenge = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - }; - EXPECT_FALSE(proof.verify(account.public_key)); -} - -TYPED_TEST(ProofOfPossessionTest, fail_zero_response) -{ - using G = grumpkin::g1; - using Hash = TypeParam; - using Proof = SchnorrProofOfPossession; - - const auto account = this->generate_account(); - auto proof = Proof(account); - // Setting the response part of the proof of posession should cause verification to fail. - proof.response = 0; - EXPECT_FALSE(proof.verify(account.public_key)); -} - -TYPED_TEST(ProofOfPossessionTest, serialize) -{ - using G = grumpkin::g1; - using Hash = TypeParam; - using Proof = SchnorrProofOfPossession; - const auto account = this->generate_account(); - const auto proof = Proof(account); - EXPECT_TRUE(proof.verify(account.public_key)); - - auto buf = to_buffer(proof); - EXPECT_EQ(buf.size(), 64); - Proof proof2{ from_buffer>(buf) }; - EXPECT_EQ(proof.response, proof2.response); - EXPECT_EQ(proof.challenge, proof2.challenge); - - EXPECT_TRUE(proof2.verify(account.public_key)); -}