LCOV - code coverage report
Current view: top level - ballet/bn254 - fd_bn254_g2_check.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 112 131 85.5 %
Date: 2026-09-17 04:28:31 Functions: 4 4 100.0 %

          Line data    Source code
       1             : #include "./fd_bn254_g2_inl.h"
       2             : #include "./fd_bn254_glv.h"
       3             : 
       4             : /* G2 scalar mul and deserialization. */
       5             : 
       6             : /* fd_bn254_g2_scalar_mul computes r = [s]P.
       7             :    p must be in affine form (p->Z == 1).
       8             :    The result is in projective coordinates over Fp2. */
       9             : fd_bn254_g2_t *
      10             : fd_bn254_g2_scalar_mul( fd_bn254_g2_t *           r,
      11             :                         fd_bn254_g2_t const *     p,
      12        1536 :                         fd_bn254_scalar_t const * s ) {
      13        1536 :   if( FD_UNLIKELY( fd_uint256_is_zero( s ) || fd_bn254_g2_is_zero( p ) ) ) {
      14          30 :     return fd_bn254_g2_set_zero( r );
      15          30 :   }
      16             : 
      17        1506 :   const ulong g1_const[ 3 ] = { 0x7a7bd9d4391eb18eUL, 0x4ccef014a773d2cfUL, 0x0000000000000002UL };
      18        1506 :   ulong b1[ 3 ], b2[ 2 ];
      19        1506 :   fd_bn254_glv_sxg3( b1, s, g1_const );
      20        1506 :   fd_bn254_glv_sxg2( b2, s, g2_const );
      21             : 
      22             :   /* k1 = s - b1*N_C - b2*N_B (may be negative for G2) */
      23        1506 :   fd_uint256_t k1_abs[1];
      24        1506 :   int k1_neg = 0;
      25        1506 :   {
      26        1506 :     ulong p_nc[ 4 ];
      27             :     /* b2*nb will produce at most 3 limbs, so we want the 4th zeroed for the addition. */
      28        1506 :     ulong p_nb[ 4 ] = {0};
      29        1506 :     ulong    t[ 4 ];
      30        1506 :     fd_bn254_glv_mul3x2( p_nc, b1, nc );
      31        1506 :     fd_bn254_glv_mul2x1( p_nb, b2, nb );
      32        1506 :     fd_bn254_glv_add4( t, p_nc, p_nb );
      33        1506 :     ulong borrow = fd_bn254_glv_sub4( k1_abs->limbs, s->limbs, t );
      34        1506 :     if( borrow ) {
      35           0 :       k1_neg = 1;
      36           0 :       fd_bn254_glv_negate4( k1_abs->limbs );
      37           0 :     }
      38        1506 :   }
      39             : 
      40             :   /* k2 = b2*N_A - b1*N_B (usually negative for G2) */
      41        1506 :   fd_uint256_t k2_abs[1];
      42        1506 :   int k2_neg = 0;
      43        1506 :   {
      44        1506 :     ulong pos[ 4 ], neg[ 4 ];
      45        1506 :     fd_bn254_glv_mul2x2( pos, b2, na );
      46        1506 :     fd_bn254_glv_mul3x1( neg, b1, nb );
      47        1506 :     ulong borrow = fd_bn254_glv_sub4( k2_abs->limbs, pos, neg );
      48        1506 :     if( borrow ) {
      49         306 :       k2_neg = 1;
      50         306 :       fd_bn254_glv_negate4( k2_abs->limbs );
      51         306 :     }
      52        1506 :   }
      53             : 
      54             :   /* pt1 = P, pt2 = phi(P) = (beta * P.x, P.y).
      55             :      If k1 < 0, negate pt1. If k2 < 0, negate pt2. */
      56        1506 :   fd_bn254_g2_t pt1[1], pt2[1];
      57        1506 :   fd_bn254_g2_set( pt1, p );
      58        1506 :   fd_bn254_fp_mul( &pt2->X.el[0], &p->X.el[0], fd_bn254_const_beta_mont );
      59        1506 :   fd_bn254_fp_mul( &pt2->X.el[1], &p->X.el[1], fd_bn254_const_beta_mont );
      60        1506 :   fd_bn254_fp2_set( &pt2->Y, &p->Y );
      61        1506 :   fd_bn254_fp2_set_one( &pt2->Z );
      62        1506 :   if( k1_neg ) {
      63           0 :     fd_bn254_fp2_neg( &pt1->Y, &pt1->Y );
      64           0 :   }
      65        1506 :   if( k2_neg ) {
      66         306 :     fd_bn254_fp2_neg( &pt2->Y, &pt2->Y );
      67         306 :   }
      68             : 
      69        1506 :   fd_bn254_g2_t pt12[1];
      70        1506 :   fd_bn254_g2_affine_add( pt12, pt1, pt2 );
      71             : 
      72             :   /* Shamir's trick: simultaneous double-and-add on k1, k2. */
      73        1506 :   int i = 255;
      74      272952 :   for( ; i>=0; i-- ) {
      75      272952 :     int k1b = !!fd_uint256_bit( k1_abs, i );
      76      272952 :     int k2b = !!fd_uint256_bit( k2_abs, i );
      77      272952 :     if( k1b || k2b ) {
      78        1506 :       fd_bn254_g2_set( r, ( k1b && k2b ) ? pt12 : ( k1b ? pt1 : pt2 ) );
      79        1506 :       break;
      80        1506 :     }
      81      272952 :   }
      82        1506 :   if( FD_UNLIKELY( i<0 ) ) {
      83           0 :     return fd_bn254_g2_set_zero( r );
      84           0 :   }
      85      114090 :   for( i--; i >= 0; i-- ) {
      86      112584 :     fd_bn254_g2_dbl( r, r );
      87      112584 :     int k1b = !!fd_uint256_bit( k1_abs, i );
      88      112584 :     int k2b = !!fd_uint256_bit( k2_abs, i );
      89      112584 :     if( k1b && k2b ) {
      90        8874 :       fd_bn254_g2_add_mixed( r, r, pt12 );
      91      103710 :     } else if( k1b ) {
      92       41724 :       fd_bn254_g2_add_mixed( r, r, pt1 );
      93       61986 :     } else if( k2b ) {
      94        8262 :       fd_bn254_g2_add_mixed( r, r, pt2 );
      95        8262 :     }
      96      112584 :   }
      97             : 
      98        1506 :   return r;
      99        1506 : }
     100             : 
     101             : /* fd_bn254_g2_frombytes_internal extracts (x, y) and performs basic checks.
     102             :    This is used by fd_bn254_g2_compress() and fd_bn254_g2_frombytes_check_subgroup(). */
     103             : fd_bn254_g2_t *
     104             : fd_bn254_g2_frombytes_internal( fd_bn254_g2_t * p,
     105             :                                 uchar const     in[128],
     106       91368 :                                 int             big_endian ) {
     107             :   /* Special case: all zeros => point at infinity */
     108       91368 :   const uchar zero[128] = { 0 };
     109       91368 :   if( FD_UNLIKELY( fd_memeq( in, zero, 128 ) ) ) {
     110          39 :     return fd_bn254_g2_set_zero( p );
     111          39 :   }
     112             : 
     113             :   /* Check x < p */
     114       91329 :   if( FD_UNLIKELY( !fd_bn254_fp2_frombytes_nm( &p->X, &in[0], big_endian, NULL, NULL ) ) ) {
     115           0 :     return NULL;
     116           0 :   }
     117             : 
     118             :   /* Check flags and y < p */
     119       91329 :   int is_inf, is_neg;
     120       91329 :   if( FD_UNLIKELY( !fd_bn254_fp2_frombytes_nm( &p->Y, &in[64], big_endian, &is_inf, &is_neg ) ) ) {
     121           0 :     return NULL;
     122           0 :   }
     123             : 
     124       91329 :   if( FD_UNLIKELY( is_inf ) ) {
     125           6 :     return fd_bn254_g2_set_zero( p );
     126           6 :   }
     127             : 
     128       91323 :   fd_bn254_fp2_set_one( &p->Z );
     129       91323 :   return p;
     130       91329 : }
     131             : 
     132             : /* fd_bn254_g2_frombytes_check_eq_only performs frombytes, checks the curve
     133             :    equation, but does NOT check subgroup membership. */
     134             : fd_bn254_g2_t *
     135             : fd_bn254_g2_frombytes_check_eq_only( fd_bn254_g2_t * p,
     136             :                                      uchar const     in[128],
     137       61266 :                                      int             big_endian ) {
     138       61266 :   if( FD_UNLIKELY( !fd_bn254_g2_frombytes_internal( p, in, big_endian ) ) ) {
     139           0 :     return NULL;
     140           0 :   }
     141       61266 :   if( FD_UNLIKELY( fd_bn254_g2_is_zero( p ) ) ) {
     142          36 :     return p;
     143          36 :   }
     144             : 
     145       61230 :   fd_bn254_fp2_to_mont( &p->X, &p->X );
     146       61230 :   fd_bn254_fp2_to_mont( &p->Y, &p->Y );
     147       61230 :   fd_bn254_fp2_set_one( &p->Z );
     148             : 
     149             :   /* Check that y^2 = x^3 + b */
     150       61230 :   fd_bn254_fp2_t y2[1], x3b[1];
     151       61230 :   fd_bn254_fp2_sqr( y2, &p->Y );
     152       61230 :   fd_bn254_fp2_sqr( x3b, &p->X );
     153       61230 :   fd_bn254_fp2_mul( x3b, x3b, &p->X );
     154       61230 :   fd_bn254_fp2_add( x3b, x3b, fd_bn254_const_twist_b_mont );
     155       61230 :   if( FD_UNLIKELY( !fd_bn254_fp2_eq( y2, x3b ) ) ) {
     156           0 :     return NULL;
     157           0 :   }
     158       61230 :   return p;
     159       61230 : }
     160             : 
     161             : /* fd_bn254_g2_frombytes_check_subgroup performs frombytes AND checks subgroup membership. */
     162             : fd_bn254_g2_t *
     163             : fd_bn254_g2_frombytes_check_subgroup( fd_bn254_g2_t * p,
     164             :                                       uchar const     in[128],
     165        1206 :                                       int             big_endian ) {
     166        1206 :   if( FD_UNLIKELY( fd_bn254_g2_frombytes_check_eq_only( p, in, big_endian )==NULL ) ) {
     167           0 :     return NULL;
     168           0 :   }
     169             : 
     170             :   /* G2 does NOT have prime order, so we have to check group membership. */
     171             : 
     172             :   /* We use the fast subgroup membership check, that requires a single 64-bit scalar mul.
     173             :      https://eprint.iacr.org/2022/348, Sec 3.1.
     174             :      [r]P == 0 <==> [x+1]P + ψ([x]P) + ψ²([x]P) = ψ³([2x]P)
     175             :      See also: https://github.com/Consensys/gnark-crypto/blob/v0.12.1/ecc/bn254/g2.go#L404
     176             : 
     177             :      For reference, the following also work:
     178             : 
     179             :      1) very slow: 256-bit scalar mul
     180             : 
     181             :      fd_bn254_g2_t r[1];
     182             :      fd_bn254_g2_scalar_mul( r, p, fd_bn254_const_r );
     183             :      if( !fd_bn254_g2_is_zero( r ) ) return NULL;
     184             : 
     185             :      2) slow: 128-bit scalar mul
     186             : 
     187             :      fd_bn254_g2_t a[1], b[1];
     188             :      const fd_bn254_scalar_t six_x_sqr[1] = {{{ 0xf83e9682e87cfd46, 0x6f4d8248eeb859fb, 0x0, 0x0, }}};
     189             :      fd_bn254_g2_scalar_mul( a, p, six_x_sqr );
     190             :      fd_bn254_g2_frob( b, p );
     191             :      if( !fd_bn254_g2_eq( a, b ) ) return NULL; */
     192             : 
     193        1206 :   fd_bn254_g2_t xp[1], l[1], psi[1], r[1];
     194        1206 :   fd_bn254_g2_scalar_mul( xp, p, fd_bn254_const_x ); /* 64-bit */
     195        1206 :   fd_bn254_g2_add_mixed( l, xp, p );
     196             : 
     197             :   /* l will not be equal to psi unless p==0 */
     198        1206 :   fd_bn254_g2_frob( psi, xp );
     199        1206 :   fd_bn254_g2_add( l, l, psi );
     200             : 
     201        1206 :   fd_bn254_g2_frob2( psi, xp ); /* faster than frob( psi, psi ) */
     202        1206 :   fd_bn254_g2_add( l, l, psi );
     203             : 
     204        1206 :   fd_bn254_g2_frob( psi, psi );
     205        1206 :   fd_bn254_g2_dbl( r, psi );
     206        1206 :   if( FD_UNLIKELY( !fd_bn254_g2_eq( l, r ) ) ) {
     207           0 :     return NULL;
     208           0 :   }
     209        1206 :   return p;
     210        1206 : }

Generated by: LCOV version 1.14