Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
ecdsa_impl.hpp
Go to the documentation of this file.
1// === AUDIT STATUS ===
2// internal: { status: not started, auditors: [], date: YYYY-MM-DD }
3// external_1: { status: not started, auditors: [], date: YYYY-MM-DD }
4// external_2: { status: not started, auditors: [], date: YYYY-MM-DD }
5// =====================
6
7#pragma once
8
9#include "../hmac/hmac.hpp"
12
13namespace bb::crypto {
14
15template <typename Hash, typename Fq, typename Fr, typename G1>
16ecdsa_signature ecdsa_construct_signature(const std::string& message, const ecdsa_key_pair<Fr, G1>& account)
17{
19
20 // use HMAC in PRF mode to derive 32-byte secret `k`
21 std::vector<uint8_t> pkey_buffer;
22 write(pkey_buffer, account.private_key);
23 Fr k = crypto::get_unbiased_field_from_hmac<Hash, Fr>(message, pkey_buffer);
24
25 typename G1::affine_element R(G1::one * k);
26 Fq::serialize_to_buffer(R.x, &sig.r[0]);
27
28 std::vector<uint8_t> message_buffer;
29 std::copy(message.begin(), message.end(), std::back_inserter(message_buffer));
30 auto ev = Hash::hash(message_buffer);
31
32 Fr z = Fr::serialize_from_buffer(&ev[0]);
33 Fr r_fr = Fr::serialize_from_buffer(&sig.r[0]);
34 Fr s_fr = (z + r_fr * account.private_key) / k;
35
36 // Ensure that the value of s is "low", i.e. s := min{ s_fr, (|Fr| - s_fr) }
37 const bool is_s_low = (uint256_t(s_fr) < (uint256_t(Fr::modulus) / 2));
38 uint256_t s_uint256 = is_s_low ? uint256_t(s_fr) : (uint256_t(Fr::modulus) - uint256_t(s_fr));
39
40 Fr::serialize_to_buffer(Fr(s_uint256), &sig.s[0]);
41
42 // compute recovery_id: given R = (x, y)
43 // 0: y is even && x < |Fr|
44 // 1: y is odd && x < |Fr|
45 // 2: y is even && |Fr| <= x < |Fq|
46 // 3: y is odd && |Fr| <= x < |Fq|
47 // v = offset + recovery_id
48 Fq r_fq = Fq(R.x);
49 bool is_r_finite = (uint256_t(r_fq) == uint256_t(r_fr));
50 bool y_parity = uint256_t(R.y).get_bit(0);
51 bool recovery_bit = y_parity ^ is_s_low;
52 constexpr uint8_t offset = 27;
53
54 int value = offset + recovery_bit + static_cast<uint8_t>(2) * !is_r_finite;
55 BB_ASSERT_LTE(value, UINT8_MAX);
56 sig.v = static_cast<uint8_t>(value);
57 return sig;
58}
59
60template <typename Hash, typename Fq, typename Fr, typename G1>
61typename G1::affine_element ecdsa_recover_public_key(const std::string& message, const ecdsa_signature& sig)
62{
63 using serialize::read;
64 uint256_t r_uint;
65 uint256_t s_uint;
66 uint8_t v_uint;
68
69 const auto* r_buf = &sig.r[0];
70 const auto* s_buf = &sig.s[0];
71 const auto* v_buf = &sig.v;
72 read(r_buf, r_uint);
73 read(s_buf, s_uint);
74 read(v_buf, v_uint);
75
76 // We need to check that r and s are in Field according to specification
77 if ((r_uint >= mod) || (s_uint >= mod)) {
78 throw_or_abort("r or s value exceeds the modulus");
79 }
80 if ((r_uint == 0) || (s_uint == 0)) {
81 throw_or_abort("r or s value is zero");
82 }
83
84 // Check that the s value is less than |Fr| / 2
85 if (s_uint * 2 > mod) {
86 throw_or_abort("s value is not less than curve order by 2");
87 }
88
89 // Check that v must either be in {27, 28, 29, 30}
90 Fr r = Fr(r_uint);
91 Fr s = Fr(s_uint);
92 Fq r_fq = Fq(r_uint);
93 bool is_r_finite = true;
94
95 if ((v_uint == 27) || (v_uint == 28)) {
97 } else if ((v_uint == 29) || (v_uint == 30)) {
99 is_r_finite = false;
100 } else {
101 throw_or_abort("v value is not in {27, 28, 29, 30}");
102 }
103
104 // Decompress the x-coordinate r_uint to get two possible R points
105 // The first uncompressed R point is selected when r < |Fr|
106 // The second uncompressed R point is selected when |Fr| <= r < |Fq|
107 // Note that the second condition can occur with probability 1/2^128 so its highly unlikely.
108 auto uncompressed_points = G1::affine_element::from_compressed_unsafe(r_uint);
109 typename G1::affine_element point_R = uncompressed_points[!is_r_finite];
110
111 // Negate the y-coordinate point of R based on the parity of v
112 bool y_parity_R = uint256_t(point_R.y).get_bit(0);
113 if ((v_uint & 1) ^ y_parity_R) {
114 point_R.y = -point_R.y;
115 }
116
117 // Start key recovery algorithm
118 std::vector<uint8_t> message_buffer;
119 std::copy(message.begin(), message.end(), std::back_inserter(message_buffer));
120 auto ev = Hash::hash(message_buffer);
121 Fr z = Fr::serialize_from_buffer(&ev[0]);
122
123 Fr r_inv = r.invert();
124
125 Fr u1 = -(z * r_inv);
126 Fr u2 = s * r_inv;
127
128 typename G1::affine_element recovered_public_key(typename G1::element(point_R) * u2 + G1::one * u1);
129 return recovered_public_key;
130}
131
132template <typename Hash, typename Fq, typename Fr, typename G1>
133bool ecdsa_verify_signature(const std::string& message,
134 const typename G1::affine_element& public_key,
135 const ecdsa_signature& sig)
136{
137 using serialize::read;
138 uint256_t r_uint;
139 uint256_t s_uint;
141 if (!public_key.on_curve()) {
142 return false;
143 }
144 const auto* r_buf = &sig.r[0];
145 const auto* s_buf = &sig.s[0];
146 read(r_buf, r_uint);
147 read(s_buf, s_uint);
148 // We need to check that r and s are in Field according to specification
149 if ((r_uint >= mod) || (s_uint >= mod)) {
150 return false;
151 }
152 if ((r_uint == 0) || (s_uint == 0)) {
153 return false;
154 }
155
156 // Check that the s value is less than |Fr| / 2
157 if (s_uint * 2 > mod) {
158 throw_or_abort("s value is not less than curve order by 2");
159 }
160
161 Fr r = Fr(r_uint);
162 Fr s = Fr(s_uint);
163
164 std::vector<uint8_t> message_buffer;
165 std::copy(message.begin(), message.end(), std::back_inserter(message_buffer));
166 auto ev = Hash::hash(message_buffer);
167 Fr z = Fr::serialize_from_buffer(&ev[0]);
168
169 Fr s_inv = s.invert();
170
171 Fr u1 = z * s_inv;
172 Fr u2 = r * s_inv;
173
174 typename G1::affine_element R(typename G1::element(public_key) * u2 + G1::one * u1);
175 uint256_t Rx(R.x);
176 Fr result(Rx);
177 return result == r;
178}
179} // namespace bb::crypto
#define BB_ASSERT_EQ(actual, expected,...)
Definition assert.hpp:59
#define BB_ASSERT_LTE(left, right,...)
Definition assert.hpp:129
#define BB_ASSERT_LT(left, right,...)
Definition assert.hpp:115
constexpr bool get_bit(uint64_t bit_index) const
ssize_t offset
Definition engine.cpp:36
G1::affine_element ecdsa_recover_public_key(const std::string &message, const ecdsa_signature &sig)
void read(B &it, SchnorrProofOfPossession< G1, Hash > &proof_of_possession)
ecdsa_signature ecdsa_construct_signature(const std::string &message, const ecdsa_key_pair< Fr, G1 > &account)
void write(B &buf, SchnorrProofOfPossession< G1, Hash > const &proof_of_possession)
bool ecdsa_verify_signature(const std::string &message, const typename G1::affine_element &public_key, const ecdsa_signature &signature)
void read(auto &it, msgpack_concepts::HasMsgPack auto &obj)
Automatically derived read for any object that defines .msgpack() (implicitly defined by MSGPACK_FIEL...
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
Curve::ScalarField Fr
std::array< uint8_t, 32 > r
Definition ecdsa.hpp:26
std::array< uint8_t, 32 > s
Definition ecdsa.hpp:27
static constexpr uint256_t modulus
constexpr field invert() const noexcept
static field serialize_from_buffer(const uint8_t *buffer)
static void serialize_to_buffer(const field &value, uint8_t *buffer)
void throw_or_abort(std::string const &err)
bb::fq Fq