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