vendor.github.com.cloudflare.circl.ecc.p384.p384opt.go Maven / Gradle / Ivy
The newest version!
//go:build (!noasm && arm64) || (!noasm && amd64)
// +build !noasm,arm64 !noasm,amd64
package p384
import (
"crypto/subtle"
"math/big"
"github.com/cloudflare/circl/math"
)
type curve struct{}
// P384 returns a Curve which implements P-384 (see FIPS 186-3, section D.2.4).
func P384() Curve { return curve{} }
// IsOnCurve reports whether the given (x,y) lies on the curve.
func (c curve) IsOnCurve(x, y *big.Int) bool {
x1, y1 := &fp384{}, &fp384{}
x1.SetBigInt(x)
y1.SetBigInt(y)
montEncode(x1, x1)
montEncode(y1, y1)
y2, x3 := &fp384{}, &fp384{}
fp384Sqr(y2, y1)
fp384Sqr(x3, x1)
fp384Mul(x3, x3, x1)
threeX := &fp384{}
fp384Add(threeX, x1, x1)
fp384Add(threeX, threeX, x1)
fp384Sub(x3, x3, threeX)
fp384Add(x3, x3, &bb)
return *y2 == *x3
}
// Add returns the sum of (x1,y1) and (x2,y2).
func (c curve) Add(x1, y1, x2, y2 *big.Int) (x, y *big.Int) {
P := newAffinePoint(x1, y1).toJacobian()
P.mixadd(P, newAffinePoint(x2, y2))
return P.toAffine().toInt()
}
// Double returns 2*(x,y).
func (c curve) Double(x1, y1 *big.Int) (x, y *big.Int) {
P := newAffinePoint(x1, y1).toJacobian()
P.double()
return P.toAffine().toInt()
}
// reduceScalar shorten a scalar modulo the order of the curve.
func (c curve) reduceScalar(k []byte) []byte {
bigK := new(big.Int).SetBytes(k)
bigK.Mod(bigK, c.Params().N)
return bigK.FillBytes(make([]byte, sizeFp))
}
// toOdd performs k = (-k mod N) if k is even.
func (c curve) toOdd(k []byte) ([]byte, int) {
var X, Y big.Int
X.SetBytes(k)
Y.Neg(&X).Mod(&Y, c.Params().N)
isEven := 1 - int(X.Bit(0))
x := X.Bytes()
y := Y.Bytes()
if len(x) < len(y) {
x = append(make([]byte, len(y)-len(x)), x...)
} else if len(x) > len(y) {
y = append(make([]byte, len(x)-len(y)), y...)
}
subtle.ConstantTimeCopy(isEven, x, y)
return x, isEven
}
// ScalarMult returns (Qx,Qy)=k*(Px,Py) where k is a number in big-endian form.
func (c curve) ScalarMult(x1, y1 *big.Int, k []byte) (x, y *big.Int) {
return c.scalarMultOmega(x1, y1, k, 5)
}
func (c curve) scalarMultOmega(x1, y1 *big.Int, k []byte, omega uint) (x, y *big.Int) {
k = c.reduceScalar(k)
oddK, isEvenK := c.toOdd(k)
var scalar big.Int
scalar.SetBytes(oddK)
if scalar.Sign() == 0 {
return new(big.Int), new(big.Int)
}
const bitsN = uint(384)
L := math.SignedDigit(&scalar, omega, bitsN)
var R jacobianPoint
Q := zeroPoint().toJacobian()
TabP := newAffinePoint(x1, y1).oddMultiples(omega)
for i := len(L) - 1; i > 0; i-- {
for j := uint(0); j < omega-1; j++ {
Q.double()
}
idx := absolute(L[i]) >> 1
for j := range TabP {
R.cmov(&TabP[j], subtle.ConstantTimeEq(int32(j), idx))
}
R.cneg(int(L[i]>>31) & 1)
Q.add(Q, &R)
}
// Calculate the last iteration using complete addition formula.
for j := uint(0); j < omega-1; j++ {
Q.double()
}
idx := absolute(L[0]) >> 1
for j := range TabP {
R.cmov(&TabP[j], subtle.ConstantTimeEq(int32(j), idx))
}
R.cneg(int(L[0]>>31) & 1)
QQ := Q.toProjective()
QQ.completeAdd(QQ, R.toProjective())
QQ.cneg(isEvenK)
return QQ.toAffine().toInt()
}
// ScalarBaseMult returns k*G, where G is the base point of the group
// and k is an integer in big-endian form.
func (c curve) ScalarBaseMult(k []byte) (x, y *big.Int) {
params := c.Params()
return c.ScalarMult(params.Gx, params.Gy, k)
}
// CombinedMult calculates P=mG+nQ, where G is the generator and Q=(x,y,z).
// The scalars m and n are integers in big-endian form. Non-constant time.
func (c curve) CombinedMult(xQ, yQ *big.Int, m, n []byte) (xP, yP *big.Int) {
const nOmega = uint(5)
var k big.Int
k.SetBytes(m)
nafM := math.OmegaNAF(&k, baseOmega)
k.SetBytes(n)
nafN := math.OmegaNAF(&k, nOmega)
if len(nafM) > len(nafN) {
nafN = append(nafN, make([]int32, len(nafM)-len(nafN))...)
} else if len(nafM) < len(nafN) {
nafM = append(nafM, make([]int32, len(nafN)-len(nafM))...)
}
TabQ := newAffinePoint(xQ, yQ).oddMultiples(nOmega)
var jR jacobianPoint
var aR affinePoint
P := zeroPoint().toJacobian()
for i := len(nafN) - 1; i >= 0; i-- {
P.double()
// Generator point
if nafM[i] != 0 {
idxM := absolute(nafM[i]) >> 1
aR = baseOddMultiples[idxM]
if nafM[i] < 0 {
aR.neg()
}
P.mixadd(P, &aR)
}
// Input point
if nafN[i] != 0 {
idxN := absolute(nafN[i]) >> 1
jR = TabQ[idxN]
if nafN[i] < 0 {
jR.neg()
}
P.add(P, &jR)
}
}
return P.toAffine().toInt()
}
// absolute returns always a positive value.
func absolute(x int32) int32 {
mask := x >> 31
return (x + mask) ^ mask
}