Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
schnorr.test.cpp
Go to the documentation of this file.
1#include "schnorr.hpp"
5#include <gtest/gtest.h>
6
7using namespace bb;
8using namespace bb::crypto;
9
13
14TEST(schnorr, verify_signature)
15{
18 account.public_key = G1::one * account.private_key;
19
20 Fq message_field = Fq::random_element();
21
22 auto sig = schnorr_construct_signature<Fr, G1>(message_field, account);
23 bool result = schnorr_verify_signature<Fr, G1>(message_field, account.public_key, sig);
24
25 EXPECT_TRUE(result);
26}
27
28TEST(schnorr, verify_signature_failure_wrong_message)
29{
32 account.public_key = G1::one * account.private_key;
33
34 Fq message_field = Fq::random_element();
35 Fq wrong_message = Fq::random_element();
36
37 auto sig = schnorr_construct_signature<Fr, G1>(message_field, account);
38 bool result = schnorr_verify_signature<Fr, G1>(wrong_message, account.public_key, sig);
39
40 EXPECT_FALSE(result);
41}
42
43TEST(schnorr, verify_signature_failure_wrong_key)
44{
47 account.public_key = G1::one * account.private_key;
48
49 Fq message_field = Fq::random_element();
50
51 auto sig = schnorr_construct_signature<Fr, G1>(message_field, account);
52
53 auto wrong_key = G1::affine_element(G1::one * Fr::random_element());
54 bool result = schnorr_verify_signature<Fr, G1>(message_field, wrong_key, sig);
55
56 EXPECT_FALSE(result);
57}
58
59TEST(schnorr, signatures_not_deterministic)
60{
63 account.public_key = G1::one * account.private_key;
64
65 Fq message_field = Fq::random_element();
66
67 auto sig_a = schnorr_construct_signature<Fr, G1>(message_field, account);
68 auto sig_b = schnorr_construct_signature<Fr, G1>(message_field, account);
69
70 // Different nonces should produce different signatures
71 EXPECT_NE(sig_a.e, sig_b.e);
72 EXPECT_NE(sig_a.s, sig_b.s);
73
74 // But both should verify
75 bool result_a = schnorr_verify_signature<Fr, G1>(message_field, account.public_key, sig_a);
76 EXPECT_TRUE(result_a);
77 bool result_b = schnorr_verify_signature<Fr, G1>(message_field, account.public_key, sig_b);
78 EXPECT_TRUE(result_b);
79}
80
87TEST(schnorr, signature_internals_consistency)
88{
89 // Use a fixed private key for reproducibility
90 Fr private_key = Fr(12345);
91 G1::affine_element public_key(G1::one * private_key);
92 schnorr_key_pair<Fr, G1> account = { private_key, public_key };
93
94 Fq message_field = Fq(67890);
95
96 auto sig = schnorr_construct_signature<Fr, G1>(message_field, account);
97
98 // Reconstruct R = g^s * pub^e (this is what the verifier does)
99 G1::affine_element R(G1::element(public_key) * sig.e + G1::one * sig.s);
100
101 // Independently compute the Poseidon2 challenge from DST, R, pubkey, message
103 { compute_schnorr_challenge_dst<Fq>(), R.x, public_key.x, public_key.y, message_field });
104
105 // Convert to the grumpkin scalar field via byte round-trip (the same path the implementation uses)
106 std::array<uint8_t, 32> expected_e_buf;
107 Fq::serialize_to_buffer(expected_e_base, expected_e_buf.data());
108 Fr expected_e_scalar = Fr::serialize_from_buffer(expected_e_buf.data());
109
110 // The challenge in the signature must match the independently computed one
111 EXPECT_EQ(sig.e, expected_e_scalar);
112
113 // Also verify that R is not the point at infinity (would indicate k=0)
114 EXPECT_FALSE(R.is_point_at_infinity());
115}
116
125TEST(schnorr, challenge_dst_pinned)
126{
127 Fq derived = compute_schnorr_challenge_dst<Fq>();
128 EXPECT_EQ(derived, Fq(std::string("0x024c76938ed06b8ec1d9094b1013d190baa4011372f0604643bda812a63b832e")));
129}
130
137TEST(schnorr, cross_field_serialization_is_lossless)
138{
139 for (int i = 0; i < 100; i++) {
140 // Generate a random Fq element (BN254 Fr, the Poseidon2 output field)
141 Fq original = Fq::random_element();
142
143 // Serialize to bytes
144 std::array<uint8_t, 32> buf;
145 Fq::serialize_to_buffer(original, buf.data());
146
147 // Deserialize as Fr (BN254 Fq, the Grumpkin scalar field)
148 Fr converted = Fr::serialize_from_buffer(buf.data());
149
150 // Serialize Fr back to bytes
151 std::array<uint8_t, 32> buf2;
152 Fr::serialize_to_buffer(converted, buf2.data());
153
154 // The byte representations must be identical (no information lost in the conversion)
155 EXPECT_EQ(buf, buf2);
156 }
157}
158
165TEST(schnorr, bbapi_byte_interface_round_trip)
166{
167 Fr private_key = Fr::random_element();
168 G1::affine_element public_key(G1::one * private_key);
169 schnorr_key_pair<Fr, G1> account = { private_key, public_key };
170
171 // Simulate bbapi: start from a field element, serialize to bytes, then deserialize
172 Fq original_message = Fq::random_element();
173 std::array<uint8_t, 32> message_bytes;
174 Fq::serialize_to_buffer(original_message, message_bytes.data());
175
176 // This is what bbapi does
177 Fq deserialized_message = Fq::serialize_from_buffer(message_bytes.data());
178 EXPECT_EQ(original_message, deserialized_message);
179
180 // Sign with deserialized message, verify with original — must agree
181 auto sig = schnorr_construct_signature<Fr, G1>(deserialized_message, account);
182 bool result = schnorr_verify_signature<Fr, G1>(original_message, public_key, sig);
183 EXPECT_TRUE(result);
184}
185
186namespace {
187
194struct DeterministicSig {
195 G1::affine_element public_key;
197 Fq e_base; // raw Poseidon2 output, lives in the grumpkin base field (bb::fr)
198 Fr e_scalar; // re-encoded into the grumpkin scalar field (bb::fq) for the signature equation
199 Fr s; // s = k - priv * e_scalar
201};
202
203DeterministicSig build_deterministic_sig(const Fr& private_key, const Fr& nonce_k, const Fq& message_field)
204{
205 DeterministicSig out;
206 out.public_key = G1::affine_element(G1::one * private_key);
207 out.R = G1::affine_element(G1::one * nonce_k);
208 out.e_base = schnorr_generate_challenge<G1>(message_field, out.public_key, out.R);
209 std::array<uint8_t, 32> e_buf;
210 Fq::serialize_to_buffer(out.e_base, e_buf.data());
211 out.e_scalar = Fr::serialize_from_buffer(e_buf.data());
212 out.s = nonce_k - private_key * out.e_scalar;
213 out.sig.s = out.s;
214 out.sig.e = out.e_scalar;
215 return out;
216}
217
218void dump_vector(const char* label, const Fr& private_key, const Fr& nonce_k, const Fq& message_field)
219{
220 auto v = build_deterministic_sig(private_key, nonce_k, message_field);
221 info("=== ", label, " ===");
222 info(" private_key = ", private_key);
223 info(" nonce_k = ", nonce_k);
224 info(" message = ", message_field);
225 info(" public_key.x = ", v.public_key.x);
226 info(" public_key.y = ", v.public_key.y);
227 info(" R.x = ", v.R.x);
228 info(" R.y = ", v.R.y);
229 info(" s = ", v.s);
230 info(" e = ", v.e_scalar);
231}
232
233} // namespace
234
242TEST(schnorr, pinned_test_vector_small)
243{
244 Fr private_key(std::string("0x000000000000000000000000000000000000000000000000000000000000007b")); // 123
245 Fr nonce_k(std::string("0x00000000000000000000000000000000000000000000000000000000000001c8")); // 456
246 Fq message_field(std::string("0x00000000000000000000000000000000000000000000000000000000000002bc")); // 700
247
248 auto v = build_deterministic_sig(private_key, nonce_k, message_field);
249 dump_vector("pinned_test_vector_small", private_key, nonce_k, message_field);
250
251 bool ok = schnorr_verify_signature<Fr, G1>(message_field, v.public_key, v.sig);
252 EXPECT_TRUE(ok);
253
254 EXPECT_EQ(v.public_key.x, Fq(std::string("0x2c39bbbde2d0ffcb5c4317dcbfa1771cf554a2f33c647446632fa707a5bf5f3f")));
255 EXPECT_EQ(v.public_key.y, Fq(std::string("0x2b9c81935298af5ebe22f1a7279bb76781e6cadba3fb6c5c41ed942392dc687c")));
256 EXPECT_EQ(v.R.x, Fq(std::string("0x2f410c5089a00d9a4664f262272dbc091b121acf58abdf919c1bc8b974fb720e")));
257 EXPECT_EQ(v.s, Fr(std::string("0x281906862cdb4e0efec7226d757fe8035fd1ac0ad411110674830c54cb506212")));
258 EXPECT_EQ(v.e_scalar, Fr(std::string("0x013f6a902c6c0efafdadbd4de409690d6c368959f958e525d761d06c47fd2ad6")));
259}
260
264TEST(schnorr, pinned_test_vector_large)
265{
266 Fr private_key(std::string("0x1f2e3d4c5b6a79880f1e2d3c4b5a69788f9e0d1c2b3a4958e7f6d5c4b3a29180"));
267 Fr nonce_k(std::string("0x2a1b3c4d5e6f78890a1b2c3d4e5f60718293a4b5c6d7e8f90a1b2c3d4e5f6071"));
268 Fq message_field(std::string("0x0123456789abcdef0fedcba9876543210123456789abcdef0fedcba987654321"));
269
270 auto v = build_deterministic_sig(private_key, nonce_k, message_field);
271 dump_vector("pinned_test_vector_large", private_key, nonce_k, message_field);
272
273 bool ok = schnorr_verify_signature<Fr, G1>(message_field, v.public_key, v.sig);
274 EXPECT_TRUE(ok);
275
276 EXPECT_EQ(v.public_key.x, Fq(std::string("0x065812e335a97c2108ea8cf4ccfe2f9dd6b117a0714f5e18461575be93f61da6")));
277 EXPECT_EQ(v.public_key.y, Fq(std::string("0x1a915003e8ec534f9a15d926a7ded478e178468ccc4f28e236e67450a55ac622")));
278 EXPECT_EQ(v.R.x, Fq(std::string("0x04e780bc3d2b86b5f41f3b8d2820c1f2c3164cd5efc607cd48c428495a8f47b7")));
279 EXPECT_EQ(v.s, Fr(std::string("0x08599f379f0301dfefdbd0272554454df3bc3b7147acb9c621fd9f72dbf15ffa")));
280 EXPECT_EQ(v.e_scalar, Fr(std::string("0x2ceaee87f45b7a417f0ffb05451a8c9297065383ebbbd76620398792bd259bc2")));
281}
static FF hash(const std::vector< FF > &input)
Hashes a vector of field elements.
group_elements::affine_element< Fq, Fr, Params > affine_element
Definition group.hpp:44
static constexpr element one
Definition group.hpp:48
group_elements::element< Fq, Fr, Params > element
Definition group.hpp:43
#define info(...)
Definition log.hpp:93
bb::group< bb::fr, bb::fq, G1Params > g1
Definition grumpkin.hpp:46
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
TEST(BoomerangMegaCircuitBuilder, BasicCircuit)
Curve::AffineElement G1
grumpkin::fq Fq
grumpkin::fr Fr
G1::affine_element public_key
Definition schnorr.hpp:14
static field random_element(numeric::RNG *engine=nullptr) noexcept
static field serialize_from_buffer(const uint8_t *buffer)
static void serialize_to_buffer(const field &value, uint8_t *buffer)