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 using Point = G1::affine_element;
65 uint256_t r_uint;
66 uint256_t s_uint;
67 uint8_t v_uint;
69
70 const auto* r_buf = &sig.r[0];
71 const auto* s_buf = &sig.s[0];
72 const auto* v_buf = &sig.v;
73 read(r_buf, r_uint);
74 read(s_buf, s_uint);
75 read(v_buf, v_uint);
76
77 // We need to check that r and s are in Field according to specification
78 BB_ASSERT((r_uint < mod) && (s_uint < mod), "r or s value exceeds the modulus");
79 BB_ASSERT((r_uint != 0) && (s_uint != 0), "r or s value is zero");
80
81 // Check that the s value is less than |Fr| / 2
82 BB_ASSERT_LTE(s_uint * 2, mod, "s value is not less than curve order by 2");
83
84 // Check that v must either be in {27, 28, 29, 30}
85 Fr r = Fr(r_uint);
86 Fr s = Fr(s_uint);
87 Fq r_fq = Fq(r_uint);
88 bool is_r_finite = true;
89
90 if ((v_uint == 27) || (v_uint == 28)) {
92 } else if ((v_uint == 29) || (v_uint == 30)) {
94 is_r_finite = false;
95 } else {
96 bb::assert_failure("v value is not in {27, 28, 29, 30}");
97 }
98
99 // Decompress the x-coordinate r_uint to get two possible R points
100 // The first uncompressed R point is selected when r < |Fr|
101 // The second uncompressed R point is selected when |Fr| <= r < |Fq|
102 // Note that the second condition can occur with probability 1/2^128 so its highly unlikely.
103 auto uncompressed_points = Point::from_compressed_unsafe(r_uint);
104 Point point_R = uncompressed_points[!is_r_finite];
105
106 // Negate the y-coordinate point of R based on the parity of v
107 bool y_parity_R = uint256_t(point_R.y).get_bit(0);
108 if ((v_uint & 1) ^ y_parity_R) {
109 point_R.y = -point_R.y;
110 }
111
112 // Start key recovery algorithm
113 std::vector<uint8_t> message_buffer;
114 std::copy(message.begin(), message.end(), std::back_inserter(message_buffer));
115 auto ev = Hash::hash(message_buffer);
116 Fr z = Fr::serialize_from_buffer(&ev[0]);
117
118 Fr r_inv = r.invert();
119
120 Fr u1 = -(z * r_inv);
121 Fr u2 = s * r_inv;
122
123 Point recovered_public_key(Point(point_R) * u2 + G1::one * u1);
124 return recovered_public_key;
125}
126
127template <typename Hash, typename Fq, typename Fr, typename G1>
128bool ecdsa_verify_signature(const std::string& message,
129 const typename G1::affine_element& public_key,
130 const ecdsa_signature& sig)
131{
132 using serialize::read;
133 uint256_t r_uint;
134 uint256_t s_uint;
136 if ((!public_key.on_curve()) || (public_key.is_point_at_infinity())) {
137 return false;
138 }
139 const auto* r_buf = &sig.r[0];
140 const auto* s_buf = &sig.s[0];
141 read(r_buf, r_uint);
142 read(s_buf, s_uint);
143 // We need to check that r and s are in Field according to specification
144 if ((r_uint >= mod) || (s_uint >= mod)) {
145 return false;
146 }
147 if ((r_uint == 0) || (s_uint == 0)) {
148 return false;
149 }
150
151 // Check that the s value is less than |Fr| / 2
152 BB_ASSERT_LT(s_uint, (mod + 1) / 2, "s value is not less than curve order by 2");
153
154 Fr r = Fr(r_uint);
155 Fr s = Fr(s_uint);
156
157 std::vector<uint8_t> message_buffer;
158 std::copy(message.begin(), message.end(), std::back_inserter(message_buffer));
159 auto ev = Hash::hash(message_buffer);
160 Fr z = Fr::serialize_from_buffer(&ev[0]);
161
162 Fr s_inv = s.invert();
163
164 Fr u1 = z * s_inv;
165 Fr u2 = r * s_inv;
166
167 typename G1::affine_element R((typename G1::element(public_key) * u2) + (G1::one * u1));
168 BB_ASSERT_EQ(R.is_point_at_infinity(), false, "Result of the scalar multiplication is the point at infinity.");
169
170 uint256_t Rx(R.x);
171 Fr result(Rx);
172 return result == r;
173}
174} // namespace bb::crypto
#define BB_ASSERT(expression,...)
Definition assert.hpp:67
#define BB_ASSERT_EQ(actual, expected,...)
Definition assert.hpp:77
#define BB_ASSERT_LTE(left, right,...)
Definition assert.hpp:152
#define BB_ASSERT_LT(left, right,...)
Definition assert.hpp:137
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 assert_failure(std::string const &err)
Definition assert.cpp:11
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)
curve::BN254::BaseField Fq