barretenberg
Loading...
Searching...
No Matches
affine_element_impl.hpp
1#pragma once
2#include "./element.hpp"
3#include "barretenberg/crypto/blake3s/blake3s.hpp"
4#include "barretenberg/crypto/keccak/keccak.hpp"
5
6namespace barretenberg::group_elements {
7template <class Fq, class Fr, class T>
8constexpr affine_element<Fq, Fr, T>::affine_element(const Fq& a, const Fq& b) noexcept
9 : x(a)
10 , y(b)
11{}
12
13template <class Fq, class Fr, class T>
14template <typename BaseField, typename CompileTimeEnabled>
16{
17 uint256_t x_coordinate = compressed;
18 x_coordinate.data[3] = x_coordinate.data[3] & (~0x8000000000000000ULL);
19 bool y_bit = compressed.get_bit(255);
20
21 Fq x = Fq(x_coordinate);
22 Fq y2 = (x.sqr() * x + T::b);
23 if constexpr (T::has_a) {
24 y2 += (x * T::a);
25 }
26 auto [is_quadratic_remainder, y] = y2.sqrt();
27 if (!is_quadratic_remainder) {
28 return affine_element(Fq::zero(), Fq::zero());
29 }
30 if (uint256_t(y).get_bit(0) != y_bit) {
31 y = -y;
32 }
33
34 return affine_element<Fq, Fr, T>(x, y);
35}
36
37template <class Fq, class Fr, class T>
38template <typename BaseField, typename CompileTimeEnabled>
39constexpr std::array<affine_element<Fq, Fr, T>, 2> affine_element<Fq, Fr, T>::from_compressed_unsafe(
40 const uint256_t& compressed) noexcept
41{
42 auto get_y_coordinate = [](const uint256_t& x_coordinate) {
43 Fq x = Fq(x_coordinate);
44 Fq y2 = (x.sqr() * x + T::b);
45 if constexpr (T::has_a) {
46 y2 += (x * T::a);
47 }
48 return y2.sqrt();
49 };
50
51 uint256_t x_1 = compressed;
52 uint256_t x_2 = compressed + Fr::modulus;
53 auto [is_quadratic_remainder_1, y_1] = get_y_coordinate(x_1);
54 auto [is_quadratic_remainder_2, y_2] = get_y_coordinate(x_2);
55
56 auto output_1 = is_quadratic_remainder_1 ? affine_element<Fq, Fr, T>(Fq(x_1), y_1)
57 : affine_element<Fq, Fr, T>(Fq::zero(), Fq::zero());
58 auto output_2 = is_quadratic_remainder_2 ? affine_element<Fq, Fr, T>(Fq(x_2), y_2)
59 : affine_element<Fq, Fr, T>(Fq::zero(), Fq::zero());
60
61 return { output_1, output_2 };
62}
63
64template <class Fq, class Fr, class T>
66 const affine_element<Fq, Fr, T>& other) const noexcept
67{
68 return affine_element(element<Fq, Fr, T>(*this) + element<Fq, Fr, T>(other));
69}
70
71template <class Fq, class Fr, class T>
72template <typename BaseField, typename CompileTimeEnabled>
73
74constexpr uint256_t affine_element<Fq, Fr, T>::compress() const noexcept
75{
76 uint256_t out(x);
77 if (uint256_t(y).get_bit(0)) {
78 out.data[3] = out.data[3] | 0x8000000000000000ULL;
79 }
80 return out;
81}
82
83template <class Fq, class Fr, class T> affine_element<Fq, Fr, T> affine_element<Fq, Fr, T>::infinity()
84{
86 e.self_set_infinity();
87 return e;
88}
89
90template <class Fq, class Fr, class T>
92{
93 affine_element result(*this);
94 result.self_set_infinity();
95 return result;
96}
97
98template <class Fq, class Fr, class T> constexpr void affine_element<Fq, Fr, T>::self_set_infinity() noexcept
99{
100 if constexpr (Fq::modulus.data[3] >= 0x4000000000000000ULL) {
101 // We set the value of x equal to modulus to represent inifinty
102 x.data[0] = Fq::modulus.data[0];
103 x.data[1] = Fq::modulus.data[1];
104 x.data[2] = Fq::modulus.data[2];
105 x.data[3] = Fq::modulus.data[3];
106
107 } else {
108 x.self_set_msb();
109 }
110}
111
112template <class Fq, class Fr, class T> constexpr bool affine_element<Fq, Fr, T>::is_point_at_infinity() const noexcept
113{
114 if constexpr (Fq::modulus.data[3] >= 0x4000000000000000ULL) {
115 // We check if the value of x is equal to modulus to represent inifinty
116 return ((x.data[0] ^ Fq::modulus.data[0]) | (x.data[1] ^ Fq::modulus.data[1]) |
117 (x.data[2] ^ Fq::modulus.data[2]) | (x.data[3] ^ Fq::modulus.data[3])) == 0;
118
119 } else {
120 return (x.is_msb_set());
121 }
122}
123
124template <class Fq, class Fr, class T> constexpr bool affine_element<Fq, Fr, T>::on_curve() const noexcept
125{
126 if (is_point_at_infinity()) {
127 return true;
128 }
129 Fq xxx = x.sqr() * x + T::b;
130 Fq yy = y.sqr();
131 if constexpr (T::has_a) {
132 xxx += (x * T::a);
133 }
134 return (xxx == yy);
135}
136
137template <class Fq, class Fr, class T>
138constexpr bool affine_element<Fq, Fr, T>::operator==(const affine_element& other) const noexcept
139{
140 bool this_is_infinity = is_point_at_infinity();
141 bool other_is_infinity = other.is_point_at_infinity();
142 bool both_infinity = this_is_infinity && other_is_infinity;
143 bool only_one_is_infinity = this_is_infinity != other_is_infinity;
144 return !only_one_is_infinity && (both_infinity || ((x == other.x) && (y == other.y)));
145}
146
152template <class Fq, class Fr, class T>
153constexpr bool affine_element<Fq, Fr, T>::operator>(const affine_element& other) const noexcept
154{
155 // We are setting point at infinity to always be the lowest element
156 if (is_point_at_infinity()) {
157 return false;
158 }
159 if (other.is_point_at_infinity()) {
160 return true;
161 }
162
163 if (x > other.x) {
164 return true;
165 }
166 if (x == other.x && y > other.y) {
167 return true;
168 }
169 return false;
170}
171
172template <class Fq, class Fr, class T>
173constexpr std::optional<affine_element<Fq, Fr, T>> affine_element<Fq, Fr, T>::derive_from_x_coordinate(
174 const Fq& x, bool sign_bit) noexcept
175{
176 auto yy = x.sqr() * x + T::b;
177 if constexpr (T::has_a) {
178 yy += (x * T::a);
179 }
180 auto [found_root, y] = yy.sqrt();
181
182 if (found_root) {
183 if (uint256_t(y).get_bit(0) != sign_bit) {
184 y = -y;
185 }
186 return affine_element(x, y);
187 }
188 return std::nullopt;
189}
190
223template <class Fq, class Fr, class T>
225 uint8_t attempt_count) noexcept
227
228{
229 std::vector<uint8_t> target_seed(seed);
230 // expand by 2 bytes to cover incremental hash attempts
231 const size_t seed_size = seed.size();
232 for (size_t i = 0; i < 2; ++i) {
233 target_seed.push_back(0);
234 }
235 target_seed[seed_size] = attempt_count;
236 target_seed[seed_size + 1] = 0;
237 const auto hash_hi = blake3::blake3s_constexpr(&target_seed[0], target_seed.size());
238 target_seed[seed_size + 1] = 1;
239 const auto hash_lo = blake3::blake3s_constexpr(&target_seed[0], target_seed.size());
240 // custom serialize methods as common/serialize.hpp is not constexpr!
241 const auto read_uint256 = [](const uint8_t* in) {
242 const auto read_limb = [](const uint8_t* in, uint64_t& out) {
243 for (size_t i = 0; i < 8; ++i) {
244 out += static_cast<uint64_t>(in[i]) << ((7 - i) * 8);
245 }
246 };
247 uint256_t out = 0;
248 read_limb(&in[0], out.data[3]);
249 read_limb(&in[8], out.data[2]);
250 read_limb(&in[16], out.data[1]);
251 read_limb(&in[24], out.data[0]);
252 return out;
253 };
254 // interpret 64 byte hash output as a uint512_t, reduce to Fq element
255 //(512 bits of entropy ensures result is not biased as 512 >> Fq::modulus.get_msb())
256 Fq x(uint512_t(read_uint256(&hash_lo[0]), read_uint256(&hash_hi[0])));
257 bool sign_bit = hash_hi[0] > 127;
258 std::optional<affine_element> result = derive_from_x_coordinate(x, sign_bit);
259 if (result.has_value()) {
260 return result.value();
261 }
262 return hash_to_curve(seed, attempt_count + 1);
263}
264
265template <typename Fq, typename Fr, typename T>
267{
268 if (engine == nullptr) {
269 engine = &numeric::random::get_engine();
270 }
271
272 Fq x;
273 Fq y;
274 while (true) {
275 // Sample a random x-coordinate and check if it satisfies curve equation.
276 x = Fq::random_element(engine);
277 // Negate the y-coordinate based on a randomly sampled bit.
278 bool sign_bit = (engine->get_random_uint8() & 1) != 0;
279
280 std::optional<affine_element> result = derive_from_x_coordinate(x, sign_bit);
281
282 if (result.has_value()) {
283 return result.value();
284 }
285 }
286 throw_or_abort("affine_element::random_element error");
287 return affine_element<Fq, Fr, T>(x, y);
288}
289
290} // namespace barretenberg::group_elements
Definition: affine_element.hpp:11
static constexpr affine_element hash_to_curve(const std::vector< uint8_t > &seed, uint8_t attempt_count=0) noexcept
Hash a seed buffer into a point.
Definition: affine_element_impl.hpp:224
static constexpr affine_element from_compressed(const uint256_t &compressed) noexcept
Reconstruct a point in affine coordinates from compressed form.
constexpr bool operator>(const affine_element &other) const noexcept
Definition: affine_element_impl.hpp:153
static affine_element random_element(numeric::random::Engine *engine=nullptr) noexcept
Samples a random point on the curve.
Definition: affine_element_impl.hpp:266
static constexpr std::array< affine_element, 2 > from_compressed_unsafe(const uint256_t &compressed) noexcept
Reconstruct a point in affine coordinates from compressed form.
Definition: engine.hpp:10
Definition: uint256.hpp:25
Definition: affine_element.hpp:10
BBERG_INLINE constexpr field sqr() const noexcept
Definition: field_impl.hpp:61
constexpr std::pair< bool, field > sqrt() const noexcept
Compute square root of the field element.
Definition: field_impl.hpp:507