Line data Source code
1 : #include "./fd_bn254_internal.h"
2 :
3 : #include "./fd_bn254_field.c"
4 : #include "./fd_bn254_field_ext.c"
5 : #include "./fd_bn254_glv.h"
6 : #include "./fd_bn254_g1.c"
7 : #include "./fd_bn254_g2.c"
8 : #include "./fd_bn254_pairing.c"
9 :
10 : /* Compress/Decompress */
11 :
12 : uchar *
13 : fd_bn254_g1_compress( uchar out[32],
14 : uchar const in [64],
15 30099 : int big_endian ) {
16 30099 : fd_bn254_g1_t p[1] = { 0 };
17 30099 : if( FD_UNLIKELY( !fd_bn254_g1_frombytes_internal( p, in, big_endian ) ) ) {
18 0 : return NULL;
19 0 : }
20 30099 : int is_inf = fd_bn254_g1_is_zero( p );
21 30099 : int flag_inf = in[ big_endian ? 32 : 63 ] & FLAG_INF;
22 :
23 : /* Serialize compressed point:
24 : https://github.com/arkworks-rs/algebra/blob/v0.4.2/ec/src/models/short_weierstrass/mod.rs#L122
25 :
26 : 1. If the infinity flags is set, return point at infinity
27 : 2. Else, copy x and set neg_y flag */
28 :
29 30099 : if( FD_UNLIKELY( is_inf ) ) {
30 6 : fd_memset( out, 0, 32 );
31 : /* The infinity flag in the result is set iff the infinity flag is set in the Y coordinate */
32 6 : out[ big_endian ? 0 : 31 ] |= (uchar)flag_inf;
33 6 : return out;
34 6 : }
35 :
36 30093 : int is_neg = fd_bn254_fp_is_neg_nm( &p->Y );
37 30093 : fd_bn254_fp_tobytes_nm( out, &p->X, big_endian );
38 30093 : if( is_neg ) {
39 30024 : out[ big_endian ? 0 : 31 ] |= FLAG_NEG;
40 30024 : }
41 30093 : return out;
42 30099 : }
43 :
44 : uchar *
45 : fd_bn254_g1_decompress( uchar out[64],
46 : uchar const in [32],
47 30096 : int big_endian ) {
48 : /* Special case: all zeros in => all zeros out, no flags */
49 30096 : const uchar zero[32] = { 0 };
50 30096 : if( fd_memeq( in, zero, 32 ) ) {
51 3 : return fd_memset( out, 0, 64UL );
52 3 : }
53 :
54 30093 : fd_bn254_fp_t x_nm[1], x[1], x2[1], x3_plus_b[1], y[1];
55 30093 : int is_inf, is_neg;
56 30093 : if( FD_UNLIKELY( !fd_bn254_fp_frombytes_nm( x_nm, in, big_endian, &is_inf, &is_neg ) ) ) {
57 0 : return NULL;
58 0 : }
59 :
60 : /* Point at infinity.
61 : If the point at infinity flag is set (bit 6), return the point at
62 : infinity with no check on coords.
63 : https://github.com/arkworks-rs/algebra/blob/v0.4.2/ec/src/models/short_weierstrass/mod.rs#L156-L160
64 : */
65 30093 : if( is_inf ) {
66 0 : fd_memset( out, 0, 64UL );
67 : /* no flags */
68 0 : return out;
69 0 : }
70 :
71 30093 : fd_bn254_fp_to_mont( x, x_nm );
72 30093 : fd_bn254_fp_sqr( x2, x );
73 30093 : fd_bn254_fp_mul( x3_plus_b, x2, x );
74 30093 : fd_bn254_fp_add( x3_plus_b, x3_plus_b, fd_bn254_const_b_mont );
75 30093 : if( FD_UNLIKELY( !fd_bn254_fp_sqrt( y, x3_plus_b ) ) ) {
76 0 : return NULL;
77 0 : }
78 :
79 30093 : fd_bn254_fp_from_mont( y, y );
80 30093 : if( is_neg != fd_bn254_fp_is_neg_nm( y ) ) {
81 9 : fd_bn254_fp_neg_nm( y, y );
82 9 : }
83 :
84 30093 : fd_bn254_fp_tobytes_nm( out, x_nm, big_endian );
85 30093 : fd_bn254_fp_tobytes_nm( &out[32], y, big_endian );
86 : /* no flags */
87 30093 : return out;
88 30093 : }
89 :
90 : uchar *
91 : fd_bn254_g2_compress( uchar out[64],
92 : uchar const in[128],
93 30102 : int big_endian ) {
94 30102 : fd_bn254_g2_t p[1] = { 0 };
95 30102 : if( FD_UNLIKELY( !fd_bn254_g2_frombytes_internal( p, in, big_endian ) ) ) {
96 0 : return NULL;
97 0 : }
98 30102 : int is_inf = fd_bn254_g2_is_zero( p );
99 30102 : int flag_inf = in[ big_endian ? 64 : 127 ] & FLAG_INF;
100 :
101 : /* Serialize compressed point */
102 :
103 30102 : if( FD_UNLIKELY( is_inf ) ) {
104 9 : fd_memset( out, 0, 64 );
105 : /* The infinity flag in the result is set iff the infinity flag is set in the Y coordinate */
106 9 : out[ big_endian ? 0 : 63 ] |= (uchar)flag_inf;
107 9 : return out;
108 9 : }
109 :
110 : /* Serialize x coordinate. The flags are on the 2nd element.
111 : https://github.com/arkworks-rs/algebra/blob/v0.4.2/ff/src/fields/models/quadratic_extension.rs#L700-L702 */
112 30093 : int is_neg = fd_bn254_fp2_is_neg_nm( &p->Y );
113 30093 : fd_bn254_fp2_tobytes_nm( out, &p->X, big_endian );
114 30093 : if( is_neg ) {
115 30042 : out[ big_endian ? 0 : 63 ] |= FLAG_NEG;
116 30042 : }
117 30093 : return out;
118 30102 : }
119 :
120 : uchar *
121 : fd_bn254_g2_decompress( uchar out[128],
122 : uchar const in [64],
123 3099 : int big_endian ) {
124 : /* Special case: all zeros in => all zeros out, no flags */
125 3099 : const uchar zero[64] = { 0 };
126 3099 : if( fd_memeq( in, zero, 64 ) ) {
127 3 : return fd_memset( out, 0, 128UL );
128 3 : }
129 :
130 3096 : fd_bn254_fp2_t x_nm[1], x[1], x2[1], x3_plus_b[1], y[1];
131 3096 : int is_inf, is_neg;
132 3096 : if( FD_UNLIKELY( !fd_bn254_fp2_frombytes_nm( x_nm, in, big_endian, &is_inf, &is_neg ) ) ) {
133 0 : return NULL;
134 0 : }
135 :
136 : /* Point at infinity.
137 : If the point at infinity flag is set (bit 6), return the point at
138 : infinity with no check on coords.
139 : https://github.com/arkworks-rs/algebra/blob/v0.4.2/ec/src/models/short_weierstrass/mod.rs#L156-L160 */
140 3096 : if( is_inf ) {
141 3 : fd_memset( out, 0, 128UL );
142 : /* no flags */
143 3 : return out;
144 3 : }
145 :
146 3093 : fd_bn254_fp2_to_mont( x, x_nm );
147 3093 : fd_bn254_fp2_sqr( x2, x );
148 3093 : fd_bn254_fp2_mul( x3_plus_b, x2, x );
149 3093 : fd_bn254_fp2_add( x3_plus_b, x3_plus_b, fd_bn254_const_twist_b_mont );
150 3093 : if( FD_UNLIKELY( !fd_bn254_fp2_sqrt( y, x3_plus_b ) ) ) {
151 0 : return NULL;
152 0 : }
153 :
154 3093 : fd_bn254_fp2_from_mont( y, y );
155 3093 : if( is_neg != fd_bn254_fp2_is_neg_nm( y ) ) {
156 3060 : fd_bn254_fp2_neg_nm( y, y );
157 3060 : }
158 :
159 3093 : fd_bn254_fp2_tobytes_nm( out, x_nm, big_endian );
160 3093 : fd_bn254_fp2_tobytes_nm( &out[64], y, big_endian );
161 : /* no flags */
162 3093 : return out;
163 3093 : }
164 :
165 : /* Ops */
166 :
167 : int
168 : fd_bn254_g1_add_syscall( uchar out[64],
169 : uchar const in[],
170 : ulong in_sz,
171 30069 : int big_endian ) {
172 : /* Expected 128-byte input (2 points). Pad input with 0s (only big endian). */
173 30069 : if( FD_UNLIKELY( in_sz > 128UL ) ) {
174 0 : return -1;
175 0 : }
176 30069 : if( FD_UNLIKELY( !big_endian && in_sz != 128UL ) ) {
177 12 : return -1;
178 12 : }
179 30057 : uchar FD_ALIGNED buf[128] = { 0 };
180 30057 : fd_memcpy( buf, in, in_sz );
181 :
182 : /* Validate inputs */
183 30057 : fd_bn254_g1_t r[1], a[1], b[1];
184 30057 : if( FD_UNLIKELY( !fd_bn254_g1_frombytes_check_subgroup( a, &buf[ 0], big_endian ) ) ) {
185 0 : return -1;
186 0 : }
187 30057 : if( FD_UNLIKELY( !fd_bn254_g1_frombytes_check_subgroup( b, &buf[64], big_endian ) ) ) {
188 0 : return -1;
189 0 : }
190 :
191 : /* Compute point add and serialize result */
192 30057 : fd_bn254_g1_affine_add( r, a, b );
193 30057 : fd_bn254_g1_tobytes( out, r, big_endian );
194 30057 : return 0;
195 30057 : }
196 :
197 : int
198 : fd_bn254_g2_add_syscall( uchar out[128],
199 : uchar const in[],
200 : ulong in_sz,
201 30030 : int big_endian ) {
202 : /* Expected 256-byte input (2 points).
203 : https://github.com/anza-xyz/solana-sdk/blob/bn254%40v3.2.1/bn254/src/addition.rs#L234-L236 */
204 30030 : if( FD_UNLIKELY( in_sz != 256UL ) ) {
205 0 : return -1;
206 0 : }
207 30030 : uchar FD_ALIGNED buf[256] = { 0 };
208 30030 : fd_memcpy( buf, in, in_sz );
209 :
210 : /* Validate inputs (curve eq only, no subgroup)
211 : https://github.com/anza-xyz/solana-sdk/blob/bn254%40v3.2.1/bn254/src/addition.rs#L238-L250 */
212 30030 : fd_bn254_g2_t r[1], a[1], b[1];
213 30030 : if( FD_UNLIKELY( !fd_bn254_g2_frombytes_check_eq_only( a, &buf[ 0], big_endian ) ) ) {
214 0 : return -1;
215 0 : }
216 30030 : if( FD_UNLIKELY( !fd_bn254_g2_frombytes_check_eq_only( b, &buf[128], big_endian ) ) ) {
217 0 : return -1;
218 0 : }
219 :
220 : /* Compute point add and serialize result */
221 30030 : fd_bn254_g2_affine_add( r, a, b );
222 30030 : fd_bn254_g2_tobytes( out, r, big_endian );
223 30030 : return 0;
224 30030 : }
225 :
226 : int
227 : fd_bn254_g1_scalar_mul_syscall( uchar out[64],
228 : uchar const in[],
229 : ulong in_sz,
230 30132 : int big_endian ) {
231 : /* Expected 96-byte input (1 point + 1 scalar). Pad input with 0s (only big endian). */
232 30132 : if( FD_UNLIKELY( in_sz > 96UL ) ) {
233 0 : return -1;
234 0 : }
235 30132 : if( FD_UNLIKELY( !big_endian && in_sz != 96UL ) ) {
236 6 : return -1;
237 6 : }
238 30126 : uchar FD_ALIGNED buf[96] = { 0 };
239 30126 : fd_memcpy( buf, in, fd_ulong_min( in_sz, 96UL ) );
240 :
241 : /* Validate inputs */
242 30126 : fd_bn254_g1_t r[1], a[1];
243 30126 : fd_bn254_scalar_t s[1];
244 30126 : if( FD_UNLIKELY( !fd_bn254_g1_frombytes_check_subgroup( a, &buf[ 0], big_endian ) ) ) {
245 0 : return -1;
246 0 : }
247 :
248 : /* Scalar is big endian and NOT validated
249 : https://github.com/anza-xyz/agave/blob/v1.18.6/sdk/program/src/alt_bn128/mod.rs#L211-L214 */
250 30126 : if( FD_BIG_ENDIAN_LIKELY( big_endian ) ) {
251 63 : fd_uint256_bswap( s, fd_type_pun_const( &buf[64] ) ); /* &buf[64] is always FD_ALIGNED */
252 30063 : } else {
253 30063 : memcpy( s, &buf[64], 32 );
254 30063 : }
255 : // no: if( FD_UNLIKELY( !fd_bn254_scalar_validate( s ) ) ) return -1;
256 :
257 : /* Compute scalar mul and serialize result */
258 30126 : fd_bn254_g1_scalar_mul( r, a, s );
259 30126 : fd_bn254_g1_tobytes( out, r, big_endian );
260 30126 : return 0;
261 30126 : }
262 :
263 : int
264 : fd_bn254_g2_scalar_mul_syscall( uchar out[128],
265 : uchar const in[],
266 : ulong in_sz,
267 330 : int big_endian ) {
268 : /* Expected 160-byte input (1 point + 1 scalar).
269 : https://github.com/anza-xyz/solana-sdk/blob/bn254%40v3.2.1/bn254/src/multiplication.rs#L248-L250 */
270 330 : if( FD_UNLIKELY( in_sz != 160UL ) ) {
271 0 : return -1;
272 0 : }
273 330 : uchar FD_ALIGNED buf[160] = { 0 };
274 330 : fd_memcpy( buf, in, 160UL );
275 :
276 : /* Validate point (curve equation and subgroup membership)
277 : https://github.com/anza-xyz/solana-sdk/blob/bn254%40v3.2.1/bn254/src/multiplication.rs#L252-L255 */
278 330 : fd_bn254_g2_t r[1], a[1];
279 330 : fd_bn254_scalar_t s[1];
280 330 : if( FD_UNLIKELY( !fd_bn254_g2_frombytes_check_subgroup( a, &buf[ 0], big_endian ) ) ) {
281 0 : return -1;
282 0 : }
283 :
284 : /* Scalar is little endian and NOT validated
285 : https://github.com/anza-xyz/solana-sdk/blob/bn254%40v3.2.1/bn254/src/multiplication.rs#L256-L272 */
286 330 : if( FD_BIG_ENDIAN_LIKELY( big_endian ) ) {
287 15 : fd_uint256_bswap( s, fd_type_pun_const( &buf[128] ) ); /* &buf[128] is always FD_ALIGNED */
288 315 : } else {
289 315 : memcpy( s, &buf[128], 32 );
290 315 : }
291 : // no: if( FD_UNLIKELY( !fd_bn254_scalar_validate( s ) ) ) return -1;
292 :
293 : /* Compute scalar mul and serialize result */
294 330 : fd_bn254_g2_scalar_mul( r, a, s );
295 330 : fd_bn254_g2_tobytes( out, r, big_endian );
296 330 : return 0;
297 330 : }
298 :
299 : int
300 : fd_bn254_pairing_is_one_syscall( uchar out[32],
301 : uchar const in[],
302 : ulong in_sz,
303 : int big_endian,
304 387 : int check_len ) {
305 : /* https://github.com/anza-xyz/agave/blob/v1.18.6/sdk/program/src/alt_bn128/mod.rs#L244
306 : Note: Solana had a bug where it checked if input.len().checked_rem(192).is_none(),
307 : which only fails when dividing by zero, so the check never triggered.
308 : When check_len is true, we properly validate that input size is a multiple of 192.
309 : This corresponds to the fix_alt_bn128_pairing_length_check feature gate. */
310 387 : if( check_len ) {
311 345 : if( FD_UNLIKELY( (in_sz % 192UL) != 0 ) ) {
312 0 : return -1; /* Invalid input length */
313 0 : }
314 345 : }
315 387 : ulong elements_len = in_sz / 192UL;
316 387 : fd_bn254_g1_t p[FD_BN254_PAIRING_BATCH_MAX];
317 387 : fd_bn254_g2_t q[FD_BN254_PAIRING_BATCH_MAX];
318 :
319 : /* Important: set r=1 so that the result of 0 pairings is 1. */
320 387 : fd_bn254_fp12_t r[1];
321 387 : fd_bn254_fp12_set_one( r );
322 :
323 387 : ulong sz=0;
324 1263 : for( ulong i=0; i<elements_len; i++ ) {
325 : /* G1: deserialize and check subgroup membership */
326 876 : if( FD_UNLIKELY( !fd_bn254_g1_frombytes_check_subgroup( &p[sz], &in[i*192 ], big_endian ) ) ) {
327 0 : return -1;
328 0 : }
329 : /* G2: deserialize and check subgroup membership */
330 876 : if( FD_UNLIKELY( !fd_bn254_g2_frombytes_check_subgroup( &q[sz], &in[i*192+64], big_endian ) ) ) {
331 0 : return -1;
332 0 : }
333 : /* Skip any pair where either P or Q is the point at infinity */
334 876 : if( FD_UNLIKELY( fd_bn254_g1_is_zero(&p[sz]) || fd_bn254_g2_is_zero(&q[sz]) ) ) {
335 0 : continue;
336 0 : }
337 876 : ++sz;
338 : /* Compute the Miller loop and aggregate into r */
339 876 : if( sz==FD_BN254_PAIRING_BATCH_MAX ) {
340 0 : fd_bn254_fp12_t tmp[1];
341 0 : fd_bn254_miller_loop( tmp, p, q, sz );
342 0 : fd_bn254_fp12_mul( r, r, tmp );
343 0 : sz = 0;
344 0 : }
345 876 : }
346 387 : if( sz>0 ) {
347 387 : fd_bn254_fp12_t tmp[1];
348 387 : fd_bn254_miller_loop( tmp, p, q, sz );
349 387 : fd_bn254_fp12_mul( r, r, tmp );
350 387 : sz = 0;
351 387 : }
352 :
353 : /* Compute the final exponentiation */
354 387 : fd_bn254_final_exp( r, r );
355 :
356 : /* Output is 0 or 1, serialized as big endian uint256. */
357 387 : fd_memset( out, 0, 32 );
358 387 : if( FD_LIKELY( fd_bn254_fp12_is_one( r ) ) ) {
359 375 : out[ big_endian ? 31 : 0 ] = 1;
360 375 : }
361 387 : return 0;
362 387 : }
|