LCOV - code coverage report
Current view: top level - ballet/ed25519/avx512 - fd_r43x6_ge.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 196 206 95.1 %
Date: 2026-10-18 04:43:39 Functions: 6 7 85.7 %

          Line data    Source code
       1             : #include "fd_r43x6_ge.h"
       2             : 
       3             : /* Direct quotes from RFC 8032 are indicated with '//' style comments. */
       4             : 
       5             : wv_t
       6           0 : fd_r43x6_ge_encode( wwl_t P03, wwl_t P14, wwl_t P25 ) {
       7             : 
       8             :   // 5.1.2.  Encoding
       9             :   //
      10             :   // All values are coded as octet strings, and integers are coded using
      11             :   // little-endian convention, i.e., a 32-octet string h h[0],...h[31]
      12             :   // represents the integer h[0] + 2^8 * h[1] + ... + 2^248 * h[31].
      13             :   //
      14             :   // A curve point (x,y), with coordinates in the range 0 <= x,y < p, is
      15             :   // coded as follows.
      16             : 
      17             :   /* The below computes x=mod(X/Z) and y=mod(Y/Z) but doesn't repack
      18             :      the results into fd_r43x6_t's since we need to tweak y's limbs with
      19             :      values from x0 in the next part. */
      20             : 
      21           0 :   fd_r43x6_t X,Y,Z,T; FD_R43X6_QUAD_UNPACK( X,Y,Z,T, P ); /* in u47/u47/u47/u47 */
      22           0 :   (void)T;
      23             : 
      24           0 :   fd_r43x6_t one_Z = fd_r43x6_invert( Z ); /* in u44 */
      25             : 
      26             :   /* TODO: Consider a FD_R43X6_MUL2_FAST_INL? */
      27           0 :   fd_r43x6_t x = fd_r43x6_mul_fast( X, one_Z ); /* in u63 */     fd_r43x6_t y = fd_r43x6_mul_fast( Y, one_Z ); /* in u63 */
      28             : 
      29           0 :   long x0,x1,x2,x3,x4,x5; fd_r43x6_extract_limbs( x, x );        long y0,y1,y2,y3,y4,y5; fd_r43x6_extract_limbs( y, y );
      30             : 
      31           0 :   fd_r43x6_biased_carry_propagate_limbs( x, x, 0L ); /* in nr */ fd_r43x6_biased_carry_propagate_limbs( y, y, 0L ); /* in nr */
      32             : 
      33           0 :   fd_r43x6_mod_nearly_reduced_limbs( x, x ); /* in [0,p) */      fd_r43x6_mod_nearly_reduced_limbs( y, y ); /* in [0,p) */
      34             : 
      35             :   // First, encode the y-coordinate as a little-endian string of 32
      36             :   // octets.  The most significant bit of the final octet is always
      37             :   // zero.  To form the encoding of the point, copy the least
      38             :   // significant bit of the x-coordinate to the most significant bit of
      39             :   // the final octet.
      40             : 
      41             :   /* Since the limbs of y are already in a packed form, we copy the
      42             :      least significant bit before we pack up the uint256 to save some
      43             :      memory operations. */
      44             : 
      45           0 :   return fd_r43x6_pack( fd_r43x6( y0, y1, y2, y3, y4, y5 | ((x0 & 1L)<<40) ) );
      46           0 : }
      47             : 
      48             : int
      49             : fd_r43x6_ge_decode( wwl_t * _P03, wwl_t * _P14, wwl_t * _P25,
      50      177747 :                     void const * _vs ) {
      51             : 
      52             :   // 5.1.3.  Decoding
      53             :   //
      54             :   // Decoding a point, given as a 32-octet string, is a little more
      55             :   // complicated.
      56             :   //
      57             :   // 1.  First, interpret the string as an integer in little-endian
      58             :   //     representation.
      59             : 
      60      177747 :   ulong _s[4] __attribute__((aligned(32)));
      61      177747 :   memcpy( _s, _vs, 32UL );
      62      177747 :   ulong y0 = _s[0]; /* Bits   0- 63 */
      63      177747 :   ulong y1 = _s[1]; /* Bits  64-127 */
      64      177747 :   ulong y2 = _s[2]; /* Bits 128-191 */
      65      177747 :   ulong y3 = _s[3]; /* Bits 192-255 */
      66             : 
      67             :   // Bit 255 of this number is the least significant bit of the
      68             :   // x-coordinate and denote this value x_0.
      69             : 
      70      177747 :   int x_0 = (int)(y3>>63);
      71             : 
      72             :   // The y-coordinate is recovered simply by clearing this bit.
      73             : 
      74      177747 :   y3 &= ~(1UL<<63);
      75             : 
      76      177747 :   fd_r43x6_t y = fd_r43x6_unpack( wv( y0, y1, y2, y3 ) );
      77             : 
      78             :   // 2.  To recover the x-coordinate, the curve equation implies x^2 =
      79             :   //     (y^2 - 1) / (d y^2 + 1) (mod p).  The denominator is always
      80             :   //     non-zero mod p.  Let u = y^2 - 1 and v = d y^2 + 1.
      81             : 
      82      177747 :   fd_r43x6_t const one = fd_r43x6_one();
      83      177747 :   fd_r43x6_t const d   = fd_r43x6_d();
      84             : 
      85      177747 :   fd_r43x6_t ysq = fd_r43x6_sqr( y );                                /*       y^2     in u44 */
      86      177747 :   fd_r43x6_t u   = fd_r43x6_sub( ysq, one );                         /* u =   y^2 - 1 in u44 */
      87      177747 :   fd_r43x6_t v   = fd_r43x6_add_fast( fd_r43x6_mul( d, ysq ), one ); /* v = d y^2 + 1 in u44 */
      88             : 
      89             :   // To compute the square root of (u/v), the first step is to compute
      90             :   // the candidate root x = (u/v)^((p+3)/8).  This can be done with the
      91             :   // following trick, using a single modular powering for both the
      92             :   // inversion of v and the square root:
      93             :   //
      94             :   //            (p+3)/8      3        (p-5)/8
      95             :   //   x = (u/v)        = u v  (u v^7)         (mod p)
      96             : 
      97      177747 :   fd_r43x6_t v2  = fd_r43x6_sqr( v        ); /* v^2               in u44 */
      98      177747 :   fd_r43x6_t v4  = fd_r43x6_sqr( v2       ); /* v^4               in u44 */
      99      177747 :   fd_r43x6_t v3  = fd_r43x6_mul( v,   v2  ); /* v^3               in u44 */
     100      177747 :   fd_r43x6_t uv3 = fd_r43x6_mul( u,   v3  ); /* u v^3             in u44 */
     101      177747 :   fd_r43x6_t uv7 = fd_r43x6_mul( uv3, v4  ); /* u v^7             in u44 */
     102      177747 :   fd_r43x6_t t0  = fd_r43x6_pow22523( uv7 ); /* (u v^7)^((p-5)/8) in u44 */
     103      177747 :   fd_r43x6_t x   = fd_r43x6_mul( uv3, t0  ); /* x                 in u44 */
     104             : 
     105             :   // 3.  Again, there are three cases:
     106             :   //
     107             :   //     1.  If v x^2 = u (mod p), x is a square root.
     108             :   //
     109             :   //     2.  If v x^2 = -u (mod p), set x <-- x * 2^((p-1)/4), which is
     110             :   //         a square root.
     111             :   //
     112             :   //     3.  Otherwise, no square root exists for modulo p, and decoding
     113             :   //         fails
     114             : 
     115             :   /* The implementation below has flattened the above branches to make
     116             :      it more amenable to doing multiple decodes concurrently.  This also
     117             :      makes this essentially a constant time algorithm (but such isn't
     118             :      required here). */
     119             : 
     120      177747 :   fd_r43x6_t x2  = fd_r43x6_sqr( x );           /* x^2       in u44 */
     121      177747 :   fd_r43x6_t vx2 = fd_r43x6_mul( v, x2 );       /* v x^2     in u44 */
     122      177747 :   fd_r43x6_t t1  = fd_r43x6_sub_fast( vx2, u ); /* v x^2 - u in s44 */
     123      177747 :   fd_r43x6_t t2  = fd_r43x6_add_fast( vx2, u ); /* v x^2 + u in u45 */
     124      177747 :   int t1nz = fd_r43x6_is_nonzero( t1 );
     125      177747 :   int t2nz = fd_r43x6_is_nonzero( t2 );
     126      177747 :   if( FD_UNLIKELY( t1nz & t2nz ) ) goto fail; /* case 3 */
     127      106887 :   fd_r43x6_t t3  = fd_r43x6_if( t1nz, fd_r43x6_imag(), one ); /* in u43 */
     128      106887 :   /**/       x   = fd_r43x6_mul( x, t3 );                     /* in u44 */
     129             : 
     130             :   // 4.  Finally, use the x_0 bit to select the right square root.
     131             :   //     RFC 8032 says "if x=0 and x_0=1, decoding fails", but Dalek
     132             :   //     does not enforce this — neg(0)==0 so it silently accepts.
     133             :   //     We match Dalek for compatibility.
     134             :   //     https://github.com/dalek-cryptography/curve25519-dalek/blob/curve25519-4.1.3/curve25519-dalek/src/edwards.rs#L194-L240
     135             :   //     https://github.com/dalek-cryptography/curve25519-dalek/blob/3.2.1/src/edwards.rs#L193-L209
     136             : 
     137      106887 :   int x_mod_2 = fd_r43x6_diagnose( x );
     138             : 
     139             :   // If x_0 != x mod 2, set x <-- p - x.
     140             : 
     141      106887 :   x = fd_r43x6_if( x_0!=x_mod_2, fd_r43x6_neg( x ) /* in u44 */, x );
     142             : 
     143             :   // Return the decoded point (x,y).
     144             : 
     145      106887 :   FD_R43X6_QUAD_PACK( *_P,
     146      106887 :     x,                      /* in u44 */
     147      106887 :     y,                      /* Reduced */
     148      106887 :     one,                    /* Reduced */
     149      106887 :     fd_r43x6_mul( x, y ) ); /* in u44 */
     150      106887 :   FD_R43X6_QUAD_FOLD_UNSIGNED( *_P, *_P );
     151      106887 :   return 0;
     152             : 
     153       70860 : fail:
     154       70860 :   *_P03 = fd_r43x6_zero();
     155       70860 :   *_P14 = fd_r43x6_zero();
     156       70860 :   *_P25 = fd_r43x6_zero();
     157       70860 :   return -1;
     158      177747 : }
     159             : 
     160             : /* Staged decode: fd_r43x6_ge_decode split around its pow22523.  Keep
     161             :    in sync with fd_r43x6_ge_decode and fd_r43x6_pow22523. */
     162             : 
     163             : void
     164             : fd_r43x6_ge_decode_init( fd_r43x6_ge_decode_t * st,
     165      110142 :                          void const *           _vs ) {
     166             : 
     167      110142 :   ulong _s[4] __attribute__((aligned(32)));
     168      110142 :   memcpy( _s, _vs, 32UL );
     169      110142 :   ulong y0 = _s[0];
     170      110142 :   ulong y1 = _s[1];
     171      110142 :   ulong y2 = _s[2];
     172      110142 :   ulong y3 = _s[3];
     173             : 
     174      110142 :   st->x_0 = (int)(y3>>63);
     175             : 
     176      110142 :   y3 &= ~(1UL<<63);
     177             : 
     178      110142 :   fd_r43x6_t y = fd_r43x6_unpack( wv( y0, y1, y2, y3 ) );
     179             : 
     180      110142 :   fd_r43x6_t const one = fd_r43x6_one();
     181      110142 :   fd_r43x6_t const d   = fd_r43x6_d();
     182             : 
     183      110142 :   fd_r43x6_t ysq = fd_r43x6_sqr( y );                                /*       y^2     in u44 */
     184      110142 :   fd_r43x6_t u   = fd_r43x6_sub( ysq, one );                         /* u =   y^2 - 1 in u44 */
     185      110142 :   fd_r43x6_t v   = fd_r43x6_add_fast( fd_r43x6_mul( d, ysq ), one ); /* v = d y^2 + 1 in u44 */
     186             : 
     187      110142 :   fd_r43x6_t v2  = fd_r43x6_sqr( v        ); /* v^2               in u44 */
     188      110142 :   fd_r43x6_t v4  = fd_r43x6_sqr( v2       ); /* v^4               in u44 */
     189      110142 :   fd_r43x6_t v3  = fd_r43x6_mul( v,   v2  ); /* v^3               in u44 */
     190      110142 :   fd_r43x6_t uv3 = fd_r43x6_mul( u,   v3  ); /* u v^3             in u44 */
     191      110142 :   fd_r43x6_t uv7 = fd_r43x6_mul( uv3, v4  ); /* u v^7             in u44 */
     192             : 
     193      110142 :   st->y   = y;
     194      110142 :   st->u   = u;
     195      110142 :   st->v   = v;
     196      110142 :   st->uv3 = uv3;
     197             : 
     198             :   /* Chain: t0 = pow22523( uv7 ), stage 0 is z2 = sqr( z ) */
     199             : 
     200      110142 :   st->z     = uv7;
     201      110142 :   st->x     = uv7;
     202      110142 :   st->m     = uv7;
     203      110142 :   st->stage = 0UL;
     204      110142 :   st->rem   = 1UL;
     205      110142 : }
     206             : 
     207             : /* decode_stage ends the current chain stage with its multiply. */
     208             : 
     209             : static void
     210     1321704 : decode_stage( fd_r43x6_ge_decode_t * st ) {
     211             : 
     212             :   /* Squarings per stage, as in fd_r43x6_pow22523 */
     213             : 
     214     1321704 :   static ulong const sqr_cnt[12] = { 1UL, 2UL, 0UL, 1UL, 5UL, 10UL, 20UL, 10UL, 50UL, 100UL, 50UL, 2UL };
     215             : 
     216     1321704 :   fd_r43x6_t x = st->x;
     217     1321704 :   switch( st->stage ) {
     218      110142 :   case  0UL:                                    st->z2      = x; break; /* z2       = z^2                   */
     219      110142 :   case  1UL: x = fd_r43x6_mul( x, st->z       ); st->z9      = x; break; /* z9       = z2^(2^2) z            */
     220      110142 :   case  2UL: x = fd_r43x6_mul( x, st->z2      );                  break; /* z11      = z9 z2                 */
     221      110142 :   case  3UL: x = fd_r43x6_mul( x, st->z9      );                  break; /* z2e5m1   = z11^2 z9              */
     222      110142 :   case  4UL: x = fd_r43x6_mul( x, st->m       ); st->z2e10m1 = x; break; /* z2e10m1  = z2e5m1^(2^5) z2e5m1   */
     223      110142 :   case  5UL: x = fd_r43x6_mul( x, st->m       );                  break; /* z2e20m1  = z2e10m1^(2^10) z2e10m1 */
     224      110142 :   case  6UL: x = fd_r43x6_mul( x, st->m       );                  break; /* z2e40m1  = z2e20m1^(2^20) z2e20m1 */
     225      110142 :   case  7UL: x = fd_r43x6_mul( x, st->z2e10m1 ); st->z2e50m1 = x; break; /* z2e50m1  = z2e40m1^(2^10) z2e10m1 */
     226      110142 :   case  8UL: x = fd_r43x6_mul( x, st->m       );                  break; /* z2e100m1 = z2e50m1^(2^50) z2e50m1 */
     227      110142 :   case  9UL: x = fd_r43x6_mul( x, st->m       );                  break; /* z2e200m1 = z2e100m1^(2^100) z2e100m1 */
     228      110142 :   case 10UL: x = fd_r43x6_mul( x, st->z2e50m1 );                  break; /* z2e250m1 = z2e200m1^(2^50) z2e50m1 */
     229      110142 :   default:   x = fd_r43x6_mul( x, st->z       );                  break; /* t0       = z2e250m1^(2^2) z      */
     230     1321704 :   }
     231     1321704 :   st->x = x;
     232     1321704 :   st->m = x;
     233     1321704 :   st->stage++;
     234     1321704 :   st->rem = st->stage<12UL ? sqr_cnt[ st->stage ] : 0UL;
     235     1321704 : }
     236             : 
     237             : void
     238             : fd_r43x6_ge_decode_step_slow( fd_r43x6_ge_decode_t * st,
     239      984597 :                               ulong                  n ) {
     240     2862264 :   while( n && st->stage<12UL ) {
     241     1877667 :     ulong k = fd_ulong_min( st->rem, n );
     242     1877667 :     fd_r43x6_t x = st->x;
     243    11891199 :     for( ulong i=0UL; i<k; i++ ) x = fd_r43x6_sqr( x );
     244     1877667 :     st->x    = x;
     245     1877667 :     st->rem -= k;
     246     1877667 :     n       -= k;
     247     1877667 :     if( n && !st->rem ) { decode_stage( st ); n--; }
     248     1877667 :   }
     249      984597 : }
     250             : 
     251             : int
     252             : fd_r43x6_ge_decode_fini( wwl_t * _P03, wwl_t * _P14, wwl_t * _P25,
     253      110142 :                          fd_r43x6_ge_decode_t * st ) {
     254             : 
     255      110142 :   fd_r43x6_ge_decode_step_slow( st, ULONG_MAX );
     256             : 
     257      110142 :   fd_r43x6_t const one = fd_r43x6_one();
     258      110142 :   fd_r43x6_t y   = st->y;
     259      110142 :   fd_r43x6_t u   = st->u;
     260      110142 :   fd_r43x6_t v   = st->v;
     261      110142 :   int        x_0 = st->x_0;
     262             : 
     263      110142 :   fd_r43x6_t x   = fd_r43x6_mul( st->uv3, st->x ); /* x         in u44 */
     264             : 
     265      110142 :   fd_r43x6_t x2  = fd_r43x6_sqr( x );           /* x^2       in u44 */
     266      110142 :   fd_r43x6_t vx2 = fd_r43x6_mul( v, x2 );       /* v x^2     in u44 */
     267      110142 :   fd_r43x6_t t1  = fd_r43x6_sub_fast( vx2, u ); /* v x^2 - u in s44 */
     268      110142 :   fd_r43x6_t t2  = fd_r43x6_add_fast( vx2, u ); /* v x^2 + u in u45 */
     269      110142 :   int t1nz = fd_r43x6_is_nonzero( t1 );
     270      110142 :   int t2nz = fd_r43x6_is_nonzero( t2 );
     271      110142 :   if( FD_UNLIKELY( t1nz & t2nz ) ) goto fail; /* case 3 */
     272       92827 :   fd_r43x6_t t3  = fd_r43x6_if( t1nz, fd_r43x6_imag(), one ); /* in u43 */
     273       92827 :   /**/       x   = fd_r43x6_mul( x, t3 );                     /* in u44 */
     274             : 
     275       92827 :   int x_mod_2 = fd_r43x6_diagnose( x );
     276             : 
     277       92827 :   x = fd_r43x6_if( x_0!=x_mod_2, fd_r43x6_neg( x ) /* in u44 */, x );
     278             : 
     279       92827 :   FD_R43X6_QUAD_PACK( *_P,
     280       92827 :     x,                      /* in u44 */
     281       92827 :     y,                      /* Reduced */
     282       92827 :     one,                    /* Reduced */
     283       92827 :     fd_r43x6_mul( x, y ) ); /* in u44 */
     284       92827 :   FD_R43X6_QUAD_FOLD_UNSIGNED( *_P, *_P );
     285       92827 :   return 0;
     286             : 
     287       17315 : fail:
     288       17315 :   *_P03 = fd_r43x6_zero();
     289       17315 :   *_P14 = fd_r43x6_zero();
     290       17315 :   *_P25 = fd_r43x6_zero();
     291       17315 :   return -1;
     292      110142 : }
     293             : 
     294             : int
     295             : fd_r43x6_ge_decode2( wwl_t * _Pa03, wwl_t * _Pa14, wwl_t * _Pa25,
     296             :                      void const * _vsa,
     297             :                      wwl_t * _Pb03, wwl_t * _Pb14, wwl_t * _Pb25,
     298      143882 :                      void const * _vsb ) {
     299             : 
     300             : # if 0 /* Reference implementation */
     301             : 
     302             :   if( FD_UNLIKELY( fd_r43x6_ge_decode( _Pa03, _Pa14, _Pa25, _vsa ) ) ) {
     303             :     *_Pb03 = wwl_zero(); *_Pb14 = wwl_zero(); *_Pb25 = wwl_zero();
     304             :     return -1;
     305             :   }
     306             : 
     307             :   if( FD_UNLIKELY( fd_r43x6_ge_decode( _Pb03, _Pb14, _Pb25, _vsb ) ) ) {
     308             :     *_Pa03 = wwl_zero(); *_Pa14 = wwl_zero(); *_Pa25 = wwl_zero();
     309             :     return -2;
     310             :   }
     311             : 
     312             :   return 0;
     313             : 
     314             : # else /* HPC implementation */
     315             : 
     316      143882 :   fd_r43x6_t const one     = fd_r43x6_one();
     317      143882 :   fd_r43x6_t const d       = fd_r43x6_d();
     318      143882 :   fd_r43x6_t const sqrt_m1 = fd_r43x6_imag();
     319             : 
     320      143882 :   ulong _sa[4] __attribute__((aligned(32)));                       ulong _sb[4] __attribute__((aligned(32)));
     321      143882 :   memcpy( _sa, _vsa, 32UL );                                       memcpy( _sb, _vsb, 32UL );
     322      143882 :   ulong y0a = _sa[0];                                              ulong y0b = _sb[0];
     323      143882 :   ulong y1a = _sa[1];                                              ulong y1b = _sb[1];
     324      143882 :   ulong y2a = _sa[2];                                              ulong y2b = _sb[2];
     325      143882 :   ulong y3a = _sa[3];                                              ulong y3b = _sb[3];
     326             : 
     327      143882 :   int x_0a = (int)(y3a>>63);                                       int x_0b = (int)(y3b>>63);
     328             : 
     329      143882 :   y3a &= ~(1UL<<63);                                               y3b &= ~(1UL<<63);
     330             : 
     331      143882 :   _sa[0] = y0a;                                                    _sb[0] = y0b;
     332      143882 :   _sa[1] = y1a;                                                    _sb[1] = y1b;
     333      143882 :   _sa[2] = y2a;                                                    _sb[2] = y2b;
     334      143882 :   _sa[3] = y3a;                                                    _sb[3] = y3b;
     335      143882 :   fd_r43x6_t ya = fd_r43x6_unpack( wv_ld( _sa ) );                 fd_r43x6_t yb = fd_r43x6_unpack( wv_ld( _sb ) );
     336             : 
     337      143882 :   fd_r43x6_t ysqa, ysqb; FD_R43X6_SQR2_INL   ( ysqa, ya,           ysqb, yb       );
     338      143882 :   fd_r43x6_t ua = fd_r43x6_sub( ysqa, one );                       fd_r43x6_t ub = fd_r43x6_sub( ysqb, one );
     339      143882 :   fd_r43x6_t va = fd_r43x6_add_fast( fd_r43x6_mul(d,ysqa), one );  fd_r43x6_t vb = fd_r43x6_add_fast( fd_r43x6_mul(d,ysqb), one );
     340             : 
     341      143882 :   fd_r43x6_t v2a,  v2b;  FD_R43X6_SQR2_INL      ( v2a,  va,        v2b,  vb       );
     342      143882 :   fd_r43x6_t v4a,  v4b;  FD_R43X6_SQR2_INL      ( v4a,  v2a,       v4b,  v2b      );
     343      143882 :   fd_r43x6_t v3a,  v3b;  FD_R43X6_MUL2_INL      ( v3a,  va,v2a,    v3b,  vb,v2b   );
     344      143882 :   fd_r43x6_t uv3a, uv3b; FD_R43X6_MUL2_INL      ( uv3a, ua,v3a,    uv3b, ub,v3b   );
     345      143882 :   fd_r43x6_t uv7a, uv7b; FD_R43X6_MUL2_INL      ( uv7a, uv3a,v4a,  uv7b, uv3b,v4b );
     346      143882 :   fd_r43x6_t t0a,  t0b;  FD_R43X6_POW22523_2_INL( t0a,  uv7a,      t0b,  uv7b     );
     347      143882 :   fd_r43x6_t xa,   xb;   FD_R43X6_MUL2_INL      ( xa,   uv3a,t0a,  xb,   uv3b,t0b );
     348             : 
     349      143882 :   fd_r43x6_t x2a,  x2b;  FD_R43X6_SQR2_INL      ( x2a,  xa,        x2b,  xb       );
     350      143882 :   fd_r43x6_t vx2a, vx2b; FD_R43X6_MUL2_INL      ( vx2a, va,x2a,    vx2b, vb,x2b   );
     351      143882 :   fd_r43x6_t t1a = fd_r43x6_sub_fast( vx2a, ua );                  fd_r43x6_t t1b = fd_r43x6_sub_fast( vx2b, ub );
     352      143882 :   fd_r43x6_t t2a = fd_r43x6_add_fast( vx2a, ua );                  fd_r43x6_t t2b = fd_r43x6_add_fast( vx2b, ub );
     353      143882 :   int t1nza = fd_r43x6_is_nonzero( t1a );                          int t1nzb = fd_r43x6_is_nonzero( t1b );
     354      143882 :   int t2nza = fd_r43x6_is_nonzero( t2a );                          int t2nzb = fd_r43x6_is_nonzero( t2b );
     355      143882 :   if( FD_UNLIKELY( t1nza & t2nza ) ) goto faila;
     356      126500 :   /**/                                                             if( FD_UNLIKELY( t1nzb & t2nzb ) ) goto failb;
     357             : 
     358      109962 :   fd_r43x6_t t3a = fd_r43x6_if( t1nza, sqrt_m1, one );             fd_r43x6_t t3b = fd_r43x6_if( t1nzb, sqrt_m1, one );
     359      109962 :   /**/                   FD_R43X6_MUL2_INL      ( xa,   xa,t3a,    xb,   xb,t3b   );
     360             : 
     361             :   /* Note: RFC 8032 says "if x=0 and x_0=1, decoding fails", but Dalek does not enforce
     362             :      this — neg(0)==0 so it silently accepts.  We match Dalek for compatibility.
     363             :      https://github.com/dalek-cryptography/curve25519-dalek/blob/curve25519-4.1.3/curve25519-dalek/src/edwards.rs#L194-L240
     364             :      https://github.com/dalek-cryptography/curve25519-dalek/blob/3.2.1/src/edwards.rs#L193-L209 */
     365      109962 :   int x_mod_2a = fd_r43x6_diagnose( xa );                          int x_mod_2b = fd_r43x6_diagnose( xb );
     366      109962 :   xa = fd_r43x6_if( x_0a!=x_mod_2a, fd_r43x6_neg( xa ), xa );      xb = fd_r43x6_if( x_0b!=x_mod_2b, fd_r43x6_neg( xb ), xb );
     367             : 
     368      109962 :   fd_r43x6_t xya,  xyb;  FD_R43X6_MUL2_INL      ( xya,  xa,ya,     xyb,  xb,yb    );
     369             : 
     370      109962 :   FD_R43X6_QUAD_PACK( *_Pa, xa,ya,one,xya );                       FD_R43X6_QUAD_PACK( *_Pb, xb,yb,one,xyb );
     371      109962 :   FD_R43X6_QUAD_FOLD_UNSIGNED( *_Pa, *_Pa );                       FD_R43X6_QUAD_FOLD_UNSIGNED( *_Pb, *_Pb );
     372      109962 :   return 0;
     373             : 
     374       17382 : faila:
     375       17382 :   *_Pa03 = wwl_zero();                                             *_Pb03 = wwl_zero();
     376       17382 :   *_Pa14 = wwl_zero();                                             *_Pb14 = wwl_zero();
     377       17382 :   *_Pa25 = wwl_zero();                                             *_Pb25 = wwl_zero();
     378       17382 :   return -1;
     379             : 
     380       16538 : failb:
     381       16538 :   *_Pa03 = wwl_zero();                                             *_Pb03 = wwl_zero();
     382       16538 :   *_Pa14 = wwl_zero();                                             *_Pb14 = wwl_zero();
     383       16538 :   *_Pa25 = wwl_zero();                                             *_Pb25 = wwl_zero();
     384       16538 :   return -2;
     385             : 
     386      126500 : # endif
     387      126500 : }

Generated by: LCOV version 1.14