barretenberg
Loading...
Searching...
No Matches
ecdsa_impl.hpp
1#pragma once
2
3#include "../hmac/hmac.hpp"
4#include "barretenberg/common/serialize.hpp"
5#include "barretenberg/numeric/uint256/uint256.hpp"
6
7namespace crypto {
8namespace ecdsa {
9
10template <typename Hash, typename Fq, typename Fr, typename G1>
11signature construct_signature(const std::string& message, const key_pair<Fr, G1>& account)
12{
13 signature sig;
14
15 // use HMAC in PRF mode to derive 32-byte secret `k`
16 std::vector<uint8_t> pkey_buffer;
17 write(pkey_buffer, account.private_key);
18 Fr k = crypto::get_unbiased_field_from_hmac<Hash, Fr>(message, pkey_buffer);
19
20 typename G1::affine_element R(G1::one * k);
21 Fq::serialize_to_buffer(R.x, &sig.r[0]);
22
23 std::vector<uint8_t> message_buffer;
24 std::copy(message.begin(), message.end(), std::back_inserter(message_buffer));
25 auto ev = Hash::hash(message_buffer);
26
27 Fr z = Fr::serialize_from_buffer(&ev[0]);
28 Fr r_fr = Fr::serialize_from_buffer(&sig.r[0]);
29 Fr s_fr = (z + r_fr * account.private_key) / k;
30
31 // Ensure that the value of s is "low", i.e. s := min{ s_fr, (|Fr| - s_fr) }
32 const bool is_s_low = (uint256_t(s_fr) < (uint256_t(Fr::modulus) / 2));
33 uint256_t s_uint256 = is_s_low ? uint256_t(s_fr) : (uint256_t(Fr::modulus) - uint256_t(s_fr));
34
35 Fr::serialize_to_buffer(Fr(s_uint256), &sig.s[0]);
36
37 // compute recovery_id: given R = (x, y)
38 // 0: y is even && x < |Fr|
39 // 1: y is odd && x < |Fr|
40 // 2: y is even && |Fr| <= x < |Fq|
41 // 3: y is odd && |Fr| <= x < |Fq|
42 // v = offset + recovery_id
43 Fq r_fq = Fq(R.x);
44 bool is_r_finite = (uint256_t(r_fq) == uint256_t(r_fr));
45 bool y_parity = uint256_t(R.y).get_bit(0);
46 bool recovery_bit = y_parity ^ is_s_low;
47 constexpr uint8_t offset = 27;
48
49 int value = offset + recovery_bit + static_cast<uint8_t>(2) * !is_r_finite;
50 ASSERT(value <= UINT8_MAX);
51 sig.v = static_cast<uint8_t>(value);
52 return sig;
53}
54
55template <typename Hash, typename Fq, typename Fr, typename G1>
56typename G1::affine_element recover_public_key(const std::string& message, const signature& sig)
57{
58 using serialize::read;
59 uint256_t r_uint;
60 uint256_t s_uint;
61 uint8_t v_uint;
62 uint256_t mod = uint256_t(Fr::modulus);
63
64 const auto* r_buf = &sig.r[0];
65 const auto* s_buf = &sig.s[0];
66 const auto* v_buf = &sig.v;
67 read(r_buf, r_uint);
68 read(s_buf, s_uint);
69 read(v_buf, v_uint);
70
71 // We need to check that r and s are in Field according to specification
72 if ((r_uint >= mod) || (s_uint >= mod)) {
73 throw_or_abort("r or s value exceeds the modulus");
74 }
75 if ((r_uint == 0) || (s_uint == 0)) {
76 throw_or_abort("r or s value is zero");
77 }
78
79 // Check that the s value is less than |Fr| / 2
80 if (s_uint * 2 > mod) {
81 throw_or_abort("s value is not less than curve order by 2");
82 }
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)) {
91 ASSERT(uint256_t(r) == uint256_t(r_fq));
92 } else if ((v_uint == 29) || (v_uint == 30)) {
93 ASSERT(uint256_t(r) < uint256_t(r_fq));
94 is_r_finite = false;
95 } else {
96 throw_or_abort("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 = G1::affine_element::from_compressed_unsafe(r_uint);
104 typename G1::affine_element 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 typename G1::affine_element recovered_public_key(typename G1::element(point_R) * u2 + G1::one * u1);
124 return recovered_public_key;
125}
126
127template <typename Hash, typename Fq, typename Fr, typename G1>
128bool verify_signature(const std::string& message, const typename G1::affine_element& public_key, const signature& sig)
129{
130 using serialize::read;
131 uint256_t r_uint;
132 uint256_t s_uint;
133 uint256_t mod = uint256_t(Fr::modulus);
134 if (!public_key.on_curve()) {
135 return false;
136 }
137 const auto* r_buf = &sig.r[0];
138 const auto* s_buf = &sig.s[0];
139 read(r_buf, r_uint);
140 read(s_buf, s_uint);
141 // We need to check that r and s are in Field according to specification
142 if ((r_uint >= mod) || (s_uint >= mod)) {
143 return false;
144 }
145 if ((r_uint == 0) || (s_uint == 0)) {
146 return false;
147 }
148
149 // Check that the s value is less than |Fr| / 2
150 if (s_uint * 2 > mod) {
151 throw_or_abort("s value is not less than curve order by 2");
152 }
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 uint256_t Rx(R.x);
169 Fr result(Rx);
170 return result == r;
171}
172} // namespace ecdsa
173} // namespace crypto
Definition: uint256.hpp:25
Definition: aes128.cpp:9