github.com/cloudflare/circl@v1.5.0/dh/csidh/fp511.go (about)

     1  package csidh
     2  
     3  import (
     4  	"math/bits"
     5  )
     6  
     7  // Constant time select.
     8  // if pick == 0xFF..FF (out = in1)
     9  // if pick == 0 (out = in2)
    10  // else out is undefined.
    11  func ctPick64(which uint64, in1, in2 uint64) uint64 {
    12  	return (in1 & which) | (in2 & ^which)
    13  }
    14  
    15  // ctIsNonZero64 returns 0 in case i == 0, otherwise it returns 1.
    16  // Constant-time.
    17  func ctIsNonZero64(i uint64) int {
    18  	// In case i==0 then i-1 will set MSB. Only in such case (i OR ~(i-1))
    19  	// will result in MSB being not set (logical implication: (i-1)=>i is
    20  	// false iff (i-1)==0 and i==non-zero). In every other case MSB is
    21  	// set and hence function returns 1.
    22  	return int((i | (^(i - 1))) >> 63)
    23  }
    24  
    25  // Returns result of x<y operation.
    26  func isLess(x, y *fp) bool {
    27  	for i := numWords - 1; i >= 0; i-- {
    28  		v, c := bits.Sub64(y[i], x[i], 0)
    29  		if c != 0 {
    30  			return false
    31  		}
    32  		if v != 0 {
    33  			return true
    34  		}
    35  	}
    36  	// x == y
    37  	return false
    38  }
    39  
    40  // r = x + y mod p.
    41  func addRdc(r, x, y *fp) {
    42  	var c uint64
    43  	var t fp
    44  	r[0], c = bits.Add64(x[0], y[0], 0)
    45  	r[1], c = bits.Add64(x[1], y[1], c)
    46  	r[2], c = bits.Add64(x[2], y[2], c)
    47  	r[3], c = bits.Add64(x[3], y[3], c)
    48  	r[4], c = bits.Add64(x[4], y[4], c)
    49  	r[5], c = bits.Add64(x[5], y[5], c)
    50  	r[6], c = bits.Add64(x[6], y[6], c)
    51  	r[7], _ = bits.Add64(x[7], y[7], c)
    52  
    53  	t[0], c = bits.Sub64(r[0], p[0], 0)
    54  	t[1], c = bits.Sub64(r[1], p[1], c)
    55  	t[2], c = bits.Sub64(r[2], p[2], c)
    56  	t[3], c = bits.Sub64(r[3], p[3], c)
    57  	t[4], c = bits.Sub64(r[4], p[4], c)
    58  	t[5], c = bits.Sub64(r[5], p[5], c)
    59  	t[6], c = bits.Sub64(r[6], p[6], c)
    60  	t[7], c = bits.Sub64(r[7], p[7], c)
    61  
    62  	w := 0 - c
    63  	r[0] = ctPick64(w, r[0], t[0])
    64  	r[1] = ctPick64(w, r[1], t[1])
    65  	r[2] = ctPick64(w, r[2], t[2])
    66  	r[3] = ctPick64(w, r[3], t[3])
    67  	r[4] = ctPick64(w, r[4], t[4])
    68  	r[5] = ctPick64(w, r[5], t[5])
    69  	r[6] = ctPick64(w, r[6], t[6])
    70  	r[7] = ctPick64(w, r[7], t[7])
    71  }
    72  
    73  // r = x - y.
    74  func sub512(r, x, y *fp) uint64 {
    75  	var c uint64
    76  	r[0], c = bits.Sub64(x[0], y[0], 0)
    77  	r[1], c = bits.Sub64(x[1], y[1], c)
    78  	r[2], c = bits.Sub64(x[2], y[2], c)
    79  	r[3], c = bits.Sub64(x[3], y[3], c)
    80  	r[4], c = bits.Sub64(x[4], y[4], c)
    81  	r[5], c = bits.Sub64(x[5], y[5], c)
    82  	r[6], c = bits.Sub64(x[6], y[6], c)
    83  	r[7], c = bits.Sub64(x[7], y[7], c)
    84  	return c
    85  }
    86  
    87  // r = x - y mod p.
    88  func subRdc(r, x, y *fp) {
    89  	var c uint64
    90  
    91  	// Same as sub512(r,x,y). Unfortunately
    92  	// compiler is not able to inline it.
    93  	r[0], c = bits.Sub64(x[0], y[0], 0)
    94  	r[1], c = bits.Sub64(x[1], y[1], c)
    95  	r[2], c = bits.Sub64(x[2], y[2], c)
    96  	r[3], c = bits.Sub64(x[3], y[3], c)
    97  	r[4], c = bits.Sub64(x[4], y[4], c)
    98  	r[5], c = bits.Sub64(x[5], y[5], c)
    99  	r[6], c = bits.Sub64(x[6], y[6], c)
   100  	r[7], c = bits.Sub64(x[7], y[7], c)
   101  
   102  	// if x<y => r=x-y+p
   103  	w := 0 - c
   104  	r[0], c = bits.Add64(r[0], ctPick64(w, p[0], 0), 0)
   105  	r[1], c = bits.Add64(r[1], ctPick64(w, p[1], 0), c)
   106  	r[2], c = bits.Add64(r[2], ctPick64(w, p[2], 0), c)
   107  	r[3], c = bits.Add64(r[3], ctPick64(w, p[3], 0), c)
   108  	r[4], c = bits.Add64(r[4], ctPick64(w, p[4], 0), c)
   109  	r[5], c = bits.Add64(r[5], ctPick64(w, p[5], 0), c)
   110  	r[6], c = bits.Add64(r[6], ctPick64(w, p[6], 0), c)
   111  	r[7], _ = bits.Add64(r[7], ctPick64(w, p[7], 0), c)
   112  }
   113  
   114  // Fixed-window mod exp for fpBitLen bit value with 4 bit window. Returned
   115  // result is a number in montgomery domain.
   116  // r = b ^ e (mod p).
   117  // Constant time.
   118  func modExpRdcCommon(r, b, e *fp, fpBitLen int) {
   119  	var precomp [16]fp
   120  	var t fp
   121  	var c uint64
   122  
   123  	// Precompute step, computes an array of small powers of 'b'. As this
   124  	// algorithm implements 4-bit window, we need 2^4=16 of such values.
   125  	// b^0 = 1, which is equal to R from REDC.
   126  	precomp[0] = one // b ^ 0
   127  	precomp[1] = *b  // b ^ 1
   128  	for i := 2; i < 16; i = i + 2 {
   129  		// OPTIMIZE: implement fast squaring. Then interleaving fast squaring
   130  		// with multiplication should improve performance.
   131  		mulRdc(&precomp[i], &precomp[i/2], &precomp[i/2]) // sqr
   132  		mulRdc(&precomp[i+1], &precomp[i], b)
   133  	}
   134  
   135  	*r = one
   136  	for i := fpBitLen/4 - 1; i >= 0; i-- {
   137  		for j := 0; j < 4; j++ {
   138  			mulRdc(r, r, r)
   139  		}
   140  		// note: non resistant to cache SCA
   141  		idx := (e[i/16] >> uint((i%16)*4)) & 15
   142  		mulRdc(r, r, &precomp[idx])
   143  	}
   144  
   145  	// if p <= r < 2p then r = r-p
   146  	t[0], c = bits.Sub64(r[0], p[0], 0)
   147  	t[1], c = bits.Sub64(r[1], p[1], c)
   148  	t[2], c = bits.Sub64(r[2], p[2], c)
   149  	t[3], c = bits.Sub64(r[3], p[3], c)
   150  	t[4], c = bits.Sub64(r[4], p[4], c)
   151  	t[5], c = bits.Sub64(r[5], p[5], c)
   152  	t[6], c = bits.Sub64(r[6], p[6], c)
   153  	t[7], c = bits.Sub64(r[7], p[7], c)
   154  
   155  	w := 0 - c
   156  	r[0] = ctPick64(w, r[0], t[0])
   157  	r[1] = ctPick64(w, r[1], t[1])
   158  	r[2] = ctPick64(w, r[2], t[2])
   159  	r[3] = ctPick64(w, r[3], t[3])
   160  	r[4] = ctPick64(w, r[4], t[4])
   161  	r[5] = ctPick64(w, r[5], t[5])
   162  	r[6] = ctPick64(w, r[6], t[6])
   163  	r[7] = ctPick64(w, r[7], t[7])
   164  }
   165  
   166  // modExpRdc does modular exponentiation of 512-bit number.
   167  // Constant-time.
   168  func modExpRdc512(r, b, e *fp) {
   169  	modExpRdcCommon(r, b, e, 512)
   170  }
   171  
   172  // modExpRdc does modular exponentiation of 64-bit number.
   173  // Constant-time.
   174  func modExpRdc64(r, b *fp, e uint64) {
   175  	modExpRdcCommon(r, b, &fp{e}, 64)
   176  }
   177  
   178  // isNonQuadRes checks whether value v is quadratic residue.
   179  // Implementation uses Fermat's little theorem (or
   180  // Euler's criterion)
   181  //
   182  //	a^(p-1) == 1, hence
   183  //	(a^2) ((p-1)/2) == 1
   184  //
   185  // Which means v is a quadratic residue iff v^((p-1)/2) == 1.
   186  // Caller provided v must be in montgomery domain.
   187  // Returns 0 in case v is quadratic residue or 1 in case
   188  // v is quadratic non-residue.
   189  func (v *fp) isNonQuadRes() int {
   190  	var res fp
   191  	var b uint64
   192  
   193  	modExpRdc512(&res, v, &pMin1By2)
   194  	for i := range res {
   195  		b |= res[i] ^ one[i]
   196  	}
   197  
   198  	return ctIsNonZero64(b)
   199  }
   200  
   201  // isZero returns false in case v is equal to 0, otherwise
   202  // true. Constant time.
   203  func (v *fp) isZero() bool {
   204  	var r uint64
   205  	for i := 0; i < numWords; i++ {
   206  		r |= v[i]
   207  	}
   208  	return ctIsNonZero64(r) == 0
   209  }
   210  
   211  // equal checks if v is equal to in. Constant time.
   212  func (v *fp) equal(in *fp) bool {
   213  	var r uint64
   214  	for i := range v {
   215  		r |= v[i] ^ in[i]
   216  	}
   217  	return ctIsNonZero64(r) == 0
   218  }