作者 | SHA1 | 备注 | 提交日期 |
---|---|---|---|
Henry Case | 9a6b2b414e | test: run go vet with 'test' target | 6 年前 |
Kris Kwiatkowski | a1db6b7e3e | Makes SIDH independent on underlying field arithmetic. | 6 年前 |
Henry Case | 1664c12293 | makefile: improvements | 6 年前 |
Henry Case | e240204c18 | cleanup: FromAffine already sets Z in projective point to 1 | 6 年前 |
Henry Case | 6d1840307d |
makes GeneratePublicKey method of PrivateKey
It makes a little bit more sense to have GeneratePublicKey as a method of PrivateKey. In this case code doesn't need to check if caller provided pointer is nil. Object was created by NewPrivateKey(), so it code can assume object was correctly initialized. The old GeneratePublicKey was returning an error when caller provided pointer was nil. As this possibility is now removed, method doesn't return error anymore. |
6 年前 |
Henry Case | 903f1e96b6 |
cleanup: moves from/to bytes conversion
From/to bytes conversion will be refactored when p503 is introduced. Patch splits part that uses field specific functions from part that converts Fp element to bytes. Patch also removes some testing helpers which are no longer needed. |
6 年前 |
@@ -4,7 +4,7 @@ PRJ_DIR = $(abspath $(dir $(MK_FILE_PATH))) | |||
GOPATH_LOCAL = $(PRJ_DIR)/build | |||
GOPATH_DIR = github.com/cloudflare/p751sidh | |||
CSHAKE_PKG ?= github.com/henrydcase/nobs/hash/sha3 | |||
TARGETS = p751toolbox sidh sike | |||
TARGETS = p751 sidh sike | |||
GOARCH ?= | |||
OPTS_GCCGO ?= -compiler gccgo -O2 -g | |||
OPTS ?= | |||
@@ -24,6 +24,8 @@ ifeq ($(V),1) | |||
BENCH_OPTS += -gcflags=-m # Show results from inlining | |||
endif | |||
# Targets | |||
all: test | |||
clean: | |||
rm -rf $(GOPATH_LOCAL) | |||
rm -rf coverage*.txt | |||
@@ -31,6 +33,7 @@ clean: | |||
build_env: | |||
GOPATH=$(GOPATH_LOCAL) go get $(CSHAKE_PKG) | |||
mkdir -p $(GOPATH_LOCAL)/src/$(GOPATH_DIR) | |||
cp -rf internal $(GOPATH_LOCAL)/src/$(GOPATH_DIR) | |||
cp -rf etc $(GOPATH_LOCAL)/src/$(GOPATH_DIR) | |||
copy-target-%: | |||
@@ -42,10 +45,11 @@ install-%: prep_targets | |||
GOPATH=$(GOPATH_LOCAL) go install $(OPTS) $(GOPATH_DIR)/$* | |||
test-%: prep_targets | |||
GOPATH=$(GOPATH_LOCAL) go vet $(GOPATH_DIR)/$* | |||
GOPATH=$(GOPATH_LOCAL) go test $(OPTS) $(GOPATH_DIR)/$* | |||
bench-%: prep_targets | |||
cd $*; GOPATH=$(GOPATH_LOCAL) go test $(OPTS) $(BENCH_OPTS) | |||
GOPATH=$(GOPATH_LOCAL) go test $(OPTS) $(GOPATH_DIR)/$* $(BENCH_OPTS) | |||
cover-%: prep_targets | |||
GOPATH=$(GOPATH_LOCAL) go test \ | |||
@@ -0,0 +1,440 @@ | |||
package internal | |||
type CurveOperations struct { | |||
Params *SidhParams | |||
} | |||
// Computes j-invariant for a curve y2=x3+A/Cx+x with A,C in F_(p^2). Result | |||
// is returned in jBytes buffer, encoded in little-endian format. Caller | |||
// provided jBytes buffer has to be big enough to j-invariant value. In case | |||
// of SIDH, buffer size must be at least size of shared secret. | |||
// Implementation corresponds to Algorithm 9 from SIKE. | |||
func (c *CurveOperations) Jinvariant(cparams *ProjectiveCurveParameters, jBytes []byte) { | |||
var j, t0, t1 Fp2Element | |||
op := c.Params.Op | |||
op.Square(&j, &cparams.A) // j = A^2 | |||
op.Square(&t1, &cparams.C) // t1 = C^2 | |||
op.Add(&t0, &t1, &t1) // t0 = t1 + t1 | |||
op.Sub(&t0, &j, &t0) // t0 = j - t0 | |||
op.Sub(&t0, &t0, &t1) // t0 = t0 - t1 | |||
op.Sub(&j, &t0, &t1) // t0 = t0 - t1 | |||
op.Square(&t1, &t1) // t1 = t1^2 | |||
op.Mul(&j, &j, &t1) // j = j * t1 | |||
op.Add(&t0, &t0, &t0) // t0 = t0 + t0 | |||
op.Add(&t0, &t0, &t0) // t0 = t0 + t0 | |||
op.Square(&t1, &t0) // t1 = t0^2 | |||
op.Mul(&t0, &t0, &t1) // t0 = t0 * t1 | |||
op.Add(&t0, &t0, &t0) // t0 = t0 + t0 | |||
op.Add(&t0, &t0, &t0) // t0 = t0 + t0 | |||
op.Inv(&j, &j) // j = 1/j | |||
op.Mul(&j, &t0, &j) // j = t0 * j | |||
c.Fp2ToBytes(jBytes, &j) | |||
} | |||
// Given affine points x(P), x(Q) and x(Q-P) in a extension field F_{p^2}, function | |||
// recorvers projective coordinate A of a curve. This is Algorithm 10 from SIKE. | |||
func (c *CurveOperations) RecoverCoordinateA(curve *ProjectiveCurveParameters, xp, xq, xr *Fp2Element) { | |||
var t0, t1 Fp2Element | |||
op := c.Params.Op | |||
op.Add(&t1, xp, xq) // t1 = Xp + Xq | |||
op.Mul(&t0, xp, xq) // t0 = Xp * Xq | |||
op.Mul(&curve.A, xr, &t1) // A = X(q-p) * t1 | |||
op.Add(&curve.A, &curve.A, &t0) // A = A + t0 | |||
op.Mul(&t0, &t0, xr) // t0 = t0 * X(q-p) | |||
op.Sub(&curve.A, &curve.A, &c.Params.OneFp2) // A = A - 1 | |||
op.Add(&t0, &t0, &t0) // t0 = t0 + t0 | |||
op.Add(&t1, &t1, xr) // t1 = t1 + X(q-p) | |||
op.Add(&t0, &t0, &t0) // t0 = t0 + t0 | |||
op.Square(&curve.A, &curve.A) // A = A^2 | |||
op.Inv(&t0, &t0) // t0 = 1/t0 | |||
op.Mul(&curve.A, &curve.A, &t0) // A = A * t0 | |||
op.Sub(&curve.A, &curve.A, &t1) // A = A - t1 | |||
} | |||
// Computes equivalence (A:C) ~ (A+2C : A-2C) | |||
func (c *CurveOperations) CalcCurveParamsEquiv3(cparams *ProjectiveCurveParameters) CurveCoefficientsEquiv { | |||
var coef CurveCoefficientsEquiv | |||
var c2 Fp2Element | |||
var op = c.Params.Op | |||
op.Add(&c2, &cparams.C, &cparams.C) | |||
// A24p = A+2*C | |||
op.Add(&coef.A, &cparams.A, &c2) | |||
// A24m = A-2*C | |||
op.Sub(&coef.C, &cparams.A, &c2) | |||
return coef | |||
} | |||
// Computes equivalence (A:C) ~ (A+2C : 4C) | |||
func (c *CurveOperations) CalcCurveParamsEquiv4(cparams *ProjectiveCurveParameters) CurveCoefficientsEquiv { | |||
var coefEq CurveCoefficientsEquiv | |||
var op = c.Params.Op | |||
op.Add(&coefEq.C, &cparams.C, &cparams.C) | |||
// A24p = A+2C | |||
op.Add(&coefEq.A, &cparams.A, &coefEq.C) | |||
// C24 = 4*C | |||
op.Add(&coefEq.C, &coefEq.C, &coefEq.C) | |||
return coefEq | |||
} | |||
// Helper function for RightToLeftLadder(). Returns A+2C / 4. | |||
func (c *CurveOperations) CalcAplus2Over4(cparams *ProjectiveCurveParameters) (ret Fp2Element) { | |||
var tmp Fp2Element | |||
var op = c.Params.Op | |||
// 2C | |||
op.Add(&tmp, &cparams.C, &cparams.C) | |||
// A+2C | |||
op.Add(&ret, &cparams.A, &tmp) | |||
// 1/4C | |||
op.Add(&tmp, &tmp, &tmp) | |||
op.Inv(&tmp, &tmp) | |||
// A+2C/4C | |||
op.Mul(&ret, &ret, &tmp) | |||
return | |||
} | |||
// Recovers (A:C) curve parameters from projectively equivalent (A+2C:A-2C). | |||
func (c *CurveOperations) RecoverCurveCoefficients3(cparams *ProjectiveCurveParameters, coefEq *CurveCoefficientsEquiv) { | |||
var op = c.Params.Op | |||
op.Add(&cparams.A, &coefEq.A, &coefEq.C) | |||
// cparams.A = 2*(A+2C+A-2C) = 4A | |||
op.Add(&cparams.A, &cparams.A, &cparams.A) | |||
// cparams.C = (A+2C-A+2C) = 4C | |||
op.Sub(&cparams.C, &coefEq.A, &coefEq.C) | |||
return | |||
} | |||
// Recovers (A:C) curve parameters from projectively equivalent (A+2C:4C). | |||
func (c *CurveOperations) RecoverCurveCoefficients4(cparams *ProjectiveCurveParameters, coefEq *CurveCoefficientsEquiv) { | |||
var op = c.Params.Op | |||
// cparams.C = (4C)*1/2=2C | |||
op.Mul(&cparams.C, &coefEq.C, &c.Params.HalfFp2) | |||
// cparams.A = A+2C - 2C = A | |||
op.Sub(&cparams.A, &coefEq.A, &cparams.C) | |||
// cparams.C = 2C * 1/2 = C | |||
op.Mul(&cparams.C, &cparams.C, &c.Params.HalfFp2) | |||
return | |||
} | |||
// Combined coordinate doubling and differential addition. Takes projective points | |||
// P,Q,Q-P and (A+2C)/4C curve E coefficient. Returns 2*P and P+Q calculated on E. | |||
// Function is used only by RightToLeftLadder. Corresponds to Algorithm 5 of SIKE | |||
func (c *CurveOperations) xDblAdd(P, Q, QmP *ProjectivePoint, a24 *Fp2Element) (dblP, PaQ ProjectivePoint) { | |||
var t0, t1, t2 Fp2Element | |||
var op = c.Params.Op | |||
xQmP, zQmP := &QmP.X, &QmP.Z | |||
xPaQ, zPaQ := &PaQ.X, &PaQ.Z | |||
x2P, z2P := &dblP.X, &dblP.Z | |||
xP, zP := &P.X, &P.Z | |||
xQ, zQ := &Q.X, &Q.Z | |||
op.Add(&t0, xP, zP) // t0 = Xp+Zp | |||
op.Sub(&t1, xP, zP) // t1 = Xp-Zp | |||
op.Square(x2P, &t0) // 2P.X = t0^2 | |||
op.Sub(&t2, xQ, zQ) // t2 = Xq-Zq | |||
op.Add(xPaQ, xQ, zQ) // Xp+q = Xq+Zq | |||
op.Mul(&t0, &t0, &t2) // t0 = t0 * t2 | |||
op.Mul(z2P, &t1, &t1) // 2P.Z = t1 * t1 | |||
op.Mul(&t1, &t1, xPaQ) // t1 = t1 * Xp+q | |||
op.Sub(&t2, x2P, z2P) // t2 = 2P.X - 2P.Z | |||
op.Mul(x2P, x2P, z2P) // 2P.X = 2P.X * 2P.Z | |||
op.Mul(xPaQ, a24, &t2) // Xp+q = A24 * t2 | |||
op.Sub(zPaQ, &t0, &t1) // Zp+q = t0 - t1 | |||
op.Add(z2P, xPaQ, z2P) // 2P.Z = Xp+q + 2P.Z | |||
op.Add(xPaQ, &t0, &t1) // Xp+q = t0 + t1 | |||
op.Mul(z2P, z2P, &t2) // 2P.Z = 2P.Z * t2 | |||
op.Square(zPaQ, zPaQ) // Zp+q = Zp+q ^ 2 | |||
op.Square(xPaQ, xPaQ) // Xp+q = Xp+q ^ 2 | |||
op.Mul(zPaQ, xQmP, zPaQ) // Zp+q = Xq-p * Zp+q | |||
op.Mul(xPaQ, zQmP, xPaQ) // Xp+q = Zq-p * Xp+q | |||
return | |||
} | |||
// Given the curve parameters, xP = x(P), computes xP = x([2^k]P) | |||
// Safe to overlap xP, x2P. | |||
func (c *CurveOperations) Pow2k(xP *ProjectivePoint, params *CurveCoefficientsEquiv, k uint32) { | |||
var t0, t1 Fp2Element | |||
var op = c.Params.Op | |||
x, z := &xP.X, &xP.Z | |||
for i := uint32(0); i < k; i++ { | |||
op.Sub(&t0, x, z) // t0 = Xp - Zp | |||
op.Add(&t1, x, z) // t1 = Xp + Zp | |||
op.Square(&t0, &t0) // t0 = t0 ^ 2 | |||
op.Square(&t1, &t1) // t1 = t1 ^ 2 | |||
op.Mul(z, ¶ms.C, &t0) // Z2p = C24 * t0 | |||
op.Mul(x, z, &t1) // X2p = Z2p * t1 | |||
op.Sub(&t1, &t1, &t0) // t1 = t1 - t0 | |||
op.Mul(&t0, ¶ms.A, &t1) // t0 = A24+ * t1 | |||
op.Add(z, z, &t0) // Z2p = Z2p + t0 | |||
op.Mul(z, z, &t1) // Zp = Z2p * t1 | |||
} | |||
} | |||
// Given the curve parameters, xP = x(P), and k >= 0, compute xP = x([3^k]P). | |||
// | |||
// Safe to overlap xP, xR. | |||
func (c *CurveOperations) Pow3k(xP *ProjectivePoint, params *CurveCoefficientsEquiv, k uint32) { | |||
var t0, t1, t2, t3, t4, t5, t6 Fp2Element | |||
var op = c.Params.Op | |||
x, z := &xP.X, &xP.Z | |||
for i := uint32(0); i < k; i++ { | |||
op.Sub(&t0, x, z) // t0 = Xp - Zp | |||
op.Square(&t2, &t0) // t2 = t0^2 | |||
op.Add(&t1, x, z) // t1 = Xp + Zp | |||
op.Square(&t3, &t1) // t3 = t1^2 | |||
op.Add(&t4, &t1, &t0) // t4 = t1 + t0 | |||
op.Sub(&t0, &t1, &t0) // t0 = t1 - t0 | |||
op.Square(&t1, &t4) // t1 = t4^2 | |||
op.Sub(&t1, &t1, &t3) // t1 = t1 - t3 | |||
op.Sub(&t1, &t1, &t2) // t1 = t1 - t2 | |||
op.Mul(&t5, &t3, ¶ms.A) // t5 = t3 * A24+ | |||
op.Mul(&t3, &t3, &t5) // t3 = t5 * t3 | |||
op.Mul(&t6, &t2, ¶ms.C) // t6 = t2 * A24- | |||
op.Mul(&t2, &t2, &t6) // t2 = t2 * t6 | |||
op.Sub(&t3, &t2, &t3) // t3 = t2 - t3 | |||
op.Sub(&t2, &t5, &t6) // t2 = t5 - t6 | |||
op.Mul(&t1, &t2, &t1) // t1 = t2 * t1 | |||
op.Add(&t2, &t3, &t1) // t2 = t3 + t1 | |||
op.Square(&t2, &t2) // t2 = t2^2 | |||
op.Mul(x, &t2, &t4) // X3p = t2 * t4 | |||
op.Sub(&t1, &t3, &t1) // t1 = t3 - t1 | |||
op.Square(&t1, &t1) // t1 = t1^2 | |||
op.Mul(z, &t1, &t0) // Z3p = t1 * t0 | |||
} | |||
} | |||
// Set (y1, y2, y3) = (1/x1, 1/x2, 1/x3). | |||
// | |||
// All xi, yi must be distinct. | |||
func (c *CurveOperations) Fp2Batch3Inv(x1, x2, x3, y1, y2, y3 *Fp2Element) { | |||
var x1x2, t Fp2Element | |||
var op = c.Params.Op | |||
op.Mul(&x1x2, x1, x2) // x1*x2 | |||
op.Mul(&t, &x1x2, x3) // 1/(x1*x2*x3) | |||
op.Inv(&t, &t) | |||
op.Mul(y1, &t, x2) // 1/x1 | |||
op.Mul(y1, y1, x3) | |||
op.Mul(y2, &t, x1) // 1/x2 | |||
op.Mul(y2, y2, x3) | |||
op.Mul(y3, &t, &x1x2) // 1/x3 | |||
} | |||
// ScalarMul3Pt is a right-to-left point multiplication that given the | |||
// x-coordinate of P, Q and P-Q calculates the x-coordinate of R=Q+[scalar]P. | |||
// nbits must be smaller or equal to len(scalar). | |||
func (c *CurveOperations) ScalarMul3Pt(cparams *ProjectiveCurveParameters, P, Q, PmQ *ProjectivePoint, nbits uint, scalar []uint8) ProjectivePoint { | |||
var R0, R2, R1 ProjectivePoint | |||
var op = c.Params.Op | |||
aPlus2Over4 := c.CalcAplus2Over4(cparams) | |||
R1 = *P | |||
R2 = *PmQ | |||
R0 = *Q | |||
// Iterate over the bits of the scalar, bottom to top | |||
prevBit := uint8(0) | |||
for i := uint(0); i < nbits; i++ { | |||
bit := (scalar[i>>3] >> (i & 7) & 1) | |||
swap := prevBit ^ bit | |||
prevBit = bit | |||
op.CondSwap(&R1.X, &R1.Z, &R2.X, &R2.Z, swap) | |||
R0, R2 = c.xDblAdd(&R0, &R2, &R1, &aPlus2Over4) | |||
} | |||
op.CondSwap(&R1.X, &R1.Z, &R2.X, &R2.Z, prevBit) | |||
return R1 | |||
} | |||
// Convert the input to wire format. | |||
// | |||
// The output byte slice must be at least 2*bytelen(p) bytes long. | |||
func (c *CurveOperations) Fp2ToBytes(output []byte, fp2 *Fp2Element) { | |||
if len(output) < 2*c.Params.Bytelen { | |||
panic("output byte slice too short") | |||
} | |||
var a Fp2Element | |||
c.Params.Op.FromMontgomery(fp2,&a) | |||
// convert to bytes in little endian form | |||
for i := 0; i < c.Params.Bytelen; i++ { | |||
// set i = j*8 + k | |||
fp2 := i / 8 | |||
k := uint64(i % 8) | |||
output[i] = byte(a.A[fp2] >> (8 * k)) | |||
output[i+c.Params.Bytelen] = byte(a.B[fp2] >> (8 * k)) | |||
} | |||
} | |||
// Read 2*bytelen(p) bytes into the given ExtensionFieldElement. | |||
// | |||
// It is an error to call this function if the input byte slice is less than 2*bytelen(p) bytes long. | |||
func (c *CurveOperations) Fp2FromBytes(fp2 *Fp2Element, input []byte) { | |||
if len(input) < 2*c.Params.Bytelen { | |||
panic("input byte slice too short") | |||
} | |||
for i:=0; i<c.Params.Bytelen; i++ { | |||
j := i / 8 | |||
k := uint64(i % 8) | |||
fp2.A[j] |= uint64(input[i]) << (8 * k) | |||
fp2.B[j] |= uint64(input[i+c.Params.Bytelen]) << (8 * k) | |||
} | |||
c.Params.Op.ToMontgomery(fp2) | |||
} | |||
/* ------------------------------------------------------------------------- | |||
Mechnisms used for isogeny calculations | |||
-------------------------------------------------------------------------*/ | |||
// Constructs isogeny3 objects | |||
func Newisogeny3(op FieldOps) Isogeny { | |||
return &isogeny3{Field: op} | |||
} | |||
// Constructs isogeny4 objects | |||
func Newisogeny4(op FieldOps) Isogeny { | |||
return &isogeny4{isogeny3: isogeny3{Field: op}} | |||
} | |||
// Given a three-torsion point p = x(PB) on the curve E_(A:C), construct the | |||
// three-isogeny phi : E_(A:C) -> E_(A:C)/<P_3> = E_(A':C'). | |||
// | |||
// Input: (XP_3: ZP_3), where P_3 has exact order 3 on E_A/C | |||
// Output: * Curve coordinates (A' + 2C', A' - 2C') corresponding to E_A'/C' = A_E/C/<P3> | |||
// * Isogeny phi with constants in F_p^2 | |||
func (phi *isogeny3) GenerateCurve(p *ProjectivePoint) CurveCoefficientsEquiv { | |||
var t0, t1, t2, t3, t4 Fp2Element | |||
var coefEq CurveCoefficientsEquiv | |||
var K1, K2 = &phi.K1, &phi.K2 | |||
op := phi.Field | |||
op.Sub(K1, &p.X, &p.Z) // K1 = XP3 - ZP3 | |||
op.Square(&t0, K1) // t0 = K1^2 | |||
op.Add(K2, &p.X, &p.Z) // K2 = XP3 + ZP3 | |||
op.Square(&t1, K2) // t1 = K2^2 | |||
op.Add(&t2, &t0, &t1) // t2 = t0 + t1 | |||
op.Add(&t3, K1, K2) // t3 = K1 + K2 | |||
op.Square(&t3, &t3) // t3 = t3^2 | |||
op.Sub(&t3, &t3, &t2) // t3 = t3 - t2 | |||
op.Add(&t2, &t1, &t3) // t2 = t1 + t3 | |||
op.Add(&t3, &t3, &t0) // t3 = t3 + t0 | |||
op.Add(&t4, &t3, &t0) // t4 = t3 + t0 | |||
op.Add(&t4, &t4, &t4) // t4 = t4 + t4 | |||
op.Add(&t4, &t1, &t4) // t4 = t1 + t4 | |||
op.Mul(&coefEq.C, &t2, &t4) // A24m = t2 * t4 | |||
op.Add(&t4, &t1, &t2) // t4 = t1 + t2 | |||
op.Add(&t4, &t4, &t4) // t4 = t4 + t4 | |||
op.Add(&t4, &t0, &t4) // t4 = t0 + t4 | |||
op.Mul(&t4, &t3, &t4) // t4 = t3 * t4 | |||
op.Sub(&t0, &t4, &coefEq.C) // t0 = t4 - A24m | |||
op.Add(&coefEq.A, &coefEq.C, &t0) // A24p = A24m + t0 | |||
return coefEq | |||
} | |||
// Given a 3-isogeny phi and a point pB = x(PB), compute x(QB), the x-coordinate | |||
// of the image QB = phi(PB) of PB under phi : E_(A:C) -> E_(A':C'). | |||
// | |||
// The output xQ = x(Q) is then a point on the curve E_(A':C'); the curve | |||
// parameters are returned by the GenerateCurve function used to construct phi. | |||
func (phi *isogeny3) EvaluatePoint(p *ProjectivePoint) ProjectivePoint { | |||
var t0, t1, t2 Fp2Element | |||
var q ProjectivePoint | |||
var K1, K2 = &phi.K1, &phi.K2 | |||
var px, pz = &p.X, &p.Z | |||
op := phi.Field | |||
op.Add(&t0, px, pz) // t0 = XQ + ZQ | |||
op.Sub(&t1, px, pz) // t1 = XQ - ZQ | |||
op.Mul(&t0, K1, &t0) // t2 = K1 * t0 | |||
op.Mul(&t1, K2, &t1) // t1 = K2 * t1 | |||
op.Add(&t2, &t0, &t1) // t2 = t0 + t1 | |||
op.Sub(&t0, &t1, &t0) // t0 = t1 - t0 | |||
op.Square(&t2, &t2) // t2 = t2 ^ 2 | |||
op.Square(&t0, &t0) // t0 = t0 ^ 2 | |||
op.Mul(&q.X, px, &t2) // XQ'= XQ * t2 | |||
op.Mul(&q.Z, pz, &t0) // ZQ'= ZQ * t0 | |||
return q | |||
} | |||
// Given a four-torsion point p = x(PB) on the curve E_(A:C), construct the | |||
// four-isogeny phi : E_(A:C) -> E_(A:C)/<P_4> = E_(A':C'). | |||
// | |||
// Input: (XP_4: ZP_4), where P_4 has exact order 4 on E_A/C | |||
// Output: * Curve coordinates (A' + 2C', 4C') corresponding to E_A'/C' = A_E/C/<P4> | |||
// * Isogeny phi with constants in F_p^2 | |||
func (phi *isogeny4) GenerateCurve(p *ProjectivePoint) CurveCoefficientsEquiv { | |||
var coefEq CurveCoefficientsEquiv | |||
var xp4, zp4 = &p.X, &p.Z | |||
var K1, K2, K3 = &phi.K1, &phi.K2, &phi.K3 | |||
op := phi.Field | |||
op.Sub(K2, xp4, zp4) | |||
op.Add(K3, xp4, zp4) | |||
op.Square(K1, zp4) | |||
op.Add(K1, K1, K1) | |||
op.Square(&coefEq.C, K1) | |||
op.Add(K1, K1, K1) | |||
op.Square(&coefEq.A, xp4) | |||
op.Add(&coefEq.A, &coefEq.A, &coefEq.A) | |||
op.Square(&coefEq.A, &coefEq.A) | |||
return coefEq | |||
} | |||
// Given a 4-isogeny phi and a point xP = x(P), compute x(Q), the x-coordinate | |||
// of the image Q = phi(P) of P under phi : E_(A:C) -> E_(A':C'). | |||
// | |||
// Input: Isogeny returned by GenerateCurve and point q=(Qx,Qz) from E0_A/C | |||
// Output: Corresponding point q from E1_A'/C', where E1 is 4-isogenous to E0 | |||
func (phi *isogeny4) EvaluatePoint(p *ProjectivePoint) ProjectivePoint { | |||
var t0, t1 Fp2Element | |||
var q = *p | |||
var xq, zq = &q.X, &q.Z | |||
var K1, K2, K3 = &phi.K1, &phi.K2, &phi.K3 | |||
op := phi.Field | |||
op.Add(&t0, xq, zq) | |||
op.Sub(&t1, xq, zq) | |||
op.Mul(xq, &t0, K2) | |||
op.Mul(zq, &t1, K3) | |||
op.Mul(&t0, &t0, &t1) | |||
op.Mul(&t0, &t0, K1) | |||
op.Add(&t1, xq, zq) | |||
op.Sub(zq, xq, zq) | |||
op.Square(&t1, &t1) | |||
op.Square(zq, zq) | |||
op.Add(xq, &t0, &t1) | |||
op.Sub(&t0, zq, &t0) | |||
op.Mul(xq, xq, &t1) | |||
op.Mul(zq, zq, &t0) | |||
return q | |||
} | |||
/* ------------------------------------------------------------------------- | |||
Utils | |||
-------------------------------------------------------------------------*/ | |||
func (point *ProjectivePoint) ToAffine(c *CurveOperations) *Fp2Element { | |||
var affine_x Fp2Element | |||
c.Params.Op.Inv(&affine_x, &point.Z) | |||
c.Params.Op.Mul(&affine_x, &affine_x, &point.X) | |||
return &affine_x | |||
} | |||
// Cleans data in fp | |||
func (fp *Fp2Element) Zeroize() { | |||
// Zeroizing in 2 seperated loops tells compiler to | |||
// use fast runtime.memclr() | |||
for i:=range fp.A { | |||
fp.A[i] = 0 | |||
} | |||
for i:=range fp.B { | |||
fp.B[i] = 0 | |||
} | |||
} |
@@ -0,0 +1,146 @@ | |||
package internal | |||
const ( | |||
FP_MAX_WORDS = 12 // Currently p751.NumWords | |||
) | |||
// I keep it bool in order to be able to apply logical NOT | |||
type KeyVariant uint | |||
type PrimeFieldId uint | |||
// Representation of an element of the base field F_p. | |||
// | |||
// No particular meaning is assigned to the representation -- it could represent | |||
// an element in Montgomery form, or not. Tracking the meaning of the field | |||
// element is left to higher types. | |||
type FpElement [FP_MAX_WORDS]uint64 | |||
// Represents an intermediate product of two elements of the base field F_p. | |||
type FpElementX2 [2 * FP_MAX_WORDS]uint64 | |||
// Represents an element of the extended field Fp^2 = Fp(x+i) | |||
type Fp2Element struct { | |||
A FpElement | |||
B FpElement | |||
} | |||
type DomainParams struct { | |||
// P, Q and R=P-Q base points | |||
Affine_P, Affine_Q, Affine_R Fp2Element | |||
// Max size of secret key for x-torsion group | |||
SecretBitLen uint | |||
// MaskBytes | |||
MaskBytes []byte | |||
// Size of a compuatation strategy for x-torsion group | |||
IsogenyStrategy []uint32 | |||
} | |||
type SidhParams struct { | |||
Id PrimeFieldId | |||
// Bytelen of P | |||
Bytelen int | |||
// The secret key size, in bytes. | |||
SecretKeySize int | |||
// The public key size, in bytes. | |||
PublicKeySize int | |||
// The shared secret size, in bytes. | |||
SharedSecretSize uint | |||
// 2- and 3-torsion group parameter definitions | |||
A, B DomainParams | |||
// Sample rate to obtain a value in [0,3^238] | |||
SampleRate uint | |||
// Precomputed identity element in the Fp2 in Montgomery domain | |||
OneFp2 Fp2Element | |||
// Precomputed 1/2 in the Fp2 in Montgomery domain | |||
HalfFp2 Fp2Element | |||
// Length of SIKE secret message. Must be one of {24,32,40}, | |||
// depending on size of prime field used (see [SIKE], 1.4 and 5.1) | |||
MsgLen uint | |||
// Length of SIKE ephemeral KEM key (see [SIKE], 1.4 and 5.1) | |||
KemSize uint | |||
// Access to field arithmetic | |||
Op FieldOps | |||
} | |||
// Interface for working with isogenies. | |||
type Isogeny interface { | |||
// Given a torsion point on a curve computes isogenous curve. | |||
// Returns curve coefficients (A:C), so that E_(A/C) = E_(A/C)/<P>, | |||
// where P is a provided projective point. Sets also isogeny constants | |||
// that are needed for isogeny evaluation. | |||
GenerateCurve(*ProjectivePoint) CurveCoefficientsEquiv | |||
// Evaluates isogeny at caller provided point. Requires isogeny curve constants | |||
// to be earlier computed by GenerateCurve. | |||
EvaluatePoint(*ProjectivePoint) ProjectivePoint | |||
} | |||
// Stores curve projective parameters equivalent to A/C. Meaning of the | |||
// values depends on the context. When working with isogenies over | |||
// subgroup that are powers of: | |||
// * three then (A:C) ~ (A+2C:A-2C) | |||
// * four then (A:C) ~ (A+2C: 4C) | |||
// See Appendix A of SIKE for more details | |||
type CurveCoefficientsEquiv struct { | |||
A Fp2Element | |||
C Fp2Element | |||
} | |||
// A point on the projective line P^1(F_{p^2}). | |||
// | |||
// This represents a point on the Kummer line of a Montgomery curve. The | |||
// curve is specified by a ProjectiveCurveParameters struct. | |||
type ProjectivePoint struct { | |||
X Fp2Element | |||
Z Fp2Element | |||
} | |||
// A point on the projective line P^1(F_{p^2}). | |||
// | |||
// This is used to work projectively with the curve coefficients. | |||
type ProjectiveCurveParameters struct { | |||
A Fp2Element | |||
C Fp2Element | |||
} | |||
// Stores Isogeny 3 curve constants | |||
type isogeny3 struct { | |||
Field FieldOps | |||
K1 Fp2Element | |||
K2 Fp2Element | |||
} | |||
// Stores Isogeny 4 curve constants | |||
type isogeny4 struct { | |||
isogeny3 | |||
K3 Fp2Element | |||
} | |||
type FieldOps interface { | |||
// Set res = lhs + rhs. | |||
// | |||
// Allowed to overlap lhs or rhs with res. | |||
Add(res, lhs, rhs *Fp2Element) | |||
// Set res = lhs - rhs. | |||
// | |||
// Allowed to overlap lhs or rhs with res. | |||
Sub(res, lhs, rhs *Fp2Element) | |||
// Set res = lhs * rhs. | |||
// | |||
// Allowed to overlap lhs or rhs with res. | |||
Mul(res, lhs, rhs *Fp2Element) | |||
// Set res = x * x | |||
// | |||
// Allowed to overlap res with x. | |||
Square(res, x *Fp2Element) | |||
// Set res = 1/x | |||
// | |||
// Allowed to overlap res with x. | |||
Inv(res, x *Fp2Element) | |||
// If choice = 1u8, set (x,y) = (y,x). If choice = 0u8, set (x,y) = (x,y). | |||
CondSwap(xPx, xPz, xQx, xQz *Fp2Element, choice uint8) | |||
// Converts Fp2Element to Montgomery domain (x*R mod p) | |||
ToMontgomery(x *Fp2Element) | |||
// Converts 'a' in montgomery domain to element from Fp2Element | |||
// and stores it in 'x' | |||
FromMontgomery(x *Fp2Element, a *Fp2Element) | |||
} |
@@ -1,42 +1,44 @@ | |||
// +build amd64,!noasm | |||
package p751toolbox | |||
package p751 | |||
import . "github.com/cloudflare/p751sidh/internal/isogeny" | |||
// If choice = 0, leave x,y unchanged. If choice = 1, set x,y = y,x. | |||
// If choice is neither 0 nor 1 then behaviour is undefined. | |||
// This function executes in constant time. | |||
//go:noescape | |||
func fp751ConditionalSwap(x, y *Fp751Element, choice uint8) | |||
func fp751ConditionalSwap(x, y *FpElement, choice uint8) | |||
// Compute z = x + y (mod p). | |||
//go:noescape | |||
func fp751AddReduced(z, x, y *Fp751Element) | |||
func fp751AddReduced(z, x, y *FpElement) | |||
// Compute z = x - y (mod p). | |||
//go:noescape | |||
func fp751SubReduced(z, x, y *Fp751Element) | |||
func fp751SubReduced(z, x, y *FpElement) | |||
// Compute z = x + y, without reducing mod p. | |||
//go:noescape | |||
func fp751AddLazy(z, x, y *Fp751Element) | |||
func fp751AddLazy(z, x, y *FpElement) | |||
// Compute z = x + y, without reducing mod p. | |||
//go:noescape | |||
func fp751X2AddLazy(z, x, y *fp751X2) | |||
func fp751X2AddLazy(z, x, y *FpElementX2) | |||
// Compute z = x - y, without reducing mod p. | |||
//go:noescape | |||
func fp751X2SubLazy(z, x, y *fp751X2) | |||
func fp751X2SubLazy(z, x, y *FpElementX2) | |||
// Compute z = x * y. | |||
//go:noescape | |||
func fp751Mul(z *fp751X2, x, y *Fp751Element) | |||
func fp751Mul(z *FpElementX2, x, y *FpElement) | |||
// Perform Montgomery reduction: set z = x R^{-1} (mod 2*p). | |||
// Destroys the input value. | |||
//go:noescape | |||
func fp751MontgomeryReduce(z *Fp751Element, x *fp751X2) | |||
func fp751MontgomeryReduce(z *FpElement, x *FpElementX2) | |||
// Reduce a field element in [0, 2*p) to one in [0,p). | |||
//go:noescape | |||
func fp751StrongReduce(x *Fp751Element) | |||
func fp751StrongReduce(x *FpElement) |
@@ -1,6 +1,10 @@ | |||
// +build noasm arm64 arm | |||
package p751toolbox | |||
package p751 | |||
import ( | |||
. "github.com/cloudflare/p751sidh/internal/isogeny" | |||
) | |||
// helper used for uint128 representation | |||
type uint128 struct { | |||
@@ -66,40 +70,40 @@ func mul64(a, b uint64) (res uint128) { | |||
} | |||
// Compute z = x + y (mod p). | |||
func fp751AddReduced(z, x, y *Fp751Element) { | |||
func fp751AddReduced(z, x, y *FpElement) { | |||
var carry uint64 | |||
// z=x+y % p751 | |||
for i := 0; i < fp751NumWords; i++ { | |||
for i := 0; i < NumWords; i++ { | |||
z[i], carry = addc64(carry, x[i], y[i]) | |||
} | |||
// z = z - p751x2 | |||
carry = 0 | |||
for i := 0; i < fp751NumWords; i++ { | |||
for i := 0; i < NumWords; i++ { | |||
z[i], carry = subc64(carry, z[i], p751x2[i]) | |||
} | |||
// z = z + p751x2 | |||
mask := uint64(0 - carry) | |||
carry = 0 | |||
for i := 0; i < fp751NumWords; i++ { | |||
for i := 0; i < NumWords; i++ { | |||
z[i], carry = addc64(carry, z[i], p751x2[i]&mask) | |||
} | |||
} | |||
// Compute z = x - y (mod p). | |||
func fp751SubReduced(z, x, y *Fp751Element) { | |||
func fp751SubReduced(z, x, y *FpElement) { | |||
var borrow uint64 | |||
for i := 0; i < fp751NumWords; i++ { | |||
for i := 0; i < NumWords; i++ { | |||
z[i], borrow = subc64(borrow, x[i], y[i]) | |||
} | |||
mask := uint64(0 - borrow) | |||
borrow = 0 | |||
for i := 0; i < fp751NumWords; i++ { | |||
for i := 0; i < NumWords; i++ { | |||
z[i], borrow = addc64(borrow, z[i], p751x2[i]&mask) | |||
} | |||
} | |||
@@ -109,7 +113,7 @@ func fp751SubReduced(z, x, y *Fp751Element) { | |||
// For details see "Hackers Delight, 2.20" | |||
// | |||
// Implementation doesn't actually depend on a prime field. | |||
func fp751ConditionalSwap(x, y *Fp751Element, mask uint8) { | |||
func fp751ConditionalSwap(x, y *FpElement, mask uint8) { | |||
var tmp, mask64 uint64 | |||
mask64 = 0 - uint64(mask) | |||
@@ -122,14 +126,14 @@ func fp751ConditionalSwap(x, y *Fp751Element, mask uint8) { | |||
// Perform Montgomery reduction: set z = x R^{-1} (mod 2*p) | |||
// with R=2^768. Destroys the input value. | |||
func fp751MontgomeryReduce(z *Fp751Element, x *fp751X2) { | |||
func fp751MontgomeryReduce(z *FpElement, x *FpElementX2) { | |||
var carry, t, u, v uint64 | |||
var uv uint128 | |||
var count int | |||
count = 5 // number of 0 digits in the least significat part of p751 + 1 | |||
for i := 0; i < fp751NumWords; i++ { | |||
for i := 0; i < NumWords; i++ { | |||
for j := 0; j < i; j++ { | |||
if j < (i - count + 1) { | |||
uv = mul64(z[j], p751p1[i-j]) | |||
@@ -148,12 +152,12 @@ func fp751MontgomeryReduce(z *Fp751Element, x *fp751X2) { | |||
t = 0 | |||
} | |||
for i := fp751NumWords; i < 2*fp751NumWords-1; i++ { | |||
for i := NumWords; i < 2*NumWords-1; i++ { | |||
if count > 0 { | |||
count-- | |||
} | |||
for j := i - fp751NumWords + 1; j < fp751NumWords; j++ { | |||
if j < (fp751NumWords - count) { | |||
for j := i - NumWords + 1; j < NumWords; j++ { | |||
if j < (NumWords - count) { | |||
uv = mul64(z[j], p751p1[i-j]) | |||
v, carry = addc64(0, uv.L, v) | |||
u, carry = addc64(carry, uv.H, u) | |||
@@ -164,22 +168,22 @@ func fp751MontgomeryReduce(z *Fp751Element, x *fp751X2) { | |||
u, carry = addc64(carry, u, 0) | |||
t += carry | |||
z[i-fp751NumWords] = v | |||
z[i-NumWords] = v | |||
v = u | |||
u = t | |||
t = 0 | |||
} | |||
v, carry = addc64(0, v, x[2*fp751NumWords-1]) | |||
z[fp751NumWords-1] = v | |||
v, carry = addc64(0, v, x[2*NumWords-1]) | |||
z[NumWords-1] = v | |||
} | |||
// Compute z = x * y. | |||
func fp751Mul(z *fp751X2, x, y *Fp751Element) { | |||
func fp751Mul(z *FpElementX2, x, y *FpElement) { | |||
var u, v, t uint64 | |||
var carry uint64 | |||
var uv uint128 | |||
for i := uint64(0); i < fp751NumWords; i++ { | |||
for i := uint64(0); i < NumWords; i++ { | |||
for j := uint64(0); j <= i; j++ { | |||
uv = mul64(x[j], y[i-j]) | |||
v, carry = addc64(0, uv.L, v) | |||
@@ -192,8 +196,8 @@ func fp751Mul(z *fp751X2, x, y *Fp751Element) { | |||
t = 0 | |||
} | |||
for i := fp751NumWords; i < (2*fp751NumWords)-1; i++ { | |||
for j := i - fp751NumWords + 1; j < fp751NumWords; j++ { | |||
for i := NumWords; i < (2*NumWords)-1; i++ { | |||
for j := i - NumWords + 1; j < NumWords; j++ { | |||
uv = mul64(x[j], y[i-j]) | |||
v, carry = addc64(0, uv.L, v) | |||
u, carry = addc64(carry, uv.H, u) | |||
@@ -204,42 +208,42 @@ func fp751Mul(z *fp751X2, x, y *Fp751Element) { | |||
u = t | |||
t = 0 | |||
} | |||
z[2*fp751NumWords-1] = v | |||
z[2*NumWords-1] = v | |||
} | |||
// Compute z = x + y, without reducing mod p. | |||
func fp751AddLazy(z, x, y *Fp751Element) { | |||
func fp751AddLazy(z, x, y *FpElement) { | |||
var carry uint64 | |||
for i := 0; i < fp751NumWords; i++ { | |||
for i := 0; i < NumWords; i++ { | |||
z[i], carry = addc64(carry, x[i], y[i]) | |||
} | |||
} | |||
// Compute z = x + y, without reducing mod p. | |||
func fp751X2AddLazy(z, x, y *fp751X2) { | |||
func fp751X2AddLazy(z, x, y *FpElementX2) { | |||
var carry uint64 | |||
for i := 0; i < 2*fp751NumWords; i++ { | |||
for i := 0; i < 2*NumWords; i++ { | |||
z[i], carry = addc64(carry, x[i], y[i]) | |||
} | |||
} | |||
// Reduce a field element in [0, 2*p) to one in [0,p). | |||
func fp751StrongReduce(x *Fp751Element) { | |||
func fp751StrongReduce(x *FpElement) { | |||
var borrow, mask uint64 | |||
for i := 0; i < fp751NumWords; i++ { | |||
for i := 0; i < NumWords; i++ { | |||
x[i], borrow = subc64(borrow, x[i], p751[i]) | |||
} | |||
// Sets all bits if borrow = 1 | |||
mask = 0 - borrow | |||
borrow = 0 | |||
for i := 0; i < fp751NumWords; i++ { | |||
for i := 0; i < NumWords; i++ { | |||
x[i], borrow = addc64(borrow, x[i], p751[i]&mask) | |||
} | |||
} | |||
// Compute z = x - y, without reducing mod p. | |||
func fp751X2SubLazy(z, x, y *fp751X2) { | |||
func fp751X2SubLazy(z, x, y *FpElementX2) { | |||
var borrow, mask uint64 | |||
for i := 0; i < len(z); i++ { | |||
z[i], borrow = subc64(borrow, x[i], y[i]) | |||
@@ -248,7 +252,7 @@ func fp751X2SubLazy(z, x, y *fp751X2) { | |||
// Sets all bits if borrow = 1 | |||
mask = 0 - borrow | |||
borrow = 0 | |||
for i := fp751NumWords; i < len(z); i++ { | |||
z[i], borrow = addc64(borrow, z[i], p751[i-fp751NumWords]&mask) | |||
for i := NumWords; i < len(z); i++ { | |||
z[i], borrow = addc64(borrow, z[i], p751[i-NumWords]&mask) | |||
} | |||
} |
@@ -0,0 +1,322 @@ | |||
package p751 | |||
import ( | |||
"math/big" | |||
"testing" | |||
"testing/quick" | |||
. "github.com/cloudflare/p751sidh/internal/isogeny" | |||
) | |||
func TestPrimeFieldElementToBigInt(t *testing.T) { | |||
// Chosen so that p < xR < 2p | |||
x := primeFieldElement{A: FpElement{ | |||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 140737488355328, | |||
}} | |||
// Computed using Sage: | |||
// sage: p = 2^372 * 3^239 - 1 | |||
// sage: R = 2^768 | |||
// sage: from_radix_64 = lambda xs: sum((xi * (2**64)**i for i,xi in enumerate(xs))) | |||
// sage: xR = from_radix_64([1]*11 + [2^47]) | |||
// sage: assert(p < xR) | |||
// sage: assert(xR < 2*p) | |||
// sage: (xR / R) % p | |||
xBig, _ := new(big.Int).SetString("4469946751055876387821312289373600189787971305258234719850789711074696941114031433609871105823930699680637820852699269802003300352597419024286385747737509380032982821081644521634652750355306547718505685107272222083450567982240", 10) | |||
if xBig.Cmp(toBigInt(&x.A)) != 0 { | |||
t.Error("Expected", xBig, "found", toBigInt(&x.A)) | |||
} | |||
} | |||
//------------------------------------------------------------------------------ | |||
// Extended Field | |||
//------------------------------------------------------------------------------ | |||
func TestOneFp2ToBytes(t *testing.T) { | |||
var x = P751_OneFp2 | |||
var xBytes [188]byte | |||
kCurveOps.Fp2ToBytes(xBytes[:], &x) | |||
if xBytes[0] != 1 { | |||
t.Error("Expected 1, got", xBytes[0]) | |||
} | |||
for i := 1; i < 188; i++ { | |||
if xBytes[i] != 0 { | |||
t.Error("Expected 0, got", xBytes[0]) | |||
} | |||
} | |||
} | |||
func TestFp2ElementToBytesRoundTrip(t *testing.T) { | |||
roundTrips := func(x GeneratedTestParams) bool { | |||
var xBytes [188]byte | |||
var xPrime Fp2Element | |||
kCurveOps.Fp2ToBytes(xBytes[:], &x.ExtElem) | |||
kCurveOps.Fp2FromBytes(&xPrime, xBytes[:]) | |||
return VartimeEqFp2(&xPrime, &x.ExtElem) | |||
} | |||
if err := quick.Check(roundTrips, quickCheckConfig); err != nil { | |||
t.Error(err) | |||
} | |||
} | |||
func TestFp2ElementMulDistributesOverAdd(t *testing.T) { | |||
mulDistributesOverAdd := func(x, y, z GeneratedTestParams) bool { | |||
// Compute t1 = (x+y)*z | |||
t1 := new(Fp2Element) | |||
kFieldOps.Add(t1, &x.ExtElem, &y.ExtElem) | |||
kFieldOps.Mul(t1, t1, &z.ExtElem) | |||
// Compute t2 = x*z + y*z | |||
t2 := new(Fp2Element) | |||
t3 := new(Fp2Element) | |||
kFieldOps.Mul(t2, &x.ExtElem, &z.ExtElem) | |||
kFieldOps.Mul(t3, &y.ExtElem, &z.ExtElem) | |||
kFieldOps.Add(t2, t2, t3) | |||
return VartimeEqFp2(t1, t2) | |||
} | |||
if err := quick.Check(mulDistributesOverAdd, quickCheckConfig); err != nil { | |||
t.Error(err) | |||
} | |||
} | |||
func TestFp2ElementMulIsAssociative(t *testing.T) { | |||
isAssociative := func(x, y, z GeneratedTestParams) bool { | |||
// Compute t1 = (x*y)*z | |||
t1 := new(Fp2Element) | |||
kFieldOps.Mul(t1, &x.ExtElem, &y.ExtElem) | |||
kFieldOps.Mul(t1, t1, &z.ExtElem) | |||
// Compute t2 = (y*z)*x | |||
t2 := new(Fp2Element) | |||
kFieldOps.Mul(t2, &y.ExtElem, &z.ExtElem) | |||
kFieldOps.Mul(t2, t2, &x.ExtElem) | |||
return VartimeEqFp2(t1, t2) | |||
} | |||
if err := quick.Check(isAssociative, quickCheckConfig); err != nil { | |||
t.Error(err) | |||
} | |||
} | |||
func TestFp2ElementSquareMatchesMul(t *testing.T) { | |||
sqrMatchesMul := func(x GeneratedTestParams) bool { | |||
// Compute t1 = (x*x) | |||
t1 := new(Fp2Element) | |||
kFieldOps.Mul(t1, &x.ExtElem, &x.ExtElem) | |||
// Compute t2 = x^2 | |||
t2 := new(Fp2Element) | |||
kFieldOps.Square(t2, &x.ExtElem) | |||
return VartimeEqFp2(t1, t2) | |||
} | |||
if err := quick.Check(sqrMatchesMul, quickCheckConfig); err != nil { | |||
t.Error(err) | |||
} | |||
} | |||
func TestFp2ElementInv(t *testing.T) { | |||
inverseIsCorrect := func(x GeneratedTestParams) bool { | |||
z := new(Fp2Element) | |||
kFieldOps.Inv(z, &x.ExtElem) | |||
// Now z = (1/x), so (z * x) * x == x | |||
kFieldOps.Mul(z, z, &x.ExtElem) | |||
kFieldOps.Mul(z, z, &x.ExtElem) | |||
return VartimeEqFp2(z, &x.ExtElem) | |||
} | |||
// This is more expensive; run fewer tests | |||
var quickCheckConfig = &quick.Config{MaxCount: (1 << (8 + quickCheckScaleFactor))} | |||
if err := quick.Check(inverseIsCorrect, quickCheckConfig); err != nil { | |||
t.Error(err) | |||
} | |||
} | |||
func TestFp2ElementBatch3Inv(t *testing.T) { | |||
batchInverseIsCorrect := func(x1, x2, x3 GeneratedTestParams) bool { | |||
var x1Inv, x2Inv, x3Inv Fp2Element | |||
kFieldOps.Inv(&x1Inv, &x1.ExtElem) | |||
kFieldOps.Inv(&x2Inv, &x2.ExtElem) | |||
kFieldOps.Inv(&x3Inv, &x3.ExtElem) | |||
var y1, y2, y3 Fp2Element | |||
kCurveOps.Fp2Batch3Inv(&x1.ExtElem, &x2.ExtElem, &x3.ExtElem, &y1, &y2, &y3) | |||
return (VartimeEqFp2(&x1Inv,&y1) && VartimeEqFp2(&x2Inv,&y2) && VartimeEqFp2(&x3Inv,&y3)) | |||
} | |||
// This is more expensive; run fewer tests | |||
var quickCheckConfig = &quick.Config{MaxCount: (1 << (5 + quickCheckScaleFactor))} | |||
if err := quick.Check(batchInverseIsCorrect, quickCheckConfig); err != nil { | |||
t.Error(err) | |||
} | |||
} | |||
//------------------------------------------------------------------------------ | |||
// Prime Field | |||
//------------------------------------------------------------------------------ | |||
func TestPrimeFieldElementMulVersusBigInt(t *testing.T) { | |||
mulMatchesBigInt := func(x, y primeFieldElement) bool { | |||
z := new(primeFieldElement) | |||
z.Mul(&x, &y) | |||
check := new(big.Int) | |||
check.Mul(toBigInt(&x.A), toBigInt(&y.A)) | |||
check.Mod(check, cln16prime) | |||
return check.Cmp(toBigInt(&z.A)) == 0 | |||
} | |||
if err := quick.Check(mulMatchesBigInt, quickCheckConfig); err != nil { | |||
t.Error(err) | |||
} | |||
} | |||
func TestPrimeFieldElementP34VersusBigInt(t *testing.T) { | |||
var p34, _ = new(big.Int).SetString("2588679435442326313244442059466701330356847411387267792529047419763669735170619711625720724140266678406138302904710050596300977994130638598261040117192787954244176710019728333589599932738193731745058771712747875468166412894207", 10) | |||
p34MatchesBigInt := func(x primeFieldElement) bool { | |||
z := new(primeFieldElement) | |||
z.P34(&x) | |||
check := toBigInt(&x.A) | |||
check.Exp(check, p34, cln16prime) | |||
return check.Cmp(toBigInt(&z.A)) == 0 | |||
} | |||
// This is more expensive; run fewer tests | |||
var quickCheckConfig = &quick.Config{MaxCount: (1 << (8 + quickCheckScaleFactor))} | |||
if err := quick.Check(p34MatchesBigInt, quickCheckConfig); err != nil { | |||
t.Error(err) | |||
} | |||
} | |||
func BenchmarkFp2ElementMul(b *testing.B) { | |||
z := &Fp2Element{A: bench_x, B: bench_y} | |||
w := new(Fp2Element) | |||
for n := 0; n < b.N; n++ { | |||
kFieldOps.Mul(w, z, z) | |||
} | |||
} | |||
func BenchmarkFp2ElementInv(b *testing.B) { | |||
z := &Fp2Element{A: bench_x, B: bench_y} | |||
w := new(Fp2Element) | |||
for n := 0; n < b.N; n++ { | |||
kFieldOps.Inv(w, z) | |||
} | |||
} | |||
func BenchmarkFp2ElementSquare(b *testing.B) { | |||
z := &Fp2Element{A: bench_x, B: bench_y} | |||
w := new(Fp2Element) | |||
for n := 0; n < b.N; n++ { | |||
kFieldOps.Square(w, z) | |||
} | |||
} | |||
func BenchmarkFp2ElementAdd(b *testing.B) { | |||
z := &Fp2Element{A: bench_x, B: bench_y} | |||
w := new(Fp2Element) | |||
for n := 0; n < b.N; n++ { | |||
kFieldOps.Add(w, z, z) | |||
} | |||
} | |||
func BenchmarkFp2ElementSub(b *testing.B) { | |||
z := &Fp2Element{A: bench_x, B: bench_y} | |||
w := new(Fp2Element) | |||
for n := 0; n < b.N; n++ { | |||
kFieldOps.Sub(w, z, z) | |||
} | |||
} | |||
func BenchmarkPrimeFieldElementMul(b *testing.B) { | |||
z := &primeFieldElement{A: bench_x} | |||
w := new(primeFieldElement) | |||
for n := 0; n < b.N; n++ { | |||
w.Mul(z, z) | |||
} | |||
} | |||
// --- field operation functions | |||
func BenchmarkFp751Multiply(b *testing.B) { | |||
for n := 0; n < b.N; n++ { | |||
fp751Mul(&benchmarkFpElementX2, &bench_x, &bench_y) | |||
} | |||
} | |||
func BenchmarkFp751MontgomeryReduce(b *testing.B) { | |||
z := bench_z | |||
// This benchmark actually computes garbage, because | |||
// fp751MontgomeryReduce mangles its input, but since it's | |||
// constant-time that shouldn't matter for the benchmarks. | |||
for n := 0; n < b.N; n++ { | |||
fp751MontgomeryReduce(&benchmarkFpElement, &z) | |||
} | |||
} | |||
func BenchmarkFp751AddReduced(b *testing.B) { | |||
for n := 0; n < b.N; n++ { | |||
fp751AddReduced(&benchmarkFpElement, &bench_x, &bench_y) | |||
} | |||
} | |||
func BenchmarkFp751SubReduced(b *testing.B) { | |||
for n := 0; n < b.N; n++ { | |||
fp751SubReduced(&benchmarkFpElement, &bench_x, &bench_y) | |||
} | |||
} | |||
func BenchmarkFp751ConditionalSwap(b *testing.B) { | |||
x, y := bench_x, bench_y | |||
for n := 0; n < b.N; n++ { | |||
fp751ConditionalSwap(&x, &y, 1) | |||
fp751ConditionalSwap(&x, &y, 0) | |||
} | |||
} | |||
func BenchmarkFp751StrongReduce(b *testing.B) { | |||
x := bench_x | |||
for n := 0; n < b.N; n++ { | |||
fp751StrongReduce(&x) | |||
} | |||
} | |||
func BenchmarkFp751AddLazy(b *testing.B) { | |||
var z FpElement | |||
x, y := bench_x, bench_y | |||
for n := 0; n < b.N; n++ { | |||
fp751AddLazy(&z, &x, &y) | |||
} | |||
} | |||
func BenchmarkFp751X2AddLazy(b *testing.B) { | |||
x, y, z := bench_z, bench_z, bench_z | |||
for n := 0; n < b.N; n++ { | |||
fp751X2AddLazy(&x, &y, &z) | |||
} | |||
} | |||
func BenchmarkFp751X2SubLazy(b *testing.B) { | |||
x, y, z := bench_z, bench_z, bench_z | |||
for n := 0; n < b.N; n++ { | |||
fp751X2SubLazy(&x, &y, &z) | |||
} | |||
} |
@@ -1,4 +1,6 @@ | |||
package p751toolbox | |||
package p751 | |||
import . "github.com/cloudflare/p751sidh/internal/isogeny" | |||
const ( | |||
// The secret key size, in bytes. Secret key is actually different for | |||
@@ -21,21 +23,25 @@ const ( | |||
P751_MaskBobByte = 0x03 | |||
// Sample rate to obtain a value in [0,3^238] | |||
P751_SampleRate = 102 | |||
// P751 bytelen ceil(751/8) | |||
P751_Bytelen = 94 | |||
// Size of a compuatation strategy for 2-torsion group | |||
strategySizeA = 185 | |||
// Size of a compuatation strategy for 3-torsion group | |||
strategySizeB = 238 | |||
// Number of 64-bit limbs used to store Fp element | |||
NumWords = 12 | |||
) | |||
// The x-coordinate of PA | |||
var P751_affine_PA = ExtensionFieldElement{ | |||
A: Fp751Element{ | |||
var P751_affine_PA = Fp2Element{ | |||
A: FpElement{ | |||
0xC2FC08CEAB50AD8B, 0x1D7D710F55E457B1, 0xE8738D92953DCD6E, | |||
0xBAA7EBEE8A3418AA, 0xC9A288345F03F46F, 0xC8D18D167CFE2616, | |||
0x02043761F6B1C045, 0xAA1975E13180E7E9, 0x9E13D3FDC6690DE6, | |||
0x3A024640A3A3BB4F, 0x4E5AD44E6ACBBDAE, 0x0000544BEB561DAD, | |||
}, | |||
B: Fp751Element{ | |||
B: FpElement{ | |||
0xE6CC41D21582E411, 0x07C2ECB7C5DF400A, 0xE8E34B521432AEC4, | |||
0x50761E2AB085167D, 0x032CFBCAA6094B3C, 0x6C522F5FDF9DDD71, | |||
0x1319217DC3A1887D, 0xDC4FB25803353A86, 0x362C8D7B63A6AB09, | |||
@@ -44,14 +50,14 @@ var P751_affine_PA = ExtensionFieldElement{ | |||
} | |||
// The x-coordinate of QA | |||
var P751_affine_QA = ExtensionFieldElement{ | |||
A: Fp751Element{ | |||
var P751_affine_QA = Fp2Element{ | |||
A: FpElement{ | |||
0xD56FE52627914862, 0x1FAD60DC96B5BAEA, 0x01E137D0BF07AB91, | |||
0x404D3E9252161964, 0x3C5385E4CD09A337, 0x4476426769E4AF73, | |||
0x9790C6DB989DFE33, 0xE06E1C04D2AA8B5E, 0x38C08185EDEA73B9, | |||
0xAA41F678A4396CA6, 0x92B9259B2229E9A0, 0x00002F9326818BE0, | |||
}, | |||
B: Fp751Element{ | |||
B: FpElement{ | |||
0x0000000000000000, 0x0000000000000000, 0x0000000000000000, | |||
0x0000000000000000, 0x0000000000000000, 0x0000000000000000, | |||
0x0000000000000000, 0x0000000000000000, 0x0000000000000000, | |||
@@ -60,14 +66,14 @@ var P751_affine_QA = ExtensionFieldElement{ | |||
} | |||
// The x-coordinate of RA = PA-QA | |||
var P751_affine_RA = ExtensionFieldElement{ | |||
A: Fp751Element{ | |||
var P751_affine_RA = Fp2Element{ | |||
A: FpElement{ | |||
0x0BB84441DFFD19B3, 0x84B4DEA99B48C18E, 0x692DE648AD313805, | |||
0xE6D72761B6DFAEE0, 0x223975C672C3058D, 0xA0FDE0C3CBA26FDC, | |||
0xA5326132A922A3CA, 0xCA5E7F5D5EA96FA4, 0x127C7EFE33FFA8C6, | |||
0x4749B1567E2A23C4, 0x2B7DF5B4AF413BFA, 0x0000656595B9623C, | |||
}, | |||
B: Fp751Element{ | |||
B: FpElement{ | |||
0xED78C17F1EC71BE8, 0xF824D6DF753859B1, 0x33A10839B2A8529F, | |||
0xFC03E9E25FDEA796, 0xC4708A8054DF1762, 0x4034F2EC034C6467, | |||
0xABFB70FBF06ECC79, 0xDABE96636EC108B7, 0x49CBCFB090605FD3, | |||
@@ -76,14 +82,14 @@ var P751_affine_RA = ExtensionFieldElement{ | |||
} | |||
// The x-coordinate of PB | |||
var P751_affine_PB = ExtensionFieldElement{ | |||
A: Fp751Element{ | |||
var P751_affine_PB = Fp2Element{ | |||
A: FpElement{ | |||
0xCFB6D71EF867AB0B, 0x4A5FDD76E9A45C76, 0x38B1EE69194B1F03, | |||
0xF6E7B18A7761F3F0, 0xFCF01A486A52C84C, 0xCBE2F63F5AA75466, | |||
0x6487BCE837B5E4D6, 0x7747F5A8C622E9B8, 0x4CBFE1E4EE6AEBBA, | |||
0x8A8616A13FA91512, 0x53DB980E1579E0A5, 0x000058FEBFF3BE69, | |||
}, | |||
B: Fp751Element{ | |||
B: FpElement{ | |||
0xA492034E7C075CC3, 0x677BAF00B04AA430, 0x3AAE0C9A755C94C8, | |||
0x1DC4B064E9EBB08B, 0x3684EDD04E826C66, 0x9BAA6CB661F01B22, | |||
0x20285A00AD2EFE35, 0xDCE95ABD0497065F, 0x16C7FBB3778E3794, | |||
@@ -92,14 +98,14 @@ var P751_affine_PB = ExtensionFieldElement{ | |||
} | |||
// The x-coordinate of QB | |||
var P751_affine_QB = ExtensionFieldElement{ | |||
A: Fp751Element{ | |||
var P751_affine_QB = Fp2Element{ | |||
A: FpElement{ | |||
0xF1A8C9ED7B96C4AB, 0x299429DA5178486E, 0xEF4926F20CD5C2F4, | |||
0x683B2E2858B4716A, 0xDDA2FBCC3CAC3EEB, 0xEC055F9F3A600460, | |||
0xD5A5A17A58C3848B, 0x4652D836F42EAED5, 0x2F2E71ED78B3A3B3, | |||
0xA771C057180ADD1D, 0xC780A5D2D835F512, 0x0000114EA3B55AC1, | |||
}, | |||
B: Fp751Element{ | |||
B: FpElement{ | |||
0x0000000000000000, 0x0000000000000000, 0x0000000000000000, | |||
0x0000000000000000, 0x0000000000000000, 0x0000000000000000, | |||
0x0000000000000000, 0x0000000000000000, 0x0000000000000000, | |||
@@ -108,14 +114,14 @@ var P751_affine_QB = ExtensionFieldElement{ | |||
} | |||
// The x-coordinate of RB = PB - QB | |||
var P751_affine_RB = ExtensionFieldElement{ | |||
A: Fp751Element{ | |||
var P751_affine_RB = Fp2Element{ | |||
A: FpElement{ | |||
0x1C0D6733769D0F31, 0xF084C3086E2659D1, 0xE23D5DA27BCBD133, | |||
0xF38EC9A8D5864025, 0x6426DC781B3B645B, 0x4B24E8E3C9FB03EE, | |||
0x6432792F9D2CEA30, 0x7CC8E8B1AE76E857, 0x7F32BFB626BB8963, | |||
0xB9F05995B48D7B74, 0x4D71200A7D67E042, 0x0000228457AF0637, | |||
}, | |||
B: Fp751Element{ | |||
B: FpElement{ | |||
0x4AE37E7D8F72BD95, 0xDD2D504B3E993488, 0x5D14E7FA1ECB3C3E, | |||
0x127610CEB75D6350, 0x255B4B4CAC446B11, 0x9EA12336C1F70CAF, | |||
0x79FA68A2147BC2F8, 0x11E895CFDADBBC49, 0xE4B9D3C4D6356C18, | |||
@@ -171,3 +177,27 @@ var P751_BobIsogenyStrategy = [strategySizeB]uint32{ | |||
0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x09, 0x05, | |||
0x03, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, | |||
0x04, 0x02, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01} | |||
var P751_OneFp2 = Fp2Element{ | |||
A: FpElement{ | |||
0x249ad, 0x0, 0x0, 0x0, 0x0, 0x8310000000000000, 0x5527b1e4375c6c66, 0x697797bf3f4f24d0, 0xc89db7b2ac5c4e2e, 0x4ca4b439d2076956, 0x10f7926c7512c7e9, 0x2d5b24bce5e2}, | |||
B: FpElement{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, | |||
} | |||
var P751_HalfFp2 = Fp2Element{ | |||
A: FpElement{ | |||
0x00000000000124D6, 0x0000000000000000, 0x0000000000000000, | |||
0x0000000000000000, 0x0000000000000000, 0xB8E0000000000000, | |||
0x9C8A2434C0AA7287, 0xA206996CA9A378A3, 0x6876280D41A41B52, | |||
0xE903B49F175CE04F, 0x0F8511860666D227, 0x00004EA07CFF6E7F}, | |||
B: FpElement{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, | |||
} | |||
// (2^768)^2 mod p | |||
// This can't be a constant because Go doesn't allow array constants, so try | |||
// not to modify it. | |||
var montgomeryRsq = FpElement{ | |||
2535603850726686808, 15780896088201250090, 6788776303855402382, | |||
17585428585582356230, 5274503137951975249, 2266259624764636289, | |||
11695651972693921304, 13072885652150159301, 4908312795585420432, | |||
6229583484603254826, 488927695601805643, 72213483953973} |
@@ -0,0 +1,192 @@ | |||
package p751 | |||
import ( | |||
"bytes" | |||
"testing" | |||
"testing/quick" | |||
. "github.com/cloudflare/p751sidh/internal/isogeny" | |||
) | |||
func TestOne(t *testing.T) { | |||
var tmp Fp2Element | |||
kFieldOps.Mul(&tmp, &P751_OneFp2, &affine_xP) | |||
if !VartimeEqFp2(&tmp, &affine_xP) { | |||
t.Error("Not equal 1") | |||
} | |||
} | |||
// This test is here only to ensure that ScalarMult helper works correctly | |||
func TestScalarMultVersusSage(t *testing.T) { | |||
var xP ProjectivePoint | |||
xP = ProjectivePoint{X: affine_xP, Z: P751_OneFp2} | |||
xP = ScalarMult(&curve, &xP, mScalarBytes[:]) // = x([m]P) | |||
affine_xQ := xP.ToAffine(kCurveOps) | |||
if !VartimeEqFp2(&affine_xaP, affine_xQ) { | |||
t.Error("\nExpected\n", affine_xaP, "\nfound\n", affine_xQ) | |||
} | |||
} | |||
func Test_jInvariant(t *testing.T) { | |||
var curve = ProjectiveCurveParameters{A: curve_A, C: curve_C} | |||
var jbufRes [P751_SharedSecretSize]byte | |||
var jbufExp [P751_SharedSecretSize]byte | |||
// Computed using Sage | |||
// j = 3674553797500778604587777859668542828244523188705960771798425843588160903687122861541242595678107095655647237100722594066610650373491179241544334443939077738732728884873568393760629500307797547379838602108296735640313894560419*i + 3127495302417548295242630557836520229396092255080675419212556702820583041296798857582303163183558315662015469648040494128968509467224910895884358424271180055990446576645240058960358037224785786494172548090318531038910933793845 | |||
var known_j = Fp2Element{ | |||
A: FpElement{0xc7a8921c1fb23993, 0xa20aea321327620b, 0xf1caa17ed9676fa8, 0x61b780e6b1a04037, 0x47784af4c24acc7a, 0x83926e2e300b9adf, 0xcd891d56fae5b66, 0x49b66985beb733bc, 0xd4bcd2a473d518f, 0xe242239991abe224, 0xa8af5b20f98672f8, 0x139e4d4e4d98}, | |||
B: FpElement{0xb5b52a21f81f359, 0x715e3a865db6d920, 0x9bac2f9d8911978b, 0xef14acd8ac4c1e3d, 0xe81aacd90cfb09c8, 0xaf898288de4a09d9, 0xb85a7fb88c5c4601, 0x2c37c3f1dd303387, 0x7ad3277fe332367c, 0xd4cbee7f25a8e6f8, 0x36eacbe979eaeffa, 0x59eb5a13ac33}, | |||
} | |||
kCurveOps.Jinvariant(&curve, jbufRes[:]) | |||
kCurveOps.Fp2ToBytes(jbufExp[:], &known_j) | |||
if !bytes.Equal(jbufRes[:], jbufExp[:]) { | |||
t.Error("Computed incorrect j-invariant: found\n", jbufRes, "\nexpected\n", jbufExp) | |||
} | |||
} | |||
func TestProjectivePointVartimeEq(t *testing.T) { | |||
var xP ProjectivePoint | |||
xP = ProjectivePoint{X: affine_xP, Z: P751_OneFp2} | |||
xQ := xP | |||
// Scale xQ, which results in the same projective point | |||
kFieldOps.Mul(&xQ.X, &xQ.X, &curve_A) | |||
kFieldOps.Mul(&xQ.Z, &xQ.Z, &curve_A) | |||
if !VartimeEqProjFp2(&xP, &xQ) { | |||
t.Error("Expected the scaled point to be equal to the original") | |||
} | |||
} | |||
func TestPointDoubleVersusSage(t *testing.T) { | |||
var curve = ProjectiveCurveParameters{A: curve_A, C: curve_C} | |||
var params = kCurveOps.CalcCurveParamsEquiv4(&curve) | |||
var xP ProjectivePoint | |||
xP = ProjectivePoint{X: affine_xP, Z: P751_OneFp2} | |||
kCurveOps.Pow2k(&xP, ¶ms, 1) | |||
affine_xQ := xP.ToAffine(kCurveOps) | |||
if !VartimeEqFp2(affine_xQ, &affine_xP2) { | |||
t.Error("\nExpected\n", affine_xP2, "\nfound\n", affine_xQ) | |||
} | |||
} | |||
func TestPointMul4VersusSage(t *testing.T) { | |||
var params = kCurveOps.CalcCurveParamsEquiv4(&curve) | |||
var xP ProjectivePoint | |||
xP = ProjectivePoint{X: affine_xP, Z: P751_OneFp2} | |||
kCurveOps.Pow2k(&xP, ¶ms, 2) | |||
affine_xQ := xP.ToAffine(kCurveOps) | |||
if !VartimeEqFp2(affine_xQ, &affine_xP4) { | |||
t.Error("\nExpected\n", affine_xP4, "\nfound\n", affine_xQ) | |||
} | |||
} | |||
func TestPointMul9VersusSage(t *testing.T) { | |||
var params = kCurveOps.CalcCurveParamsEquiv3(&curve) | |||
var xP ProjectivePoint | |||
xP = ProjectivePoint{X: affine_xP, Z: P751_OneFp2} | |||
kCurveOps.Pow3k(&xP, ¶ms, 2) | |||
affine_xQ := xP.ToAffine(kCurveOps) | |||
if !VartimeEqFp2(affine_xQ, &affine_xP9) { | |||
t.Error("\nExpected\n", affine_xP9, "\nfound\n", affine_xQ) | |||
} | |||
} | |||
func TestPointPow2kVersusScalarMult(t *testing.T) { | |||
var xP, xQ, xR ProjectivePoint | |||
var params = kCurveOps.CalcCurveParamsEquiv4(&curve) | |||
xP = ProjectivePoint{X: affine_xP, Z: P751_OneFp2} | |||
xQ = xP | |||
kCurveOps.Pow2k(&xQ, ¶ms, 5) | |||
xR = ScalarMult(&curve, &xP, []byte{32}) | |||
affine_xQ := xQ.ToAffine(kCurveOps) // = x([32]P) | |||
affine_xR := xR.ToAffine(kCurveOps) // = x([32]P) | |||
if !VartimeEqFp2(affine_xQ, affine_xR) { | |||
t.Error("\nExpected\n", affine_xQ, "\nfound\n", affine_xR) | |||
} | |||
} | |||
func TestRecoverCoordinateA(t *testing.T) { | |||
var cparam ProjectiveCurveParameters | |||
// Vectors generated with SIKE reference implementation | |||
var a = Fp2Element{ | |||
A: FpElement{0x9331D9C5AAF59EA4, 0xB32B702BE4046931, 0xCEBB333912ED4D34, 0x5628CE37CD29C7A2, 0x0BEAC5ED48B7F58E, 0x1FB9D3E281D65B07, 0x9C0CFACC1E195662, 0xAE4BCE0F6B70F7D9, 0x59E4E63D43FE71A0, 0xEF7CE57560CC8615, 0xE44A8FB7901E74E8, 0x000069D13C8366D1}, | |||
B: FpElement{0xF6DA1070279AB966, 0xA78FB0CE7268C762, 0x19B40F044A57ABFA, 0x7AC8EE6160C0C233, 0x93D4993442947072, 0x757D2B3FA4E44860, 0x073A920F8C4D5257, 0x2031F1B054734037, 0xDEFAA1D2406555CD, 0x26F9C70E1496BE3D, 0x5B3F335A0A4D0976, 0x000013628B2E9C59}} | |||
var affine_xP = Fp2Element{ | |||
A: FpElement{0xea6b2d1e2aebb250, 0x35d0b205dc4f6386, 0xb198e93cb1830b8d, 0x3b5b456b496ddcc6, 0x5be3f0d41132c260, 0xce5f188807516a00, 0x54f3e7469ea8866d, 0x33809ef47f36286, 0x6fa45f83eabe1edb, 0x1b3391ae5d19fd86, 0x1e66daf48584af3f, 0xb430c14aaa87}, | |||
B: FpElement{0x97b41ebc61dcb2ad, 0x80ead31cb932f641, 0x40a940099948b642, 0x2a22fd16cdc7fe84, 0xaabf35b17579667f, 0x76c1d0139feb4032, 0x71467e1e7b1949be, 0x678ca8dadd0d6d81, 0x14445daea9064c66, 0x92d161eab4fa4691, 0x8dfbb01b6b238d36, 0x2e3718434e4e}} | |||
var affine_xQ = Fp2Element{ | |||
A: FpElement{0xb055cf0ca1943439, 0xa9ff5de2fa6c69ed, 0x4f2761f934e5730a, 0x61a1dcaa1f94aa4b, 0xce3c8fadfd058543, 0xeac432aaa6701b8e, 0x8491d523093aea8b, 0xba273f9bd92b9b7f, 0xd8f59fd34439bb5a, 0xdc0350261c1fe600, 0x99375ab1eb151311, 0x14d175bbdbc5}, | |||
B: FpElement{0xffb0ef8c2111a107, 0x55ceca3825991829, 0xdbf8a1ccc075d34b, 0xb8e9187bd85d8494, 0x670aa2d5c34a03b0, 0xef9fe2ed2b064953, 0xc911f5311d645aee, 0xf4411f409e410507, 0x934a0a852d03e1a8, 0xe6274e67ae1ad544, 0x9f4bc563c69a87bc, 0x6f316019681e}} | |||
var affine_xQmP = Fp2Element{ | |||
A: FpElement{0x6ffb44306a153779, 0xc0ffef21f2f918f3, 0x196c46d35d77f778, 0x4a73f80452edcfe6, 0x9b00836bce61c67f, 0x387879418d84219e, 0x20700cf9fc1ec5d1, 0x1dfe2356ec64155e, 0xf8b9e33038256b1c, 0xd2aaf2e14bada0f0, 0xb33b226e79a4e313, 0x6be576fad4e5}, | |||
B: FpElement{0x7db5dbc88e00de34, 0x75cc8cb9f8b6e11e, 0x8c8001c04ebc52ac, 0x67ef6c981a0b5a94, 0xc3654fbe73230738, 0xc6a46ee82983ceca, 0xed1aa61a27ef49f0, 0x17fe5a13b0858fe0, 0x9ae0ca945a4c6b3c, 0x234104a218ad8878, 0xa619627166104394, 0x556a01ff2e7e}} | |||
cparam.C = P751_OneFp2 | |||
kCurveOps.RecoverCoordinateA(&cparam, &affine_xP, &affine_xQ, &affine_xQmP) | |||
// Check A is correct | |||
if !VartimeEqFp2(&cparam.A, &a) { | |||
t.Error("\nExpected\n", a, "\nfound\n", cparam.A) | |||
} | |||
// Check C is not changed | |||
if !VartimeEqFp2(&cparam.C, &P751_OneFp2) { | |||
t.Error("\nExpected\n", cparam.C, "\nfound\n", P751_OneFp2) | |||
} | |||
} | |||
func TestR2LVersusSage(t *testing.T) { | |||
var xR ProjectivePoint | |||
sageAffine_xR := Fp2Element{ | |||
A: FpElement{0x729465ba800d4fd5, 0x9398015b59e514a1, 0x1a59dd6be76c748e, 0x1a7db94eb28dd55c, 0x444686e680b1b8ec, 0xcc3d4ace2a2454ff, 0x51d3dab4ec95a419, 0xc3b0f33594acac6a, 0x9598a74e7fd44f8a, 0x4fbf8c638f1c2e37, 0x844e347033052f51, 0x6cd6de3eafcf}, | |||
B: FpElement{0x85da145412d73430, 0xd83c0e3b66eb3232, 0xd08ff2d453ec1369, 0xa64aaacfdb395b13, 0xe9cba211a20e806e, 0xa4f80b175d937cfc, 0x556ce5c64b1f7937, 0xb59b39ea2b3fdf7a, 0xc2526b869a4196b3, 0x8dad90bca9371750, 0xdfb4a30c9d9147a2, 0x346d2130629b}} | |||
xR = kCurveOps.ScalarMul3Pt(&curve, &threePointLadderInputs[0], &threePointLadderInputs[1], &threePointLadderInputs[2], uint(len(mScalarBytes)*8), mScalarBytes[:]) | |||
affine_xR := xR.ToAffine(kCurveOps) | |||
if !VartimeEqFp2(affine_xR, &sageAffine_xR) { | |||
t.Error("\nExpected\n", sageAffine_xR, "\nfound\n", affine_xR) | |||
} | |||
} | |||
func TestPointTripleVersusAddDouble(t *testing.T) { | |||
tripleEqualsAddDouble := func(params GeneratedTestParams) bool { | |||
var P2, P3, P2plusP ProjectivePoint | |||
eqivParams4 := kCurveOps.CalcCurveParamsEquiv4(¶ms.Cparam) | |||
eqivParams3 := kCurveOps.CalcCurveParamsEquiv3(¶ms.Cparam) | |||
P2 = params.Point | |||
P3 = params.Point | |||
kCurveOps.Pow2k(&P2, &eqivParams4, 1) // = x([2]P) | |||
kCurveOps.Pow3k(&P3, &eqivParams3, 1) // = x([3]P) | |||
P2plusP = AddProjFp2(&P2, ¶ms.Point, ¶ms.Point) // = x([2]P + P) | |||
return VartimeEqProjFp2(&P3, &P2plusP) | |||
} | |||
if err := quick.Check(tripleEqualsAddDouble, quickCheckConfig); err != nil { | |||
t.Error(err) | |||
} | |||
} | |||
func BenchmarkThreePointLadder379BitScalar(b *testing.B) { | |||
var mScalarBytes = [...]uint8{84, 222, 146, 63, 85, 18, 173, 162, 167, 38, 10, 8, 143, 176, 93, 228, 247, 128, 50, 128, 205, 42, 15, 137, 119, 67, 43, 3, 61, 91, 237, 24, 235, 12, 53, 96, 186, 164, 232, 223, 197, 224, 64, 109, 137, 63, 246, 4} | |||
for n := 0; n < b.N; n++ { | |||
kCurveOps.ScalarMul3Pt(&curve, &threePointLadderInputs[0], &threePointLadderInputs[1], &threePointLadderInputs[2], uint(len(mScalarBytes)*8), mScalarBytes[:]) | |||
} | |||
} | |||
func BenchmarkR2L379BitScalar(b *testing.B) { | |||
var mScalarBytes = [...]uint8{84, 222, 146, 63, 85, 18, 173, 162, 167, 38, 10, 8, 143, 176, 93, 228, 247, 128, 50, 128, 205, 42, 15, 137, 119, 67, 43, 3, 61, 91, 237, 24, 235, 12, 53, 96, 186, 164, 232, 223, 197, 224, 64, 109, 137, 63, 246, 4} | |||
for n := 0; n < b.N; n++ { | |||
kCurveOps.ScalarMul3Pt(&curve, &threePointLadderInputs[0], &threePointLadderInputs[1], &threePointLadderInputs[2], uint(len(mScalarBytes)*8), mScalarBytes[:]) | |||
} | |||
} |
@@ -0,0 +1,270 @@ | |||
package p751 | |||
import . "github.com/cloudflare/p751sidh/internal/isogeny" | |||
// 2*p751 | |||
var ( | |||
// p751 | |||
p751 = FpElement{ | |||
0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, | |||
0xffffffffffffffff, 0xffffffffffffffff, 0xeeafffffffffffff, | |||
0xe3ec968549f878a8, 0xda959b1a13f7cc76, 0x084e9867d6ebe876, | |||
0x8562b5045cb25748, 0x0e12909f97badc66, 0x00006fe5d541f71c} | |||
// p751 + 1 | |||
p751p1 = FpElement{ | |||
0x0000000000000000, 0x0000000000000000, 0x0000000000000000, | |||
0x0000000000000000, 0x0000000000000000, 0xeeb0000000000000, | |||
0xe3ec968549f878a8, 0xda959b1a13f7cc76, 0x084e9867d6ebe876, | |||
0x8562b5045cb25748, 0x0e12909f97badc66, 0x00006fe5d541f71c} | |||
// 2*p751 | |||
p751x2 = FpElement{ | |||
0xFFFFFFFFFFFFFFFE, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, | |||
0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xDD5FFFFFFFFFFFFF, | |||
0xC7D92D0A93F0F151, 0xB52B363427EF98ED, 0x109D30CFADD7D0ED, | |||
0x0AC56A08B964AE90, 0x1C25213F2F75B8CD, 0x0000DFCBAA83EE38} | |||
) | |||
//------------------------------------------------------------------------------ | |||
// Implementtaion of FieldOperations | |||
//------------------------------------------------------------------------------ | |||
// Implements FieldOps | |||
type fp751Ops struct{} | |||
func FieldOperations() FieldOps { | |||
return &fp751Ops{} | |||
} | |||
func (fp751Ops) Add(dest, lhs, rhs *Fp2Element) { | |||
fp751AddReduced(&dest.A, &lhs.A, &rhs.A) | |||
fp751AddReduced(&dest.B, &lhs.B, &rhs.B) | |||
} | |||
func (fp751Ops) Sub(dest, lhs, rhs *Fp2Element) { | |||
fp751SubReduced(&dest.A, &lhs.A, &rhs.A) | |||
fp751SubReduced(&dest.B, &lhs.B, &rhs.B) | |||
} | |||
func (fp751Ops) Mul(dest, lhs, rhs *Fp2Element) { | |||
// Let (a,b,c,d) = (lhs.a,lhs.b,rhs.a,rhs.b). | |||
a := &lhs.A | |||
b := &lhs.B | |||
c := &rhs.A | |||
d := &rhs.B | |||
// We want to compute | |||
// | |||
// (a + bi)*(c + di) = (a*c - b*d) + (a*d + b*c)i | |||
// | |||
// Use Karatsuba's trick: note that | |||
// | |||
// (b - a)*(c - d) = (b*c + a*d) - a*c - b*d | |||
// | |||
// so (a*d + b*c) = (b-a)*(c-d) + a*c + b*d. | |||
var ac, bd FpElementX2 | |||
fp751Mul(&ac, a, c) // = a*c*R*R | |||
fp751Mul(&bd, b, d) // = b*d*R*R | |||
var b_minus_a, c_minus_d FpElement | |||
fp751SubReduced(&b_minus_a, b, a) // = (b-a)*R | |||
fp751SubReduced(&c_minus_d, c, d) // = (c-d)*R | |||
var ad_plus_bc FpElementX2 | |||
fp751Mul(&ad_plus_bc, &b_minus_a, &c_minus_d) // = (b-a)*(c-d)*R*R | |||
fp751X2AddLazy(&ad_plus_bc, &ad_plus_bc, &ac) // = ((b-a)*(c-d) + a*c)*R*R | |||
fp751X2AddLazy(&ad_plus_bc, &ad_plus_bc, &bd) // = ((b-a)*(c-d) + a*c + b*d)*R*R | |||
fp751MontgomeryReduce(&dest.B, &ad_plus_bc) // = (a*d + b*c)*R mod p | |||
var ac_minus_bd FpElementX2 | |||
fp751X2SubLazy(&ac_minus_bd, &ac, &bd) // = (a*c - b*d)*R*R | |||
fp751MontgomeryReduce(&dest.A, &ac_minus_bd) // = (a*c - b*d)*R mod p | |||
} | |||
func (fp751Ops) Square(dest, x *Fp2Element) { | |||
a := &x.A | |||
b := &x.B | |||
// We want to compute | |||
// | |||
// (a + bi)*(a + bi) = (a^2 - b^2) + 2abi. | |||
var a2, a_plus_b, a_minus_b FpElement | |||
fp751AddReduced(&a2, a, a) // = a*R + a*R = 2*a*R | |||
fp751AddReduced(&a_plus_b, a, b) // = a*R + b*R = (a+b)*R | |||
fp751SubReduced(&a_minus_b, a, b) // = a*R - b*R = (a-b)*R | |||
var asq_minus_bsq, ab2 FpElementX2 | |||
fp751Mul(&asq_minus_bsq, &a_plus_b, &a_minus_b) // = (a+b)*(a-b)*R*R = (a^2 - b^2)*R*R | |||
fp751Mul(&ab2, &a2, b) // = 2*a*b*R*R | |||
fp751MontgomeryReduce(&dest.A, &asq_minus_bsq) // = (a^2 - b^2)*R mod p | |||
fp751MontgomeryReduce(&dest.B, &ab2) // = 2*a*b*R mod p | |||
} | |||
// Set dest = 1/x | |||
// | |||
// Allowed to overlap dest with x. | |||
// | |||
// Returns dest to allow chaining operations. | |||
func (fp751Ops) Inv(dest, x *Fp2Element) { | |||
a := &x.A | |||
b := &x.B | |||
// We want to compute | |||
// | |||
// 1 1 (a - bi) (a - bi) | |||
// -------- = -------- -------- = ----------- | |||
// (a + bi) (a + bi) (a - bi) (a^2 + b^2) | |||
// | |||
// Letting c = 1/(a^2 + b^2), this is | |||
// | |||
// 1/(a+bi) = a*c - b*ci. | |||
var asq_plus_bsq primeFieldElement | |||
var asq, bsq FpElementX2 | |||
fp751Mul(&asq, a, a) // = a*a*R*R | |||
fp751Mul(&bsq, b, b) // = b*b*R*R | |||
fp751X2AddLazy(&asq, &asq, &bsq) // = (a^2 + b^2)*R*R | |||
fp751MontgomeryReduce(&asq_plus_bsq.A, &asq) // = (a^2 + b^2)*R mod p | |||
// Now asq_plus_bsq = a^2 + b^2 | |||
// Invert asq_plus_bsq | |||
inv := asq_plus_bsq | |||
inv.Mul(&asq_plus_bsq, &asq_plus_bsq) | |||
inv.P34(&inv) | |||
inv.Mul(&inv, &inv) | |||
inv.Mul(&inv, &asq_plus_bsq) | |||
var ac FpElementX2 | |||
fp751Mul(&ac, a, &inv.A) | |||
fp751MontgomeryReduce(&dest.A, &ac) | |||
var minus_b FpElement | |||
fp751SubReduced(&minus_b, &minus_b, b) | |||
var minus_bc FpElementX2 | |||
fp751Mul(&minus_bc, &minus_b, &inv.A) | |||
fp751MontgomeryReduce(&dest.B, &minus_bc) | |||
} | |||
// In case choice == 1, performs following swap in constant time: | |||
// xPx <-> xQx | |||
// xPz <-> xQz | |||
// Otherwise returns xPx, xPz, xQx, xQz unchanged | |||
func (fp751Ops) CondSwap(xPx, xPz, xQx, xQz *Fp2Element, choice uint8) { | |||
fp751ConditionalSwap(&xPx.A, &xQx.A, choice) | |||
fp751ConditionalSwap(&xPx.B, &xQx.B, choice) | |||
fp751ConditionalSwap(&xPz.A, &xQz.A, choice) | |||
fp751ConditionalSwap(&xPz.B, &xQz.B, choice) | |||
} | |||
// Converts values in x.A and x.B to Montgomery domain | |||
// x.A = x.A * R mod p | |||
// x.B = x.B * R mod p | |||
func (fp751Ops) ToMontgomery(x *Fp2Element) { | |||
var aRR FpElementX2 | |||
// convert to montgomery domain | |||
fp751Mul(&aRR, &x.A, &montgomeryRsq) // = a*R*R | |||
fp751MontgomeryReduce(&x.A, &aRR) // = a*R mod p | |||
fp751Mul(&aRR, &x.B, &montgomeryRsq) | |||
fp751MontgomeryReduce(&x.B, &aRR) | |||
} | |||
// Converts values in x.A and x.B from Montgomery domain | |||
// a = x.A mod p | |||
// b = x.B mod p | |||
// | |||
// After returning from the call x is not modified. | |||
func (fp751Ops) FromMontgomery(x *Fp2Element, out *Fp2Element) { | |||
var aR FpElementX2 | |||
// convert from montgomery domain | |||
copy(aR[:], x.A[:]) | |||
fp751MontgomeryReduce(&out.A, &aR) // = a mod p in [0, 2p) | |||
fp751StrongReduce(&out.A) // = a mod p in [0, p) | |||
copy(aR[:], x.B[:]) | |||
fp751MontgomeryReduce(&out.B, &aR) | |||
fp751StrongReduce(&out.B) | |||
} | |||
//------------------------------------------------------------------------------ | |||
// Prime Field | |||
//------------------------------------------------------------------------------ | |||
// Represents an element of the prime field F_p in Montgomery domain | |||
type primeFieldElement struct { | |||
// The value `A`is represented by `aR mod p`. | |||
A FpElement | |||
} | |||
// Set dest = lhs * rhs. | |||
// | |||
// Allowed to overlap lhs or rhs with dest. | |||
// | |||
// Returns dest to allow chaining operations. | |||
func (dest *primeFieldElement) Mul(lhs, rhs *primeFieldElement) *primeFieldElement { | |||
a := &lhs.A // = a*R | |||
b := &rhs.A // = b*R | |||
var ab FpElementX2 | |||
fp751Mul(&ab, a, b) // = a*b*R*R | |||
fp751MontgomeryReduce(&dest.A, &ab) // = a*b*R mod p | |||
return dest | |||
} | |||
// Set dest = x^(2^k), for k >= 1, by repeated squarings. | |||
// | |||
// Allowed to overlap x with dest. | |||
// | |||
// Returns dest to allow chaining operations. | |||
func (dest *primeFieldElement) Pow2k(x *primeFieldElement, k uint8) *primeFieldElement { | |||
dest.Mul(x, x) | |||
for i := uint8(1); i < k; i++ { | |||
dest.Mul(dest, dest) | |||
} | |||
return dest | |||
} | |||
// Set dest = x^((p-3)/4). If x is square, this is 1/sqrt(x). | |||
// | |||
// Allowed to overlap x with dest. | |||
// | |||
// Returns dest to allow chaining operations. | |||
func (dest *primeFieldElement) P34(x *primeFieldElement) *primeFieldElement { | |||
// Sliding-window strategy computed with Sage, awk, sed, and tr. | |||
// | |||
// This performs sum(powStrategy) = 744 squarings and len(mulStrategy) | |||
// = 137 multiplications, in addition to 1 squaring and 15 | |||
// multiplications to build a lookup table. | |||
// | |||
// In total this is 745 squarings, 152 multiplications. Since squaring | |||
// is not implemented for the prime field, this is 897 multiplications | |||
// in total. | |||
powStrategy := [137]uint8{5, 7, 6, 2, 10, 4, 6, 9, 8, 5, 9, 4, 7, 5, 5, 4, 8, 3, 9, 5, 5, 4, 10, 4, 6, 6, 6, 5, 8, 9, 3, 4, 9, 4, 5, 6, 6, 2, 9, 4, 5, 5, 5, 7, 7, 9, 4, 6, 4, 8, 5, 8, 6, 6, 2, 9, 7, 4, 8, 8, 8, 4, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2} | |||
mulStrategy := [137]uint8{31, 23, 21, 1, 31, 7, 7, 7, 9, 9, 19, 15, 23, 23, 11, 7, 25, 5, 21, 17, 11, 5, 17, 7, 11, 9, 23, 9, 1, 19, 5, 3, 25, 15, 11, 29, 31, 1, 29, 11, 13, 9, 11, 27, 13, 19, 15, 31, 3, 29, 23, 31, 25, 11, 1, 21, 19, 15, 15, 21, 29, 13, 23, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 3} | |||
initialMul := uint8(27) | |||
// Build a lookup table of odd multiples of x. | |||
lookup := [16]primeFieldElement{} | |||
xx := &primeFieldElement{} | |||
xx.Mul(x, x) // Set xx = x^2 | |||
lookup[0] = *x | |||
for i := 1; i < 16; i++ { | |||
lookup[i].Mul(&lookup[i-1], xx) | |||
} | |||
// Now lookup = {x, x^3, x^5, ... } | |||
// so that lookup[i] = x^{2*i + 1} | |||
// so that lookup[k/2] = x^k, for odd k | |||
*dest = lookup[initialMul/2] | |||
for i := uint8(0); i < 137; i++ { | |||
dest.Pow2k(dest, powStrategy[i]) | |||
dest.Mul(dest, &lookup[mulStrategy[i]/2]) | |||
} | |||
return dest | |||
} |
@@ -0,0 +1,129 @@ | |||
package p751 | |||
import ( | |||
"testing" | |||
. "github.com/cloudflare/p751sidh/internal/isogeny" | |||
) | |||
func TestFourIsogenyVersusSage(t *testing.T) { | |||
var xR, xP4, resPhiXr, expPhiXr ProjectivePoint | |||
var phi = Newisogeny4(kFieldOps) | |||
// sage: p = 2^372 * 3^239 - 1; Fp = GF(p) | |||
// sage: R.<x> = Fp[] | |||
// sage: Fp2 = Fp.extension(x^2 + 1, 'i') | |||
// sage: i = Fp2.gen() | |||
// sage: E0Fp = EllipticCurve(Fp, [0,0,0,1,0]) | |||
// sage: E0Fp2 = EllipticCurve(Fp2, [0,0,0,1,0]) | |||
// sage: x_PA = 11 | |||
// sage: y_PA = -Fp(11^3 + 11).sqrt() | |||
// sage: x_PB = 6 | |||
// sage: y_PB = -Fp(6^3 + 6).sqrt() | |||
// sage: P_A = 3^239 * E0Fp((x_PA,y_PA)) | |||
// sage: P_B = 2^372 * E0Fp((x_PB,y_PB)) | |||
// sage: def tau(P): | |||
// ....: return E0Fp2( (-P.xy()[0], i*P.xy()[1])) | |||
// ....: | |||
// sage: m_B = 3*randint(0,3^238) | |||
// sage: m_A = 2*randint(0,2^371) | |||
// sage: R_A = E0Fp2(P_A) + m_A*tau(P_A) | |||
// sage: def y_recover(x, a): | |||
// ....: return (x**3 + a*x**2 + x).sqrt() | |||
// ....: | |||
// sage: first_4_torsion_point = E0Fp2(1, y_recover(Fp2(1),0)) | |||
// sage: sage_first_4_isogeny = E0Fp2.isogeny(first_4_torsion_point) | |||
// sage: a = Fp2(0) | |||
// sage: E1A = EllipticCurve(Fp2, [0,(2*(a+6))/(a-2),0,1,0]) | |||
// sage: sage_isomorphism = sage_first_4_isogeny.codomain().isomorphism_to(E1A) | |||
// sage: isogenized_R_A = sage_isomorphism(sage_first_4_isogeny(R_A)) | |||
// sage: P_4 = (2**(372-4))*isogenized_R_A | |||
// sage: P_4._order = 4 #otherwise falls back to generic group methods for order | |||
// sage: X4, Z4 = P_4.xy()[0], 1 | |||
// sage: phi4 = EllipticCurveIsogeny(E1A, P_4, None, 4) | |||
// sage: E2A_sage = phi4.codomain() # not in monty form | |||
// sage: Aprime, Cprime = 2*(2*X4^4 - Z4^4), Z4^4 | |||
// sage: E2A = EllipticCurve(Fp2, [0,Aprime/Cprime,0,1,0]) | |||
// sage: sage_iso = E2A_sage.isomorphism_to(E2A) | |||
// sage: isogenized2_R_A = sage_iso(phi4(isogenized_R_A)) | |||
xP4 = ProjectivePoint{ | |||
X: Fp2Element{ | |||
A: FpElement{0x2afd75a913f3d5e7, 0x2918fba06f88c9ab, 0xa4ac4dc7cb526f05, 0x2d19e9391a607300, 0x7a79e2b34091b54, 0x3ad809dcb42f1792, 0xd46179328bd6402a, 0x1afa73541e2c4f3f, 0xf602d73ace9bdbd8, 0xd77ac58f6bab7004, 0x4689d97f6793b3b3, 0x4f26b00e42b7}, | |||
B: FpElement{0x6cdf918dafdcb890, 0x666f273cc29cfae2, 0xad00fcd31ba618e2, 0x5fbcf62bef2f6a33, 0xf408bb88318e5098, 0x84ab97849453d175, 0x501bbfcdcfb8e1ac, 0xf2370098e6b5542c, 0xc7dc73f5f0f6bd32, 0xdd76dcd86729d1cf, 0xca22c905029996e4, 0x5cf4a9373de3}}, | |||
Z: P751_OneFp2} | |||
xR = ProjectivePoint{ | |||
X: Fp2Element{ | |||
A: FpElement{0xff99e76f78da1e05, 0xdaa36bd2bb8d97c4, 0xb4328cee0a409daf, 0xc28b099980c5da3f, 0xf2d7cd15cfebb852, 0x1935103dded6cdef, 0xade81528de1429c3, 0x6775b0fa90a64319, 0x25f89817ee52485d, 0x706e2d00848e697, 0xc4958ec4216d65c0, 0xc519681417f}, | |||
B: FpElement{0x742fe7dde60e1fb9, 0x801a3c78466a456b, 0xa9f945b786f48c35, 0x20ce89e1b144348f, 0xf633970b7776217e, 0x4c6077a9b38976e5, 0x34a513fc766c7825, 0xacccba359b9cd65, 0xd0ca8383f0fd0125, 0x77350437196287a, 0x9fe1ad7706d4ea21, 0x4d26129ee42d}}, | |||
Z: P751_OneFp2} | |||
expPhiXr = ProjectivePoint{ | |||
X: Fp2Element{ | |||
A: FpElement{0x111efd8bd0b7a01e, 0x6ab75a4f3789ca9b, 0x939dbe518564cac4, 0xf9eeaba1601d0434, 0x8d41f8ba6edac998, 0xfcd2557efe9aa170, 0xb3c3549c098b7844, 0x52874fef6f81127c, 0xb2b9ac82aa518bb3, 0xee70820230520a86, 0xd4012b7f5efb184a, 0x573e4536329b}, | |||
B: FpElement{0xa99952281e932902, 0x569a89a571f2c7b1, 0x6150143846ba3f6b, 0x11fd204441e91430, 0x7f469bd55c9b07b, 0xb72db8b9de35b161, 0x455a9a37a940512a, 0xb0cff7670abaf906, 0x18c785b7583375fe, 0x603ab9ca403c9148, 0xab54ba3a6e6c62c1, 0x2726d7d57c4f}}, | |||
Z: P751_OneFp2} | |||
phi.GenerateCurve(&xP4) | |||
resPhiXr = phi.EvaluatePoint(&xR) | |||
if !VartimeEqProjFp2(&expPhiXr, &resPhiXr) { | |||
t.Error("\nExpected\n", expPhiXr.ToAffine(kCurveOps), "\nfound\n", resPhiXr.ToAffine(kCurveOps)) | |||
} | |||
} | |||
func TestThreeIsogenyVersusSage(t *testing.T) { | |||
var xR, xP3, resPhiXr, expPhiXr ProjectivePoint | |||
var phi = Newisogeny3(kFieldOps) | |||
// sage: %colors Linux | |||
// sage: p = 2^372 * 3^239 - 1; Fp = GF(p) | |||
// sage: R.<x> = Fp[] | |||
// sage: Fp2 = Fp.extension(x^2 + 1, 'i') | |||
// sage: i = Fp2.gen() | |||
// sage: E0Fp = EllipticCurve(Fp, [0,0,0,1,0]) | |||
// sage: E0Fp2 = EllipticCurve(Fp2, [0,0,0,1,0]) | |||
// sage: x_PA = 11 | |||
// sage: y_PA = -Fp(11^3 + 11).sqrt() | |||
// sage: x_PB = 6 | |||
// sage: y_PB = -Fp(6^3 + 6).sqrt() | |||
// sage: P_A = 3^239 * E0Fp((x_PA,y_PA)) | |||
// sage: P_B = 2^372 * E0Fp((x_PB,y_PB)) | |||
// sage: def tau(P): | |||
// ....: return E0Fp2( (-P.xy()[0], i*P.xy()[1])) | |||
// ....: | |||
// sage: m_B = 3*randint(0,3^238) | |||
// sage: R_B = E0Fp2(P_B) + m_B*tau(P_B) | |||
// sage: P_3 = (3^238)*R_B | |||
// sage: def three_isog(P_3, P): | |||
// ....: X3, Z3 = P_3.xy()[0], 1 | |||
// ....: XP, ZP = P.xy()[0], 1 | |||
// ....: x = (XP*(X3*XP - Z3*ZP)^2)/(ZP*(Z3*XP - X3*ZP)^2) | |||
// ....: A3, C3 = (Z3^4 + 9*X3^2*(2*Z3^2 - 3*X3^2)), 4*X3*Z3^3 | |||
// ....: cod = EllipticCurve(Fp2, [0,A3/C3,0,1,0]) | |||
// ....: return cod.lift_x(x) | |||
// ....: | |||
// sage: isogenized_R_B = three_isog(P_3, R_B) | |||
xR = ProjectivePoint{ | |||
X: Fp2Element{ | |||
A: FpElement{0xbd0737ed5cc9a3d7, 0x45ae6d476517c101, 0x6f228e9e7364fdb2, 0xbba4871225b3dbd, 0x6299ccd2e5da1a07, 0x38488fe4af5f2d0e, 0xec23cae5a86e980c, 0x26c804ba3f1edffa, 0xfbbed81932df60e5, 0x7e00e9d182ae9187, 0xc7654abb66d05f4b, 0x262d0567237b}, | |||
B: FpElement{0x3a3b5b6ad0b2ac33, 0x246602b5179127d3, 0x502ae0e9ad65077d, 0x10a3a37237e1bf70, 0x4a1ab9294dd05610, 0xb0f3adac30fe1fa6, 0x341995267faf70cb, 0xa14dd94d39cf4ec1, 0xce4b7527d1bf5568, 0xe0410423ed45c7e4, 0x38011809b6425686, 0x28f52472ebed}}, | |||
Z: P751_OneFp2} | |||
xP3 = ProjectivePoint{ | |||
X: Fp2Element{ | |||
A: FpElement{0x7bb7a4a07b0788dc, 0xdc36a3f6607b21b0, 0x4750e18ee74cf2f0, 0x464e319d0b7ab806, 0xc25aa44c04f758ff, 0x392e8521a46e0a68, 0xfc4e76b63eff37df, 0x1f3566d892e67dd8, 0xf8d2eb0f73295e65, 0x457b13ebc470bccb, 0xfda1cc9efef5be33, 0x5dbf3d92cc02}, | |||
B: FpElement{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, | |||
Z: P751_OneFp2} | |||
expPhiXr = ProjectivePoint{ | |||
X: Fp2Element{ | |||
A: FpElement{0x286db7d75913c5b1, 0xcb2049ad50189220, 0xccee90ef765fa9f4, 0x65e52ce2730e7d88, 0xa6b6b553bd0d06e7, 0xb561ecec14591590, 0x17b7a66d8c64d959, 0x77778cecbe1461e, 0x9405c9c0c41a57ce, 0x8f6b4847e8ca7d3d, 0xf625eb987b366937, 0x421b3590e345}, | |||
B: FpElement{0x566b893803e7d8d6, 0xe8c71a04d527e696, 0x5a1d8f87bf5eb51, 0x42ae08ae098724f, 0x4ee3d7c7af40ca2e, 0xd9f9ab9067bb10a7, 0xecd53d69edd6328c, 0xa581e9202dea107d, 0x8bcdfb6c8ecf9257, 0xe7cbbc2e5cbcf2af, 0x5f031a8701f0e53e, 0x18312d93e3cb}}, | |||
Z: P751_OneFp2} | |||
phi.GenerateCurve(&xP3) | |||
resPhiXr = phi.EvaluatePoint(&xR) | |||
if !VartimeEqProjFp2(&expPhiXr, &resPhiXr) { | |||
t.Error("\nExpected\n", expPhiXr.ToAffine(kCurveOps), "\nfound\n", resPhiXr.ToAffine(kCurveOps)) | |||
} | |||
} |
@@ -0,0 +1,341 @@ | |||
package p751 | |||
// Tools used for testing and debugging | |||
import ( | |||
"fmt" | |||
"math/big" | |||
"math/rand" | |||
"reflect" | |||
"testing/quick" | |||
. "github.com/cloudflare/p751sidh/internal/isogeny" | |||
) | |||
/* ------------------------------------------------------------------------- | |||
Underlying field configuration | |||
-------------------------------------------------------------------------*/ | |||
var ( | |||
kFieldOps = FieldOperations() | |||
kParams = &SidhParams{ | |||
Op: kFieldOps, | |||
OneFp2: P751_OneFp2, | |||
HalfFp2: P751_HalfFp2, | |||
Bytelen: P751_Bytelen, | |||
} | |||
kCurveOps = &CurveOperations{Params: kParams} | |||
) | |||
/* ------------------------------------------------------------------------- | |||
Configure testing/quick | |||
-------------------------------------------------------------------------*/ | |||
var ( | |||
quickCheckScaleFactor = uint8(3) | |||
quickCheckConfig = &quick.Config{MaxCount: (1 << (12 + quickCheckScaleFactor))} | |||
) | |||
/* ------------------------------------------------------------------------- | |||
Structure used by tests | |||
-------------------------------------------------------------------------*/ | |||
type GeneratedTestParams struct { | |||
Point ProjectivePoint | |||
Cparam ProjectiveCurveParameters | |||
ExtElem Fp2Element | |||
} | |||
/* ------------------------------------------------------------------------- | |||
Test values | |||
-------------------------------------------------------------------------*/ | |||
// A = 4385300808024233870220415655826946795549183378139271271040522089756750951667981765872679172832050962894122367066234419550072004266298327417513857609747116903999863022476533671840646615759860564818837299058134292387429068536219*i + 1408083354499944307008104531475821995920666351413327060806684084512082259107262519686546161682384352696826343970108773343853651664489352092568012759783386151707999371397181344707721407830640876552312524779901115054295865393760 | |||
var ( | |||
curve_A = Fp2Element{ | |||
A: FpElement{0x8319eb18ca2c435e, 0x3a93beae72cd0267, 0x5e465e1f72fd5a84, 0x8617fa4150aa7272, 0x887da24799d62a13, 0xb079b31b3c7667fe, 0xc4661b150fa14f2e, 0xd4d2b2967bc6efd6, 0x854215a8b7239003, 0x61c5302ccba656c2, 0xf93194a27d6f97a2, 0x1ed9532bca75}, | |||
B: FpElement{0xb6f541040e8c7db6, 0x99403e7365342e15, 0x457e9cee7c29cced, 0x8ece72dc073b1d67, 0x6e73cef17ad28d28, 0x7aed836ca317472, 0x89e1de9454263b54, 0x745329277aa0071b, 0xf623dfc73bc86b9b, 0xb8e3c1d8a9245882, 0x6ad0b3d317770bec, 0x5b406e8d502b}} | |||
// C = 933177602672972392833143808100058748100491911694554386487433154761658932801917030685312352302083870852688835968069519091048283111836766101703759957146191882367397129269726925521881467635358356591977198680477382414690421049768*i + 9088894745865170214288643088620446862479558967886622582768682946704447519087179261631044546285104919696820250567182021319063155067584445633834024992188567423889559216759336548208016316396859149888322907914724065641454773776307 | |||
curve_C = Fp2Element{ | |||
A: FpElement{0x4fb2358bbf723107, 0x3a791521ac79e240, 0x283e24ef7c4c922f, 0xc89baa1205e33cc, 0x3031be81cff6fee1, 0xaf7a494a2f6a95c4, 0x248d251eaac83a1d, 0xc122fca1e2550c88, 0xbc0451b11b6cfd3d, 0x9c0a114ab046222c, 0x43b957b32f21f6ea, 0x5b9c87fa61de}, | |||
B: FpElement{0xacf142afaac15ec6, 0xfd1322a504a071d5, 0x56bb205e10f6c5c6, 0xe204d2849a97b9bd, 0x40b0122202fe7f2e, 0xecf72c6fafacf2cb, 0x45dfc681f869f60a, 0x11814c9aff4af66c, 0x9278b0c4eea54fe7, 0x9a633d5baf7f2e2e, 0x69a329e6f1a05112, 0x1d874ace23e4}} | |||
// x(P) = 8172151271761071554796221948801462094972242987811852753144865524899433583596839357223411088919388342364651632180452081960511516040935428737829624206426287774255114241789158000915683252363913079335550843837650671094705509470594*i + 9326574858039944121604015439381720195556183422719505497448541073272720545047742235526963773359004021838961919129020087515274115525812121436661025030481584576474033630899768377131534320053412545346268645085054880212827284581557 | |||
affine_xP = Fp2Element{ | |||
A: FpElement{0xe8d05f30aac47247, 0x576ec00c55441de7, 0xbf1a8ec5fe558518, 0xd77cb17f77515881, 0x8e9852837ee73ec4, 0x8159634ad4f44a6b, 0x2e4eb5533a798c5, 0x9be8c4354d5bc849, 0xf47dc61806496b84, 0x25d0e130295120e0, 0xdbef54095f8139e3, 0x5a724f20862c}, | |||
B: FpElement{0x3ca30d7623602e30, 0xfb281eddf45f07b7, 0xd2bf62d5901a45bc, 0xc67c9baf86306dd2, 0x4e2bd93093f538ca, 0xcfd92075c25b9cbe, 0xceafe9a3095bcbab, 0x7d928ad380c85414, 0x37c5f38b2afdc095, 0x75325899a7b779f4, 0xf130568249f20fdd, 0x178f264767d1}} | |||
// x([2]P) = 1476586462090705633631615225226507185986710728845281579274759750260315746890216330325246185232948298241128541272709769576682305216876843626191069809810990267291824247158062860010264352034514805065784938198193493333201179504845*i + 3623708673253635214546781153561465284135688791018117615357700171724097420944592557655719832228709144190233454198555848137097153934561706150196041331832421059972652530564323645509890008896574678228045006354394485640545367112224 | |||
affine_xP2 = Fp2Element{ | |||
A: FpElement{0x2a77afa8576ce979, 0xab1360e69b0aeba0, 0xd79e3e3cbffad660, 0x5fd0175aa10f106b, 0x1800ebafce9fbdbc, 0x228fc9142bdd6166, 0x867cf907314e34c3, 0xa58d18c94c13c31c, 0x699a5bc78b11499f, 0xa29fc29a01f7ccf1, 0x6c69c0c5347eebce, 0x38ecee0cc57}, | |||
B: FpElement{0x43607fd5f4837da0, 0x560bad4ce27f8f4a, 0x2164927f8495b4dd, 0x621103fdb831a997, 0xad740c4eea7db2db, 0x2cde0442205096cd, 0x2af51a70ede8324e, 0x41a4e680b9f3466, 0x5481f74660b8f476, 0xfcb2f3e656ff4d18, 0x42e3ce0837171acc, 0x44238c30530c}} | |||
// x([3]P) = 9351941061182433396254169746041546943662317734130813745868897924918150043217746763025923323891372857734564353401396667570940585840576256269386471444236630417779544535291208627646172485976486155620044292287052393847140181703665*i + 9010417309438761934687053906541862978676948345305618417255296028956221117900864204687119686555681136336037659036201780543527957809743092793196559099050594959988453765829339642265399496041485088089691808244290286521100323250273 | |||
affine_xP3 = Fp2Element{ | |||
A: FpElement{0x2096e3f23feca947, 0xf36f635aa4ad8634, 0xdae3b1c6983c5e9a, 0xe08df6c262cb74b4, 0xd2ca4edc37452d3d, 0xfb5f3fe42f500c79, 0x73740aa3abc2b21f, 0xd535fd869f914cca, 0x4a558466823fb67f, 0x3e50a7a0e3bfc715, 0xf43c6da9183a132f, 0x61aca1e1b8b9}, | |||
B: FpElement{0x1e54ec26ea5077bd, 0x61380572d8769f9a, 0xc615170684f59818, 0x6309c3b93e84ef6e, 0x33c74b1318c3fcd0, 0xfe8d7956835afb14, 0x2d5a7b55423c1ecc, 0x869db67edfafea68, 0x1292632394f0a628, 0x10bba48225bfd141, 0x6466c28b408daba, 0x63cacfdb7c43}} | |||
// x([2^2]P) = 441719501189485559222919502512761433931671682884872259563221427434901842337947564993718830905758163254463901652874331063768876314142359813382575876106725244985607032091781306919778265250690045578695338669105227100119314831452*i + 6961734028200975729170216310486458180126343885294922940439352055937945948015840788921225114530454649744697857047401608073256634790353321931728699534700109268264491160589480994022419317695690866764726967221310990488404411684053 | |||
affine_xP4 = Fp2Element{ | |||
A: FpElement{0x6f9dbe4c39175153, 0xf2fec757eb99e88, 0x43d7361a93733d91, 0x3abd10ed19c85a3d, 0xc4de9ab9c5ef7181, 0x53e375901684c900, 0x68ffc3e7d71c41ff, 0x47adab62c8d942fe, 0x226a33fd6fbb381d, 0x87ef4c8fdd83309a, 0xaca1cf44c5fa8799, 0x6cbae86c755f}, | |||
B: FpElement{0x4c80c37fe68282a7, 0xbd8b9d7248bf553a, 0x1fb0e8e74d5e1762, 0xb63fa0e4e5f91482, 0xc675ab8a45a1439, 0xdfa6772deace7820, 0xf0d813d71d9a9255, 0x53a1a58c634534bd, 0x4ebfc6485fdfd888, 0x6991fe4358bcf169, 0xc0547bdaca85b6fd, 0xf461548d632}} | |||
// x([3^2]P) = 3957171963425208493644602380039721164492341594850197356580248639045894821895524981729970650520936632013218950972842867220898274664982599375786979902471523505057611521217523103474682939638645404445093536997296151472632038973463*i + 1357869545269286021642168835877253886774707209614159162748874474269328421720121175566245719916322684751967981171882659798149072149161259103020057556362998810229937432814792024248155991141511691087135859252304684633946087474060 | |||
affine_xP9 = Fp2Element{ | |||
A: FpElement{0x7c0daa0f04ded4e0, 0x52dc4f883d85e065, 0x91afbdc2c1714d0b, 0xb7b3db8e658cfeba, 0x43d4e72a692882f3, 0x535c56d83753da30, 0xc8a58724433cbf5d, 0x351153c0a5e74219, 0x2c81827d19f93dd5, 0x26ef8aca3370ea1a, 0x1cf939a6dd225dec, 0x3403cb28ad41}, | |||
B: FpElement{0x93e7bc373a9ff7b, 0x57b8cc47635ebc0f, 0x92eab55689106cf3, 0x93643111d421f24c, 0x1c58b519506f6b7a, 0xebd409fb998faa13, 0x5c86ed799d09d80e, 0xd9a1d764d6363562, 0xf95e87f92fb0c4cc, 0x6b2bbaf5632a5609, 0x2d9b6a809dfaff7f, 0x29c0460348b}} | |||
// m = 96550223052359874398280314003345143371473380422728857598463622014420884224892 | |||
mScalarBytes = [...]uint8{0x7c, 0x7b, 0x95, 0xfa, 0xb4, 0x75, 0x6c, 0x48, 0x8c, 0x17, 0x55, 0xb4, 0x49, 0xf5, 0x1e, 0xa3, 0xb, 0x31, 0xf0, 0xa4, 0xa6, 0x81, 0xad, 0x94, 0x51, 0x11, 0xe7, 0xf5, 0x5b, 0x7d, 0x75, 0xd5} | |||
// x([m]P) = 7893578558852400052689739833699289348717964559651707250677393044951777272628231794999463214496545377542328262828965953246725804301238040891993859185944339366910592967840967752138115122568615081881937109746463885908097382992642*i + 8293895847098220389503562888233557012043261770526854885191188476280014204211818299871679993460086974249554528517413590157845430186202704783785316202196966198176323445986064452630594623103149383929503089342736311904030571524837 | |||
affine_xaP = Fp2Element{ | |||
A: FpElement{0x2112f3c7d7f938bb, 0x704a677f0a4df08f, 0x825370e31fb4ef00, 0xddbf79b7469f902, 0x27640c899ea739fd, 0xfb7b8b19f244108e, 0x546a6679dd3baebc, 0xe9f0ecf398d5265f, 0x223d2b350e75e461, 0x84b322a0b6aff016, 0xfabe426f539f8b39, 0x4507a0604f50}, | |||
B: FpElement{0xac77737e5618a5fe, 0xf91c0e08c436ca52, 0xd124037bc323533c, 0xc9a772bf52c58b63, 0x3b30c8f38ef6af4d, 0xb9eed160e134f36e, 0x24e3836393b25017, 0xc828be1b11baf1d9, 0x7b7dab585df50e93, 0x1ca3852c618bd8e0, 0x4efa73bcb359fa00, 0x50b6a923c2d4}} | |||
// Inputs for testing 3-point-ladder | |||
threePointLadderInputs = []ProjectivePoint{ | |||
// x(P) | |||
ProjectivePoint{ | |||
X: Fp2Element{ | |||
A: FpElement{0xe8d05f30aac47247, 0x576ec00c55441de7, 0xbf1a8ec5fe558518, 0xd77cb17f77515881, 0x8e9852837ee73ec4, 0x8159634ad4f44a6b, 0x2e4eb5533a798c5, 0x9be8c4354d5bc849, 0xf47dc61806496b84, 0x25d0e130295120e0, 0xdbef54095f8139e3, 0x5a724f20862c}, | |||
B: FpElement{0x3ca30d7623602e30, 0xfb281eddf45f07b7, 0xd2bf62d5901a45bc, 0xc67c9baf86306dd2, 0x4e2bd93093f538ca, 0xcfd92075c25b9cbe, 0xceafe9a3095bcbab, 0x7d928ad380c85414, 0x37c5f38b2afdc095, 0x75325899a7b779f4, 0xf130568249f20fdd, 0x178f264767d1}}, | |||
Z: P751_OneFp2, | |||
}, | |||
// x(Q) | |||
ProjectivePoint{ | |||
X: Fp2Element{ | |||
A: FpElement{0x2b71a2a93ad1e10e, 0xf0b9842a92cfb333, 0xae17373615a27f5c, 0x3039239f428330c4, 0xa0c4b735ed7dcf98, 0x6e359771ddf6af6a, 0xe986e4cac4584651, 0x8233a2b622d5518, 0xbfd67bf5f06b818b, 0xdffe38d0f5b966a6, 0xa86b36a3272ee00a, 0x193e2ea4f68f}, | |||
B: FpElement{0x5a0f396459d9d998, 0x479f42250b1b7dda, 0x4016b57e2a15bf75, 0xc59f915203fa3749, 0xd5f90257399cf8da, 0x1fb2dadfd86dcef4, 0x600f20e6429021dc, 0x17e347d380c57581, 0xc1b0d5fa8fe3e440, 0xbcf035330ac20e8, 0x50c2eb5f6a4f03e6, 0x86b7c4571}}, | |||
Z: P751_OneFp2, | |||
}, | |||
// x(P-Q) | |||
ProjectivePoint{ | |||
X: Fp2Element{ | |||
A: FpElement{0x4aafa9f378f7b5ff, 0x1172a683aa8eee0, 0xea518d8cbec2c1de, 0xe191bcbb63674557, 0x97bc19637b259011, 0xdbeae5c9f4a2e454, 0x78f64d1b72a42f95, 0xe71cb4ea7e181e54, 0xe4169d4c48543994, 0x6198c2286a98730f, 0xd21d675bbab1afa5, 0x2e7269fce391}, | |||
B: FpElement{0x23355783ce1d0450, 0x683164cf4ce3d93f, 0xae6d1c4d25970fd8, 0x7807007fb80b48cf, 0xa005a62ec2bbb8a2, 0x6b5649bd016004cb, 0xbb1a13fa1330176b, 0xbf38e51087660461, 0xe577fddc5dd7b930, 0x5f38116f56947cd3, 0x3124f30b98c36fde, 0x4ca9b6e6db37}}, | |||
Z: P751_OneFp2, | |||
}, | |||
} | |||
curve = ProjectiveCurveParameters{A: curve_A, C: curve_C} | |||
cln16prime, _ = new(big.Int).SetString("10354717741769305252977768237866805321427389645549071170116189679054678940682478846502882896561066713624553211618840202385203911976522554393044160468771151816976706840078913334358399730952774926980235086850991501872665651576831", 10) | |||
) | |||
/* ------------------------------------------------------------------------- | |||
Values used by benchmarking tools | |||
-------------------------------------------------------------------------*/ | |||
// Package-level storage for this field element is intended to deter | |||
// compiler optimizations. | |||
var ( | |||
benchmarkFpElement FpElement | |||
benchmarkFpElementX2 FpElementX2 | |||
bench_x = FpElement{17026702066521327207, 5108203422050077993, 10225396685796065916, 11153620995215874678, 6531160855165088358, 15302925148404145445, 1248821577836769963, 9789766903037985294, 7493111552032041328, 10838999828319306046, 18103257655515297935, 27403304611634} | |||
bench_y = FpElement{4227467157325093378, 10699492810770426363, 13500940151395637365, 12966403950118934952, 16517692605450415877, 13647111148905630666, 14223628886152717087, 7167843152346903316, 15855377759596736571, 4300673881383687338, 6635288001920617779, 30486099554235} | |||
bench_z = FpElementX2{1595347748594595712, 10854920567160033970, 16877102267020034574, 12435724995376660096, 3757940912203224231, 8251999420280413600, 3648859773438820227, 17622716832674727914, 11029567000887241528, 11216190007549447055, 17606662790980286987, 4720707159513626555, 12887743598335030915, 14954645239176589309, 14178817688915225254, 1191346797768989683, 12629157932334713723, 6348851952904485603, 16444232588597434895, 7809979927681678066, 14642637672942531613, 3092657597757640067, 10160361564485285723, 240071237} | |||
) | |||
// Helpers | |||
func (primeElement primeFieldElement) String() string { | |||
b := toBigInt(&primeElement.A) | |||
return fmt.Sprintf("%X", b.String()) | |||
} | |||
/* ------------------------------------------------------------------------- | |||
Helpers | |||
-------------------------------------------------------------------------*/ | |||
// Given xP = x(P), xQ = x(Q), and xPmQ = x(P-Q), compute xR = x(P+Q). | |||
// | |||
// Returns xR to allow chaining. Safe to overlap xP, xQ, xR. | |||
func AddProjFp2(xP, xQ, xPmQ *ProjectivePoint) ProjectivePoint { | |||
// Algorithm 1 of Costello-Smith. | |||
var v0, v1, v2, v3, v4 Fp2Element | |||
var xR ProjectivePoint | |||
kFieldOps.Add(&v0, &xP.X, &xP.Z) // X_P + Z_P | |||
kFieldOps.Sub(&v1, &xQ.X, &xQ.Z) | |||
kFieldOps.Mul(&v1, &v1, &v0) // (X_Q - Z_Q)(X_P + Z_P) | |||
kFieldOps.Sub(&v0, &xP.X, &xP.Z) // X_P - Z_P | |||
kFieldOps.Add(&v2, &xQ.X, &xQ.Z) | |||
kFieldOps.Mul(&v2, &v2, &v0) // (X_Q + Z_Q)(X_P - Z_P) | |||
kFieldOps.Add(&v3, &v1, &v2) | |||
kFieldOps.Square(&v3, &v3) // 4(X_Q X_P - Z_Q Z_P)^2 | |||
kFieldOps.Sub(&v4, &v1, &v2) | |||
kFieldOps.Square(&v4, &v4) // 4(X_Q Z_P - Z_Q X_P)^2 | |||
kFieldOps.Mul(&v0, &xPmQ.Z, &v3) // 4X_{P-Q}(X_Q X_P - Z_Q Z_P)^2 | |||
kFieldOps.Mul(&xR.Z, &xPmQ.X, &v4) // 4Z_{P-Q}(X_Q Z_P - Z_Q X_P)^2 | |||
xR.X = v0 | |||
return xR | |||
} | |||
// Given xP = x(P) and cached curve parameters Aplus2C = A + 2*C, C4 = 4*C, | |||
// compute xQ = x([2]P). | |||
// | |||
// Returns xQ to allow chaining. Safe to overlap xP, xQ. | |||
func DoubleProjFp2(xP *ProjectivePoint, Aplus2C, C4 *Fp2Element) ProjectivePoint { | |||
// Algorithm 2 of Costello-Smith, amended to work with projective curve coefficients. | |||
var v1, v2, v3, xz4 Fp2Element | |||
var xQ ProjectivePoint | |||
kFieldOps.Add(&v1, &xP.X, &xP.Z) // (X+Z)^2 | |||
kFieldOps.Square(&v1, &v1) | |||
kFieldOps.Sub(&v2, &xP.X, &xP.Z) // (X-Z)^2 | |||
kFieldOps.Square(&v2, &v2) | |||
kFieldOps.Sub(&xz4, &v1, &v2) // 4XZ = (X+Z)^2 - (X-Z)^2 | |||
kFieldOps.Mul(&v2, &v2, C4) // 4C(X-Z)^2 | |||
kFieldOps.Mul(&xQ.X, &v1, &v2) // 4C(X+Z)^2(X-Z)^2 | |||
kFieldOps.Mul(&v3, &xz4, Aplus2C) // 4XZ(A + 2C) | |||
kFieldOps.Add(&v3, &v3, &v2) // 4XZ(A + 2C) + 4C(X-Z)^2 | |||
kFieldOps.Mul(&xQ.Z, &v3, &xz4) // (4XZ(A + 2C) + 4C(X-Z)^2)4XZ | |||
// Now (xQ.x : xQ.z) | |||
// = (4C(X+Z)^2(X-Z)^2 : (4XZ(A + 2C) + 4C(X-Z)^2)4XZ ) | |||
// = ((X+Z)^2(X-Z)^2 : (4XZ((A + 2C)/4C) + (X-Z)^2)4XZ ) | |||
// = ((X+Z)^2(X-Z)^2 : (4XZ((a + 2)/4) + (X-Z)^2)4XZ ) | |||
return xQ | |||
} | |||
// Given x(P) and a scalar m in little-endian bytes, compute x([m]P) using the | |||
// Montgomery ladder. This is described in Algorithm 8 of Costello-Smith. | |||
// | |||
// This function's execution time is dependent only on the byte-length of the | |||
// input scalar. All scalars of the same input length execute in uniform time. | |||
// The scalar can be padded with zero bytes to ensure a uniform length. | |||
// | |||
// Safe to overlap the source with the destination. | |||
func ScalarMult(curve *ProjectiveCurveParameters, xP *ProjectivePoint, scalar []uint8) ProjectivePoint { | |||
var x0, x1, tmp ProjectivePoint | |||
var Aplus2C, C4 Fp2Element | |||
kFieldOps.Add(&Aplus2C, &curve.C, &curve.C) // = 2*C | |||
kFieldOps.Add(&C4, &Aplus2C, &Aplus2C) // = 4*C | |||
kFieldOps.Add(&Aplus2C, &Aplus2C, &curve.A) // = 2*C + A | |||
x0.X = P751_OneFp2 | |||
x1 = *xP | |||
// Iterate over the bits of the scalar, top to bottom | |||
prevBit := uint8(0) | |||
for i := len(scalar) - 1; i >= 0; i-- { | |||
scalarByte := scalar[i] | |||
for j := 7; j >= 0; j-- { | |||
bit := (scalarByte >> uint(j)) & 0x1 | |||
kCurveOps.Params.Op.CondSwap(&x0.X, &x0.Z, &x1.X, &x1.Z, (bit ^ prevBit)) | |||
//sProjectivePointConditionalSwap(&x0, &x1, (bit ^ prevBit)) | |||
tmp = DoubleProjFp2(&x0, &Aplus2C, &C4) | |||
x1 = AddProjFp2(&x0, &x1, xP) | |||
x0 = tmp | |||
prevBit = bit | |||
} | |||
} | |||
// now prevBit is the lowest bit of the scalar | |||
kCurveOps.Params.Op.CondSwap(&x0.X, &x0.Z, &x1.X, &x1.Z, prevBit) | |||
return x0 | |||
} | |||
// Returns true if lhs = rhs. Takes variable time. | |||
func VartimeEqFp2(lhs, rhs *Fp2Element) bool { | |||
a := *lhs | |||
b := *rhs | |||
fp751StrongReduce(&a.A) | |||
fp751StrongReduce(&a.B) | |||
fp751StrongReduce(&b.A) | |||
fp751StrongReduce(&b.B) | |||
eq := true | |||
for i := 0; i < len(a.A)&&eq; i++ { | |||
eq = eq && (a.A[i] == b.A[i]) | |||
eq = eq && (a.B[i] == b.B[i]) | |||
} | |||
return eq | |||
} | |||
// Returns true if lhs = rhs. Takes variable time. | |||
func VartimeEqProjFp2(lhs, rhs *ProjectivePoint) bool { | |||
var t0,t1 Fp2Element | |||
kFieldOps.Mul(&t0, &lhs.X, &rhs.Z) | |||
kFieldOps.Mul(&t1, &lhs.Z, &rhs.X) | |||
return VartimeEqFp2(&t0, &t1) | |||
} | |||
func (GeneratedTestParams) generateFp2p751(rand *rand.Rand) Fp2Element { | |||
// Generation strategy: low limbs taken from [0,2^64); high limb | |||
// taken from smaller range | |||
// | |||
// Size hint is ignored since all elements are fixed size. | |||
// | |||
// Field elements taken in range [0,2p). Emulate this by capping | |||
// the high limb by the top digit of 2*p-1: | |||
// | |||
// sage: (2*p-1).digits(2^64)[-1] | |||
// 246065832128056 | |||
// | |||
// This still allows generating values >= 2p, but hopefully that | |||
// excess is OK (and if it's not, we'll find out, because it's for | |||
// testing...) | |||
// | |||
highLimb := rand.Uint64() % 246065832128056 | |||
fpElementGen := func() FpElement { | |||
return FpElement{ | |||
rand.Uint64(), | |||
rand.Uint64(), | |||
rand.Uint64(), | |||
rand.Uint64(), | |||
rand.Uint64(), | |||
rand.Uint64(), | |||
rand.Uint64(), | |||
rand.Uint64(), | |||
rand.Uint64(), | |||
rand.Uint64(), | |||
rand.Uint64(), | |||
highLimb, | |||
} | |||
} | |||
return Fp2Element{A: fpElementGen(), B: fpElementGen()} | |||
} | |||
func (c GeneratedTestParams) Generate(rand *rand.Rand, size int) reflect.Value { | |||
return reflect.ValueOf( | |||
GeneratedTestParams{ | |||
ProjectivePoint{ | |||
X: c.generateFp2p751(rand), | |||
Z: c.generateFp2p751(rand), | |||
}, | |||
ProjectiveCurveParameters{ | |||
A: c.generateFp2p751(rand), | |||
C: c.generateFp2p751(rand), | |||
}, | |||
c.generateFp2p751(rand), | |||
}) | |||
} | |||
func (x primeFieldElement) Generate(rand *rand.Rand, size int) reflect.Value { | |||
return reflect.ValueOf(primeFieldElement{ | |||
A: new(GeneratedTestParams).generateFp2p751(rand).A}) | |||
} | |||
// Convert an FpElement to a big.Int for testing. Because this is only | |||
// for testing, no big.Int to FpElement conversion is provided. | |||
func radix64ToBigInt(x []uint64) *big.Int { | |||
radix := new(big.Int) | |||
// 2^64 | |||
radix.UnmarshalText(([]byte)("18446744073709551616")) | |||
base := new(big.Int).SetUint64(1) | |||
val := new(big.Int).SetUint64(0) | |||
tmp := new(big.Int) | |||
for _, xi := range x { | |||
tmp.SetUint64(xi) | |||
tmp.Mul(tmp, base) | |||
val.Add(val, tmp) | |||
base.Mul(base, radix) | |||
} | |||
return val | |||
} | |||
// Converts number from Montgomery domain and returns big.Int | |||
func toBigInt(x *FpElement) *big.Int { | |||
var fp Fp2Element | |||
var in = Fp2Element{A:*x} | |||
kFieldOps.FromMontgomery(&in, &fp) | |||
return radix64ToBigInt(fp.A[:]) | |||
} |
@@ -1,293 +0,0 @@ | |||
package p751toolbox | |||
// A point on the projective line P^1(F_{p^2}). | |||
// | |||
// This is used to work projectively with the curve coefficients. | |||
type ProjectiveCurveParameters struct { | |||
A ExtensionFieldElement | |||
C ExtensionFieldElement | |||
} | |||
// Stores curve projective parameters equivalent to A/C. Meaning of the | |||
// values depends on the context. When working with isogenies over | |||
// subgroup that are powers of: | |||
// * three then (A:C) ~ (A+2C:A-2C) | |||
// * four then (A:C) ~ (A+2C: 4C) | |||
// See Appendix A of SIKE for more details | |||
type CurveCoefficientsEquiv struct { | |||
A ExtensionFieldElement | |||
C ExtensionFieldElement | |||
} | |||
// A point on the projective line P^1(F_{p^2}). | |||
// | |||
// This represents a point on the Kummer line of a Montgomery curve. The | |||
// curve is specified by a ProjectiveCurveParameters struct. | |||
type ProjectivePoint struct { | |||
X ExtensionFieldElement | |||
Z ExtensionFieldElement | |||
} | |||
func (params *ProjectiveCurveParameters) FromAffine(a *ExtensionFieldElement) { | |||
params.A = *a | |||
params.C.One() | |||
} | |||
// Computes j-invariant for a curve y2=x3+A/Cx+x with A,C in F_(p^2). Result | |||
// is returned in jBytes buffer, encoded in little-endian format. Caller | |||
// provided jBytes buffer has to be big enough to j-invariant value. In case | |||
// of SIDH, buffer size must be at least size of shared secret. | |||
// Implementation corresponds to Algorithm 9 from SIKE. | |||
func (cparams *ProjectiveCurveParameters) Jinvariant(jBytes []byte) { | |||
var j, t0, t1 ExtensionFieldElement | |||
j.Square(&cparams.A) // j = A^2 | |||
t1.Square(&cparams.C) // t1 = C^2 | |||
t0.Add(&t1, &t1) // t0 = t1 + t1 | |||
t0.Sub(&j, &t0) // t0 = j - t0 | |||
t0.Sub(&t0, &t1) // t0 = t0 - t1 | |||
j.Sub(&t0, &t1) // t0 = t0 - t1 | |||
t1.Square(&t1) // t1 = t1^2 | |||
j.Mul(&j, &t1) // t0 = t0 * t1 | |||
t0.Add(&t0, &t0) // t0 = t0 + t0 | |||
t0.Add(&t0, &t0) // t0 = t0 + t0 | |||
t1.Square(&t0) // t1 = t0^2 | |||
t0.Mul(&t0, &t1) // t0 = t0 * t1 | |||
t0.Add(&t0, &t0) // t0 = t0 + t0 | |||
t0.Add(&t0, &t0) // t0 = t0 + t0 | |||
j.Inv(&j) // j = 1/j | |||
j.Mul(&t0, &j) // j = t0 * j | |||
j.ToBytes(jBytes) | |||
} | |||
// Given affine points x(P), x(Q) and x(Q-P) in a extension field F_{p^2}, function | |||
// recorvers projective coordinate A of a curve. This is Algorithm 10 from SIKE. | |||
func (curve *ProjectiveCurveParameters) RecoverCoordinateA(xp, xq, xr *ExtensionFieldElement) { | |||
var t0, t1 ExtensionFieldElement | |||
t1.Add(xp, xq) // t1 = Xp + Xq | |||
t0.Mul(xp, xq) // t0 = Xp * Xq | |||
curve.A.Mul(xr, &t1) // A = X(q-p) * t1 | |||
curve.A.Add(&curve.A, &t0) // A = A + t0 | |||
t0.Mul(&t0, xr) // t0 = t0 * X(q-p) | |||
curve.A.Sub(&curve.A, &oneExtensionField) // A = A - 1 | |||
t0.Add(&t0, &t0) // t0 = t0 + t0 | |||
t1.Add(&t1, xr) // t1 = t1 + X(q-p) | |||
t0.Add(&t0, &t0) // t0 = t0 + t0 | |||
curve.A.Square(&curve.A) // A = A^2 | |||
t0.Inv(&t0) // t0 = 1/t0 | |||
curve.A.Mul(&curve.A, &t0) // A = A * t0 | |||
curve.A.Sub(&curve.A, &t1) // A = A - t1 | |||
} | |||
// Computes equivalence (A:C) ~ (A+2C : A-2C) | |||
func (curve *ProjectiveCurveParameters) CalcCurveParamsEquiv3() CurveCoefficientsEquiv { | |||
var coef CurveCoefficientsEquiv | |||
var c2 ExtensionFieldElement | |||
c2.Add(&curve.C, &curve.C) | |||
// A24p = A+2*C | |||
coef.A.Add(&curve.A, &c2) | |||
// A24m = A-2*C | |||
coef.C.Sub(&curve.A, &c2) | |||
return coef | |||
} | |||
// Computes equivalence (A:C) ~ (A+2C : 4C) | |||
func (cparams *ProjectiveCurveParameters) CalcCurveParamsEquiv4() CurveCoefficientsEquiv { | |||
var coefEq CurveCoefficientsEquiv | |||
coefEq.C.Add(&cparams.C, &cparams.C) | |||
// A24p = A+2C | |||
coefEq.A.Add(&cparams.A, &coefEq.C) | |||
// C24 = 4*C | |||
coefEq.C.Add(&coefEq.C, &coefEq.C) | |||
return coefEq | |||
} | |||
// Helper function for RightToLeftLadder(). Returns A+2C / 4. | |||
func (cparams *ProjectiveCurveParameters) calcAplus2Over4() (ret ExtensionFieldElement) { | |||
var tmp ExtensionFieldElement | |||
// 2C | |||
tmp.Add(&cparams.C, &cparams.C) | |||
// A+2C | |||
ret.Add(&cparams.A, &tmp) | |||
// 1/4C | |||
tmp.Add(&tmp, &tmp).Inv(&tmp) | |||
// A+2C/4C | |||
ret.Mul(&ret, &tmp) | |||
return | |||
} | |||
// Recovers (A:C) curve parameters from projectively equivalent (A+2C:A-2C). | |||
func (cparams *ProjectiveCurveParameters) RecoverCurveCoefficients3(coefEq *CurveCoefficientsEquiv) { | |||
cparams.A.Add(&coefEq.A, &coefEq.C) | |||
// cparams.A = 2*(A+2C+A-2C) = 4A | |||
cparams.A.Add(&cparams.A, &cparams.A) | |||
// cparams.C = (A+2C-A+2C) = 4C | |||
cparams.C.Sub(&coefEq.A, &coefEq.C) | |||
return | |||
} | |||
// Recovers (A:C) curve parameters from projectively equivalent (A+2C:4C). | |||
func (cparams *ProjectiveCurveParameters) RecoverCurveCoefficients4(coefEq *CurveCoefficientsEquiv) { | |||
var half = ExtensionFieldElement{ | |||
A: Fp751Element{ | |||
0x00000000000124D6, 0x0000000000000000, 0x0000000000000000, | |||
0x0000000000000000, 0x0000000000000000, 0xB8E0000000000000, | |||
0x9C8A2434C0AA7287, 0xA206996CA9A378A3, 0x6876280D41A41B52, | |||
0xE903B49F175CE04F, 0x0F8511860666D227, 0x00004EA07CFF6E7F}, | |||
B: Fp751Element{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, | |||
} | |||
// cparams.C = (4C)*1/2=2C | |||
cparams.C.Mul(&coefEq.C, &half) | |||
// cparams.A = A+2C - 2C = A | |||
cparams.A.Sub(&coefEq.A, &cparams.C) | |||
// cparams.C = 2C * 1/2 = C | |||
cparams.C.Mul(&cparams.C, &half) | |||
return | |||
} | |||
func (point *ProjectivePoint) FromAffine(x *ExtensionFieldElement) { | |||
point.X = *x | |||
point.Z = oneExtensionField | |||
} | |||
func (point *ProjectivePoint) ToAffine() *ExtensionFieldElement { | |||
affine_x := new(ExtensionFieldElement) | |||
affine_x.Inv(&point.Z).Mul(affine_x, &point.X) | |||
return affine_x | |||
} | |||
func (lhs *ProjectivePoint) VartimeEq(rhs *ProjectivePoint) bool { | |||
var t0, t1 ExtensionFieldElement | |||
t0.Mul(&lhs.X, &rhs.Z) | |||
t1.Mul(&lhs.Z, &rhs.X) | |||
return t0.VartimeEq(&t1) | |||
} | |||
func ProjectivePointConditionalSwap(xP, xQ *ProjectivePoint, choice uint8) { | |||
ExtensionFieldConditionalSwap(&xP.X, &xQ.X, choice) | |||
ExtensionFieldConditionalSwap(&xP.Z, &xQ.Z, choice) | |||
} | |||
// Combined coordinate doubling and differential addition. Takes projective points | |||
// P,Q,Q-P and (A+2C)/4C curve E coefficient. Returns 2*P and P+Q calculated on E. | |||
// Function is used only by RightToLeftLadder. Corresponds to Algorithm 5 of SIKE | |||
func xDblAdd(P, Q, QmP *ProjectivePoint, a24 *ExtensionFieldElement) (dblP, PaQ ProjectivePoint) { | |||
var t0, t1, t2 ExtensionFieldElement | |||
xQmP, zQmP := &QmP.X, &QmP.Z | |||
xPaQ, zPaQ := &PaQ.X, &PaQ.Z | |||
x2P, z2P := &dblP.X, &dblP.Z | |||
xP, zP := &P.X, &P.Z | |||
xQ, zQ := &Q.X, &Q.Z | |||
t0.Add(xP, zP) // t0 = Xp+Zp | |||
t1.Sub(xP, zP) // t1 = Xp-Zp | |||
x2P.Square(&t0) // 2P.X = t0^2 | |||
t2.Sub(xQ, zQ) // t2 = Xq-Zq | |||
xPaQ.Add(xQ, zQ) // Xp+q = Xq+Zq | |||
t0.Mul(&t0, &t2) // t0 = t0 * t2 | |||
z2P.Mul(&t1, &t1) // 2P.Z = t1 * t1 | |||
t1.Mul(&t1, xPaQ) // t1 = t1 * Xp+q | |||
t2.Sub(x2P, z2P) // t2 = 2P.X - 2P.Z | |||
x2P.Mul(x2P, z2P) // 2P.X = 2P.X * 2P.Z | |||
xPaQ.Mul(a24, &t2) // Xp+q = A24 * t2 | |||
zPaQ.Sub(&t0, &t1) // Zp+q = t0 - t1 | |||
z2P.Add(xPaQ, z2P) // 2P.Z = Xp+q + 2P.Z | |||
xPaQ.Add(&t0, &t1) // Xp+q = t0 + t1 | |||
z2P.Mul(z2P, &t2) // 2P.Z = 2P.Z * t2 | |||
zPaQ.Square(zPaQ) // Zp+q = Zp+q ^ 2 | |||
xPaQ.Square(xPaQ) // Xp+q = Xp+q ^ 2 | |||
zPaQ.Mul(xQmP, zPaQ) // Zp+q = Xq-p * Zp+q | |||
xPaQ.Mul(zQmP, xPaQ) // Xp+q = Zq-p * Xp+q | |||
return | |||
} | |||
// Given the curve parameters, xP = x(P), and k >= 0, compute x2P = x([2^k]P). | |||
// | |||
// Returns x2P to allow chaining. Safe to overlap xP, x2P. | |||
func (x2P *ProjectivePoint) Pow2k(params *CurveCoefficientsEquiv, xP *ProjectivePoint, k uint32) *ProjectivePoint { | |||
var t0, t1 ExtensionFieldElement | |||
*x2P = *xP | |||
x, z := &x2P.X, &x2P.Z | |||
for i := uint32(0); i < k; i++ { | |||
t0.Sub(x, z) // t0 = Xp - Zp | |||
t1.Add(x, z) // t1 = Xp + Zp | |||
t0.Square(&t0) // t0 = t0 ^ 2 | |||
t1.Square(&t1) // t1 = t1 ^ 2 | |||
z.Mul(¶ms.C, &t0) // Z2p = C24 * t0 | |||
x.Mul(z, &t1) // X2p = Z2p * t1 | |||
t1.Sub(&t1, &t0) // t1 = t1 - t0 | |||
t0.Mul(¶ms.A, &t1) // t0 = A24+ * t1 | |||
z.Add(z, &t0) // Z2p = Z2p + t0 | |||
z.Mul(z, &t1) // Zp = Z2p * t1 | |||
} | |||
return x2P | |||
} | |||
// Given the curve parameters, xP = x(P), and k >= 0, compute x3P = x([3^k]P). | |||
// | |||
// Returns x3P to allow chaining. Safe to overlap xP, xR. | |||
func (x3P *ProjectivePoint) Pow3k(params *CurveCoefficientsEquiv, xP *ProjectivePoint, k uint32) *ProjectivePoint { | |||
var t0, t1, t2, t3, t4, t5, t6 ExtensionFieldElement | |||
*x3P = *xP | |||
x, z := &x3P.X, &x3P.Z | |||
for i := uint32(0); i < k; i++ { | |||
t0.Sub(x, z) // t0 = Xp - Zp | |||
t2.Square(&t0) // t2 = t0^2 | |||
t1.Add(x, z) // t1 = Xp + Zp | |||
t3.Square(&t1) // t3 = t1^2 | |||
t4.Add(&t1, &t0) // t4 = t1 + t0 | |||
t0.Sub(&t1, &t0) // t0 = t1 - t0 | |||
t1.Square(&t4) // t1 = t4^2 | |||
t1.Sub(&t1, &t3) // t1 = t1 - t3 | |||
t1.Sub(&t1, &t2) // t1 = t1 - t2 | |||
t5.Mul(&t3, ¶ms.A) // t5 = t3 * A24+ | |||
t3.Mul(&t3, &t5) // t3 = t5 * t3 | |||
t6.Mul(&t2, ¶ms.C) // t6 = t2 * A24- | |||
t2.Mul(&t2, &t6) // t2 = t2 * t6 | |||
t3.Sub(&t2, &t3) // t3 = t2 - t3 | |||
t2.Sub(&t5, &t6) // t2 = t5 - t6 | |||
t1.Mul(&t2, &t1) // t1 = t2 * t1 | |||
t2.Add(&t3, &t1) // t2 = t3 + t1 | |||
t2.Square(&t2) // t2 = t2^2 | |||
x.Mul(&t2, &t4) // X3p = t2 * t4 | |||
t1.Sub(&t3, &t1) // t1 = t3 - t1 | |||
t1.Square(&t1) // t1 = t1^2 | |||
z.Mul(&t1, &t0) // Z3p = t1 * t0 | |||
} | |||
return x3P | |||
} | |||
// RightToLeftLadder is a right-to-left point multiplication that given the | |||
// x-coordinate of P, Q and P-Q calculates the x-coordinate of R=Q+[scalar]P. | |||
// nbits must be smaller or equal to len(scalar). | |||
func RightToLeftLadder(c *ProjectiveCurveParameters, P, Q, PmQ *ProjectivePoint, | |||
nbits uint, scalar []uint8) ProjectivePoint { | |||
var R0, R2, R1 ProjectivePoint | |||
aPlus2Over4 := c.calcAplus2Over4() | |||
R1 = *P | |||
R2 = *PmQ | |||
R0 = *Q | |||
// Iterate over the bits of the scalar, bottom to top | |||
prevBit := uint8(0) | |||
for i := uint(0); i < nbits; i++ { | |||
bit := (scalar[i>>3] >> (i & 7) & 1) | |||
swap := prevBit ^ bit | |||
prevBit = bit | |||
ProjectivePointConditionalSwap(&R1, &R2, swap) | |||
R0, R2 = xDblAdd(&R0, &R2, &R1, &aPlus2Over4) | |||
} | |||
ProjectivePointConditionalSwap(&R1, &R2, prevBit) | |||
return R1 | |||
} |
@@ -1,372 +0,0 @@ | |||
package p751toolbox | |||
import ( | |||
"bytes" | |||
"math/rand" | |||
"reflect" | |||
"testing" | |||
"testing/quick" | |||
) | |||
// Sage script for generating test vectors: | |||
// sage: p = 2^372 * 3^239 - 1; Fp = GF(p) | |||
// sage: R.<x> = Fp[] | |||
// sage: Fp2 = Fp.extension(x^2 + 1, 'i') | |||
// sage: i = Fp2.gen() | |||
// sage: A = 4385300808024233870220415655826946795549183378139271271040522089756750951667981765872679172832050962894122367066234419550072004266298327417513857609747116903999863022476533671840646615759860564818837299058134292387429068536219*i + 1408083354499944307008104531475821995920666351413327060806684084512082259107262519686546161682384352696826343970108773343853651664489352092568012759783386151707999371397181344707721407830640876552312524779901115054295865393760 | |||
// sage: C = 933177602672972392833143808100058748100491911694554386487433154761658932801917030685312352302083870852688835968069519091048283111836766101703759957146191882367397129269726925521881467635358356591977198680477382414690421049768*i + 9088894745865170214288643088620446862479558967886622582768682946704447519087179261631044546285104919696820250567182021319063155067584445633834024992188567423889559216759336548208016316396859149888322907914724065641454773776307 | |||
// sage: E = EllipticCurve(Fp2, [0,A/C,0,1,0]) | |||
// sage: XP, YP, ZP = (8172151271761071554796221948801462094972242987811852753144865524899433583596839357223411088919388342364651632180452081960511516040935428737829624206426287774255114241789158000915683252363913079335550843837650671094705509470594*i + 9326574858039944121604015439381720195556183422719505497448541073272720545047742235526963773359004021838961919129020087515274115525812121436661025030481584576474033630899768377131534320053412545346268645085054880212827284581557, 2381174772709336084066332457520782192315178511983342038392622832616744048226360647551642232950959910067260611740876401494529727990031260499974773548012283808741733925525689114517493995359390158666069816204787133942283380884077*i + 5378956232034228335189697969144556552783858755832284194802470922976054645696324118966333158267442767138528227968841257817537239745277092206433048875637709652271370008564179304718555812947398374153513738054572355903547642836171, 1) | |||
// sage: XQ, YQ, ZQ = (58415083458086949460774631288254059520198050925769753726965257584725433404854146588020043440874302516770401547098747946831855304325931998303167820332782040123488940125615738529117852871565495005635582483848203360379709783913*i + 5253691516070829381103946367549411712646323371150903710970418364086793242952116417060222617507143384179331507713214357463046698181663711723374230655213762988214127964685312538735038552802853885461137159568726585741937776693865 : 5158855522131677856751020091923296498106687563952623634063620100943451774747711264382913073742659403103540173656392945587315798626256921403472228775673617431559609635074280718249654789362069112718012186738875122436282913402060*i + 6207982879267706771450280080677554401823496760317291100742934673980817977221212113336809046153999230028113719924529054308271353271269929392876016936836430864080079684360816968551808465413552611322477628209203066093559492022329 : 1) | |||
// sage: P = E((XP,YP,ZP)) | |||
// sage: X2, Y2, Z2 = 2*P | |||
// sage: X3, Y3, Z3 = 3*P | |||
// sage: m = 96550223052359874398280314003345143371473380422728857598463622014420884224892 | |||
// A = 4385300808024233870220415655826946795549183378139271271040522089756750951667981765872679172832050962894122367066234419550072004266298327417513857609747116903999863022476533671840646615759860564818837299058134292387429068536219*i + 1408083354499944307008104531475821995920666351413327060806684084512082259107262519686546161682384352696826343970108773343853651664489352092568012759783386151707999371397181344707721407830640876552312524779901115054295865393760 | |||
var curve_A = ExtensionFieldElement{ | |||
A: Fp751Element{0x8319eb18ca2c435e, 0x3a93beae72cd0267, 0x5e465e1f72fd5a84, 0x8617fa4150aa7272, 0x887da24799d62a13, 0xb079b31b3c7667fe, 0xc4661b150fa14f2e, 0xd4d2b2967bc6efd6, 0x854215a8b7239003, 0x61c5302ccba656c2, 0xf93194a27d6f97a2, 0x1ed9532bca75}, | |||
B: Fp751Element{0xb6f541040e8c7db6, 0x99403e7365342e15, 0x457e9cee7c29cced, 0x8ece72dc073b1d67, 0x6e73cef17ad28d28, 0x7aed836ca317472, 0x89e1de9454263b54, 0x745329277aa0071b, 0xf623dfc73bc86b9b, 0xb8e3c1d8a9245882, 0x6ad0b3d317770bec, 0x5b406e8d502b}} | |||
// C = 933177602672972392833143808100058748100491911694554386487433154761658932801917030685312352302083870852688835968069519091048283111836766101703759957146191882367397129269726925521881467635358356591977198680477382414690421049768*i + 9088894745865170214288643088620446862479558967886622582768682946704447519087179261631044546285104919696820250567182021319063155067584445633834024992188567423889559216759336548208016316396859149888322907914724065641454773776307 | |||
var curve_C = ExtensionFieldElement{ | |||
A: Fp751Element{0x4fb2358bbf723107, 0x3a791521ac79e240, 0x283e24ef7c4c922f, 0xc89baa1205e33cc, 0x3031be81cff6fee1, 0xaf7a494a2f6a95c4, 0x248d251eaac83a1d, 0xc122fca1e2550c88, 0xbc0451b11b6cfd3d, 0x9c0a114ab046222c, 0x43b957b32f21f6ea, 0x5b9c87fa61de}, | |||
B: Fp751Element{0xacf142afaac15ec6, 0xfd1322a504a071d5, 0x56bb205e10f6c5c6, 0xe204d2849a97b9bd, 0x40b0122202fe7f2e, 0xecf72c6fafacf2cb, 0x45dfc681f869f60a, 0x11814c9aff4af66c, 0x9278b0c4eea54fe7, 0x9a633d5baf7f2e2e, 0x69a329e6f1a05112, 0x1d874ace23e4}} | |||
var curve = ProjectiveCurveParameters{A: curve_A, C: curve_C} | |||
// x(P) = 8172151271761071554796221948801462094972242987811852753144865524899433583596839357223411088919388342364651632180452081960511516040935428737829624206426287774255114241789158000915683252363913079335550843837650671094705509470594*i + 9326574858039944121604015439381720195556183422719505497448541073272720545047742235526963773359004021838961919129020087515274115525812121436661025030481584576474033630899768377131534320053412545346268645085054880212827284581557 | |||
var affine_xP = ExtensionFieldElement{ | |||
A: Fp751Element{0xe8d05f30aac47247, 0x576ec00c55441de7, 0xbf1a8ec5fe558518, 0xd77cb17f77515881, 0x8e9852837ee73ec4, 0x8159634ad4f44a6b, 0x2e4eb5533a798c5, 0x9be8c4354d5bc849, 0xf47dc61806496b84, 0x25d0e130295120e0, 0xdbef54095f8139e3, 0x5a724f20862c}, | |||
B: Fp751Element{0x3ca30d7623602e30, 0xfb281eddf45f07b7, 0xd2bf62d5901a45bc, 0xc67c9baf86306dd2, 0x4e2bd93093f538ca, 0xcfd92075c25b9cbe, 0xceafe9a3095bcbab, 0x7d928ad380c85414, 0x37c5f38b2afdc095, 0x75325899a7b779f4, 0xf130568249f20fdd, 0x178f264767d1}} | |||
// x([2]P) = 1476586462090705633631615225226507185986710728845281579274759750260315746890216330325246185232948298241128541272709769576682305216876843626191069809810990267291824247158062860010264352034514805065784938198193493333201179504845*i + 3623708673253635214546781153561465284135688791018117615357700171724097420944592557655719832228709144190233454198555848137097153934561706150196041331832421059972652530564323645509890008896574678228045006354394485640545367112224 | |||
var affine_xP2 = ExtensionFieldElement{ | |||
A: Fp751Element{0x2a77afa8576ce979, 0xab1360e69b0aeba0, 0xd79e3e3cbffad660, 0x5fd0175aa10f106b, 0x1800ebafce9fbdbc, 0x228fc9142bdd6166, 0x867cf907314e34c3, 0xa58d18c94c13c31c, 0x699a5bc78b11499f, 0xa29fc29a01f7ccf1, 0x6c69c0c5347eebce, 0x38ecee0cc57}, | |||
B: Fp751Element{0x43607fd5f4837da0, 0x560bad4ce27f8f4a, 0x2164927f8495b4dd, 0x621103fdb831a997, 0xad740c4eea7db2db, 0x2cde0442205096cd, 0x2af51a70ede8324e, 0x41a4e680b9f3466, 0x5481f74660b8f476, 0xfcb2f3e656ff4d18, 0x42e3ce0837171acc, 0x44238c30530c}} | |||
// x([3]P) = 9351941061182433396254169746041546943662317734130813745868897924918150043217746763025923323891372857734564353401396667570940585840576256269386471444236630417779544535291208627646172485976486155620044292287052393847140181703665*i + 9010417309438761934687053906541862978676948345305618417255296028956221117900864204687119686555681136336037659036201780543527957809743092793196559099050594959988453765829339642265399496041485088089691808244290286521100323250273 | |||
var affine_xP3 = ExtensionFieldElement{ | |||
A: Fp751Element{0x2096e3f23feca947, 0xf36f635aa4ad8634, 0xdae3b1c6983c5e9a, 0xe08df6c262cb74b4, 0xd2ca4edc37452d3d, 0xfb5f3fe42f500c79, 0x73740aa3abc2b21f, 0xd535fd869f914cca, 0x4a558466823fb67f, 0x3e50a7a0e3bfc715, 0xf43c6da9183a132f, 0x61aca1e1b8b9}, | |||
B: Fp751Element{0x1e54ec26ea5077bd, 0x61380572d8769f9a, 0xc615170684f59818, 0x6309c3b93e84ef6e, 0x33c74b1318c3fcd0, 0xfe8d7956835afb14, 0x2d5a7b55423c1ecc, 0x869db67edfafea68, 0x1292632394f0a628, 0x10bba48225bfd141, 0x6466c28b408daba, 0x63cacfdb7c43}} | |||
// x([2^2]P) = 441719501189485559222919502512761433931671682884872259563221427434901842337947564993718830905758163254463901652874331063768876314142359813382575876106725244985607032091781306919778265250690045578695338669105227100119314831452*i + 6961734028200975729170216310486458180126343885294922940439352055937945948015840788921225114530454649744697857047401608073256634790353321931728699534700109268264491160589480994022419317695690866764726967221310990488404411684053 | |||
var affine_xP4 = ExtensionFieldElement{ | |||
A: Fp751Element{0x6f9dbe4c39175153, 0xf2fec757eb99e88, 0x43d7361a93733d91, 0x3abd10ed19c85a3d, 0xc4de9ab9c5ef7181, 0x53e375901684c900, 0x68ffc3e7d71c41ff, 0x47adab62c8d942fe, 0x226a33fd6fbb381d, 0x87ef4c8fdd83309a, 0xaca1cf44c5fa8799, 0x6cbae86c755f}, | |||
B: Fp751Element{0x4c80c37fe68282a7, 0xbd8b9d7248bf553a, 0x1fb0e8e74d5e1762, 0xb63fa0e4e5f91482, 0xc675ab8a45a1439, 0xdfa6772deace7820, 0xf0d813d71d9a9255, 0x53a1a58c634534bd, 0x4ebfc6485fdfd888, 0x6991fe4358bcf169, 0xc0547bdaca85b6fd, 0xf461548d632}} | |||
// x([3^2]P) = 3957171963425208493644602380039721164492341594850197356580248639045894821895524981729970650520936632013218950972842867220898274664982599375786979902471523505057611521217523103474682939638645404445093536997296151472632038973463*i + 1357869545269286021642168835877253886774707209614159162748874474269328421720121175566245719916322684751967981171882659798149072149161259103020057556362998810229937432814792024248155991141511691087135859252304684633946087474060 | |||
var affine_xP9 = ExtensionFieldElement{ | |||
A: Fp751Element{0x7c0daa0f04ded4e0, 0x52dc4f883d85e065, 0x91afbdc2c1714d0b, 0xb7b3db8e658cfeba, 0x43d4e72a692882f3, 0x535c56d83753da30, 0xc8a58724433cbf5d, 0x351153c0a5e74219, 0x2c81827d19f93dd5, 0x26ef8aca3370ea1a, 0x1cf939a6dd225dec, 0x3403cb28ad41}, | |||
B: Fp751Element{0x93e7bc373a9ff7b, 0x57b8cc47635ebc0f, 0x92eab55689106cf3, 0x93643111d421f24c, 0x1c58b519506f6b7a, 0xebd409fb998faa13, 0x5c86ed799d09d80e, 0xd9a1d764d6363562, 0xf95e87f92fb0c4cc, 0x6b2bbaf5632a5609, 0x2d9b6a809dfaff7f, 0x29c0460348b}} | |||
// m = 96550223052359874398280314003345143371473380422728857598463622014420884224892 | |||
var mScalarBytes = [...]uint8{0x7c, 0x7b, 0x95, 0xfa, 0xb4, 0x75, 0x6c, 0x48, 0x8c, 0x17, 0x55, 0xb4, 0x49, 0xf5, 0x1e, 0xa3, 0xb, 0x31, 0xf0, 0xa4, 0xa6, 0x81, 0xad, 0x94, 0x51, 0x11, 0xe7, 0xf5, 0x5b, 0x7d, 0x75, 0xd5} | |||
// x([m]P) = 7893578558852400052689739833699289348717964559651707250677393044951777272628231794999463214496545377542328262828965953246725804301238040891993859185944339366910592967840967752138115122568615081881937109746463885908097382992642*i + 8293895847098220389503562888233557012043261770526854885191188476280014204211818299871679993460086974249554528517413590157845430186202704783785316202196966198176323445986064452630594623103149383929503089342736311904030571524837 | |||
var affine_xaP = ExtensionFieldElement{ | |||
A: Fp751Element{0x2112f3c7d7f938bb, 0x704a677f0a4df08f, 0x825370e31fb4ef00, 0xddbf79b7469f902, 0x27640c899ea739fd, 0xfb7b8b19f244108e, 0x546a6679dd3baebc, 0xe9f0ecf398d5265f, 0x223d2b350e75e461, 0x84b322a0b6aff016, 0xfabe426f539f8b39, 0x4507a0604f50}, | |||
B: Fp751Element{0xac77737e5618a5fe, 0xf91c0e08c436ca52, 0xd124037bc323533c, 0xc9a772bf52c58b63, 0x3b30c8f38ef6af4d, 0xb9eed160e134f36e, 0x24e3836393b25017, 0xc828be1b11baf1d9, 0x7b7dab585df50e93, 0x1ca3852c618bd8e0, 0x4efa73bcb359fa00, 0x50b6a923c2d4}} | |||
// Inputs for testing 3-point-ladder | |||
var threePointLadderInputs = []ProjectivePoint{ | |||
// x(P) | |||
ProjectivePoint{ | |||
X: ExtensionFieldElement{ | |||
A: Fp751Element{0xe8d05f30aac47247, 0x576ec00c55441de7, 0xbf1a8ec5fe558518, 0xd77cb17f77515881, 0x8e9852837ee73ec4, 0x8159634ad4f44a6b, 0x2e4eb5533a798c5, 0x9be8c4354d5bc849, 0xf47dc61806496b84, 0x25d0e130295120e0, 0xdbef54095f8139e3, 0x5a724f20862c}, | |||
B: Fp751Element{0x3ca30d7623602e30, 0xfb281eddf45f07b7, 0xd2bf62d5901a45bc, 0xc67c9baf86306dd2, 0x4e2bd93093f538ca, 0xcfd92075c25b9cbe, 0xceafe9a3095bcbab, 0x7d928ad380c85414, 0x37c5f38b2afdc095, 0x75325899a7b779f4, 0xf130568249f20fdd, 0x178f264767d1}}, | |||
Z: oneExtensionField, | |||
}, | |||
// x(Q) | |||
ProjectivePoint{ | |||
X: ExtensionFieldElement{ | |||
A: Fp751Element{0x2b71a2a93ad1e10e, 0xf0b9842a92cfb333, 0xae17373615a27f5c, 0x3039239f428330c4, 0xa0c4b735ed7dcf98, 0x6e359771ddf6af6a, 0xe986e4cac4584651, 0x8233a2b622d5518, 0xbfd67bf5f06b818b, 0xdffe38d0f5b966a6, 0xa86b36a3272ee00a, 0x193e2ea4f68f}, | |||
B: Fp751Element{0x5a0f396459d9d998, 0x479f42250b1b7dda, 0x4016b57e2a15bf75, 0xc59f915203fa3749, 0xd5f90257399cf8da, 0x1fb2dadfd86dcef4, 0x600f20e6429021dc, 0x17e347d380c57581, 0xc1b0d5fa8fe3e440, 0xbcf035330ac20e8, 0x50c2eb5f6a4f03e6, 0x86b7c4571}}, | |||
Z: oneExtensionField, | |||
}, | |||
// x(P-Q) | |||
ProjectivePoint{ | |||
X: ExtensionFieldElement{ | |||
A: Fp751Element{0x4aafa9f378f7b5ff, 0x1172a683aa8eee0, 0xea518d8cbec2c1de, 0xe191bcbb63674557, 0x97bc19637b259011, 0xdbeae5c9f4a2e454, 0x78f64d1b72a42f95, 0xe71cb4ea7e181e54, 0xe4169d4c48543994, 0x6198c2286a98730f, 0xd21d675bbab1afa5, 0x2e7269fce391}, | |||
B: Fp751Element{0x23355783ce1d0450, 0x683164cf4ce3d93f, 0xae6d1c4d25970fd8, 0x7807007fb80b48cf, 0xa005a62ec2bbb8a2, 0x6b5649bd016004cb, 0xbb1a13fa1330176b, 0xbf38e51087660461, 0xe577fddc5dd7b930, 0x5f38116f56947cd3, 0x3124f30b98c36fde, 0x4ca9b6e6db37}}, | |||
Z: oneExtensionField, | |||
}, | |||
} | |||
func (P ProjectivePoint) Generate(rand *rand.Rand, size int) reflect.Value { | |||
f := ExtensionFieldElement{} | |||
x, _ := f.Generate(rand, size).Interface().(ExtensionFieldElement) | |||
z, _ := f.Generate(rand, size).Interface().(ExtensionFieldElement) | |||
return reflect.ValueOf(ProjectivePoint{ | |||
X: x, | |||
Z: z, | |||
}) | |||
} | |||
func (curve ProjectiveCurveParameters) Generate(rand *rand.Rand, size int) reflect.Value { | |||
f := ExtensionFieldElement{} | |||
A, _ := f.Generate(rand, size).Interface().(ExtensionFieldElement) | |||
C, _ := f.Generate(rand, size).Interface().(ExtensionFieldElement) | |||
return reflect.ValueOf(ProjectiveCurveParameters{ | |||
A: A, | |||
C: C, | |||
}) | |||
} | |||
// Helpers | |||
// Given xP = x(P), xQ = x(Q), and xPmQ = x(P-Q), compute xR = x(P+Q). | |||
// | |||
// Returns xR to allow chaining. Safe to overlap xP, xQ, xR. | |||
func (xR *ProjectivePoint) Add(xP, xQ, xPmQ *ProjectivePoint) *ProjectivePoint { | |||
// Algorithm 1 of Costello-Smith. | |||
var v0, v1, v2, v3, v4 ExtensionFieldElement | |||
v0.Add(&xP.X, &xP.Z) // X_P + Z_P | |||
v1.Sub(&xQ.X, &xQ.Z).Mul(&v1, &v0) // (X_Q - Z_Q)(X_P + Z_P) | |||
v0.Sub(&xP.X, &xP.Z) // X_P - Z_P | |||
v2.Add(&xQ.X, &xQ.Z).Mul(&v2, &v0) // (X_Q + Z_Q)(X_P - Z_P) | |||
v3.Add(&v1, &v2).Square(&v3) // 4(X_Q X_P - Z_Q Z_P)^2 | |||
v4.Sub(&v1, &v2).Square(&v4) // 4(X_Q Z_P - Z_Q X_P)^2 | |||
v0.Mul(&xPmQ.Z, &v3) // 4X_{P-Q}(X_Q X_P - Z_Q Z_P)^2 | |||
xR.Z.Mul(&xPmQ.X, &v4) // 4Z_{P-Q}(X_Q Z_P - Z_Q X_P)^2 | |||
xR.X = v0 | |||
return xR | |||
} | |||
// Given xP = x(P) and cached curve parameters Aplus2C = A + 2*C, C4 = 4*C, | |||
// compute xQ = x([2]P). | |||
// | |||
// Returns xQ to allow chaining. Safe to overlap xP, xQ. | |||
func (xQ *ProjectivePoint) Double(xP *ProjectivePoint, Aplus2C, C4 *ExtensionFieldElement) *ProjectivePoint { | |||
// Algorithm 2 of Costello-Smith, amended to work with projective curve coefficients. | |||
var v1, v2, v3, xz4 ExtensionFieldElement | |||
v1.Add(&xP.X, &xP.Z).Square(&v1) // (X+Z)^2 | |||
v2.Sub(&xP.X, &xP.Z).Square(&v2) // (X-Z)^2 | |||
xz4.Sub(&v1, &v2) // 4XZ = (X+Z)^2 - (X-Z)^2 | |||
v2.Mul(&v2, C4) // 4C(X-Z)^2 | |||
xQ.X.Mul(&v1, &v2) // 4C(X+Z)^2(X-Z)^2 | |||
v3.Mul(&xz4, Aplus2C) // 4XZ(A + 2C) | |||
v3.Add(&v3, &v2) // 4XZ(A + 2C) + 4C(X-Z)^2 | |||
xQ.Z.Mul(&v3, &xz4) // (4XZ(A + 2C) + 4C(X-Z)^2)4XZ | |||
// Now (xQ.x : xQ.z) | |||
// = (4C(X+Z)^2(X-Z)^2 : (4XZ(A + 2C) + 4C(X-Z)^2)4XZ ) | |||
// = ((X+Z)^2(X-Z)^2 : (4XZ((A + 2C)/4C) + (X-Z)^2)4XZ ) | |||
// = ((X+Z)^2(X-Z)^2 : (4XZ((a + 2)/4) + (X-Z)^2)4XZ ) | |||
return xQ | |||
} | |||
// Given x(P) and a scalar m in little-endian bytes, compute x([m]P) using the | |||
// Montgomery ladder. This is described in Algorithm 8 of Costello-Smith. | |||
// | |||
// This function's execution time is dependent only on the byte-length of the | |||
// input scalar. All scalars of the same input length execute in uniform time. | |||
// The scalar can be padded with zero bytes to ensure a uniform length. | |||
// | |||
// Safe to overlap the source with the destination. | |||
func (xQ *ProjectivePoint) ScalarMult(curve *ProjectiveCurveParameters, xP *ProjectivePoint, scalar []uint8) *ProjectivePoint { | |||
var x0, x1, tmp ProjectivePoint | |||
var Aplus2C, C4 ExtensionFieldElement | |||
Aplus2C.Add(&curve.C, &curve.C) // = 2*C | |||
C4.Add(&Aplus2C, &Aplus2C) // = 4*C | |||
Aplus2C.Add(&Aplus2C, &curve.A) // = 2*C + A | |||
x0.X.One() | |||
x0.Z.Zero() | |||
x1 = *xP | |||
// Iterate over the bits of the scalar, top to bottom | |||
prevBit := uint8(0) | |||
for i := len(scalar) - 1; i >= 0; i-- { | |||
scalarByte := scalar[i] | |||
for j := 7; j >= 0; j-- { | |||
bit := (scalarByte >> uint(j)) & 0x1 | |||
ProjectivePointConditionalSwap(&x0, &x1, (bit ^ prevBit)) | |||
tmp.Double(&x0, &Aplus2C, &C4) | |||
x1.Add(&x0, &x1, xP) | |||
x0 = tmp | |||
prevBit = bit | |||
} | |||
} | |||
// now prevBit is the lowest bit of the scalar | |||
ProjectivePointConditionalSwap(&x0, &x1, prevBit) | |||
*xQ = x0 | |||
return xQ | |||
} | |||
// Tests | |||
func TestOne(t *testing.T) { | |||
var tmp ExtensionFieldElement | |||
tmp.Mul(&oneExtensionField, &affine_xP) | |||
if !tmp.VartimeEq(&affine_xP) { | |||
t.Error("Not equal 1") | |||
} | |||
} | |||
func TestScalarMultVersusSage(t *testing.T) { | |||
var xP ProjectivePoint | |||
xP.FromAffine(&affine_xP) | |||
affine_xQ := xP.ScalarMult(&curve, &xP, mScalarBytes[:]).ToAffine() // = x([m]P) | |||
if !affine_xaP.VartimeEq(affine_xQ) { | |||
t.Error("\nExpected\n", affine_xaP, "\nfound\n", affine_xQ) | |||
} | |||
} | |||
func Test_jInvariant(t *testing.T) { | |||
var curve = ProjectiveCurveParameters{A: curve_A, C: curve_C} | |||
var jbufRes = make([]byte, P751_SharedSecretSize) | |||
var jbufExp = make([]byte, P751_SharedSecretSize) | |||
// Computed using Sage | |||
// j = 3674553797500778604587777859668542828244523188705960771798425843588160903687122861541242595678107095655647237100722594066610650373491179241544334443939077738732728884873568393760629500307797547379838602108296735640313894560419*i + 3127495302417548295242630557836520229396092255080675419212556702820583041296798857582303163183558315662015469648040494128968509467224910895884358424271180055990446576645240058960358037224785786494172548090318531038910933793845 | |||
var known_j = ExtensionFieldElement{ | |||
A: Fp751Element{0xc7a8921c1fb23993, 0xa20aea321327620b, 0xf1caa17ed9676fa8, 0x61b780e6b1a04037, 0x47784af4c24acc7a, 0x83926e2e300b9adf, 0xcd891d56fae5b66, 0x49b66985beb733bc, 0xd4bcd2a473d518f, 0xe242239991abe224, 0xa8af5b20f98672f8, 0x139e4d4e4d98}, | |||
B: Fp751Element{0xb5b52a21f81f359, 0x715e3a865db6d920, 0x9bac2f9d8911978b, 0xef14acd8ac4c1e3d, 0xe81aacd90cfb09c8, 0xaf898288de4a09d9, 0xb85a7fb88c5c4601, 0x2c37c3f1dd303387, 0x7ad3277fe332367c, 0xd4cbee7f25a8e6f8, 0x36eacbe979eaeffa, 0x59eb5a13ac33}, | |||
} | |||
curve.Jinvariant(jbufRes) | |||
known_j.ToBytes(jbufExp) | |||
if !bytes.Equal(jbufRes, jbufExp) { | |||
t.Error("Computed incorrect j-invariant: found\n", jbufRes, "\nexpected\n", jbufExp) | |||
} | |||
} | |||
func TestProjectivePointVartimeEq(t *testing.T) { | |||
var xP ProjectivePoint | |||
xP.FromAffine(&affine_xP) | |||
xQ := xP | |||
// Scale xQ, which results in the same projective point | |||
xQ.X.Mul(&xQ.X, &curve_A) | |||
xQ.Z.Mul(&xQ.Z, &curve_A) | |||
if !xQ.VartimeEq(&xP) { | |||
t.Error("Expected the scaled point to be equal to the original") | |||
} | |||
} | |||
func TestPointDoubleVersusSage(t *testing.T) { | |||
var curve = ProjectiveCurveParameters{A: curve_A, C: curve_C} | |||
var params = curve.CalcCurveParamsEquiv4() | |||
var xP, xQ ProjectivePoint | |||
xP.FromAffine(&affine_xP) | |||
affine_xQ := xQ.Pow2k(¶ms, &xP, 1).ToAffine() | |||
if !affine_xQ.VartimeEq(&affine_xP2) { | |||
t.Error("\nExpected\n", affine_xP2, "\nfound\n", affine_xQ) | |||
} | |||
} | |||
func TestPointMul4VersusSage(t *testing.T) { | |||
var params = curve.CalcCurveParamsEquiv4() | |||
var xP, xQ ProjectivePoint | |||
xP.FromAffine(&affine_xP) | |||
affine_xQ := xQ.Pow2k(¶ms, &xP, 2).ToAffine() | |||
if !affine_xQ.VartimeEq(&affine_xP4) { | |||
t.Error("\nExpected\n", affine_xP4, "\nfound\n", affine_xQ) | |||
} | |||
} | |||
func TestPointMul9VersusSage(t *testing.T) { | |||
var params = curve.CalcCurveParamsEquiv3() | |||
var xP, xQ ProjectivePoint | |||
xP.FromAffine(&affine_xP) | |||
affine_xQ := xQ.Pow3k(¶ms, &xP, 2).ToAffine() | |||
if !affine_xQ.VartimeEq(&affine_xP9) { | |||
t.Error("\nExpected\n", affine_xP9, "\nfound\n", affine_xQ) | |||
} | |||
} | |||
func TestPointPow2kVersusScalarMult(t *testing.T) { | |||
var xP, xQ, xR ProjectivePoint | |||
var params = curve.CalcCurveParamsEquiv4() | |||
xP.FromAffine(&affine_xP) | |||
affine_xQ := xQ.Pow2k(¶ms, &xP, 5).ToAffine() // = x([32]P) | |||
affine_xR := xR.ScalarMult(&curve, &xP, []byte{32}).ToAffine() // = x([32]P) | |||
if !affine_xQ.VartimeEq(affine_xR) { | |||
t.Error("\nExpected\n", affine_xQ, "\nfound\n", affine_xR) | |||
} | |||
} | |||
func TestRecoverCoordinateA(t *testing.T) { | |||
var cparam ProjectiveCurveParameters | |||
// Vectors generated with SIKE reference implementation | |||
var a = ExtensionFieldElement{ | |||
A: Fp751Element{0x9331D9C5AAF59EA4, 0xB32B702BE4046931, 0xCEBB333912ED4D34, 0x5628CE37CD29C7A2, 0x0BEAC5ED48B7F58E, 0x1FB9D3E281D65B07, 0x9C0CFACC1E195662, 0xAE4BCE0F6B70F7D9, 0x59E4E63D43FE71A0, 0xEF7CE57560CC8615, 0xE44A8FB7901E74E8, 0x000069D13C8366D1}, | |||
B: Fp751Element{0xF6DA1070279AB966, 0xA78FB0CE7268C762, 0x19B40F044A57ABFA, 0x7AC8EE6160C0C233, 0x93D4993442947072, 0x757D2B3FA4E44860, 0x073A920F8C4D5257, 0x2031F1B054734037, 0xDEFAA1D2406555CD, 0x26F9C70E1496BE3D, 0x5B3F335A0A4D0976, 0x000013628B2E9C59}} | |||
var affine_xP = ExtensionFieldElement{ | |||
A: Fp751Element{0xea6b2d1e2aebb250, 0x35d0b205dc4f6386, 0xb198e93cb1830b8d, 0x3b5b456b496ddcc6, 0x5be3f0d41132c260, 0xce5f188807516a00, 0x54f3e7469ea8866d, 0x33809ef47f36286, 0x6fa45f83eabe1edb, 0x1b3391ae5d19fd86, 0x1e66daf48584af3f, 0xb430c14aaa87}, | |||
B: Fp751Element{0x97b41ebc61dcb2ad, 0x80ead31cb932f641, 0x40a940099948b642, 0x2a22fd16cdc7fe84, 0xaabf35b17579667f, 0x76c1d0139feb4032, 0x71467e1e7b1949be, 0x678ca8dadd0d6d81, 0x14445daea9064c66, 0x92d161eab4fa4691, 0x8dfbb01b6b238d36, 0x2e3718434e4e}} | |||
var affine_xQ = ExtensionFieldElement{ | |||
A: Fp751Element{0xb055cf0ca1943439, 0xa9ff5de2fa6c69ed, 0x4f2761f934e5730a, 0x61a1dcaa1f94aa4b, 0xce3c8fadfd058543, 0xeac432aaa6701b8e, 0x8491d523093aea8b, 0xba273f9bd92b9b7f, 0xd8f59fd34439bb5a, 0xdc0350261c1fe600, 0x99375ab1eb151311, 0x14d175bbdbc5}, | |||
B: Fp751Element{0xffb0ef8c2111a107, 0x55ceca3825991829, 0xdbf8a1ccc075d34b, 0xb8e9187bd85d8494, 0x670aa2d5c34a03b0, 0xef9fe2ed2b064953, 0xc911f5311d645aee, 0xf4411f409e410507, 0x934a0a852d03e1a8, 0xe6274e67ae1ad544, 0x9f4bc563c69a87bc, 0x6f316019681e}} | |||
var affine_xQmP = ExtensionFieldElement{ | |||
A: Fp751Element{0x6ffb44306a153779, 0xc0ffef21f2f918f3, 0x196c46d35d77f778, 0x4a73f80452edcfe6, 0x9b00836bce61c67f, 0x387879418d84219e, 0x20700cf9fc1ec5d1, 0x1dfe2356ec64155e, 0xf8b9e33038256b1c, 0xd2aaf2e14bada0f0, 0xb33b226e79a4e313, 0x6be576fad4e5}, | |||
B: Fp751Element{0x7db5dbc88e00de34, 0x75cc8cb9f8b6e11e, 0x8c8001c04ebc52ac, 0x67ef6c981a0b5a94, 0xc3654fbe73230738, 0xc6a46ee82983ceca, 0xed1aa61a27ef49f0, 0x17fe5a13b0858fe0, 0x9ae0ca945a4c6b3c, 0x234104a218ad8878, 0xa619627166104394, 0x556a01ff2e7e}} | |||
cparam.RecoverCoordinateA(&affine_xP, &affine_xQ, &affine_xQmP) | |||
cparam.C.One() | |||
// Check A is correct | |||
if !cparam.A.VartimeEq(&a) { | |||
t.Error("\nExpected\n", a, "\nfound\n", cparam.A) | |||
} | |||
// Check C is not changed | |||
if !cparam.C.VartimeEq(&oneExtensionField) { | |||
t.Error("\nExpected\n", cparam.C, "\nfound\n", oneExtensionField) | |||
} | |||
} | |||
func TestR2LVersusSage(t *testing.T) { | |||
var xR ProjectivePoint | |||
sageAffine_xR := ExtensionFieldElement{ | |||
A: Fp751Element{0x729465ba800d4fd5, 0x9398015b59e514a1, 0x1a59dd6be76c748e, 0x1a7db94eb28dd55c, 0x444686e680b1b8ec, 0xcc3d4ace2a2454ff, 0x51d3dab4ec95a419, 0xc3b0f33594acac6a, 0x9598a74e7fd44f8a, 0x4fbf8c638f1c2e37, 0x844e347033052f51, 0x6cd6de3eafcf}, | |||
B: Fp751Element{0x85da145412d73430, 0xd83c0e3b66eb3232, 0xd08ff2d453ec1369, 0xa64aaacfdb395b13, 0xe9cba211a20e806e, 0xa4f80b175d937cfc, 0x556ce5c64b1f7937, 0xb59b39ea2b3fdf7a, 0xc2526b869a4196b3, 0x8dad90bca9371750, 0xdfb4a30c9d9147a2, 0x346d2130629b}} | |||
xR = RightToLeftLadder(&curve, &threePointLadderInputs[0], &threePointLadderInputs[1], &threePointLadderInputs[2], uint(len(mScalarBytes)*8), mScalarBytes[:]) | |||
affine_xR := xR.ToAffine() | |||
if !affine_xR.VartimeEq(&sageAffine_xR) { | |||
t.Error("\nExpected\n", sageAffine_xR, "\nfound\n", affine_xR) | |||
} | |||
} | |||
func TestPointTripleVersusAddDouble(t *testing.T) { | |||
tripleEqualsAddDouble := func(curve ProjectiveCurveParameters, P ProjectivePoint) bool { | |||
var P2, P3, P2plusP ProjectivePoint | |||
eqivParams4 := curve.CalcCurveParamsEquiv4() | |||
eqivParams3 := curve.CalcCurveParamsEquiv3() | |||
P2.Pow2k(&eqivParams4, &P, 1) // = x([2]P) | |||
P3.Pow3k(&eqivParams3, &P, 1) // = x([3]P) | |||
P2plusP.Add(&P2, &P, &P) // = x([2]P + P) | |||
return P3.VartimeEq(&P2plusP) | |||
} | |||
if err := quick.Check(tripleEqualsAddDouble, quickCheckConfig); err != nil { | |||
t.Error(err) | |||
} | |||
} | |||
func BenchmarkThreePointLadder379BitScalar(b *testing.B) { | |||
var mScalarBytes = [...]uint8{84, 222, 146, 63, 85, 18, 173, 162, 167, 38, 10, 8, 143, 176, 93, 228, 247, 128, 50, 128, 205, 42, 15, 137, 119, 67, 43, 3, 61, 91, 237, 24, 235, 12, 53, 96, 186, 164, 232, 223, 197, 224, 64, 109, 137, 63, 246, 4} | |||
for n := 0; n < b.N; n++ { | |||
RightToLeftLadder(&curve, &threePointLadderInputs[0], &threePointLadderInputs[1], &threePointLadderInputs[2], uint(len(mScalarBytes)*8), mScalarBytes[:]) | |||
} | |||
} | |||
func BenchmarkR2L379BitScalar(b *testing.B) { | |||
var mScalarBytes = [...]uint8{84, 222, 146, 63, 85, 18, 173, 162, 167, 38, 10, 8, 143, 176, 93, 228, 247, 128, 50, 128, 205, 42, 15, 137, 119, 67, 43, 3, 61, 91, 237, 24, 235, 12, 53, 96, 186, 164, 232, 223, 197, 224, 64, 109, 137, 63, 246, 4} | |||
for n := 0; n < b.N; n++ { | |||
RightToLeftLadder(&curve, &threePointLadderInputs[0], &threePointLadderInputs[1], &threePointLadderInputs[2], uint(len(mScalarBytes)*8), mScalarBytes[:]) | |||
} | |||
} |
@@ -1,420 +0,0 @@ | |||
package p751toolbox | |||
//------------------------------------------------------------------------------ | |||
// Extension Field | |||
//------------------------------------------------------------------------------ | |||
// Represents an element of the extension field F_{p^2}. | |||
type ExtensionFieldElement struct { | |||
// This field element is in Montgomery form, so that the value `A` is | |||
// represented by `aR mod p`. | |||
A Fp751Element | |||
// This field element is in Montgomery form, so that the value `B` is | |||
// represented by `bR mod p`. | |||
B Fp751Element | |||
} | |||
var zeroExtensionField = ExtensionFieldElement{ | |||
A: Fp751Element{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, | |||
B: Fp751Element{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, | |||
} | |||
var oneExtensionField = ExtensionFieldElement{ | |||
A: Fp751Element{0x249ad, 0x0, 0x0, 0x0, 0x0, 0x8310000000000000, 0x5527b1e4375c6c66, 0x697797bf3f4f24d0, 0xc89db7b2ac5c4e2e, 0x4ca4b439d2076956, 0x10f7926c7512c7e9, 0x2d5b24bce5e2}, | |||
B: Fp751Element{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, | |||
} | |||
// 2*p751 | |||
var p751x2 = Fp751Element{ | |||
0xFFFFFFFFFFFFFFFE, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, | |||
0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xDD5FFFFFFFFFFFFF, | |||
0xC7D92D0A93F0F151, 0xB52B363427EF98ED, 0x109D30CFADD7D0ED, | |||
0x0AC56A08B964AE90, 0x1C25213F2F75B8CD, 0x0000DFCBAA83EE38} | |||
// p751 | |||
var p751 = Fp751Element{ | |||
0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, | |||
0xffffffffffffffff, 0xffffffffffffffff, 0xeeafffffffffffff, | |||
0xe3ec968549f878a8, 0xda959b1a13f7cc76, 0x084e9867d6ebe876, | |||
0x8562b5045cb25748, 0x0e12909f97badc66, 0x00006fe5d541f71c} | |||
// p751 + 1 | |||
var p751p1 = Fp751Element{ | |||
0x0000000000000000, 0x0000000000000000, 0x0000000000000000, | |||
0x0000000000000000, 0x0000000000000000, 0xeeb0000000000000, | |||
0xe3ec968549f878a8, 0xda959b1a13f7cc76, 0x084e9867d6ebe876, | |||
0x8562b5045cb25748, 0x0e12909f97badc66, 0x00006fe5d541f71c} | |||
// Set dest = 0. | |||
// | |||
// Returns dest to allow chaining operations. | |||
func (dest *ExtensionFieldElement) Zero() *ExtensionFieldElement { | |||
*dest = zeroExtensionField | |||
return dest | |||
} | |||
// Set dest = 1. | |||
// | |||
// Returns dest to allow chaining operations. | |||
func (dest *ExtensionFieldElement) One() *ExtensionFieldElement { | |||
*dest = oneExtensionField | |||
return dest | |||
} | |||
// Set dest = lhs * rhs. | |||
// | |||
// Allowed to overlap lhs or rhs with dest. | |||
// | |||
// Returns dest to allow chaining operations. | |||
func (dest *ExtensionFieldElement) Mul(lhs, rhs *ExtensionFieldElement) *ExtensionFieldElement { | |||
// Let (a,b,c,d) = (lhs.a,lhs.b,rhs.a,rhs.b). | |||
a := &lhs.A | |||
b := &lhs.B | |||
c := &rhs.A | |||
d := &rhs.B | |||
// We want to compute | |||
// | |||
// (a + bi)*(c + di) = (a*c - b*d) + (a*d + b*c)i | |||
// | |||
// Use Karatsuba's trick: note that | |||
// | |||
// (b - a)*(c - d) = (b*c + a*d) - a*c - b*d | |||
// | |||
// so (a*d + b*c) = (b-a)*(c-d) + a*c + b*d. | |||
var ac, bd fp751X2 | |||
fp751Mul(&ac, a, c) // = a*c*R*R | |||
fp751Mul(&bd, b, d) // = b*d*R*R | |||
var b_minus_a, c_minus_d Fp751Element | |||
fp751SubReduced(&b_minus_a, b, a) // = (b-a)*R | |||
fp751SubReduced(&c_minus_d, c, d) // = (c-d)*R | |||
var ad_plus_bc fp751X2 | |||
fp751Mul(&ad_plus_bc, &b_minus_a, &c_minus_d) // = (b-a)*(c-d)*R*R | |||
fp751X2AddLazy(&ad_plus_bc, &ad_plus_bc, &ac) // = ((b-a)*(c-d) + a*c)*R*R | |||
fp751X2AddLazy(&ad_plus_bc, &ad_plus_bc, &bd) // = ((b-a)*(c-d) + a*c + b*d)*R*R | |||
fp751MontgomeryReduce(&dest.B, &ad_plus_bc) // = (a*d + b*c)*R mod p | |||
var ac_minus_bd fp751X2 | |||
fp751X2SubLazy(&ac_minus_bd, &ac, &bd) // = (a*c - b*d)*R*R | |||
fp751MontgomeryReduce(&dest.A, &ac_minus_bd) // = (a*c - b*d)*R mod p | |||
return dest | |||
} | |||
// Set dest = 1/x | |||
// | |||
// Allowed to overlap dest with x. | |||
// | |||
// Returns dest to allow chaining operations. | |||
func (dest *ExtensionFieldElement) Inv(x *ExtensionFieldElement) *ExtensionFieldElement { | |||
a := &x.A | |||
b := &x.B | |||
// We want to compute | |||
// | |||
// 1 1 (a - bi) (a - bi) | |||
// -------- = -------- -------- = ----------- | |||
// (a + bi) (a + bi) (a - bi) (a^2 + b^2) | |||
// | |||
// Letting c = 1/(a^2 + b^2), this is | |||
// | |||
// 1/(a+bi) = a*c - b*ci. | |||
var asq_plus_bsq PrimeFieldElement | |||
var asq, bsq fp751X2 | |||
fp751Mul(&asq, a, a) // = a*a*R*R | |||
fp751Mul(&bsq, b, b) // = b*b*R*R | |||
fp751X2AddLazy(&asq, &asq, &bsq) // = (a^2 + b^2)*R*R | |||
fp751MontgomeryReduce(&asq_plus_bsq.A, &asq) // = (a^2 + b^2)*R mod p | |||
// Now asq_plus_bsq = a^2 + b^2 | |||
// Invert asq_plus_bsq | |||
inv := asq_plus_bsq | |||
inv.Mul(&asq_plus_bsq, &asq_plus_bsq) | |||
inv.P34(&inv) | |||
inv.Mul(&inv, &inv) | |||
inv.Mul(&inv, &asq_plus_bsq) | |||
var ac fp751X2 | |||
fp751Mul(&ac, a, &inv.A) | |||
fp751MontgomeryReduce(&dest.A, &ac) | |||
var minus_b Fp751Element | |||
fp751SubReduced(&minus_b, &minus_b, b) | |||
var minus_bc fp751X2 | |||
fp751Mul(&minus_bc, &minus_b, &inv.A) | |||
fp751MontgomeryReduce(&dest.B, &minus_bc) | |||
return dest | |||
} | |||
// Set (y1, y2, y3) = (1/x1, 1/x2, 1/x3). | |||
// | |||
// All xi, yi must be distinct. | |||
func ExtensionFieldBatch3Inv(x1, x2, x3, y1, y2, y3 *ExtensionFieldElement) { | |||
var x1x2, t ExtensionFieldElement | |||
x1x2.Mul(x1, x2) // x1*x2 | |||
t.Mul(&x1x2, x3).Inv(&t) // 1/(x1*x2*x3) | |||
y1.Mul(&t, x2).Mul(y1, x3) // 1/x1 | |||
y2.Mul(&t, x1).Mul(y2, x3) // 1/x2 | |||
y3.Mul(&t, &x1x2) // 1/x3 | |||
} | |||
// Set dest = x * x | |||
// | |||
// Allowed to overlap dest with x. | |||
// | |||
// Returns dest to allow chaining operations. | |||
func (dest *ExtensionFieldElement) Square(x *ExtensionFieldElement) *ExtensionFieldElement { | |||
a := &x.A | |||
b := &x.B | |||
// We want to compute | |||
// | |||
// (a + bi)*(a + bi) = (a^2 - b^2) + 2abi. | |||
var a2, a_plus_b, a_minus_b Fp751Element | |||
fp751AddReduced(&a2, a, a) // = a*R + a*R = 2*a*R | |||
fp751AddReduced(&a_plus_b, a, b) // = a*R + b*R = (a+b)*R | |||
fp751SubReduced(&a_minus_b, a, b) // = a*R - b*R = (a-b)*R | |||
var asq_minus_bsq, ab2 fp751X2 | |||
fp751Mul(&asq_minus_bsq, &a_plus_b, &a_minus_b) // = (a+b)*(a-b)*R*R = (a^2 - b^2)*R*R | |||
fp751Mul(&ab2, &a2, b) // = 2*a*b*R*R | |||
fp751MontgomeryReduce(&dest.A, &asq_minus_bsq) // = (a^2 - b^2)*R mod p | |||
fp751MontgomeryReduce(&dest.B, &ab2) // = 2*a*b*R mod p | |||
return dest | |||
} | |||
// Set dest = lhs + rhs. | |||
// | |||
// Allowed to overlap lhs or rhs with dest. | |||
// | |||
// Returns dest to allow chaining operations. | |||
func (dest *ExtensionFieldElement) Add(lhs, rhs *ExtensionFieldElement) *ExtensionFieldElement { | |||
fp751AddReduced(&dest.A, &lhs.A, &rhs.A) | |||
fp751AddReduced(&dest.B, &lhs.B, &rhs.B) | |||
return dest | |||
} | |||
// Set dest = lhs - rhs. | |||
// | |||
// Allowed to overlap lhs or rhs with dest. | |||
// | |||
// Returns dest to allow chaining operations. | |||
func (dest *ExtensionFieldElement) Sub(lhs, rhs *ExtensionFieldElement) *ExtensionFieldElement { | |||
fp751SubReduced(&dest.A, &lhs.A, &rhs.A) | |||
fp751SubReduced(&dest.B, &lhs.B, &rhs.B) | |||
return dest | |||
} | |||
// If choice = 1u8, set (x,y) = (y,x). If choice = 0u8, set (x,y) = (x,y). | |||
// | |||
// Returns dest to allow chaining operations. | |||
func ExtensionFieldConditionalSwap(x, y *ExtensionFieldElement, choice uint8) { | |||
fp751ConditionalSwap(&x.A, &y.A, choice) | |||
fp751ConditionalSwap(&x.B, &y.B, choice) | |||
} | |||
// Returns true if lhs = rhs. Takes variable time. | |||
func (lhs *ExtensionFieldElement) VartimeEq(rhs *ExtensionFieldElement) bool { | |||
return lhs.A.vartimeEq(rhs.A) && lhs.B.vartimeEq(rhs.B) | |||
} | |||
// Convert the input to wire format. | |||
// | |||
// The output byte slice must be at least 188 bytes long. | |||
func (x *ExtensionFieldElement) ToBytes(output []byte) { | |||
if len(output) < 188 { | |||
panic("output byte slice too short, need 188 bytes") | |||
} | |||
x.A.toBytesFromMontgomeryForm(output[0:94]) | |||
x.B.toBytesFromMontgomeryForm(output[94:188]) | |||
} | |||
// Read 188 bytes into the given ExtensionFieldElement. | |||
// | |||
// It is an error to call this function if the input byte slice is less than 188 bytes long. | |||
func (x *ExtensionFieldElement) FromBytes(input []byte) { | |||
if len(input) < 188 { | |||
panic("input byte slice too short, need 188 bytes") | |||
} | |||
x.A.montgomeryFormFromBytes(input[:94]) | |||
x.B.montgomeryFormFromBytes(input[94:188]) | |||
} | |||
//------------------------------------------------------------------------------ | |||
// Prime Field | |||
//------------------------------------------------------------------------------ | |||
// Represents an element of the prime field F_p. | |||
type PrimeFieldElement struct { | |||
// This field element is in Montgomery form, so that the value `A` is | |||
// represented by `aR mod p`. | |||
A Fp751Element | |||
} | |||
var zeroPrimeField = PrimeFieldElement{ | |||
A: Fp751Element{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, | |||
} | |||
var onePrimeField = PrimeFieldElement{ | |||
A: Fp751Element{0x249ad, 0x0, 0x0, 0x0, 0x0, 0x8310000000000000, 0x5527b1e4375c6c66, 0x697797bf3f4f24d0, 0xc89db7b2ac5c4e2e, 0x4ca4b439d2076956, 0x10f7926c7512c7e9, 0x2d5b24bce5e2}, | |||
} | |||
// Set dest = lhs * rhs. | |||
// | |||
// Allowed to overlap lhs or rhs with dest. | |||
// | |||
// Returns dest to allow chaining operations. | |||
func (dest *PrimeFieldElement) Mul(lhs, rhs *PrimeFieldElement) *PrimeFieldElement { | |||
a := &lhs.A // = a*R | |||
b := &rhs.A // = b*R | |||
var ab fp751X2 | |||
fp751Mul(&ab, a, b) // = a*b*R*R | |||
fp751MontgomeryReduce(&dest.A, &ab) // = a*b*R mod p | |||
return dest | |||
} | |||
// Set dest = x^(2^k), for k >= 1, by repeated squarings. | |||
// | |||
// Allowed to overlap x with dest. | |||
// | |||
// Returns dest to allow chaining operations. | |||
func (dest *PrimeFieldElement) Pow2k(x *PrimeFieldElement, k uint8) *PrimeFieldElement { | |||
dest.Mul(x, x) | |||
for i := uint8(1); i < k; i++ { | |||
dest.Mul(dest, dest) | |||
} | |||
return dest | |||
} | |||
// Set dest = x^((p-3)/4). If x is square, this is 1/sqrt(x). | |||
// | |||
// Allowed to overlap x with dest. | |||
// | |||
// Returns dest to allow chaining operations. | |||
func (dest *PrimeFieldElement) P34(x *PrimeFieldElement) *PrimeFieldElement { | |||
// Sliding-window strategy computed with Sage, awk, sed, and tr. | |||
// | |||
// This performs sum(powStrategy) = 744 squarings and len(mulStrategy) | |||
// = 137 multiplications, in addition to 1 squaring and 15 | |||
// multiplications to build a lookup table. | |||
// | |||
// In total this is 745 squarings, 152 multiplications. Since squaring | |||
// is not implemented for the prime field, this is 897 multiplications | |||
// in total. | |||
powStrategy := [137]uint8{5, 7, 6, 2, 10, 4, 6, 9, 8, 5, 9, 4, 7, 5, 5, 4, 8, 3, 9, 5, 5, 4, 10, 4, 6, 6, 6, 5, 8, 9, 3, 4, 9, 4, 5, 6, 6, 2, 9, 4, 5, 5, 5, 7, 7, 9, 4, 6, 4, 8, 5, 8, 6, 6, 2, 9, 7, 4, 8, 8, 8, 4, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2} | |||
mulStrategy := [137]uint8{31, 23, 21, 1, 31, 7, 7, 7, 9, 9, 19, 15, 23, 23, 11, 7, 25, 5, 21, 17, 11, 5, 17, 7, 11, 9, 23, 9, 1, 19, 5, 3, 25, 15, 11, 29, 31, 1, 29, 11, 13, 9, 11, 27, 13, 19, 15, 31, 3, 29, 23, 31, 25, 11, 1, 21, 19, 15, 15, 21, 29, 13, 23, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 3} | |||
initialMul := uint8(27) | |||
// Build a lookup table of odd multiples of x. | |||
lookup := [16]PrimeFieldElement{} | |||
xx := &PrimeFieldElement{} | |||
xx.Mul(x, x) // Set xx = x^2 | |||
lookup[0] = *x | |||
for i := 1; i < 16; i++ { | |||
lookup[i].Mul(&lookup[i-1], xx) | |||
} | |||
// Now lookup = {x, x^3, x^5, ... } | |||
// so that lookup[i] = x^{2*i + 1} | |||
// so that lookup[k/2] = x^k, for odd k | |||
*dest = lookup[initialMul/2] | |||
for i := uint8(0); i < 137; i++ { | |||
dest.Pow2k(dest, powStrategy[i]) | |||
dest.Mul(dest, &lookup[mulStrategy[i]/2]) | |||
} | |||
return dest | |||
} | |||
//------------------------------------------------------------------------------ | |||
// Internals | |||
//------------------------------------------------------------------------------ | |||
const fp751NumWords = 12 | |||
// (2^768)^2 mod p | |||
// This can't be a constant because Go doesn't allow array constants, so try | |||
// not to modify it. | |||
var montgomeryRsq = Fp751Element{2535603850726686808, 15780896088201250090, 6788776303855402382, 17585428585582356230, 5274503137951975249, 2266259624764636289, 11695651972693921304, 13072885652150159301, 4908312795585420432, 6229583484603254826, 488927695601805643, 72213483953973} | |||
// Internal representation of an element of the base field F_p. | |||
// | |||
// This type is distinct from PrimeFieldElement in that no particular meaning | |||
// is assigned to the representation -- it could represent an element in | |||
// Montgomery form, or not. Tracking the meaning of the field element is left | |||
// to higher types. | |||
type Fp751Element [fp751NumWords]uint64 | |||
// Represents an intermediate product of two elements of the base field F_p. | |||
type fp751X2 [2 * fp751NumWords]uint64 | |||
func (x Fp751Element) vartimeEq(y Fp751Element) bool { | |||
fp751StrongReduce(&x) | |||
fp751StrongReduce(&y) | |||
eq := true | |||
for i := 0; i < fp751NumWords; i++ { | |||
eq = (x[i] == y[i]) && eq | |||
} | |||
return eq | |||
} | |||
// Read an Fp751Element from little-endian bytes and convert to Montgomery form. | |||
// | |||
// The input byte slice must be at least 94 bytes long. | |||
func (x *Fp751Element) montgomeryFormFromBytes(input []byte) { | |||
if len(input) < 94 { | |||
panic("input byte slice too short") | |||
} | |||
var a Fp751Element | |||
for i := 0; i < 94; i++ { | |||
// set i = j*8 + k | |||
j := i / 8 | |||
k := uint64(i % 8) | |||
a[j] |= uint64(input[i]) << (8 * k) | |||
} | |||
var aRR fp751X2 | |||
fp751Mul(&aRR, &a, &montgomeryRsq) // = a*R*R | |||
fp751MontgomeryReduce(x, &aRR) // = a*R mod p | |||
} | |||
// Given an Fp751Element in Montgomery form, convert to little-endian bytes. | |||
// | |||
// The output byte slice must be at least 94 bytes long. | |||
func (x *Fp751Element) toBytesFromMontgomeryForm(output []byte) { | |||
if len(output) < 94 { | |||
panic("output byte slice too short") | |||
} | |||
var a Fp751Element | |||
var aR fp751X2 | |||
copy(aR[:], x[:]) // = a*R | |||
fp751MontgomeryReduce(&a, &aR) // = a mod p in [0, 2p) | |||
fp751StrongReduce(&a) // = a mod p in [0, p) | |||
// 8*12 = 96, but we drop the last two bytes since p is 751 < 752=94*8 bits. | |||
for i := 0; i < 94; i++ { | |||
// set i = j*8 + k | |||
j := i / 8 | |||
k := uint64(i % 8) | |||
// Need parens because Go's operator precedence would interpret | |||
// a[j] >> 8*k as (a[j] >> 8) * k | |||
output[i] = byte(a[j] >> (8 * k)) | |||
} | |||
} |
@@ -1,420 +0,0 @@ | |||
package p751toolbox | |||
import ( | |||
"math/big" | |||
"math/rand" | |||
"reflect" | |||
"testing" | |||
"testing/quick" | |||
) | |||
var quickCheckScaleFactor = uint8(3) | |||
var quickCheckConfig = &quick.Config{MaxCount: (1 << (12 + quickCheckScaleFactor))} | |||
var cln16prime, _ = new(big.Int).SetString("10354717741769305252977768237866805321427389645549071170116189679054678940682478846502882896561066713624553211618840202385203911976522554393044160468771151816976706840078913334358399730952774926980235086850991501872665651576831", 10) | |||
// Convert an Fp751Element to a big.Int for testing. Because this is only | |||
// for testing, no big.Int to Fp751Element conversion is provided. | |||
func radix64ToBigInt(x []uint64) *big.Int { | |||
radix := new(big.Int) | |||
// 2^64 | |||
radix.UnmarshalText(([]byte)("18446744073709551616")) | |||
base := new(big.Int).SetUint64(1) | |||
val := new(big.Int).SetUint64(0) | |||
tmp := new(big.Int) | |||
for _, xi := range x { | |||
tmp.SetUint64(xi) | |||
tmp.Mul(tmp, base) | |||
val.Add(val, tmp) | |||
base.Mul(base, radix) | |||
} | |||
return val | |||
} | |||
func VartimeEq(x,y *PrimeFieldElement) bool { | |||
return x.A.vartimeEq(y.A) | |||
} | |||
func (x *PrimeFieldElement) toBigInt() *big.Int { | |||
// Convert from Montgomery form | |||
return x.A.toBigIntFromMontgomeryForm() | |||
} | |||
func (x *Fp751Element) toBigIntFromMontgomeryForm() *big.Int { | |||
// Convert from Montgomery form | |||
a := Fp751Element{} | |||
aR := fp751X2{} | |||
copy(aR[:], x[:]) // = a*R | |||
fp751MontgomeryReduce(&a, &aR) // = a mod p in [0,2p) | |||
fp751StrongReduce(&a) // = a mod p in [0,p) | |||
return radix64ToBigInt(a[:]) | |||
} | |||
func TestPrimeFieldElementToBigInt(t *testing.T) { | |||
// Chosen so that p < xR < 2p | |||
x := PrimeFieldElement{A: Fp751Element{ | |||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 140737488355328, | |||
}} | |||
// Computed using Sage: | |||
// sage: p = 2^372 * 3^239 - 1 | |||
// sage: R = 2^768 | |||
// sage: from_radix_64 = lambda xs: sum((xi * (2**64)**i for i,xi in enumerate(xs))) | |||
// sage: xR = from_radix_64([1]*11 + [2^47]) | |||
// sage: assert(p < xR) | |||
// sage: assert(xR < 2*p) | |||
// sage: (xR / R) % p | |||
xBig, _ := new(big.Int).SetString("4469946751055876387821312289373600189787971305258234719850789711074696941114031433609871105823930699680637820852699269802003300352597419024286385747737509380032982821081644521634652750355306547718505685107272222083450567982240", 10) | |||
if xBig.Cmp(x.toBigInt()) != 0 { | |||
t.Error("Expected", xBig, "found", x.toBigInt()) | |||
} | |||
} | |||
func generateFp751(rand *rand.Rand) Fp751Element { | |||
// Generation strategy: low limbs taken from [0,2^64); high limb | |||
// taken from smaller range | |||
// | |||
// Size hint is ignored since all elements are fixed size. | |||
// | |||
// Field elements taken in range [0,2p). Emulate this by capping | |||
// the high limb by the top digit of 2*p-1: | |||
// | |||
// sage: (2*p-1).digits(2^64)[-1] | |||
// 246065832128056 | |||
// | |||
// This still allows generating values >= 2p, but hopefully that | |||
// excess is OK (and if it's not, we'll find out, because it's for | |||
// testing...) | |||
// | |||
highLimb := rand.Uint64() % 246065832128056 | |||
return Fp751Element{ | |||
rand.Uint64(), | |||
rand.Uint64(), | |||
rand.Uint64(), | |||
rand.Uint64(), | |||
rand.Uint64(), | |||
rand.Uint64(), | |||
rand.Uint64(), | |||
rand.Uint64(), | |||
rand.Uint64(), | |||
rand.Uint64(), | |||
rand.Uint64(), | |||
highLimb, | |||
} | |||
} | |||
func (x PrimeFieldElement) Generate(rand *rand.Rand, size int) reflect.Value { | |||
return reflect.ValueOf(PrimeFieldElement{A: generateFp751(rand)}) | |||
} | |||
func (x ExtensionFieldElement) Generate(rand *rand.Rand, size int) reflect.Value { | |||
return reflect.ValueOf(ExtensionFieldElement{A: generateFp751(rand), B: generateFp751(rand)}) | |||
} | |||
//------------------------------------------------------------------------------ | |||
// Extension Field | |||
//------------------------------------------------------------------------------ | |||
func TestOneExtensionFieldToBytes(t *testing.T) { | |||
var x ExtensionFieldElement | |||
var xBytes [188]byte | |||
x.One() | |||
x.ToBytes(xBytes[:]) | |||
if xBytes[0] != 1 { | |||
t.Error("Expected 1, got", xBytes[0]) | |||
} | |||
for i := 1; i < 188; i++ { | |||
if xBytes[i] != 0 { | |||
t.Error("Expected 0, got", xBytes[0]) | |||
} | |||
} | |||
} | |||
func TestExtensionFieldElementToBytesRoundTrip(t *testing.T) { | |||
roundTrips := func(x ExtensionFieldElement) bool { | |||
var xBytes [188]byte | |||
var xPrime ExtensionFieldElement | |||
x.ToBytes(xBytes[:]) | |||
xPrime.FromBytes(xBytes[:]) | |||
return x.VartimeEq(&xPrime) | |||
} | |||
if err := quick.Check(roundTrips, quickCheckConfig); err != nil { | |||
t.Error(err) | |||
} | |||
} | |||
func TestExtensionFieldElementMulDistributesOverAdd(t *testing.T) { | |||
mulDistributesOverAdd := func(x, y, z ExtensionFieldElement) bool { | |||
// Compute t1 = (x+y)*z | |||
t1 := new(ExtensionFieldElement) | |||
t1.Add(&x, &y) | |||
t1.Mul(t1, &z) | |||
// Compute t2 = x*z + y*z | |||
t2 := new(ExtensionFieldElement) | |||
t3 := new(ExtensionFieldElement) | |||
t2.Mul(&x, &z) | |||
t3.Mul(&y, &z) | |||
t2.Add(t2, t3) | |||
return t1.VartimeEq(t2) | |||
} | |||
if err := quick.Check(mulDistributesOverAdd, quickCheckConfig); err != nil { | |||
t.Error(err) | |||
} | |||
} | |||
func TestExtensionFieldElementMulIsAssociative(t *testing.T) { | |||
isAssociative := func(x, y, z ExtensionFieldElement) bool { | |||
// Compute t1 = (x*y)*z | |||
t1 := new(ExtensionFieldElement) | |||
t1.Mul(&x, &y) | |||
t1.Mul(t1, &z) | |||
// Compute t2 = (y*z)*x | |||
t2 := new(ExtensionFieldElement) | |||
t2.Mul(&y, &z) | |||
t2.Mul(t2, &x) | |||
return t1.VartimeEq(t2) | |||
} | |||
if err := quick.Check(isAssociative, quickCheckConfig); err != nil { | |||
t.Error(err) | |||
} | |||
} | |||
func TestExtensionFieldElementSquareMatchesMul(t *testing.T) { | |||
sqrMatchesMul := func(x ExtensionFieldElement) bool { | |||
// Compute t1 = (x*x) | |||
t1 := new(ExtensionFieldElement) | |||
t1.Mul(&x, &x) | |||
// Compute t2 = x^2 | |||
t2 := new(ExtensionFieldElement) | |||
t2.Square(&x) | |||
return t1.VartimeEq(t2) | |||
} | |||
if err := quick.Check(sqrMatchesMul, quickCheckConfig); err != nil { | |||
t.Error(err) | |||
} | |||
} | |||
func TestExtensionFieldElementInv(t *testing.T) { | |||
inverseIsCorrect := func(x ExtensionFieldElement) bool { | |||
z := new(ExtensionFieldElement) | |||
z.Inv(&x) | |||
// Now z = (1/x), so (z * x) * x == x | |||
z.Mul(z, &x) | |||
z.Mul(z, &x) | |||
return z.VartimeEq(&x) | |||
} | |||
// This is more expensive; run fewer tests | |||
var quickCheckConfig = &quick.Config{MaxCount: (1 << (8 + quickCheckScaleFactor))} | |||
if err := quick.Check(inverseIsCorrect, quickCheckConfig); err != nil { | |||
t.Error(err) | |||
} | |||
} | |||
func TestExtensionFieldElementBatch3Inv(t *testing.T) { | |||
batchInverseIsCorrect := func(x1, x2, x3 ExtensionFieldElement) bool { | |||
var x1Inv, x2Inv, x3Inv ExtensionFieldElement | |||
x1Inv.Inv(&x1) | |||
x2Inv.Inv(&x2) | |||
x3Inv.Inv(&x3) | |||
var y1, y2, y3 ExtensionFieldElement | |||
ExtensionFieldBatch3Inv(&x1, &x2, &x3, &y1, &y2, &y3) | |||
return (y1.VartimeEq(&x1Inv) && y2.VartimeEq(&x2Inv) && y3.VartimeEq(&x3Inv)) | |||
} | |||
// This is more expensive; run fewer tests | |||
var quickCheckConfig = &quick.Config{MaxCount: (1 << (5 + quickCheckScaleFactor))} | |||
if err := quick.Check(batchInverseIsCorrect, quickCheckConfig); err != nil { | |||
t.Error(err) | |||
} | |||
} | |||
//------------------------------------------------------------------------------ | |||
// Prime Field | |||
//------------------------------------------------------------------------------ | |||
func TestPrimeFieldElementMulVersusBigInt(t *testing.T) { | |||
mulMatchesBigInt := func(x, y PrimeFieldElement) bool { | |||
z := new(PrimeFieldElement) | |||
z.Mul(&x, &y) | |||
check := new(big.Int) | |||
check.Mul(x.toBigInt(), y.toBigInt()) | |||
check.Mod(check, cln16prime) | |||
return check.Cmp(z.toBigInt()) == 0 | |||
} | |||
if err := quick.Check(mulMatchesBigInt, quickCheckConfig); err != nil { | |||
t.Error(err) | |||
} | |||
} | |||
func TestPrimeFieldElementP34VersusBigInt(t *testing.T) { | |||
var p34, _ = new(big.Int).SetString("2588679435442326313244442059466701330356847411387267792529047419763669735170619711625720724140266678406138302904710050596300977994130638598261040117192787954244176710019728333589599932738193731745058771712747875468166412894207", 10) | |||
p34MatchesBigInt := func(x PrimeFieldElement) bool { | |||
z := new(PrimeFieldElement) | |||
z.P34(&x) | |||
check := x.toBigInt() | |||
check.Exp(check, p34, cln16prime) | |||
return check.Cmp(z.toBigInt()) == 0 | |||
} | |||
// This is more expensive; run fewer tests | |||
var quickCheckConfig = &quick.Config{MaxCount: (1 << (8 + quickCheckScaleFactor))} | |||
if err := quick.Check(p34MatchesBigInt, quickCheckConfig); err != nil { | |||
t.Error(err) | |||
} | |||
} | |||
// Package-level storage for this field element is intended to deter | |||
// compiler optimizations. | |||
var benchmarkFp751Element Fp751Element | |||
var benchmarkFp751X2 fp751X2 | |||
var bench_x = Fp751Element{17026702066521327207, 5108203422050077993, 10225396685796065916, 11153620995215874678, 6531160855165088358, 15302925148404145445, 1248821577836769963, 9789766903037985294, 7493111552032041328, 10838999828319306046, 18103257655515297935, 27403304611634} | |||
var bench_y = Fp751Element{4227467157325093378, 10699492810770426363, 13500940151395637365, 12966403950118934952, 16517692605450415877, 13647111148905630666, 14223628886152717087, 7167843152346903316, 15855377759596736571, 4300673881383687338, 6635288001920617779, 30486099554235} | |||
var bench_z = fp751X2{1595347748594595712, 10854920567160033970, 16877102267020034574, 12435724995376660096, 3757940912203224231, 8251999420280413600, 3648859773438820227, 17622716832674727914, 11029567000887241528, 11216190007549447055, 17606662790980286987, 4720707159513626555, 12887743598335030915, 14954645239176589309, 14178817688915225254, 1191346797768989683, 12629157932334713723, 6348851952904485603, 16444232588597434895, 7809979927681678066, 14642637672942531613, 3092657597757640067, 10160361564485285723, 240071237} | |||
func BenchmarkExtensionFieldElementMul(b *testing.B) { | |||
z := &ExtensionFieldElement{A: bench_x, B: bench_y} | |||
w := new(ExtensionFieldElement) | |||
for n := 0; n < b.N; n++ { | |||
w.Mul(z, z) | |||
} | |||
} | |||
func BenchmarkExtensionFieldElementInv(b *testing.B) { | |||
z := &ExtensionFieldElement{A: bench_x, B: bench_y} | |||
w := new(ExtensionFieldElement) | |||
for n := 0; n < b.N; n++ { | |||
w.Inv(z) | |||
} | |||
} | |||
func BenchmarkExtensionFieldElementSquare(b *testing.B) { | |||
z := &ExtensionFieldElement{A: bench_x, B: bench_y} | |||
w := new(ExtensionFieldElement) | |||
for n := 0; n < b.N; n++ { | |||
w.Square(z) | |||
} | |||
} | |||
func BenchmarkExtensionFieldElementAdd(b *testing.B) { | |||
z := &ExtensionFieldElement{A: bench_x, B: bench_y} | |||
w := new(ExtensionFieldElement) | |||
for n := 0; n < b.N; n++ { | |||
w.Add(z, z) | |||
} | |||
} | |||
func BenchmarkExtensionFieldElementSub(b *testing.B) { | |||
z := &ExtensionFieldElement{A: bench_x, B: bench_y} | |||
w := new(ExtensionFieldElement) | |||
for n := 0; n < b.N; n++ { | |||
w.Sub(z, z) | |||
} | |||
} | |||
func BenchmarkPrimeFieldElementMul(b *testing.B) { | |||
z := &PrimeFieldElement{A: bench_x} | |||
w := new(PrimeFieldElement) | |||
for n := 0; n < b.N; n++ { | |||
w.Mul(z, z) | |||
} | |||
} | |||
// --- field operation functions | |||
func BenchmarkFp751Multiply(b *testing.B) { | |||
for n := 0; n < b.N; n++ { | |||
fp751Mul(&benchmarkFp751X2, &bench_x, &bench_y) | |||
} | |||
} | |||
func BenchmarkFp751MontgomeryReduce(b *testing.B) { | |||
z := bench_z | |||
// This benchmark actually computes garbage, because | |||
// fp751MontgomeryReduce mangles its input, but since it's | |||
// constant-time that shouldn't matter for the benchmarks. | |||
for n := 0; n < b.N; n++ { | |||
fp751MontgomeryReduce(&benchmarkFp751Element, &z) | |||
} | |||
} | |||
func BenchmarkFp751AddReduced(b *testing.B) { | |||
for n := 0; n < b.N; n++ { | |||
fp751AddReduced(&benchmarkFp751Element, &bench_x, &bench_y) | |||
} | |||
} | |||
func BenchmarkFp751SubReduced(b *testing.B) { | |||
for n := 0; n < b.N; n++ { | |||
fp751SubReduced(&benchmarkFp751Element, &bench_x, &bench_y) | |||
} | |||
} | |||
func BenchmarkFp751ConditionalSwap(b *testing.B) { | |||
x, y := bench_x, bench_y | |||
for n := 0; n < b.N; n++ { | |||
fp751ConditionalSwap(&x, &y, 1) | |||
fp751ConditionalSwap(&x, &y, 0) | |||
} | |||
} | |||
func BenchmarkFp751StrongReduce(b *testing.B) { | |||
x := bench_x | |||
for n := 0; n < b.N; n++ { | |||
fp751StrongReduce(&x) | |||
} | |||
} | |||
func BenchmarkFp751AddLazy(b *testing.B) { | |||
var z Fp751Element | |||
x, y := bench_x, bench_y | |||
for n := 0; n < b.N; n++ { | |||
fp751AddLazy(&z, &x, &y) | |||
} | |||
} | |||
func BenchmarkFp751X2AddLazy(b *testing.B) { | |||
x, y, z := bench_z, bench_z, bench_z | |||
for n := 0; n < b.N; n++ { | |||
fp751X2AddLazy(&x, &y, &z) | |||
} | |||
} | |||
func BenchmarkFp751X2SubLazy(b *testing.B) { | |||
x, y, z := bench_z, bench_z, bench_z | |||
for n := 0; n < b.N; n++ { | |||
fp751X2SubLazy(&x, &y, &z) | |||
} | |||
} |
@@ -1,144 +0,0 @@ | |||
package p751toolbox | |||
// Interface for working with isogenies. | |||
type Isogeny interface { | |||
// Given a torsion point on a curve computes isogenous curve. | |||
// Returns curve coefficients (A:C), so that E_(A/C) = E_(A/C)/<P>, | |||
// where P is a provided projective point. Sets also isogeny constants | |||
// that are needed for isogeny evaluation. | |||
GenerateCurve(*ProjectivePoint) CurveCoefficientsEquiv | |||
// Evaluates isogeny at caller provided point. Requires isogeny curve constants | |||
// to be earlier computed by GenerateCurve. | |||
EvaluatePoint(*ProjectivePoint) ProjectivePoint | |||
} | |||
// Stores Isogeny 4 curve constants | |||
type isogeny4 struct { | |||
isogeny3 | |||
K3 ExtensionFieldElement | |||
} | |||
// Stores Isogeny 3 curve constants | |||
type isogeny3 struct { | |||
K1 ExtensionFieldElement | |||
K2 ExtensionFieldElement | |||
} | |||
// Constructs isogeny4 objects | |||
func NewIsogeny4() Isogeny { | |||
return new(isogeny4) | |||
} | |||
// Constructs isogeny3 objects | |||
func NewIsogeny3() Isogeny { | |||
return new(isogeny3) | |||
} | |||
// Given a three-torsion point p = x(PB) on the curve E_(A:C), construct the | |||
// three-isogeny phi : E_(A:C) -> E_(A:C)/<P_3> = E_(A':C'). | |||
// | |||
// Input: (XP_3: ZP_3), where P_3 has exact order 3 on E_A/C | |||
// Output: * Curve coordinates (A' + 2C', A' - 2C') corresponding to E_A'/C' = A_E/C/<P3> | |||
// * Isogeny phi with constants in F_p^2 | |||
func (phi *isogeny3) GenerateCurve(p *ProjectivePoint) CurveCoefficientsEquiv { | |||
var t0, t1, t2, t3, t4 ExtensionFieldElement | |||
var coefEq CurveCoefficientsEquiv | |||
var K1, K2 = &phi.K1, &phi.K2 | |||
K1.Sub(&p.X, &p.Z) // K1 = XP3 - ZP3 | |||
t0.Square(K1) // t0 = K1^2 | |||
K2.Add(&p.X, &p.Z) // K2 = XP3 + ZP3 | |||
t1.Square(K2) // t1 = K2^2 | |||
t2.Add(&t0, &t1) // t2 = t0 + t1 | |||
t3.Add(K1, K2) // t3 = K1 + K2 | |||
t3.Square(&t3) // t3 = t3^2 | |||
t3.Sub(&t3, &t2) // t3 = t3 - t2 | |||
t2.Add(&t1, &t3) // t2 = t1 + t3 | |||
t3.Add(&t3, &t0) // t3 = t3 + t0 | |||
t4.Add(&t3, &t0) // t4 = t3 + t0 | |||
t4.Add(&t4, &t4) // t4 = t4 + t4 | |||
t4.Add(&t1, &t4) // t4 = t1 + t4 | |||
coefEq.C.Mul(&t2, &t4) // A24m = t2 * t4 | |||
t4.Add(&t1, &t2) // t4 = t1 + t2 | |||
t4.Add(&t4, &t4) // t4 = t4 + t4 | |||
t4.Add(&t0, &t4) // t4 = t0 + t4 | |||
t4.Mul(&t3, &t4) // t4 = t3 * t4 | |||
t0.Sub(&t4, &coefEq.C) // t0 = t4 - A24m | |||
coefEq.A.Add(&coefEq.C, &t0) // A24p = A24m + t0 | |||
return coefEq | |||
} | |||
// Given a 3-isogeny phi and a point pB = x(PB), compute x(QB), the x-coordinate | |||
// of the image QB = phi(PB) of PB under phi : E_(A:C) -> E_(A':C'). | |||
// | |||
// The output xQ = x(Q) is then a point on the curve E_(A':C'); the curve | |||
// parameters are returned by the GenerateCurve function used to construct phi. | |||
func (phi *isogeny3) EvaluatePoint(p *ProjectivePoint) ProjectivePoint { | |||
var t0, t1, t2 ExtensionFieldElement | |||
var q ProjectivePoint | |||
var K1, K2 = &phi.K1, &phi.K2 | |||
var px, pz = &p.X, &p.Z | |||
t0.Add(px, pz) // t0 = XQ + ZQ | |||
t1.Sub(px, pz) // t1 = XQ - ZQ | |||
t0.Mul(K1, &t0) // t2 = K1 * t0 | |||
t1.Mul(K2, &t1) // t1 = K2 * t1 | |||
t2.Add(&t0, &t1) // t2 = t0 + t1 | |||
t0.Sub(&t1, &t0) // t0 = t1 - t0 | |||
t2.Square(&t2) // t2 = t2 ^ 2 | |||
t0.Square(&t0) // t0 = t0 ^ 2 | |||
q.X.Mul(px, &t2) // XQ'= XQ * t2 | |||
q.Z.Mul(pz, &t0) // ZQ'= ZQ * t0 | |||
return q | |||
} | |||
// Given a four-torsion point p = x(PB) on the curve E_(A:C), construct the | |||
// four-isogeny phi : E_(A:C) -> E_(A:C)/<P_4> = E_(A':C'). | |||
// | |||
// Input: (XP_4: ZP_4), where P_4 has exact order 4 on E_A/C | |||
// Output: * Curve coordinates (A' + 2C', 4C') corresponding to E_A'/C' = A_E/C/<P4> | |||
// * Isogeny phi with constants in F_p^2 | |||
func (phi *isogeny4) GenerateCurve(p *ProjectivePoint) CurveCoefficientsEquiv { | |||
var coefEq CurveCoefficientsEquiv | |||
var xp4, zp4 = &p.X, &p.Z | |||
var K1, K2, K3 = &phi.K1, &phi.K2, &phi.K3 | |||
K2.Sub(xp4, zp4) | |||
K3.Add(xp4, zp4) | |||
K1.Square(zp4) | |||
K1.Add(K1, K1) | |||
coefEq.C.Square(K1) | |||
K1.Add(K1, K1) | |||
coefEq.A.Square(xp4) | |||
coefEq.A.Add(&coefEq.A, &coefEq.A) | |||
coefEq.A.Square(&coefEq.A) | |||
return coefEq | |||
} | |||
// Given a 4-isogeny phi and a point xP = x(P), compute x(Q), the x-coordinate | |||
// of the image Q = phi(P) of P under phi : E_(A:C) -> E_(A':C'). | |||
// | |||
// Input: Isogeny returned by GenerateCurve and point q=(Qx,Qz) from E0_A/C | |||
// Output: Corresponding point q from E1_A'/C', where E1 is 4-isogenous to E0 | |||
func (phi *isogeny4) EvaluatePoint(p *ProjectivePoint) ProjectivePoint { | |||
var t0, t1 ExtensionFieldElement | |||
var q = *p | |||
var xq, zq = &q.X, &q.Z | |||
var K1, K2, K3 = &phi.K1, &phi.K2, &phi.K3 | |||
t0.Add(xq, zq) | |||
t1.Sub(xq, zq) | |||
xq.Mul(&t0, K2) | |||
zq.Mul(&t1, K3) | |||
t0.Mul(&t0, &t1) | |||
t0.Mul(&t0, K1) | |||
t1.Add(xq, zq) | |||
zq.Sub(xq, zq) | |||
t1.Square(&t1) | |||
zq.Square(zq) | |||
xq.Add(&t0, &t1) | |||
t0.Sub(zq, &t0) | |||
xq.Mul(xq, &t1) | |||
zq.Mul(zq, &t0) | |||
return q | |||
} |
@@ -1,114 +0,0 @@ | |||
package p751toolbox | |||
import ( | |||
"testing" | |||
) | |||
func TestFourIsogenyVersusSage(t *testing.T) { | |||
var xR, xP4, resPhiXr, expPhiXr ProjectivePoint | |||
var phi = NewIsogeny4() | |||
// sage: p = 2^372 * 3^239 - 1; Fp = GF(p) | |||
// sage: R.<x> = Fp[] | |||
// sage: Fp2 = Fp.extension(x^2 + 1, 'i') | |||
// sage: i = Fp2.gen() | |||
// sage: E0Fp = EllipticCurve(Fp, [0,0,0,1,0]) | |||
// sage: E0Fp2 = EllipticCurve(Fp2, [0,0,0,1,0]) | |||
// sage: x_PA = 11 | |||
// sage: y_PA = -Fp(11^3 + 11).sqrt() | |||
// sage: x_PB = 6 | |||
// sage: y_PB = -Fp(6^3 + 6).sqrt() | |||
// sage: P_A = 3^239 * E0Fp((x_PA,y_PA)) | |||
// sage: P_B = 2^372 * E0Fp((x_PB,y_PB)) | |||
// sage: def tau(P): | |||
// ....: return E0Fp2( (-P.xy()[0], i*P.xy()[1])) | |||
// ....: | |||
// sage: m_B = 3*randint(0,3^238) | |||
// sage: m_A = 2*randint(0,2^371) | |||
// sage: R_A = E0Fp2(P_A) + m_A*tau(P_A) | |||
// sage: def y_recover(x, a): | |||
// ....: return (x**3 + a*x**2 + x).sqrt() | |||
// ....: | |||
// sage: first_4_torsion_point = E0Fp2(1, y_recover(Fp2(1),0)) | |||
// sage: sage_first_4_isogeny = E0Fp2.isogeny(first_4_torsion_point) | |||
// sage: a = Fp2(0) | |||
// sage: E1A = EllipticCurve(Fp2, [0,(2*(a+6))/(a-2),0,1,0]) | |||
// sage: sage_isomorphism = sage_first_4_isogeny.codomain().isomorphism_to(E1A) | |||
// sage: isogenized_R_A = sage_isomorphism(sage_first_4_isogeny(R_A)) | |||
// sage: P_4 = (2**(372-4))*isogenized_R_A | |||
// sage: P_4._order = 4 #otherwise falls back to generic group methods for order | |||
// sage: X4, Z4 = P_4.xy()[0], 1 | |||
// sage: phi4 = EllipticCurveIsogeny(E1A, P_4, None, 4) | |||
// sage: E2A_sage = phi4.codomain() # not in monty form | |||
// sage: Aprime, Cprime = 2*(2*X4^4 - Z4^4), Z4^4 | |||
// sage: E2A = EllipticCurve(Fp2, [0,Aprime/Cprime,0,1,0]) | |||
// sage: sage_iso = E2A_sage.isomorphism_to(E2A) | |||
// sage: isogenized2_R_A = sage_iso(phi4(isogenized_R_A)) | |||
xP4.FromAffine(&ExtensionFieldElement{ | |||
A: Fp751Element{0x2afd75a913f3d5e7, 0x2918fba06f88c9ab, 0xa4ac4dc7cb526f05, 0x2d19e9391a607300, 0x7a79e2b34091b54, 0x3ad809dcb42f1792, 0xd46179328bd6402a, 0x1afa73541e2c4f3f, 0xf602d73ace9bdbd8, 0xd77ac58f6bab7004, 0x4689d97f6793b3b3, 0x4f26b00e42b7}, | |||
B: Fp751Element{0x6cdf918dafdcb890, 0x666f273cc29cfae2, 0xad00fcd31ba618e2, 0x5fbcf62bef2f6a33, 0xf408bb88318e5098, 0x84ab97849453d175, 0x501bbfcdcfb8e1ac, 0xf2370098e6b5542c, 0xc7dc73f5f0f6bd32, 0xdd76dcd86729d1cf, 0xca22c905029996e4, 0x5cf4a9373de3}}) | |||
xR.FromAffine(&ExtensionFieldElement{ | |||
A: Fp751Element{0xff99e76f78da1e05, 0xdaa36bd2bb8d97c4, 0xb4328cee0a409daf, 0xc28b099980c5da3f, 0xf2d7cd15cfebb852, 0x1935103dded6cdef, 0xade81528de1429c3, 0x6775b0fa90a64319, 0x25f89817ee52485d, 0x706e2d00848e697, 0xc4958ec4216d65c0, 0xc519681417f}, | |||
B: Fp751Element{0x742fe7dde60e1fb9, 0x801a3c78466a456b, 0xa9f945b786f48c35, 0x20ce89e1b144348f, 0xf633970b7776217e, 0x4c6077a9b38976e5, 0x34a513fc766c7825, 0xacccba359b9cd65, 0xd0ca8383f0fd0125, 0x77350437196287a, 0x9fe1ad7706d4ea21, 0x4d26129ee42d}}) | |||
expPhiXr.FromAffine(&ExtensionFieldElement{ | |||
A: Fp751Element{0x111efd8bd0b7a01e, 0x6ab75a4f3789ca9b, 0x939dbe518564cac4, 0xf9eeaba1601d0434, 0x8d41f8ba6edac998, 0xfcd2557efe9aa170, 0xb3c3549c098b7844, 0x52874fef6f81127c, 0xb2b9ac82aa518bb3, 0xee70820230520a86, 0xd4012b7f5efb184a, 0x573e4536329b}, | |||
B: Fp751Element{0xa99952281e932902, 0x569a89a571f2c7b1, 0x6150143846ba3f6b, 0x11fd204441e91430, 0x7f469bd55c9b07b, 0xb72db8b9de35b161, 0x455a9a37a940512a, 0xb0cff7670abaf906, 0x18c785b7583375fe, 0x603ab9ca403c9148, 0xab54ba3a6e6c62c1, 0x2726d7d57c4f}}) | |||
phi.GenerateCurve(&xP4) | |||
resPhiXr = phi.EvaluatePoint(&xR) | |||
if !expPhiXr.VartimeEq(&resPhiXr) { | |||
t.Error("\nExpected\n", expPhiXr.ToAffine(), "\nfound\n", resPhiXr.ToAffine()) | |||
} | |||
} | |||
func TestThreeIsogenyVersusSage(t *testing.T) { | |||
var xR, xP3, resPhiXr, expPhiXr ProjectivePoint | |||
var phi = NewIsogeny3() | |||
// sage: %colors Linux | |||
// sage: p = 2^372 * 3^239 - 1; Fp = GF(p) | |||
// sage: R.<x> = Fp[] | |||
// sage: Fp2 = Fp.extension(x^2 + 1, 'i') | |||
// sage: i = Fp2.gen() | |||
// sage: E0Fp = EllipticCurve(Fp, [0,0,0,1,0]) | |||
// sage: E0Fp2 = EllipticCurve(Fp2, [0,0,0,1,0]) | |||
// sage: x_PA = 11 | |||
// sage: y_PA = -Fp(11^3 + 11).sqrt() | |||
// sage: x_PB = 6 | |||
// sage: y_PB = -Fp(6^3 + 6).sqrt() | |||
// sage: P_A = 3^239 * E0Fp((x_PA,y_PA)) | |||
// sage: P_B = 2^372 * E0Fp((x_PB,y_PB)) | |||
// sage: def tau(P): | |||
// ....: return E0Fp2( (-P.xy()[0], i*P.xy()[1])) | |||
// ....: | |||
// sage: m_B = 3*randint(0,3^238) | |||
// sage: R_B = E0Fp2(P_B) + m_B*tau(P_B) | |||
// sage: P_3 = (3^238)*R_B | |||
// sage: def three_isog(P_3, P): | |||
// ....: X3, Z3 = P_3.xy()[0], 1 | |||
// ....: XP, ZP = P.xy()[0], 1 | |||
// ....: x = (XP*(X3*XP - Z3*ZP)^2)/(ZP*(Z3*XP - X3*ZP)^2) | |||
// ....: A3, C3 = (Z3^4 + 9*X3^2*(2*Z3^2 - 3*X3^2)), 4*X3*Z3^3 | |||
// ....: cod = EllipticCurve(Fp2, [0,A3/C3,0,1,0]) | |||
// ....: return cod.lift_x(x) | |||
// ....: | |||
// sage: isogenized_R_B = three_isog(P_3, R_B) | |||
xR.FromAffine(&ExtensionFieldElement{ | |||
A: Fp751Element{0xbd0737ed5cc9a3d7, 0x45ae6d476517c101, 0x6f228e9e7364fdb2, 0xbba4871225b3dbd, 0x6299ccd2e5da1a07, 0x38488fe4af5f2d0e, 0xec23cae5a86e980c, 0x26c804ba3f1edffa, 0xfbbed81932df60e5, 0x7e00e9d182ae9187, 0xc7654abb66d05f4b, 0x262d0567237b}, | |||
B: Fp751Element{0x3a3b5b6ad0b2ac33, 0x246602b5179127d3, 0x502ae0e9ad65077d, 0x10a3a37237e1bf70, 0x4a1ab9294dd05610, 0xb0f3adac30fe1fa6, 0x341995267faf70cb, 0xa14dd94d39cf4ec1, 0xce4b7527d1bf5568, 0xe0410423ed45c7e4, 0x38011809b6425686, 0x28f52472ebed}}) | |||
xP3.FromAffine(&ExtensionFieldElement{ | |||
A: Fp751Element{0x7bb7a4a07b0788dc, 0xdc36a3f6607b21b0, 0x4750e18ee74cf2f0, 0x464e319d0b7ab806, 0xc25aa44c04f758ff, 0x392e8521a46e0a68, 0xfc4e76b63eff37df, 0x1f3566d892e67dd8, 0xf8d2eb0f73295e65, 0x457b13ebc470bccb, 0xfda1cc9efef5be33, 0x5dbf3d92cc02}, | |||
B: Fp751Element{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}) | |||
expPhiXr.FromAffine(&ExtensionFieldElement{ | |||
A: Fp751Element{0x286db7d75913c5b1, 0xcb2049ad50189220, 0xccee90ef765fa9f4, 0x65e52ce2730e7d88, 0xa6b6b553bd0d06e7, 0xb561ecec14591590, 0x17b7a66d8c64d959, 0x77778cecbe1461e, 0x9405c9c0c41a57ce, 0x8f6b4847e8ca7d3d, 0xf625eb987b366937, 0x421b3590e345}, | |||
B: Fp751Element{0x566b893803e7d8d6, 0xe8c71a04d527e696, 0x5a1d8f87bf5eb51, 0x42ae08ae098724f, 0x4ee3d7c7af40ca2e, 0xd9f9ab9067bb10a7, 0xecd53d69edd6328c, 0xa581e9202dea107d, 0x8bcdfb6c8ecf9257, 0xe7cbbc2e5cbcf2af, 0x5f031a8701f0e53e, 0x18312d93e3cb}}) | |||
phi.GenerateCurve(&xP3) | |||
resPhiXr = phi.EvaluatePoint(&xR) | |||
if !expPhiXr.VartimeEq(&resPhiXr) { | |||
t.Error("\nExpected\n", expPhiXr.ToAffine(), "\nfound\n", resPhiXr.ToAffine()) | |||
} | |||
} |
@@ -1,66 +0,0 @@ | |||
package p751toolbox | |||
// Tools used for testing and debugging | |||
import ( | |||
"bytes" | |||
"fmt" | |||
) | |||
func printHex(vector []byte) (out string) { | |||
var buffer bytes.Buffer | |||
buffer.WriteString("0x") | |||
len := len(vector) | |||
for i := len - 1; i >= 0; i-- { | |||
buffer.WriteString(fmt.Sprintf("%02x", vector[i])) | |||
} | |||
buffer.WriteString("\n") | |||
return buffer.String() | |||
} | |||
func (element Fp751Element) String() string { | |||
var out [94]byte | |||
element.toBytesFromMontgomeryForm(out[:]) | |||
return fmt.Sprintf("%s", printHex(out[:])) | |||
} | |||
func (primeElement PrimeFieldElement) String() string { | |||
return fmt.Sprintf("%s", primeElement.A.String()) | |||
} | |||
func (extElement ExtensionFieldElement) String() string { | |||
var out [188]byte | |||
extElement.ToBytes(out[:]) | |||
return fmt.Sprintf("A: %sB: %s", printHex(out[:94]), printHex(out[94:])) | |||
} | |||
func (point ProjectivePoint) String() string { | |||
return fmt.Sprintf("X:\n%sZ:\n%s", point.X.String(), point.Z.String()) | |||
} | |||
func (point Fp751Element) PrintHex() { | |||
fmt.Printf("\t") | |||
for i := 0; i < fp751NumWords/2; i++ { | |||
fmt.Printf("0x%0.16X, ", point[i]) | |||
} | |||
fmt.Printf("\n\t\t") | |||
for i := fp751NumWords / 2; i < fp751NumWords; i++ { | |||
fmt.Printf("0x%0.16X, ", point[i]) | |||
} | |||
fmt.Printf("\n") | |||
} | |||
func (extElement ExtensionFieldElement) PrintHex() { | |||
fmt.Printf("\t[r]: ") | |||
extElement.A.PrintHex() | |||
fmt.Printf("\t[u]: ") | |||
extElement.B.PrintHex() | |||
} | |||
func (point ProjectivePoint) PrintHex() { | |||
fmt.Printf("X:") | |||
point.X.PrintHex() | |||
fmt.Printf("Z:") | |||
point.X.PrintHex() | |||
fmt.Printf("\n") | |||
} |
@@ -2,14 +2,10 @@ package sidh | |||
import ( | |||
"errors" | |||
. "github.com/cloudflare/p751sidh/p751toolbox" | |||
. "github.com/cloudflare/p751sidh/internal/isogeny" | |||
"io" | |||
) | |||
// I keep it bool in order to be able to apply logical NOT | |||
type KeyVariant uint | |||
type PrimeFieldId uint | |||
// Id's correspond to bitlength of the prime field characteristic | |||
// Currently FP_751 is the only one supported by this implementation | |||
const ( | |||
@@ -43,9 +39,9 @@ type key struct { | |||
// Defines operations on public key | |||
type PublicKey struct { | |||
key | |||
affine_xP ExtensionFieldElement | |||
affine_xQ ExtensionFieldElement | |||
affine_xQmP ExtensionFieldElement | |||
affine_xP Fp2Element | |||
affine_xQ Fp2Element | |||
affine_xQmP Fp2Element | |||
} | |||
// Defines operations on private key | |||
@@ -91,9 +87,11 @@ func (pub *PublicKey) Import(input []byte) error { | |||
if len(input) != pub.Size() { | |||
return errors.New("sidh: input to short") | |||
} | |||
pub.affine_xP.FromBytes(input[0:pub.params.SharedSecretSize]) | |||
pub.affine_xQ.FromBytes(input[pub.params.SharedSecretSize : 2*pub.params.SharedSecretSize]) | |||
pub.affine_xQmP.FromBytes(input[2*pub.params.SharedSecretSize : 3*pub.params.SharedSecretSize]) | |||
op := CurveOperations{Params: pub.params} | |||
ssSz := pub.params.SharedSecretSize | |||
op.Fp2FromBytes(&pub.affine_xP, input[0:ssSz]) | |||
op.Fp2FromBytes(&pub.affine_xQ, input[ssSz:2*ssSz]) | |||
op.Fp2FromBytes(&pub.affine_xQmP, input[2*ssSz:3*ssSz]) | |||
return nil | |||
} | |||
@@ -101,9 +99,11 @@ func (pub *PublicKey) Import(input []byte) error { | |||
// returned byte string is filled with zeros. | |||
func (pub *PublicKey) Export() []byte { | |||
output := make([]byte, pub.params.PublicKeySize) | |||
pub.affine_xP.ToBytes(output[0:pub.params.SharedSecretSize]) | |||
pub.affine_xQ.ToBytes(output[pub.params.SharedSecretSize : 2*pub.params.SharedSecretSize]) | |||
pub.affine_xQmP.ToBytes(output[2*pub.params.SharedSecretSize : 3*pub.params.SharedSecretSize]) | |||
op := CurveOperations{Params: pub.params} | |||
ssSz := pub.params.SharedSecretSize | |||
op.Fp2ToBytes(output[0:ssSz], &pub.affine_xP) | |||
op.Fp2ToBytes(output[ssSz:2*ssSz], &pub.affine_xQ) | |||
op.Fp2ToBytes(output[2*ssSz:3*ssSz], &pub.affine_xQmP) | |||
return output | |||
} | |||
@@ -164,19 +164,14 @@ func (prv *PrivateKey) Generate(rand io.Reader) error { | |||
return err | |||
} | |||
// Generates public key corresponding to prv. KeyVariant of generated public key | |||
// is same as PrivateKey. Fails only if prv was wrongly initialized. | |||
// Constant time for properly initialzied PrivateKey | |||
func GeneratePublicKey(prv *PrivateKey) (*PublicKey, error) { | |||
if prv == nil { | |||
return nil, errors.New("sidh: invalid arguments") | |||
} | |||
// Generates public key. | |||
// | |||
// Constant time. | |||
func (prv *PrivateKey) GeneratePublicKey() (*PublicKey) { | |||
if (prv.keyVariant & KeyVariant_SIDH_A) == KeyVariant_SIDH_A { | |||
return publicKeyGenA(prv), nil | |||
} else { | |||
return publicKeyGenB(prv), nil | |||
return publicKeyGenA(prv) | |||
} | |||
return publicKeyGenB(prv) | |||
} | |||
// Computes a shared secret which is a j-invariant. Function requires that pub has | |||
@@ -1,36 +1,9 @@ | |||
package sidh | |||
import . "github.com/cloudflare/p751sidh/p751toolbox" | |||
type DomainParams struct { | |||
// P, Q and R=P-Q base points | |||
Affine_P, Affine_Q, Affine_R ExtensionFieldElement | |||
// Max size of secret key for x-torsion group | |||
SecretBitLen uint | |||
// MaskBytes | |||
MaskBytes []byte | |||
// Size of a compuatation strategy for x-torsion group | |||
IsogenyStrategy []uint32 | |||
} | |||
type SidhParams struct { | |||
Id PrimeFieldId | |||
// The secret key size, in bytes. | |||
SecretKeySize int | |||
// The public key size, in bytes. | |||
PublicKeySize int | |||
// The shared secret size, in bytes. | |||
SharedSecretSize uint | |||
// 2- and 3-torsion group parameter definitions | |||
A, B DomainParams | |||
// Sample rate to obtain a value in [0,3^238] | |||
SampleRate uint | |||
// Length of SIKE secret message. Must be one of {24,32,40}, | |||
// depending on size of prime field used (see [SIKE], 1.4 and 5.1) | |||
MsgLen uint | |||
// Length of SIKE ephemeral KEM key (see [SIKE], 1.4 and 5.1) | |||
KemSize uint | |||
} | |||
import ( | |||
p751 "github.com/cloudflare/p751sidh/p751" | |||
. "github.com/cloudflare/p751sidh/internal/isogeny" | |||
) | |||
// Keeps mapping: SIDH prime field ID to domain parameters | |||
var sidhParams = make(map[PrimeFieldId]SidhParams) | |||
@@ -47,29 +20,33 @@ func Params(id PrimeFieldId) *SidhParams { | |||
func init() { | |||
p751 := SidhParams{ | |||
Id: FP_751, | |||
SecretKeySize: P751_SecretKeySize, | |||
PublicKeySize: P751_PublicKeySize, | |||
SharedSecretSize: P751_SharedSecretSize, | |||
SecretKeySize: p751.P751_SecretKeySize, | |||
PublicKeySize: p751.P751_PublicKeySize, | |||
SharedSecretSize: p751.P751_SharedSecretSize, | |||
A: DomainParams{ | |||
Affine_P: P751_affine_PA, | |||
Affine_Q: P751_affine_QA, | |||
Affine_R: P751_affine_RA, | |||
SecretBitLen: P751_SecretBitLenA, | |||
MaskBytes: []byte{P751_MaskAliceByte1, P751_MaskAliceByte2, P751_MaskAliceByte3}, | |||
IsogenyStrategy: P751_AliceIsogenyStrategy[:], | |||
Affine_P: p751.P751_affine_PA, | |||
Affine_Q: p751.P751_affine_QA, | |||
Affine_R: p751.P751_affine_RA, | |||
SecretBitLen: p751.P751_SecretBitLenA, | |||
MaskBytes: []byte{p751.P751_MaskAliceByte1, p751.P751_MaskAliceByte2, p751.P751_MaskAliceByte3}, | |||
IsogenyStrategy: p751.P751_AliceIsogenyStrategy[:], | |||
}, | |||
B: DomainParams{ | |||
Affine_P: P751_affine_PB, | |||
Affine_Q: P751_affine_QB, | |||
Affine_R: P751_affine_RB, | |||
SecretBitLen: P751_SecretBitLenB, | |||
MaskBytes: []byte{P751_MaskBobByte}, | |||
IsogenyStrategy: P751_BobIsogenyStrategy[:], | |||
Affine_P: p751.P751_affine_PB, | |||
Affine_Q: p751.P751_affine_QB, | |||
Affine_R: p751.P751_affine_RB, | |||
SecretBitLen: p751.P751_SecretBitLenB, | |||
MaskBytes: []byte{p751.P751_MaskBobByte}, | |||
IsogenyStrategy: p751.P751_BobIsogenyStrategy[:], | |||
}, | |||
OneFp2: p751.P751_OneFp2, | |||
HalfFp2: p751.P751_HalfFp2, | |||
MsgLen: 32, | |||
// SIKEp751 provides 192 bit of classical security ([SIKE], 5.1) | |||
KemSize: 24, | |||
SampleRate: P751_SampleRate, | |||
SampleRate: p751.P751_SampleRate, | |||
Bytelen: p751.P751_Bytelen, | |||
Op: p751.FieldOperations(), | |||
} | |||
sidhParams[FP_751] = p751 | |||
@@ -4,9 +4,7 @@ import ( | |||
"errors" | |||
"io" | |||
// TODO: This is needed by ExtensionFieldElement struct, which itself | |||
// depends on implementation of p751. | |||
. "github.com/cloudflare/p751sidh/p751toolbox" | |||
. "github.com/cloudflare/p751sidh/internal/isogeny" | |||
) | |||
// ----------------------------------------------------------------------------- | |||
@@ -19,9 +17,10 @@ func traverseTreePublicKeyA(curve *ProjectiveCurveParameters, xR, phiP, phiQ, ph | |||
var points = make([]ProjectivePoint, 0, 8) | |||
var indices = make([]int, 0, 8) | |||
var i, sidx int | |||
var op = CurveOperations{Params: pub.params} | |||
cparam := curve.CalcCurveParamsEquiv4() | |||
phi := NewIsogeny4() | |||
cparam := op.CalcCurveParamsEquiv4(curve) | |||
phi := Newisogeny4(op.Params.Op) | |||
strat := pub.params.A.IsogenyStrategy | |||
stratSz := len(strat) | |||
@@ -32,7 +31,7 @@ func traverseTreePublicKeyA(curve *ProjectiveCurveParameters, xR, phiP, phiQ, ph | |||
k := strat[sidx] | |||
sidx++ | |||
xR.Pow2k(&cparam, xR, 2*k) | |||
op.Pow2k(xR, &cparam, 2*k) | |||
i += int(k) | |||
} | |||
@@ -57,9 +56,10 @@ func traverseTreeSharedKeyA(curve *ProjectiveCurveParameters, xR *ProjectivePoin | |||
var points = make([]ProjectivePoint, 0, 8) | |||
var indices = make([]int, 0, 8) | |||
var i, sidx int | |||
var op = CurveOperations{Params: pub.params} | |||
cparam := curve.CalcCurveParamsEquiv4() | |||
phi := NewIsogeny4() | |||
cparam := op.CalcCurveParamsEquiv4(curve) | |||
phi := Newisogeny4(op.Params.Op) | |||
strat := pub.params.A.IsogenyStrategy | |||
stratSz := len(strat) | |||
@@ -70,7 +70,7 @@ func traverseTreeSharedKeyA(curve *ProjectiveCurveParameters, xR *ProjectivePoin | |||
k := strat[sidx] | |||
sidx++ | |||
xR.Pow2k(&cparam, xR, 2*k) | |||
op.Pow2k(xR, &cparam, 2*k) | |||
i += int(k) | |||
} | |||
@@ -91,9 +91,10 @@ func traverseTreePublicKeyB(curve *ProjectiveCurveParameters, xR, phiP, phiQ, ph | |||
var points = make([]ProjectivePoint, 0, 8) | |||
var indices = make([]int, 0, 8) | |||
var i, sidx int | |||
var op = CurveOperations{Params: pub.params} | |||
cparam := curve.CalcCurveParamsEquiv3() | |||
phi := NewIsogeny3() | |||
cparam := op.CalcCurveParamsEquiv3(curve) | |||
phi := Newisogeny3(op.Params.Op) | |||
strat := pub.params.B.IsogenyStrategy | |||
stratSz := len(strat) | |||
@@ -104,7 +105,7 @@ func traverseTreePublicKeyB(curve *ProjectiveCurveParameters, xR, phiP, phiQ, ph | |||
k := strat[sidx] | |||
sidx++ | |||
xR.Pow3k(&cparam, xR, k) | |||
op.Pow3k(xR, &cparam, k) | |||
i += int(k) | |||
} | |||
@@ -129,9 +130,10 @@ func traverseTreeSharedKeyB(curve *ProjectiveCurveParameters, xR *ProjectivePoin | |||
var points = make([]ProjectivePoint, 0, 8) | |||
var indices = make([]int, 0, 8) | |||
var i, sidx int | |||
var op = CurveOperations{Params: pub.params} | |||
cparam := curve.CalcCurveParamsEquiv3() | |||
phi := NewIsogeny3() | |||
cparam := op.CalcCurveParamsEquiv3(curve) | |||
phi := Newisogeny3(op.Params.Op) | |||
strat := pub.params.B.IsogenyStrategy | |||
stratSz := len(strat) | |||
@@ -142,7 +144,7 @@ func traverseTreeSharedKeyB(curve *ProjectiveCurveParameters, xR *ProjectivePoin | |||
k := strat[sidx] | |||
sidx++ | |||
xR.Pow3k(&cparam, xR, k) | |||
op.Pow3k(xR, &cparam, k) | |||
i += int(k) | |||
} | |||
@@ -224,35 +226,30 @@ func (prv *PrivateKey) generatePrivateKeyB(rand io.Reader) error { | |||
func publicKeyGenA(prv *PrivateKey) (pub *PublicKey) { | |||
var xPA, xQA, xRA ProjectivePoint | |||
var xPB, xQB, xRB, xR ProjectivePoint | |||
var invZP, invZQ, invZR ExtensionFieldElement | |||
var invZP, invZQ, invZR Fp2Element | |||
var tmp ProjectiveCurveParameters | |||
var phi = NewIsogeny4() | |||
pub = NewPublicKey(prv.params.Id, KeyVariant_SIDH_A) | |||
var op = CurveOperations{Params: pub.params} | |||
var phi = Newisogeny4(op.Params.Op) | |||
// Load points for A | |||
xPA.FromAffine(&prv.params.A.Affine_P) | |||
xPA.Z.One() | |||
xQA.FromAffine(&prv.params.A.Affine_Q) | |||
xQA.Z.One() | |||
xRA.FromAffine(&prv.params.A.Affine_R) | |||
xRA.Z.One() | |||
xPA = ProjectivePoint{X: prv.params.A.Affine_P, Z: prv.params.OneFp2} | |||
xQA = ProjectivePoint{X: prv.params.A.Affine_Q, Z: prv.params.OneFp2} | |||
xRA = ProjectivePoint{X: prv.params.A.Affine_R, Z: prv.params.OneFp2} | |||
// Load points for B | |||
xRB.FromAffine(&prv.params.B.Affine_R) | |||
xRB.Z.One() | |||
xQB.FromAffine(&prv.params.B.Affine_Q) | |||
xQB.Z.One() | |||
xPB.FromAffine(&prv.params.B.Affine_P) | |||
xPB.Z.One() | |||
xRB = ProjectivePoint{X: prv.params.B.Affine_R, Z: prv.params.OneFp2} | |||
xQB = ProjectivePoint{X: prv.params.B.Affine_Q, Z: prv.params.OneFp2} | |||
xPB = ProjectivePoint{X: prv.params.B.Affine_P, Z: prv.params.OneFp2} | |||
// Find isogeny kernel | |||
tmp.A.Zero() | |||
tmp.C.One() | |||
xR = RightToLeftLadder(&tmp, &xPA, &xQA, &xRA, prv.params.A.SecretBitLen, prv.Scalar) | |||
tmp.C = pub.params.OneFp2 | |||
xR = op.ScalarMul3Pt(&tmp, &xPA, &xQA, &xRA, prv.params.A.SecretBitLen, prv.Scalar) | |||
// Reset params object and travers isogeny tree | |||
tmp.A.Zero() | |||
tmp.C.One() | |||
tmp.C = pub.params.OneFp2 | |||
tmp.A.Zeroize() | |||
traverseTreePublicKeyA(&tmp, &xR, &xPB, &xQB, &xRB, pub) | |||
// Secret isogeny | |||
@@ -260,11 +257,11 @@ func publicKeyGenA(prv *PrivateKey) (pub *PublicKey) { | |||
xPA = phi.EvaluatePoint(&xPB) | |||
xQA = phi.EvaluatePoint(&xQB) | |||
xRA = phi.EvaluatePoint(&xRB) | |||
ExtensionFieldBatch3Inv(&xPA.Z, &xQA.Z, &xRA.Z, &invZP, &invZQ, &invZR) | |||
op.Fp2Batch3Inv(&xPA.Z, &xQA.Z, &xRA.Z, &invZP, &invZQ, &invZR) | |||
pub.affine_xP.Mul(&xPA.X, &invZP) | |||
pub.affine_xQ.Mul(&xQA.X, &invZQ) | |||
pub.affine_xQmP.Mul(&xRA.X, &invZR) | |||
op.Params.Op.Mul(&pub.affine_xP, &xPA.X, &invZP) | |||
op.Params.Op.Mul(&pub.affine_xQ, &xQA.X, &invZQ) | |||
op.Params.Op.Mul(&pub.affine_xQmP, &xRA.X, &invZR) | |||
return | |||
} | |||
@@ -272,44 +269,39 @@ func publicKeyGenA(prv *PrivateKey) (pub *PublicKey) { | |||
func publicKeyGenB(prv *PrivateKey) (pub *PublicKey) { | |||
var xPB, xQB, xRB, xR ProjectivePoint | |||
var xPA, xQA, xRA ProjectivePoint | |||
var invZP, invZQ, invZR ExtensionFieldElement | |||
var invZP, invZQ, invZR Fp2Element | |||
var tmp ProjectiveCurveParameters | |||
var phi = NewIsogeny3() | |||
pub = NewPublicKey(prv.params.Id, prv.keyVariant) | |||
var op = CurveOperations{Params: pub.params} | |||
var phi = Newisogeny3(op.Params.Op) | |||
// Load points for B | |||
xRB.FromAffine(&prv.params.B.Affine_R) | |||
xRB.Z.One() | |||
xQB.FromAffine(&prv.params.B.Affine_Q) | |||
xQB.Z.One() | |||
xPB.FromAffine(&prv.params.B.Affine_P) | |||
xPB.Z.One() | |||
xRB = ProjectivePoint{X: prv.params.B.Affine_R, Z: prv.params.OneFp2} | |||
xQB = ProjectivePoint{X: prv.params.B.Affine_Q, Z: prv.params.OneFp2} | |||
xPB = ProjectivePoint{X: prv.params.B.Affine_P, Z: prv.params.OneFp2} | |||
// Load points for A | |||
xPA.FromAffine(&prv.params.A.Affine_P) | |||
xPA.Z.One() | |||
xQA.FromAffine(&prv.params.A.Affine_Q) | |||
xQA.Z.One() | |||
xRA.FromAffine(&prv.params.A.Affine_R) | |||
xRA.Z.One() | |||
tmp.A.Zero() | |||
tmp.C.One() | |||
xR = RightToLeftLadder(&tmp, &xPB, &xQB, &xRB, prv.params.B.SecretBitLen, prv.Scalar) | |||
tmp.A.Zero() | |||
tmp.C.One() | |||
xPA = ProjectivePoint{X: prv.params.A.Affine_P, Z: prv.params.OneFp2} | |||
xQA = ProjectivePoint{X: prv.params.A.Affine_Q, Z: prv.params.OneFp2} | |||
xRA = ProjectivePoint{X: prv.params.A.Affine_R, Z: prv.params.OneFp2} | |||
tmp.C = pub.params.OneFp2 | |||
xR = op.ScalarMul3Pt(&tmp, &xPB, &xQB, &xRB, prv.params.B.SecretBitLen, prv.Scalar) | |||
tmp.C = pub.params.OneFp2 | |||
tmp.A.Zeroize() | |||
traverseTreePublicKeyB(&tmp, &xR, &xPA, &xQA, &xRA, pub) | |||
phi.GenerateCurve(&xR) | |||
xPB = phi.EvaluatePoint(&xPA) | |||
xQB = phi.EvaluatePoint(&xQA) | |||
xRB = phi.EvaluatePoint(&xRA) | |||
ExtensionFieldBatch3Inv(&xPB.Z, &xQB.Z, &xRB.Z, &invZP, &invZQ, &invZR) | |||
op.Fp2Batch3Inv(&xPB.Z, &xQB.Z, &xRB.Z, &invZP, &invZQ, &invZR) | |||
pub.affine_xP.Mul(&xPB.X, &invZP) | |||
pub.affine_xQ.Mul(&xQB.X, &invZQ) | |||
pub.affine_xQmP.Mul(&xRB.X, &invZR) | |||
op.Params.Op.Mul(&pub.affine_xP, &xPB.X, &invZP) | |||
op.Params.Op.Mul(&pub.affine_xQ, &xQB.X, &invZQ) | |||
op.Params.Op.Mul(&pub.affine_xQmP, &xRB.X, &invZR) | |||
return | |||
} | |||
@@ -323,25 +315,26 @@ func deriveSecretA(prv *PrivateKey, pub *PublicKey) []byte { | |||
var cparam ProjectiveCurveParameters | |||
var xP, xQ, xQmP ProjectivePoint | |||
var xR ProjectivePoint | |||
var phi = NewIsogeny4() | |||
var op = CurveOperations{Params: prv.params} | |||
var phi = Newisogeny4(op.Params.Op) | |||
// Recover curve coefficients | |||
cparam.RecoverCoordinateA(&pub.affine_xP, &pub.affine_xQ, &pub.affine_xQmP) | |||
cparam.C.One() | |||
cparam.C = pub.params.OneFp2 | |||
op.RecoverCoordinateA(&cparam, &pub.affine_xP, &pub.affine_xQ, &pub.affine_xQmP) | |||
// Find kernel of the morphism | |||
xP.FromAffine(&pub.affine_xP) | |||
xQ.FromAffine(&pub.affine_xQ) | |||
xQmP.FromAffine(&pub.affine_xQmP) | |||
xR = RightToLeftLadder(&cparam, &xP, &xQ, &xQmP, pub.params.A.SecretBitLen, prv.Scalar) | |||
xP = ProjectivePoint{X: pub.affine_xP, Z: pub.params.OneFp2} | |||
xQ = ProjectivePoint{X: pub.affine_xQ, Z: pub.params.OneFp2} | |||
xQmP = ProjectivePoint{X: pub.affine_xQmP, Z: pub.params.OneFp2} | |||
xR = op.ScalarMul3Pt(&cparam, &xP, &xQ, &xQmP, pub.params.A.SecretBitLen, prv.Scalar) | |||
// Traverse isogeny tree | |||
traverseTreeSharedKeyA(&cparam, &xR, pub) | |||
// Calculate j-invariant on isogeneus curve | |||
c := phi.GenerateCurve(&xR) | |||
cparam.RecoverCurveCoefficients4(&c) | |||
cparam.Jinvariant(sharedSecret) | |||
op.RecoverCurveCoefficients4(&cparam, &c) | |||
op.Jinvariant(&cparam, sharedSecret) | |||
return sharedSecret | |||
} | |||
@@ -351,24 +344,25 @@ func deriveSecretB(prv *PrivateKey, pub *PublicKey) []byte { | |||
var xP, xQ, xQmP ProjectivePoint | |||
var xR ProjectivePoint | |||
var cparam ProjectiveCurveParameters | |||
var phi = NewIsogeny3() | |||
var op = CurveOperations{Params: prv.params} | |||
var phi = Newisogeny3(op.Params.Op) | |||
// Recover curve coefficients | |||
cparam.RecoverCoordinateA(&pub.affine_xP, &pub.affine_xQ, &pub.affine_xQmP) | |||
cparam.C.One() | |||
cparam.C = pub.params.OneFp2 | |||
op.RecoverCoordinateA(&cparam, &pub.affine_xP, &pub.affine_xQ, &pub.affine_xQmP) | |||
// Find kernel of the morphism | |||
xP.FromAffine(&pub.affine_xP) | |||
xQ.FromAffine(&pub.affine_xQ) | |||
xQmP.FromAffine(&pub.affine_xQmP) | |||
xR = RightToLeftLadder(&cparam, &xP, &xQ, &xQmP, pub.params.B.SecretBitLen, prv.Scalar) | |||
xP = ProjectivePoint{X: pub.affine_xP, Z: pub.params.OneFp2} | |||
xQ = ProjectivePoint{X: pub.affine_xQ, Z: pub.params.OneFp2} | |||
xQmP = ProjectivePoint{X: pub.affine_xQmP, Z: pub.params.OneFp2} | |||
xR = op.ScalarMul3Pt(&cparam, &xP, &xQ, &xQmP, pub.params.B.SecretBitLen, prv.Scalar) | |||
// Traverse isogeny tree | |||
traverseTreeSharedKeyB(&cparam, &xR, pub) | |||
// Calculate j-invariant on isogeneus curve | |||
c := phi.GenerateCurve(&xR) | |||
cparam.RecoverCurveCoefficients3(&c) | |||
cparam.Jinvariant(sharedSecret) | |||
op.RecoverCurveCoefficients3(&cparam, &c) | |||
op.Jinvariant(&cparam, sharedSecret) | |||
return sharedSecret | |||
} |
@@ -5,6 +5,8 @@ import ( | |||
"crypto/rand" | |||
"encoding/hex" | |||
"testing" | |||
. "github.com/cloudflare/p751sidh/internal/isogeny" | |||
) | |||
const ( | |||
@@ -93,10 +95,8 @@ func testKeygen(s *SidhParams, t *testing.T) { | |||
expPubA := convToPub(PkA, KeyVariant_SIDH_A) | |||
expPubB := convToPub(PkB, KeyVariant_SIDH_B) | |||
pubA, err := GeneratePublicKey(alicePrivate) | |||
checkErr(t, err, "public key A generation failed") | |||
pubB, err := GeneratePublicKey(bobPrivate) | |||
checkErr(t, err, "public key B generation failed") | |||
pubA := alicePrivate.GeneratePublicKey() | |||
pubB := bobPrivate.GeneratePublicKey() | |||
if !bytes.Equal(pubA.Export(), expPubA.Export()) { | |||
t.Fatalf("unexpected value of public key A") | |||
@@ -119,11 +119,8 @@ func testRoundtrip(s *SidhParams, t *testing.T) { | |||
checkErr(t, err, "key generation failed") | |||
// Generate public keys | |||
pubA, err := GeneratePublicKey(prvA) | |||
checkErr(t, err, "") | |||
pubB, err := GeneratePublicKey(prvB) | |||
checkErr(t, err, "") | |||
pubA := prvA.GeneratePublicKey() | |||
pubB := prvB.GeneratePublicKey() | |||
// Derive shared secret | |||
s1, err := DeriveSecret(prvB, pubA) | |||
@@ -317,7 +314,7 @@ func BenchmarkAliceKeyGenPub(b *testing.B) { | |||
prv := NewPrivateKey(params.Id, KeyVariant_SIDH_A) | |||
prv.Generate(rand.Reader) | |||
for n := 0; n < b.N; n++ { | |||
GeneratePublicKey(prv) | |||
prv.GeneratePublicKey() | |||
} | |||
} | |||
@@ -325,7 +322,7 @@ func BenchmarkBobKeyGenPub(b *testing.B) { | |||
prv := NewPrivateKey(params.Id, KeyVariant_SIDH_B) | |||
prv.Generate(rand.Reader) | |||
for n := 0; n < b.N; n++ { | |||
GeneratePublicKey(prv) | |||
prv.GeneratePublicKey() | |||
} | |||
} | |||
@@ -71,7 +71,7 @@ func Encrypt(rng io.Reader, pub *PublicKey, ptext []byte) ([]byte, error) { | |||
return nil, err | |||
} | |||
pkA, _ := GeneratePublicKey(skA) // Never fails | |||
pkA := skA.GeneratePublicKey() | |||
return encrypt(skA, pkA, pub, ptext) | |||
} | |||
@@ -152,7 +152,7 @@ func Encapsulate(rng io.Reader, pub *PublicKey) (ctext []byte, secret []byte, er | |||
return nil, nil, err | |||
} | |||
pkA, _ := GeneratePublicKey(skA) // Never fails | |||
pkA := skA.GeneratePublicKey() | |||
ctext, err = encrypt(skA, pkA, pub, ptext) | |||
if err != nil { | |||
return nil, nil, err | |||
@@ -197,7 +197,7 @@ func Decapsulate(prv *PrivateKey, pub *PublicKey, ctext []byte) ([]byte, error) | |||
skA.Import(r) | |||
// Never fails | |||
pkA, _ := GeneratePublicKey(skA) | |||
pkA := skA.GeneratePublicKey() | |||
c0 := pkA.Export() | |||
h = cshake.NewCShake256(nil, H) | |||
@@ -77,7 +77,7 @@ func TestPKEKeyGeneration(t *testing.T) { | |||
sk := NewPrivateKey(params.Id, KeyVariant_SIKE) | |||
err = sk.Generate(rand.Reader) | |||
checkErr(t, err, "PEK key generation") | |||
pk, _ := GeneratePublicKey(sk) | |||
pk := sk.GeneratePublicKey() | |||
// Try to encrypt | |||
ct, err := Encrypt(rand.Reader, pk, msg[:]) | |||
@@ -99,7 +99,7 @@ func TestNegativePKE(t *testing.T) { | |||
err = sk.Generate(rand.Reader) | |||
checkErr(t, err, "key generation") | |||
pk, _ := GeneratePublicKey(sk) | |||
pk := sk.GeneratePublicKey() | |||
ct, err := Encrypt(rand.Reader, pk, msg[:39]) | |||
if err == nil { | |||
@@ -152,7 +152,7 @@ func TestKEMKeyGeneration(t *testing.T) { | |||
// Generate key | |||
sk := NewPrivateKey(params.Id, KeyVariant_SIKE) | |||
checkErr(t, sk.Generate(rand.Reader), "error: key generation") | |||
pk, _ := GeneratePublicKey(sk) | |||
pk := sk.GeneratePublicKey() | |||
// calculated shared secret | |||
ct, ss_e, err := Encapsulate(rand.Reader, pk) | |||
@@ -168,7 +168,7 @@ func TestKEMKeyGeneration(t *testing.T) { | |||
func TestNegativeKEM(t *testing.T) { | |||
sk := NewPrivateKey(params.Id, KeyVariant_SIKE) | |||
checkErr(t, sk.Generate(rand.Reader), "error: key generation") | |||
pk, _ := GeneratePublicKey(sk) | |||
pk := sk.GeneratePublicKey() | |||
ct, ss_e, err := Encapsulate(rand.Reader, pk) | |||
checkErr(t, err, "pre-requisite for a test failed") | |||
@@ -201,7 +201,7 @@ func TestNegativeKEM(t *testing.T) { | |||
func TestNegativeKEMSameWrongResult(t *testing.T) { | |||
sk := NewPrivateKey(params.Id, KeyVariant_SIKE) | |||
checkErr(t, sk.Generate(rand.Reader), "error: key generation") | |||
pk, _ := GeneratePublicKey(sk) | |||
pk := sk.GeneratePublicKey() | |||
ct, encSs, err := Encapsulate(rand.Reader, pk) | |||
checkErr(t, err, "pre-requisite for a test failed") | |||
@@ -261,7 +261,7 @@ func testKeygen(pk, sk []byte) bool { | |||
} | |||
// Generate public key | |||
pubKey, _ := GeneratePublicKey(prvKey) | |||
pubKey := prvKey.GeneratePublicKey() | |||
return bytes.Equal(pubKey.Export(), pk) | |||
} | |||