github.com/cloudflare/circl@v1.5.0/ecc/p384/p384opt.go (about) 1 //go:build (!purego && arm64) || (!purego && amd64) 2 // +build !purego,arm64 !purego,amd64 3 4 package p384 5 6 import ( 7 "crypto/subtle" 8 "math/big" 9 10 "github.com/cloudflare/circl/math" 11 ) 12 13 type curve struct{} 14 15 // P384 returns a Curve which implements P-384 (see FIPS 186-3, section D.2.4). 16 func P384() Curve { return curve{} } 17 18 // IsOnCurve reports whether the given (x,y) lies on the curve. 19 func (c curve) IsOnCurve(x, y *big.Int) bool { 20 x1, y1 := &fp384{}, &fp384{} 21 x1.SetBigInt(x) 22 y1.SetBigInt(y) 23 montEncode(x1, x1) 24 montEncode(y1, y1) 25 26 y2, x3 := &fp384{}, &fp384{} 27 fp384Sqr(y2, y1) 28 fp384Sqr(x3, x1) 29 fp384Mul(x3, x3, x1) 30 31 threeX := &fp384{} 32 fp384Add(threeX, x1, x1) 33 fp384Add(threeX, threeX, x1) 34 35 fp384Sub(x3, x3, threeX) 36 fp384Add(x3, x3, &bb) 37 38 return *y2 == *x3 39 } 40 41 // Add returns the sum of (x1,y1) and (x2,y2). 42 func (c curve) Add(x1, y1, x2, y2 *big.Int) (x, y *big.Int) { 43 P := newAffinePoint(x1, y1).toJacobian() 44 P.mixadd(P, newAffinePoint(x2, y2)) 45 return P.toAffine().toInt() 46 } 47 48 // Double returns 2*(x,y). 49 func (c curve) Double(x1, y1 *big.Int) (x, y *big.Int) { 50 P := newAffinePoint(x1, y1).toJacobian() 51 P.double() 52 return P.toAffine().toInt() 53 } 54 55 // reduceScalar shorten a scalar modulo the order of the curve. 56 func (c curve) reduceScalar(k []byte) []byte { 57 bigK := new(big.Int).SetBytes(k) 58 bigK.Mod(bigK, c.Params().N) 59 return bigK.FillBytes(make([]byte, sizeFp)) 60 } 61 62 // toOdd performs k = (-k mod N) if k is even. 63 func (c curve) toOdd(k []byte) ([]byte, int) { 64 var X, Y big.Int 65 X.SetBytes(k) 66 Y.Neg(&X).Mod(&Y, c.Params().N) 67 isEven := 1 - int(X.Bit(0)) 68 x := X.Bytes() 69 y := Y.Bytes() 70 71 if len(x) < len(y) { 72 x = append(make([]byte, len(y)-len(x)), x...) 73 } else if len(x) > len(y) { 74 y = append(make([]byte, len(x)-len(y)), y...) 75 } 76 subtle.ConstantTimeCopy(isEven, x, y) 77 return x, isEven 78 } 79 80 // ScalarMult returns (Qx,Qy)=k*(Px,Py) where k is a number in big-endian form. 81 func (c curve) ScalarMult(x1, y1 *big.Int, k []byte) (x, y *big.Int) { 82 return c.scalarMultOmega(x1, y1, k, 5) 83 } 84 85 func (c curve) scalarMultOmega(x1, y1 *big.Int, k []byte, omega uint) (x, y *big.Int) { 86 k = c.reduceScalar(k) 87 oddK, isEvenK := c.toOdd(k) 88 89 var scalar big.Int 90 scalar.SetBytes(oddK) 91 if scalar.Sign() == 0 { 92 return new(big.Int), new(big.Int) 93 } 94 const bitsN = uint(384) 95 L := math.SignedDigit(&scalar, omega, bitsN) 96 97 var R jacobianPoint 98 Q := zeroPoint().toJacobian() 99 TabP := newAffinePoint(x1, y1).oddMultiples(omega) 100 for i := len(L) - 1; i > 0; i-- { 101 for j := uint(0); j < omega-1; j++ { 102 Q.double() 103 } 104 idx := absolute(L[i]) >> 1 105 for j := range TabP { 106 R.cmov(&TabP[j], subtle.ConstantTimeEq(int32(j), idx)) 107 } 108 R.cneg(int(L[i]>>31) & 1) 109 Q.add(Q, &R) 110 } 111 // Calculate the last iteration using complete addition formula. 112 for j := uint(0); j < omega-1; j++ { 113 Q.double() 114 } 115 idx := absolute(L[0]) >> 1 116 for j := range TabP { 117 R.cmov(&TabP[j], subtle.ConstantTimeEq(int32(j), idx)) 118 } 119 R.cneg(int(L[0]>>31) & 1) 120 QQ := Q.toProjective() 121 QQ.completeAdd(QQ, R.toProjective()) 122 QQ.cneg(isEvenK) 123 return QQ.toAffine().toInt() 124 } 125 126 // ScalarBaseMult returns k*G, where G is the base point of the group 127 // and k is an integer in big-endian form. 128 func (c curve) ScalarBaseMult(k []byte) (x, y *big.Int) { 129 params := c.Params() 130 return c.ScalarMult(params.Gx, params.Gy, k) 131 } 132 133 // CombinedMult calculates P=mG+nQ, where G is the generator and Q=(x,y,z). 134 // The scalars m and n are integers in big-endian form. Non-constant time. 135 func (c curve) CombinedMult(xQ, yQ *big.Int, m, n []byte) (xP, yP *big.Int) { 136 const nOmega = uint(5) 137 var k big.Int 138 k.SetBytes(m) 139 nafM := math.OmegaNAF(&k, baseOmega) 140 k.SetBytes(n) 141 nafN := math.OmegaNAF(&k, nOmega) 142 143 if len(nafM) > len(nafN) { 144 nafN = append(nafN, make([]int32, len(nafM)-len(nafN))...) 145 } else if len(nafM) < len(nafN) { 146 nafM = append(nafM, make([]int32, len(nafN)-len(nafM))...) 147 } 148 149 TabQ := newAffinePoint(xQ, yQ).oddMultiples(nOmega) 150 var jR jacobianPoint 151 var aR affinePoint 152 P := zeroPoint().toJacobian() 153 for i := len(nafN) - 1; i >= 0; i-- { 154 P.double() 155 // Generator point 156 if nafM[i] != 0 { 157 idxM := absolute(nafM[i]) >> 1 158 aR = baseOddMultiples[idxM] 159 if nafM[i] < 0 { 160 aR.neg() 161 } 162 P.mixadd(P, &aR) 163 } 164 // Input point 165 if nafN[i] != 0 { 166 idxN := absolute(nafN[i]) >> 1 167 jR = TabQ[idxN] 168 if nafN[i] < 0 { 169 jR.neg() 170 } 171 P.add(P, &jR) 172 } 173 } 174 return P.toAffine().toInt() 175 } 176 177 // absolute returns always a positive value. 178 func absolute(x int32) int32 { 179 mask := x >> 31 180 return (x + mask) ^ mask 181 }