Line data Source code
1 : #include "fd_bls12_381.h"
2 : #include "../bigint/fd_uint256.h"
3 :
4 : #include <blst.h>
5 :
6 : /* Scalar */
7 :
8 : typedef blst_scalar fd_bls12_381_scalar_t;
9 :
10 : static inline fd_bls12_381_scalar_t *
11 : fd_bls12_381_scalar_frombytes( fd_bls12_381_scalar_t * n,
12 : uchar const in[ 32 ],
13 660 : int big_endian ) {
14 : /* https://github.com/filecoin-project/blstrs/blob/v0.7.1/src/scalar.rs#L551-L569 */
15 660 : if( big_endian ) {
16 30 : blst_scalar_from_bendian( n, in );
17 630 : } else {
18 630 : blst_scalar_from_lendian( n, in );
19 630 : }
20 660 : if( FD_UNLIKELY( !blst_scalar_fr_check( n ) ) ) {
21 0 : return NULL;
22 0 : }
23 660 : return n;
24 660 : }
25 :
26 : /* G1 serde */
27 :
28 : typedef blst_p1_affine fd_bls12_381_g1aff_t;
29 : typedef blst_p1 fd_bls12_381_g1_t;
30 :
31 : static inline void
32 : fd_bls12_381_g1_bswap( uchar out[ 96 ], /* out can be in */
33 187380 : uchar const in [ 96 ] ) {
34 : /* copy into aligned memory */
35 187380 : ulong e[ 96/sizeof(ulong) ];
36 187380 : memcpy( e, in, 96 );
37 :
38 : /* bswap X, Y independently (48 bytes each) */
39 187380 : fd_ulong_n_bswap( e+0, 6 );
40 187380 : fd_ulong_n_bswap( e+6, 6 );
41 :
42 : /* copy to out */
43 187380 : memcpy( out, e, 96 );
44 187380 : }
45 :
46 : static inline uchar *
47 : fd_bls12_381_g1_tobytes( uchar out[ 96 ],
48 : fd_bls12_381_g1_t const * a,
49 60393 : int big_endian ) {
50 60393 : blst_p1_serialize( out, a );
51 60393 : if( !big_endian ) {
52 60348 : fd_bls12_381_g1_bswap( out, out );
53 60348 : }
54 60393 : return out;
55 60393 : }
56 :
57 : static inline fd_bls12_381_g1aff_t *
58 : fd_bls12_381_g1_frombytes_unchecked( fd_bls12_381_g1aff_t * r,
59 : uchar const _in[ 96 ],
60 124137 : int big_endian ) {
61 124137 : ulong be[ 96/sizeof(ulong) ];
62 124137 : uchar const * in = _in;
63 124137 : if( !big_endian ) {
64 124020 : fd_bls12_381_g1_bswap( (uchar *)be, _in );
65 124020 : in = (uchar *)be;
66 124020 : }
67 :
68 : /* Reject the point if the compressed or parity flag is set.
69 : https://github.com/anza-xyz/agave/blob/v4.0.0-beta.2/bls12-381/src/encoding.rs#L57-L60 */
70 124137 : if( FD_UNLIKELY( in[ 0 ] & 0xA0 ) ) {
71 3 : return NULL;
72 3 : }
73 :
74 124134 : if( FD_UNLIKELY( blst_p1_deserialize( r, in )!=BLST_SUCCESS ) ) {
75 12 : return NULL;
76 12 : }
77 124122 : return r;
78 124134 : }
79 :
80 : static inline fd_bls12_381_g1aff_t *
81 : fd_bls12_381_g1_frombytes( fd_bls12_381_g1aff_t * r,
82 : uchar const in[ 96 ],
83 4011 : int big_endian ) {
84 4011 : if( FD_UNLIKELY( !fd_bls12_381_g1_frombytes_unchecked( r, in, big_endian ) ) ) {
85 15 : return NULL;
86 15 : }
87 3996 : if( FD_UNLIKELY( !blst_p1_affine_in_g1( r ) ) ) {
88 0 : return NULL;
89 0 : }
90 3996 : return r;
91 3996 : }
92 :
93 : /* G1 syscalls */
94 :
95 : int
96 : fd_bls12_381_g1_decompress_syscall( uchar _r[ 96 ],
97 : uchar const _a[ 48 ],
98 3033 : int big_endian ) {
99 : /* blst expects input in big endian. if little endian, bswap. */
100 3033 : ulong be[ 48/sizeof(ulong) ];
101 3033 : uchar const * in = _a;
102 3033 : if( !big_endian ) {
103 3018 : in = (uchar *)be;
104 3018 : memcpy( be, _a, 48 );
105 3018 : fd_ulong_n_bswap( be, 6 );
106 3018 : }
107 :
108 : /* decompress and serialize */
109 3033 : fd_bls12_381_g1aff_t r[1];
110 3033 : if( FD_UNLIKELY( blst_p1_uncompress( r, in )!=BLST_SUCCESS ) ) {
111 6 : return -1;
112 6 : }
113 3027 : if( FD_UNLIKELY( !blst_p1_affine_in_g1( r ) ) ) {
114 6 : return -1;
115 6 : }
116 3021 : blst_p1_affine_serialize( _r, r );
117 :
118 : /* blst output is big endian. if we want little endian, bswap. */
119 3021 : if( !big_endian ) {
120 3012 : fd_bls12_381_g1_bswap( _r, _r );
121 3012 : }
122 3021 : return 0;
123 3027 : }
124 :
125 : int
126 : fd_bls12_381_g1_validate_syscall( uchar const _a[ 96 ],
127 3033 : int big_endian ) {
128 3033 : fd_bls12_381_g1aff_t a[1];
129 3033 : return !!fd_bls12_381_g1_frombytes( a, _a, big_endian );
130 3033 : }
131 :
132 : int
133 : fd_bls12_381_g1_add_syscall( uchar _r[ 96 ],
134 : uchar const _a[ 96 ],
135 : uchar const _b[ 96 ],
136 30033 : int big_endian ) {
137 : /* points a, b are unchecked per SIMD-0388 */
138 30033 : fd_bls12_381_g1aff_t a[1], b[1];
139 30033 : if( FD_UNLIKELY( fd_bls12_381_g1_frombytes_unchecked( a, _a, big_endian )==NULL ) ) {
140 0 : return -1;
141 0 : }
142 30033 : if( FD_UNLIKELY( fd_bls12_381_g1_frombytes_unchecked( b, _b, big_endian )==NULL ) ) {
143 0 : return -1;
144 0 : }
145 :
146 30033 : fd_bls12_381_g1_t r[1], p[1];
147 30033 : blst_p1_from_affine( p, a );
148 30033 : blst_p1_add_or_double_affine( r, p, b );
149 :
150 30033 : fd_bls12_381_g1_tobytes( _r, r, big_endian );
151 30033 : return 0;
152 30033 : }
153 :
154 : int
155 : fd_bls12_381_g1_sub_syscall( uchar _r[ 96 ],
156 : uchar const _a[ 96 ],
157 : uchar const _b[ 96 ],
158 30030 : int big_endian ) {
159 : /* points a, b are unchecked per SIMD-0388 */
160 30030 : fd_bls12_381_g1aff_t a[1], b[1];
161 30030 : if( FD_UNLIKELY( fd_bls12_381_g1_frombytes_unchecked( a, _a, big_endian )==NULL ) ) {
162 0 : return -1;
163 0 : }
164 30030 : if( FD_UNLIKELY( fd_bls12_381_g1_frombytes_unchecked( b, _b, big_endian )==NULL ) ) {
165 0 : return -1;
166 0 : }
167 :
168 30030 : fd_bls12_381_g1_t r[1], p[1];
169 30030 : blst_p1_from_affine( p, a );
170 30030 : blst_fp_cneg( &b->y, &b->y, 1 ); /* -b, it works also with b=0 */
171 30030 : blst_p1_add_or_double_affine( r, p, b );
172 :
173 30030 : fd_bls12_381_g1_tobytes( _r, r, big_endian );
174 30030 : return 0;
175 30030 : }
176 :
177 : int
178 : fd_bls12_381_g1_mul_syscall( uchar _r[ 96 ],
179 : uchar const _n[ 32 ],
180 : uchar const _a[ 96 ],
181 330 : int big_endian ) {
182 : /* point a, scalar n are validated per SIMD-0388 */
183 330 : fd_bls12_381_g1aff_t a[1];
184 330 : fd_bls12_381_scalar_t n[1];
185 330 : if( FD_UNLIKELY( fd_bls12_381_g1_frombytes( a, _a, big_endian )==NULL ) ) {
186 0 : return -1;
187 0 : }
188 330 : if( FD_UNLIKELY( fd_bls12_381_scalar_frombytes( n, _n, big_endian )==NULL ) ) {
189 0 : return -1;
190 0 : }
191 :
192 330 : fd_bls12_381_g1_t r[1], p[1];
193 330 : blst_p1_from_affine( p, a );
194 : /* https://github.com/filecoin-project/blstrs/blob/v0.7.1/src/g1.rs#L578-L580 */
195 330 : blst_p1_mult( r, p, n->b, 255 );
196 :
197 330 : fd_bls12_381_g1_tobytes( _r, r, big_endian );
198 330 : return 0;
199 330 : }
200 :
201 : /* G2 serde */
202 :
203 : typedef blst_p2_affine fd_bls12_381_g2aff_t;
204 : typedef blst_p2 fd_bls12_381_g2_t;
205 :
206 : static inline void
207 : fd_bls12_381_g2_bswap( uchar out[ 96*2 ], /* out can be in */
208 187368 : uchar const in [ 96*2 ] ) {
209 : /* copy into aligned memory */
210 187368 : ulong e[ 96*2/sizeof(ulong) ];
211 187368 : memcpy( e, in, 96*2 );
212 :
213 : /* bswap X, Y independently (96 bytes each) */
214 187368 : fd_ulong_n_bswap( e+00, 12 );
215 187368 : fd_ulong_n_bswap( e+12, 12 );
216 :
217 : /* copy to out */
218 187368 : memcpy( out, e, 96*2 );
219 187368 : }
220 :
221 : static inline uchar *
222 : fd_bls12_381_g2_tobytes( uchar out[ 96*2 ],
223 : fd_bls12_381_g2_t const * a,
224 60390 : int big_endian ) {
225 60390 : blst_p2_serialize( out, a );
226 60390 : if( !big_endian ) {
227 60345 : fd_bls12_381_g2_bswap( out, out );
228 60345 : }
229 60390 : return out;
230 60390 : }
231 :
232 : static inline fd_bls12_381_g2aff_t *
233 : fd_bls12_381_g2_frombytes_unchecked( fd_bls12_381_g2aff_t * r,
234 : uchar const _in[ 96*2 ],
235 124125 : int big_endian ) {
236 124125 : ulong be[ 96*2/sizeof(ulong) ];
237 124125 : uchar const * in = _in;
238 124125 : if( !big_endian ) {
239 124011 : fd_bls12_381_g2_bswap( (uchar *)be, _in );
240 124011 : in = (uchar *)be;
241 124011 : }
242 :
243 : /* Reject the point if the compressed or parity flag is set.
244 : https://github.com/anza-xyz/agave/blob/v4.0.0-beta.2/bls12-381/src/encoding.rs#L103-L106 */
245 124125 : if( FD_UNLIKELY( in[ 0 ] & 0xA0 ) ) {
246 3 : return NULL;
247 3 : }
248 :
249 124122 : if( FD_UNLIKELY( blst_p2_deserialize( r, in )!=BLST_SUCCESS ) ) {
250 12 : return NULL;
251 12 : }
252 124110 : return r;
253 124122 : }
254 :
255 : static inline fd_bls12_381_g2aff_t *
256 : fd_bls12_381_g2_frombytes( fd_bls12_381_g2aff_t * r,
257 : uchar const in[ 96*2 ],
258 4005 : int big_endian ) {
259 4005 : if( FD_UNLIKELY( !fd_bls12_381_g2_frombytes_unchecked( r, in, big_endian ) ) ) {
260 15 : return NULL;
261 15 : }
262 3990 : if( FD_UNLIKELY( !blst_p2_affine_in_g2( r ) ) ) {
263 0 : return NULL;
264 0 : }
265 3990 : return r;
266 3990 : }
267 :
268 : /* G2 syscalls */
269 :
270 : int
271 : fd_bls12_381_g2_decompress_syscall( uchar _r[ 96*2 ],
272 : uchar const _a[ 48*2 ],
273 3033 : int big_endian ) {
274 : /* blst expects input in big endian. if little endian, bswap. */
275 3033 : ulong be[ 48*2/sizeof(ulong) ];
276 3033 : uchar const * in = _a;
277 3033 : if( !big_endian ) {
278 3018 : in = (uchar *)be;
279 3018 : memcpy( be, _a, 48*2 );
280 3018 : fd_ulong_n_bswap( be, 6*2 );
281 3018 : }
282 :
283 : /* decompress and serialize */
284 3033 : fd_bls12_381_g2aff_t r[1];
285 3033 : if( FD_UNLIKELY( blst_p2_uncompress( r, in )!=BLST_SUCCESS ) ) {
286 6 : return -1;
287 6 : }
288 3027 : if( FD_UNLIKELY( !blst_p2_affine_in_g2( r ) ) ) {
289 6 : return -1;
290 6 : }
291 3021 : blst_p2_affine_serialize( _r, r );
292 :
293 : /* blst output is big endian. if we want little endian, bswap. */
294 3021 : if( !big_endian ) {
295 3012 : fd_bls12_381_g2_bswap( _r, _r );
296 3012 : }
297 3021 : return 0;
298 3027 : }
299 :
300 : int
301 : fd_bls12_381_g2_validate_syscall( uchar const _a[ 96*2 ],
302 3027 : int big_endian ) {
303 3027 : fd_bls12_381_g2aff_t a[1];
304 3027 : return !!fd_bls12_381_g2_frombytes( a, _a, big_endian );
305 3027 : }
306 :
307 : int
308 : fd_bls12_381_g2_add_syscall( uchar _r[ 96*2 ],
309 : uchar const _a[ 96*2 ],
310 : uchar const _b[ 96*2 ],
311 30030 : int big_endian ) {
312 : /* points a, b are unchecked per SIMD-0388 */
313 30030 : fd_bls12_381_g2aff_t a[1], b[1];
314 30030 : if( FD_UNLIKELY( fd_bls12_381_g2_frombytes_unchecked( a, _a, big_endian )==NULL ) ) {
315 0 : return -1;
316 0 : }
317 30030 : if( FD_UNLIKELY( fd_bls12_381_g2_frombytes_unchecked( b, _b, big_endian )==NULL ) ) {
318 0 : return -1;
319 0 : }
320 :
321 30030 : fd_bls12_381_g2_t r[1], p[1];
322 30030 : blst_p2_from_affine( p, a );
323 30030 : blst_p2_add_or_double_affine( r, p, b );
324 :
325 30030 : fd_bls12_381_g2_tobytes( _r, r, big_endian );
326 30030 : return 0;
327 30030 : }
328 :
329 : int
330 : fd_bls12_381_g2_sub_syscall( uchar _r[ 96*2 ],
331 : uchar const _a[ 96*2 ],
332 : uchar const _b[ 96*2 ],
333 30030 : int big_endian ) {
334 : /* points a, b are unchecked per SIMD-0388 */
335 30030 : fd_bls12_381_g2aff_t a[1], b[1];
336 30030 : if( FD_UNLIKELY( fd_bls12_381_g2_frombytes_unchecked( a, _a, big_endian )==NULL ) ) {
337 0 : return -1;
338 0 : }
339 30030 : if( FD_UNLIKELY( fd_bls12_381_g2_frombytes_unchecked( b, _b, big_endian )==NULL ) ) {
340 0 : return -1;
341 0 : }
342 :
343 30030 : fd_bls12_381_g2_t r[1], p[1];
344 30030 : blst_p2_from_affine( p, a );
345 30030 : blst_fp2_cneg( &b->y, &b->y, 1 ); /* -b, it works also with b=0 */
346 30030 : blst_p2_add_or_double_affine( r, p, b );
347 :
348 30030 : fd_bls12_381_g2_tobytes( _r, r, big_endian );
349 30030 : return 0;
350 30030 : }
351 :
352 : int
353 : fd_bls12_381_g2_mul_syscall( uchar _r[ 96*2 ],
354 : uchar const _n[ 32 ],
355 : uchar const _a[ 96*2 ],
356 330 : int big_endian ) {
357 : /* point a, scalar n are validated per SIMD-0388 */
358 330 : fd_bls12_381_g2aff_t a[1];
359 330 : fd_bls12_381_scalar_t n[1];
360 330 : if( FD_UNLIKELY( fd_bls12_381_g2_frombytes( a, _a, big_endian )==NULL ) ) {
361 0 : return -1;
362 0 : }
363 330 : if( FD_UNLIKELY( fd_bls12_381_scalar_frombytes( n, _n, big_endian )==NULL ) ) {
364 0 : return -1;
365 0 : }
366 :
367 330 : fd_bls12_381_g2_t r[1], p[1];
368 330 : blst_p2_from_affine( p, a );
369 : /* https://github.com/filecoin-project/blstrs/blob/v0.7.1/src/g2.rs#L545-L547 */
370 330 : blst_p2_mult( r, p, n->b, 255 );
371 :
372 330 : fd_bls12_381_g2_tobytes( _r, r, big_endian );
373 330 : return 0;
374 330 : }
375 :
376 : int
377 : fd_bls12_381_pairing_syscall( uchar _r[ 48*12 ],
378 : uchar const _a[], /* 96*n */
379 : uchar const _b[], /* 96*2*n */
380 : ulong const _n,
381 330 : int big_endian ) {
382 :
383 330 : if( FD_UNLIKELY( _n>FD_BLS12_381_PAIRING_BATCH_SZ ) ) {
384 0 : return -1;
385 0 : }
386 :
387 330 : fd_bls12_381_g1aff_t a[ FD_BLS12_381_PAIRING_BATCH_SZ ];
388 330 : fd_bls12_381_g2aff_t b[ FD_BLS12_381_PAIRING_BATCH_SZ ];
389 330 : fd_bls12_381_g1aff_t const * aptr[ FD_BLS12_381_PAIRING_BATCH_SZ ];
390 330 : fd_bls12_381_g2aff_t const * bptr[ FD_BLS12_381_PAIRING_BATCH_SZ ];
391 978 : for( ulong j=0; j<_n; j++ ) {
392 648 : if( FD_UNLIKELY( fd_bls12_381_g1_frombytes( &a[ j ], _a+96*j, big_endian )==NULL ) ) {
393 0 : return -1;
394 0 : }
395 648 : if( FD_UNLIKELY( fd_bls12_381_g2_frombytes( &b[ j ], _b+96*2*j, big_endian )==NULL ) ) {
396 0 : return -1;
397 0 : }
398 : /* blst wants an array of pointers (not necessarily a compact array) */
399 648 : aptr[ j ] = &a[ j ];
400 648 : bptr[ j ] = &b[ j ];
401 648 : }
402 :
403 330 : blst_fp12 r[1];
404 330 : memcpy( r, blst_fp12_one(), sizeof(blst_fp12) );
405 :
406 330 : if( FD_LIKELY ( _n>0 ) ) {
407 324 : blst_miller_loop_n( r, bptr, aptr, _n );
408 324 : blst_final_exp( r, r );
409 324 : }
410 :
411 330 : if( big_endian ) {
412 195 : for( ulong j=0; j<12; j++ ) {
413 180 : blst_bendian_from_fp( _r+48*(12-1-j), &r[ 0 ].fp6[ j/6 ].fp2[ (j/2)%3 ].fp[ j%2 ] );
414 180 : }
415 315 : } else {
416 4095 : for( ulong j=0; j<12; j++ ) {
417 3780 : blst_lendian_from_fp( _r+48*j, &r[ 0 ].fp6[ j/6 ].fp2[ (j/2)%3 ].fp[ j%2 ] );
418 3780 : }
419 315 : }
420 :
421 330 : return 0;
422 330 : }
423 :
424 : /* Proof of possession */
425 :
426 : #define FD_BLS_SIG_DOMAIN_NUL "BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_NUL_"
427 315 : #define FD_BLS_SIG_DOMAIN_POP "BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_"
428 312 : #define FD_BLS_SIG_DOMAIN_SZ (43UL)
429 :
430 : /* fd_bls12_381_core_verify verifies a BLS signature in the mathematical
431 : sense, i.e. computes a pairing to check that the signature is correct.
432 : This is the core computation both for "real world" signatures and proofs
433 : of possession. In both cases, the difference between the math paper and
434 : the RFC implementation is an additional domain separator that's used
435 : in computing the hash to G2.
436 :
437 : See also:
438 : https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-bls-signature-06#name-coreverify
439 :
440 : We use a1, a2 for points in G1, b1, b2 for points in G2.
441 : We have to check that e( pk, H(msg) ) == e( g1, sig ), or equivalently
442 : e( pk, H(msg) ) * e( -g1, sig ) == 1.
443 :
444 : Replacing the variables we get:
445 : - a1 <- public_key, input needs to be decompressed in G1
446 : - b1 <- msg, input needs to be hashed to G2
447 : - a2 <- -g1, the const generator of G1, negated
448 : - b2 <- signature, input needs to be decompressed in G2
449 : */
450 : static inline int
451 : fd_bls12_381_core_verify( uchar const msg[], /* msg_sz */
452 : ulong msg_sz,
453 : uchar const signature[ 96 ],
454 : uchar const public_key[ 48 ],
455 315 : char const * domain ) {
456 315 : fd_bls12_381_g1aff_t a1[1]; /* a2 is const, we don't need a var */
457 315 : fd_bls12_381_g2aff_t b1[1], b2[1];
458 :
459 : /* decompress public_key into a1 and check that it's a valid point in G1 */
460 315 : if( FD_UNLIKELY( blst_p1_uncompress( a1, public_key )!=BLST_SUCCESS ) ) {
461 3 : return -1;
462 3 : }
463 312 : if( FD_UNLIKELY( !blst_p1_affine_in_g1( a1 ) ) ) {
464 0 : return -1;
465 0 : }
466 : /* https://github.com/anza-xyz/solana-sdk/blob/b66abfddd564aef5b4b82cf4e76381e96f2459f0/bls-signatures/src/pubkey/verify.rs#L120 */
467 312 : if( FD_UNLIKELY( blst_p1_affine_is_inf( a1 ) ) ) {
468 0 : return -1;
469 0 : }
470 :
471 : /* hash msg into b1. the check that it's a valid point in G2 is implicit/guaranteed */
472 312 : fd_bls12_381_g2_t _b1[1];
473 312 : blst_hash_to_g2( _b1, msg, msg_sz, (uchar const *)domain, FD_BLS_SIG_DOMAIN_SZ, NULL, 0UL );
474 312 : blst_p2_to_affine( b1, _b1 );
475 :
476 : /* decompress signature into b2 and check that it's a valid point in G2 */
477 312 : if( FD_UNLIKELY( blst_p2_uncompress( b2, signature )!=BLST_SUCCESS ) ) {
478 3 : return -1;
479 3 : }
480 309 : if( FD_UNLIKELY( !blst_p2_affine_in_g2( b2 ) ) ) {
481 0 : return -1;
482 0 : }
483 :
484 : /* prepare pairing input: blst needs 2 arrays of pointers, and the result
485 : needs to be initialized to 1. */
486 309 : fd_bls12_381_g1aff_t const * aptr[ 2 ] = { a1, &BLS12_381_NEG_G1 };
487 309 : fd_bls12_381_g2aff_t const * bptr[ 2 ] = { b1, b2 };
488 309 : blst_fp12 r[1];
489 309 : memcpy( r, blst_fp12_one(), sizeof(blst_fp12) );
490 :
491 : /* compute the actual pairing and check that it's 1 */
492 309 : blst_miller_loop_n( r, bptr, aptr, 2 );
493 309 : if( FD_LIKELY( blst_fp12_finalverify( r, blst_fp12_one() ) ) ) {
494 306 : return 0; /* success */
495 306 : }
496 3 : return -1;
497 309 : }
498 :
499 : int
500 : fd_bls12_381_proof_of_possession_verify( uchar const msg[], /* msg_sz */
501 : ulong msg_sz,
502 : uchar const proof[ 96 ],
503 321 : uchar const public_key[ 48 ] ) {
504 : /* Agave supports the case of empty msg, where the public key is used
505 : instead (i.e. the plain RFC proof of possession). But that's not really
506 : used anywhere, and probably shouldn't be used for security reasons.
507 : In order to avoid accidental future changes, we prefer to not implement
508 : the case msg_sz==0 and instead explicitly throw an error.
509 : Since the public key must be part of the message, we check that
510 : msg_sz >= public key size, again to avoid accidental mistakes. */
511 321 : if( FD_UNLIKELY( msg_sz<48 ) ) {
512 6 : return -1;
513 6 : }
514 :
515 315 : return fd_bls12_381_core_verify( msg, msg_sz, proof, public_key, FD_BLS_SIG_DOMAIN_POP );
516 321 : }
|