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

Generated by: LCOV version 1.14