# Conflicts:
#	sm9/bn256/gfp12_b6.go
This commit is contained in:
emmansun 2023-07-21 20:43:00 +08:00
commit e769cefbd8
23 changed files with 2526 additions and 486 deletions

View File

@ -2298,14 +2298,12 @@ internalMulBMI2:
MULXQ t1, mul0, acc2
ADDQ mul0, acc1
ADCQ $0, acc2
MULXQ t2, mul0, acc3
ADDQ mul0, acc2
ADCQ $0, acc3
ADCQ mul0, acc2
MULXQ t3, mul0, acc4
ADDQ mul0, acc3
ADCQ mul0, acc3
ADCQ $0, acc4
MOVQ acc5, mul1

View File

@ -4,23 +4,23 @@ package bn256
func lineFunctionAdd(r, p, rOut *twistPoint, q *curvePoint, r2, a, b, c *gfP2) {
// See the mixed addition algorithm from "Faster Computation of the
// Tate Pairing", http://arxiv.org/pdf/0904.0854v3.pdf
B := (&gfP2{}).MulNC(&p.x, &r.t) // B = Xp * Zr^2
B := (&gfP2{}).Mul(&p.x, &r.t) // B = Xp * Zr^2
D := (&gfP2{}).Add(&p.y, &r.z) // D = Yp + Zr
D.Square(D).Sub(D, r2).Sub(D, &r.t).Mul(D, &r.t) // D = ((Yp + Zr)^2 - Zr^2 - Yp^2)*Zr^2 = 2Yp*Zr^3
H := (&gfP2{}).Sub(B, &r.x) // H = Xp * Zr^2 - Xr
I := (&gfP2{}).SquareNC(H) // I = (Xp * Zr^2 - Xr)^2 = Xp^2*Zr^4 + Xr^2 - 2Xr*Xp*Zr^2
I := (&gfP2{}).Square(H) // I = (Xp * Zr^2 - Xr)^2 = Xp^2*Zr^4 + Xr^2 - 2Xr*Xp*Zr^2
E := (&gfP2{}).Double(I) // E = 2*(Xp * Zr^2 - Xr)^2
E.Double(E) // E = 4*(Xp * Zr^2 - Xr)^2
J := (&gfP2{}).MulNC(H, E) // J = 4*(Xp * Zr^2 - Xr)^3
J := (&gfP2{}).Mul(H, E) // J = 4*(Xp * Zr^2 - Xr)^3
L1 := (&gfP2{}).Sub(D, &r.y) // L1 = 2Yp*Zr^3 - Yr
L1.Sub(L1, &r.y) // L1 = 2Yp*Zr^3 - 2*Yr
V := (&gfP2{}).MulNC(&r.x, E) // V = 4 * Xr * (Xp * Zr^2 - Xr)^2
V := (&gfP2{}).Mul(&r.x, E) // V = 4 * Xr * (Xp * Zr^2 - Xr)^2
rOut.x.Square(L1).Sub(&rOut.x, J).Sub(&rOut.x, V).Sub(&rOut.x, V) // rOut.x = L1^2 - J - 2V
@ -28,11 +28,11 @@ func lineFunctionAdd(r, p, rOut *twistPoint, q *curvePoint, r2, a, b, c *gfP2) {
t := (&gfP2{}).Sub(V, &rOut.x) // t = V - rOut.x
t.Mul(t, L1) // t = L1*(V-rOut.x)
t2 := (&gfP2{}).MulNC(&r.y, J)
t2 := (&gfP2{}).Mul(&r.y, J)
t2.Double(t2) // t2 = 2Yr * J
rOut.y.Sub(t, t2) // rOut.y = L1*(V-rOut.x) - 2Yr*J
rOut.t.SquareNC(&rOut.z)
rOut.t.Square(&rOut.z)
// t = (Yp + rOut.Z)^2 - Yp^2 - rOut.Z^2 = 2Yp*rOut.Z
t.Add(&p.y, &rOut.z).Square(t).Sub(t, r2).Sub(t, &rOut.t)
@ -51,9 +51,9 @@ func lineFunctionAdd(r, p, rOut *twistPoint, q *curvePoint, r2, a, b, c *gfP2) {
func lineFunctionDouble(r, rOut *twistPoint, q *curvePoint, a, b, c *gfP2) {
// See the doubling algorithm for a=0 from "Faster Computation of the
// Tate Pairing", http://arxiv.org/pdf/0904.0854v3.pdf
A := (&gfP2{}).SquareNC(&r.x)
B := (&gfP2{}).SquareNC(&r.y)
C := (&gfP2{}).SquareNC(B) // C = Yr ^ 4
A := (&gfP2{}).Square(&r.x)
B := (&gfP2{}).Square(&r.y)
C := (&gfP2{}).Square(B) // C = Yr ^ 4
D := (&gfP2{}).Add(&r.x, B)
D.Square(D).Sub(D, A).Sub(D, C).Double(D)
@ -61,7 +61,7 @@ func lineFunctionDouble(r, rOut *twistPoint, q *curvePoint, a, b, c *gfP2) {
E := (&gfP2{}).Double(A) //
E.Add(E, A) // E = 3 * Xr ^ 2
G := (&gfP2{}).SquareNC(E) // G = 9 * Xr^4
G := (&gfP2{}).Square(E) // G = 9 * Xr^4
rOut.x.Sub(G, D).Sub(&rOut.x, D)
@ -72,7 +72,7 @@ func lineFunctionDouble(r, rOut *twistPoint, q *curvePoint, a, b, c *gfP2) {
t.Double(t).Double(t) // t = 8 * Yr ^ 4
rOut.y.Sub(&rOut.y, t)
rOut.t.SquareNC(&rOut.z)
rOut.t.Square(&rOut.z)
t.Mul(E, &r.t).Double(t) // t = 2(E * Tr)
b.Neg(t) // b = -2(E * Tr)
@ -127,7 +127,7 @@ func miller(q *twistPoint, p *curvePoint) *gfP12 {
r := &twistPoint{}
r.Set(aAffine)
r2 := (&gfP2{}).SquareNC(&aAffine.y)
r2 := (&gfP2{}).Square(&aAffine.y)
a, b, c := &gfP2{}, &gfP2{}, &gfP2{}
newR := &twistPoint{}
@ -218,10 +218,9 @@ func finalExponentiation(in *gfP12) *gfP12 {
y0.MulNC(fp, fp2).Mul(y0, fp3) // y0 = (t1^p) * (t1^(p^2)) * (t1^(p^3))
// reuse fp, fp2, fp3 local variables
// [gfP12ExpU] is most time consuming operation
fu := fp.gfP12ExpU(t1)
fu2 := fp2.gfP12ExpU(fu)
fu3 := fp3.gfP12ExpU(fu2)
fu := fp.Cyclo6PowToU(t1)
fu2 := fp2.Cyclo6PowToU(fu)
fu3 := fp3.Cyclo6PowToU(fu2)
fu2p := (&gfP12{}).Frobenius(fu2)
fu3p := (&gfP12{}).Frobenius(fu3)
@ -237,14 +236,14 @@ func finalExponentiation(in *gfP12) *gfP12 {
y6.Conjugate(y6) // y6 = 1 / (t1^(u^3) * (t1^(u^3))^p)
// https://eprint.iacr.org/2008/490.pdf
t0 := (&gfP12{}).SpecialSquareNC(y6)
t0 := (&gfP12{}).Cyclo6SquareNC(y6)
t0.Mul(t0, y4).Mul(t0, y5)
t1.Mul(y3, y5).Mul(t1, t0)
t0.Mul(t0, y2)
t1.SpecialSquare(t1).Mul(t1, t0).SpecialSquare(t1)
t1.Cyclo6Square(t1).Mul(t1, t0).Cyclo6Square(t1)
t0.Mul(t1, y1)
t1.Mul(t1, y0)
t0.SpecialSquare(t0).Mul(t0, t1)
t0.Cyclo6Square(t0).Mul(t0, t1)
return t0
}

View File

@ -50,7 +50,7 @@ func millerB6(q *twistPoint, p *curvePoint) *gfP12b6 {
r := &twistPoint{}
r.Set(aAffine)
r2 := (&gfP2{}).SquareNC(&aAffine.y)
r2 := (&gfP2{}).Square(&aAffine.y)
a, b, c := &gfP2{}, &gfP2{}, &gfP2{}
newR := &twistPoint{}
@ -155,10 +155,9 @@ func finalExponentiationB6(in *gfP12b6) *gfP12b6 {
y0.MulNC(fp, fp2).Mul(y0, fp3)
// reuse fp, fp2, fp3 local variables
// [gfP12ExpU] is most time consuming operation
fu := fp.gfP12ExpU(t1)
fu2 := fp2.gfP12ExpU(fu)
fu3 := fp3.gfP12ExpU(fu2)
fu := fp.Cyclo6PowToU(t1)
fu2 := fp2.Cyclo6PowToU(fu)
fu3 := fp3.Cyclo6PowToU(fu2)
y3 := (&gfP12b6{}).Frobenius(fu)
fu2p := (&gfP12b6{}).Frobenius(fu2)
@ -174,14 +173,14 @@ func finalExponentiationB6(in *gfP12b6) *gfP12b6 {
y6 := (&gfP12b6{}).MulNC(fu3, fu3p)
y6.Conjugate(y6)
t0 := (&gfP12b6{}).SpecialSquareNC(y6)
t0 := (&gfP12b6{}).Cyclo6SquareNC(y6)
t0.Mul(t0, y4).Mul(t0, y5)
t1.Mul(y3, y5).Mul(t1, t0)
t0.Mul(t0, y2)
t1.SpecialSquare(t1).Mul(t1, t0).SpecialSquare(t1)
t1.Cyclo6Square(t1).Mul(t1, t0).Cyclo6Square(t1)
t0.Mul(t1, y1)
t1.Mul(t1, y0)
t0.SpecialSquare(t0).Mul(t0, t1)
t0.Cyclo6Square(t0).Mul(t0, t1)
return t0
}

View File

@ -12,6 +12,7 @@ type curvePoint struct {
}
var curveB = newGFp(5)
var threeCurveB = newGFp(3 * 5)
// curveGen is the generator of G₁.
var curveGen = &curvePoint{
@ -82,125 +83,6 @@ func (c *curvePoint) IsInfinity() bool {
return c.z.Equal(zero) == 1
}
func (c *curvePoint) Add(a, b *curvePoint) {
if a.IsInfinity() {
c.Set(b)
return
}
if b.IsInfinity() {
c.Set(a)
return
}
// See http://hyperelliptic.org/EFD/g1p/auto-code/shortw/jacobian-0/addition/add-2007-bl.op3
// Normalize the points by replacing a = [x1:y1:z1] and b = [x2:y2:z2]
// by [u1:s1:z1·z2] and [u2:s2:z1·z2]
// where u1 = x1·z2², s1 = y1·z2³ and u1 = x2·z1², s2 = y2·z1³
z12, z22 := &gfP{}, &gfP{}
gfpSqr(z12, &a.z, 1)
gfpSqr(z22, &b.z, 1)
u1, u2 := &gfP{}, &gfP{}
gfpMul(u1, &a.x, z22)
gfpMul(u2, &b.x, z12)
t, s1 := &gfP{}, &gfP{}
gfpMul(t, &b.z, z22)
gfpMul(s1, &a.y, t)
s2 := &gfP{}
gfpMul(t, &a.z, z12)
gfpMul(s2, &b.y, t)
// Compute x = (2h)²(s²-u1-u2)
// where s = (s2-s1)/(u2-u1) is the slope of the line through
// (u1,s1) and (u2,s2). The extra factor 2h = 2(u2-u1) comes from the value of z below.
// This is also:
// 4(s2-s1)² - 4h²(u1+u2) = 4(s2-s1)² - 4h³ - 4h²(2u1)
// = r² - j - 2v
// with the notations below.
h := &gfP{}
gfpSub(h, u2, u1)
gfpDouble(t, h)
// i = 4h²
i := &gfP{}
gfpSqr(i, t, 1)
// j = 4h³
j := &gfP{}
gfpMul(j, h, i)
gfpSub(t, s2, s1)
if h.Equal(zero) == 1 && t.Equal(one) == 1 {
c.Double(a)
return
}
r := &gfP{}
gfpDouble(r, t)
v := &gfP{}
gfpMul(v, u1, i)
// t4 = 4(s2-s1)²
t4, t6 := &gfP{}, &gfP{}
gfpSqr(t4, r, 1)
gfpDouble(t, v)
gfpSub(t6, t4, j)
gfpSub(&c.x, t6, t)
// Set y = -(2h)³(s1 + s*(x/4h²-u1))
// This is also
// y = - 2·s1·j - (s2-s1)(2x - 2i·u1) = r(v-x) - 2·s1·j
gfpSub(t, v, &c.x) // t7
gfpMul(t4, s1, j) // t8
gfpDouble(t6, t4) // t9
gfpMul(t4, r, t) // t10
gfpSub(&c.y, t4, t6)
// Set z = 2(u2-u1)·z1·z2 = 2h·z1·z2
gfpAdd(t, &a.z, &b.z) // t11
gfpSqr(t4, t, 1) // t12
gfpSub(t, t4, z12) // t13
gfpSub(t4, t, z22) // t14
gfpMul(&c.z, t4, h)
}
func (c *curvePoint) Double(a *curvePoint) {
// See http://hyperelliptic.org/EFD/g1p/auto-code/shortw/jacobian-0/doubling/dbl-2009-l.op3
A, B, C := &gfP{}, &gfP{}, &gfP{}
gfpSqr(A, &a.x, 1)
gfpSqr(B, &a.y, 1)
gfpSqr(C, B, 1)
t, t2 := &gfP{}, &gfP{}
gfpAdd(t, &a.x, B)
gfpSqr(t2, t, 1)
gfpSub(t, t2, A)
gfpSub(t2, t, C)
d, e, f := &gfP{}, &gfP{}, &gfP{}
gfpAdd(d, t2, t2)
gfpDouble(t, A)
gfpAdd(e, t, A)
gfpSqr(f, e, 1)
gfpDouble(t, d)
gfpSub(&c.x, f, t)
gfpMul(&c.z, &a.y, &a.z)
gfpDouble(&c.z, &c.z)
gfpDouble(t, C)
gfpDouble(t2, t)
gfpDouble(t, t2)
gfpSub(&c.y, d, &c.x)
gfpMul(t2, e, &c.y)
gfpSub(&c.y, t2, t)
}
func (c *curvePoint) Mul(a *curvePoint, scalar *big.Int) {
sum, t := &curvePoint{}, &curvePoint{}
sum.SetInfinity()
@ -217,7 +99,10 @@ func (c *curvePoint) Mul(a *curvePoint, scalar *big.Int) {
c.Set(sum)
}
func (c *curvePoint) MakeAffine() {
// MakeAffine reverses the Jacobian transform.
// the Jacobian coordinates are (x1, y1, z1)
// where x = x1/z1² and y = y1/z1³.
func (c *curvePoint) AffineFromJacobian() {
if c.z.Equal(one) == 1 {
return
} else if c.z.Equal(zero) == 1 {
@ -231,11 +116,11 @@ func (c *curvePoint) MakeAffine() {
zInv.Invert(&c.z)
t, zInv2 := &gfP{}, &gfP{}
gfpMul(t, &c.y, zInv)
gfpMul(t, &c.y, zInv) // t = y/z
gfpSqr(zInv2, zInv, 1)
gfpMul(&c.x, &c.x, zInv2)
gfpMul(&c.y, t, zInv2)
gfpMul(&c.x, &c.x, zInv2) // x = x / z^2
gfpMul(&c.y, t, zInv2) // y = y / z^3
c.z.Set(one)
c.t.Set(one)
@ -265,3 +150,123 @@ func (table *curvePointTable) Select(p *curvePoint, n uint8) {
curvePointMovCond(p, f, p, cond)
}
}
// Equal compare e and other
func (e *curvePoint) Equal(other *curvePoint) bool {
return e.x.Equal(&other.x) == 1 &&
e.y.Equal(&other.y) == 1 &&
e.z.Equal(&other.z) == 1 &&
e.t.Equal(&other.t) == 1
}
// Below methods are POC yet, the line add/double functions are still based on
// Jacobian coordination.
func (c *curvePoint) Add(p1, p2 *curvePoint) {
// Complete addition formula for a = 0 from "Complete addition formulas for
// prime order elliptic curves" (https://eprint.iacr.org/2015/1060), §3.2.
// Algorithm 7: Complete, projective point addition for prime order j-invariant 0 short Weierstrass curves.
t0, t1, t2, t3, t4 := new(gfP), new(gfP), new(gfP), new(gfP), new(gfP)
x3, y3, z3 := new(gfP), new(gfP), new(gfP)
gfpMul(t0, &p1.x, &p2.x) // t0 := X1X2
gfpMul(t1, &p1.y, &p2.y) // t1 := Y1Y2
gfpMul(t2, &p1.z, &p2.z) // t2 := Z1Z2
gfpAdd(t3, &p1.x, &p1.y) // t3 := X1 + Y1
gfpAdd(t4, &p2.x, &p2.y) // t4 := X2 + Y2
gfpMul(t3, t3, t4) // t3 := t3 * t4 = (X1 + Y1) * (X2 + Y2)
gfpAdd(t4, t0, t1) // t4 := t0 + t1
gfpSub(t3, t3, t4) // t3 := t3 - t4 = X1Y2 + X2Y1
gfpAdd(t4, &p1.y, &p1.z) // t4 := Y1 + Z1
gfpAdd(x3, &p2.y, &p2.z) // X3 := Y2 + Z2
gfpMul(t4, t4, x3) // t4 := t4 * X3 = (Y1 + Z1)(Y2 + Z2)
gfpAdd(x3, t1, t2) // X3 := t1 + t2
gfpSub(t4, t4, x3) // t4 := t4 - X3 = Y1Z2 + Y2Z1
gfpAdd(x3, &p1.x, &p1.z) // X3 := X1 + Z1
gfpAdd(y3, &p2.x, &p2.z) // Y3 := X2 + Z2
gfpMul(x3, x3, y3) // X3 := X3 * Y3
gfpAdd(y3, t0, t2) // Y3 := t0 + t2
gfpSub(y3, x3, y3) // Y3 := X3 - Y3 = X1Z2 + X2Z1
gfpTriple(t0, t0) // t0 := t0 + t0 + t0 = 3X1X2
gfpMul(t2, threeCurveB, t2) // t2 := 3b * t2 = 3bZ1Z2
gfpAdd(z3, t1, t2) // Z3 := t1 + t2 = Y1Y2 + 3bZ1Z2
gfpSub(t1, t1, t2) // t1 := t1 - t2 = Y1Y2 - 3bZ1Z2
gfpMul(y3, threeCurveB, y3) // Y3 = 3b * Y3 = 3b(X1Z2 + X2Z1)
gfpMul(x3, t4, y3) // X3 := t4 * Y3 = 3b(X1Z2 + X2Z1)(Y1Z2 + Y2Z1)
gfpMul(t2, t3, t1) // t2 := t3 * t1 = (X1Y2 + X2Y1)(Y1Y2 - 3bZ1Z2)
gfpSub(x3, t2, x3) // X3 := t2 - X3 = (X1Y2 + X2Y1)(Y1Y2 - 3bZ1Z2) - 3b(Y1Z2 + Y2Z1)(X1Z2 + X2Z1)
gfpMul(y3, y3, t0) // Y3 := Y3 * t0 = 9bX1X2(X1Z2 + X2Z1)
gfpMul(t1, t1, z3) // t1 := t1 * Z3 = (Y1Y2 + 3bZ1Z2)(Y1Y2 - 3bZ1Z2)
gfpAdd(y3, t1, y3) // Y3 := t1 + Y3 = (Y1Y2 + 3bZ1Z2)(Y1Y2 - 3bZ1Z2) + 9bX1X2(X1Z2 + X2Z1)
gfpMul(t0, t0, t3) // t0 := t0 * t3 = 3X1X2(X1Y2 + X2Y1)
gfpMul(z3, z3, t4) // Z3 := Z3 * t4 = (Y1Z2 + Y2Z1)(Y1Y2 + 3bZ1Z2)
gfpAdd(z3, z3, t0) // Z3 := Z3 + t0 = (Y1Z2 + Y2Z1)(Y1Y2 + 3bZ1Z2) + 3X1X2(X1Y2 + X2Y1)
c.x.Set(x3)
c.y.Set(y3)
c.z.Set(z3)
}
func (c *curvePoint) AddComplete(p1, p2 *curvePoint) {
c.Add(p1, p2)
}
func (c *curvePoint) Double(p *curvePoint) {
// Complete addition formula for a = 0 from "Complete addition formulas for
// prime order elliptic curves" (https://eprint.iacr.org/2015/1060), §3.2.
// Algorithm 9: Exception-free point doubling for prime order j-invariant 0 short Weierstrass curves.
t0, t1, t2 := new(gfP), new(gfP), new(gfP)
x3, y3, z3 := new(gfP), new(gfP), new(gfP)
gfpSqr(t0, &p.y, 1) // t0 := Y^2
gfpDouble(z3, t0) // Z3 := t0 + t0
gfpDouble(z3, z3) // Z3 := Z3 + Z3
gfpDouble(z3, z3) // Z3 := Z3 + Z3
gfpMul(t1, &p.y, &p.z) // t1 := YZ
gfpSqr(t2, &p.z, 1) // t0 := Z^2
gfpMul(t2, threeCurveB, t2) // t2 := 3b * t2 = 3bZ^2
gfpMul(x3, t2, z3) // X3 := t2 * Z3
gfpAdd(y3, t0, t2) // Y3 := t0 + t2
gfpMul(z3, t1, z3) // Z3 := t1 * Z3
gfpTriple(t2, t2) // t2 := t2 + t2 + t2
gfpSub(t0, t0, t2) // t0 := t0 - t2
gfpMul(y3, t0, y3) // t0 := t0 * Y3
gfpAdd(y3, x3, y3) // Y3 := X3 + Y3
gfpMul(t1, &p.x, &p.y) // t1 := XY
gfpMul(x3, t0, t1) // X3 := t0 * t1
gfpDouble(x3, x3) // X3 := X3 + X3
c.x.Set(x3)
c.y.Set(y3)
c.z.Set(z3)
}
func (c *curvePoint) DoubleComplete(p *curvePoint) {
c.Double(p)
}
// MakeAffine reverses the Projective transform.
// A = 1/Z1
// X3 = A*X1
// Y3 = A*Y1
// Z3 = 1
func (c *curvePoint) MakeAffine() {
// TODO: do we need to change it to constant-time implementation?
if c.z.Equal(one) == 1 {
return
} else if c.z.Equal(zero) == 1 {
c.x.Set(zero)
c.y.Set(one)
c.t.Set(zero)
return
}
zInv := &gfP{}
zInv.Invert(&c.z)
gfpMul(&c.x, &c.x, zInv)
gfpMul(&c.y, &c.y, zInv)
c.z.Set(one)
c.t.Set(one)
}
func (c *curvePoint) AffineFromProjective() {
c.MakeAffine()
}

View File

@ -368,10 +368,7 @@ func (e *G1) Equal(other *G1) bool {
if e.p == nil && other.p == nil {
return true
}
return e.p.x.Equal(&other.p.x) == 1 &&
e.p.y.Equal(&other.p.y) == 1 &&
e.p.z.Equal(&other.p.z) == 1 &&
e.p.t.Equal(&other.p.t) == 1
return e.p.Equal(other.p)
}
// IsOnCurve returns true if e is on the curve.

View File

@ -3,6 +3,7 @@ package bn256
import (
"bytes"
"crypto/rand"
"fmt"
"io"
"math/big"
"testing"
@ -24,6 +25,24 @@ func TestG1AddNeg(t *testing.T) {
}
}
func TestG1AddSame(t *testing.T) {
g1, g2 := &G1{}, &G1{}
g1.Add(Gen1, Gen1)
g2.Double(Gen1)
if !g1.Equal(g2) {
t.Fail()
}
}
func TestCurvePointDouble(t *testing.T) {
p := &curvePoint{}
p.Double(p)
if !p.IsInfinity() {
t.Fail()
}
}
type g1BaseMultTest struct {
k string
}
@ -157,35 +176,84 @@ func TestG1BaseMult(t *testing.T) {
}
}
func TestG1ScaleMult(t *testing.T) {
k, e, err := RandomG1(rand.Reader)
func TestG1ScalarMult(t *testing.T) {
checkScalar := func(t *testing.T, scalar []byte) {
p1, err := (&G1{}).ScalarBaseMult(scalar)
fatalIfErr(t, err)
p2, err := (&G1{}).ScalarMult(Gen1, scalar)
fatalIfErr(t, err)
p1.p.MakeAffine()
p2.p.MakeAffine()
if !p1.Equal(p2) {
t.Error("[k]G != ScalarBaseMult(k)")
}
d := new(big.Int).SetBytes(scalar)
d.Sub(Order, d)
d.Mod(d, Order)
g1, err := (&G1{}).ScalarBaseMult(d.FillBytes(make([]byte, len(scalar))))
fatalIfErr(t, err)
g1.Add(g1, p1)
g1.p.MakeAffine()
if !g1.p.IsInfinity() {
t.Error("[N - k]G + [k]G != ∞")
}
}
byteLen := len(Order.Bytes())
bitLen := Order.BitLen()
t.Run("0", func(t *testing.T) { checkScalar(t, make([]byte, byteLen)) })
t.Run("1", func(t *testing.T) {
checkScalar(t, big.NewInt(1).FillBytes(make([]byte, byteLen)))
})
t.Run("N-6", func(t *testing.T) {
checkScalar(t, new(big.Int).Sub(Order, big.NewInt(6)).Bytes())
})
t.Run("N-1", func(t *testing.T) {
checkScalar(t, new(big.Int).Sub(Order, big.NewInt(1)).Bytes())
})
t.Run("N", func(t *testing.T) { checkScalar(t, Order.Bytes()) })
t.Run("N+1", func(t *testing.T) {
checkScalar(t, new(big.Int).Add(Order, big.NewInt(1)).Bytes())
})
t.Run("N+22", func(t *testing.T) {
checkScalar(t, new(big.Int).Add(Order, big.NewInt(22)).Bytes())
})
t.Run("all1s", func(t *testing.T) {
s := new(big.Int).Lsh(big.NewInt(1), uint(bitLen))
s.Sub(s, big.NewInt(1))
checkScalar(t, s.Bytes())
})
if testing.Short() {
return
}
for i := 0; i < bitLen; i++ {
t.Run(fmt.Sprintf("1<<%d", i), func(t *testing.T) {
s := new(big.Int).Lsh(big.NewInt(1), uint(i))
checkScalar(t, s.FillBytes(make([]byte, byteLen)))
})
}
for i := 0; i <= 64; i++ {
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
checkScalar(t, big.NewInt(int64(i)).FillBytes(make([]byte, byteLen)))
})
}
// Test N-64...N+64 since they risk overlapping with precomputed table values
// in the final additions.
for i := int64(-64); i <= 64; i++ {
t.Run(fmt.Sprintf("N%+d", i), func(t *testing.T) {
checkScalar(t, new(big.Int).Add(Order, big.NewInt(i)).Bytes())
})
}
}
func fatalIfErr(t *testing.T, err error) {
t.Helper()
if err != nil {
t.Fatal(err)
}
e.p.MakeAffine()
e2, e3 := &G1{}, &G1{}
if e2.p == nil {
e2.p = &curvePoint{}
}
e2.p.Mul(curveGen, k)
e2.p.MakeAffine()
if !e.Equal(e2) {
t.Errorf("not same")
}
_, err = e3.ScalarMult(Gen1, NormalizeScalar(k.Bytes()))
if err != nil {
t.Fatal(err)
}
e3.p.MakeAffine()
if !e.Equal(e3) {
t.Errorf("not same")
}
}
func TestFuzz(t *testing.T) {
@ -542,3 +610,131 @@ func BenchmarkMarshalUnmarshal(b *testing.B) {
})
})
}
func TestCurvePointAddComplete(t *testing.T) {
t.Run("normal case", func(t *testing.T) {
p1 := &curvePoint{}
curvePointDouble(p1, curveGen)
p1.AffineFromJacobian()
p2 := &curvePoint{}
p2.AddComplete(p1, curveGen)
p2.AffineFromProjective()
p3 := &curvePoint{}
curvePointAdd(p3, curveGen, p1)
p3.AffineFromJacobian()
if !p2.Equal(p3) {
t.Errorf("Got %v, expected %v", p2, p3)
}
})
t.Run("exception case: double", func(t *testing.T) {
p2 := &curvePoint{}
p2.AddComplete(curveGen, curveGen)
p2.AffineFromProjective()
p3 := &curvePoint{}
curvePointDouble(p3, curveGen)
p3.AffineFromJacobian()
if !p2.Equal(p3) {
t.Errorf("Got %v, expected %v", p2, p3)
}
})
t.Run("exception case: neg", func(t *testing.T) {
p1 := &curvePoint{}
p1.Neg(curveGen)
p2 := &curvePoint{}
p2.AddComplete(curveGen, p1)
p2.AffineFromProjective()
if !p2.IsInfinity() {
t.Fatal("should be infinity")
}
})
t.Run("exception case: IsInfinity", func(t *testing.T) {
p1 := &curvePoint{}
p1.SetInfinity()
p2 := &curvePoint{}
p2.AddComplete(curveGen, p1)
p2.AffineFromProjective()
if !p2.Equal(curveGen) {
t.Fatal("should be curveGen")
}
p2.AddComplete(p1, curveGen)
p2.AffineFromProjective()
if !p2.Equal(curveGen) {
t.Fatal("should be curveGen")
}
p2.AddComplete(p1, p1)
p2.AffineFromProjective()
if !p2.IsInfinity() {
t.Fatal("should be infinity")
}
})
}
func BenchmarkAddPoint(b *testing.B) {
p1 := &curvePoint{}
curvePointDouble(p1, curveGen)
p1.AffineFromJacobian()
p2 := &curvePoint{}
b.Run("Add complete", func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
p2.AddComplete(curveGen, p1)
}
})
b.Run("Add traditional", func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
curvePointAdd(p2, curveGen, p1)
}
})
}
func TestCurvePointDobuleComplete(t *testing.T) {
t.Run("normal case", func(t *testing.T) {
p2 := &curvePoint{}
p2.DoubleComplete(curveGen)
p2.AffineFromProjective()
p3 := &curvePoint{}
curvePointDouble(p3, curveGen)
p3.AffineFromJacobian()
if !p2.Equal(p3) {
t.Errorf("Got %v, expected %v", p2, p3)
}
})
t.Run("exception case: IsInfinity", func(t *testing.T) {
p1 := &curvePoint{}
p1.SetInfinity()
p2 := &curvePoint{}
p2.DoubleComplete(p1)
p2.AffineFromProjective()
if !p2.IsInfinity() {
t.Fatal("should be infinity")
}
})
}
func BenchmarkDoublePoint(b *testing.B) {
p2 := &curvePoint{}
b.Run("Double complete", func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
p2.DoubleComplete(curveGen)
}
})
b.Run("Double traditional", func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
curvePointDouble(p2, curveGen)
}
})
}

View File

@ -226,13 +226,19 @@ func (e *gfP12) SquareNC(a *gfP12) *gfP12 {
return e
}
// Special squaring for use on elements in T_6(fp2) (after the
// easy part of the final exponentiation. Used in the hard part
// of the final exponentiation. Function uses formulas in
// Granger/Scott (PKC2010).
func (e *gfP12) SpecialSquare(a *gfP12) *gfP12 {
// Cyclo6Square is used in final exponentiation after easy part(a ^ ((p^2 + 1)(p^6-1))).
// Note that after the easy part of the final exponentiation,
// the resulting element lies in cyclotomic subgroup.
// "New software speed records for cryptographic pairings"
// Section 3.3, Final exponentiation
// https://cryptojedi.org/papers/dclxvi-20100714.pdf
// The fomula reference:
// Granger/Scott (PKC2010).
// Section 3.2
// https://eprint.iacr.org/2009/565.pdf
func (e *gfP12) Cyclo6Square(a *gfP12) *gfP12 {
tmp := &gfP12{}
tmp.SpecialSquareNC(a)
tmp.Cyclo6SquareNC(a)
gfp12Copy(e, tmp)
return e
}
@ -241,7 +247,7 @@ func (e *gfP12) SpecialSquare(a *gfP12) *gfP12 {
// easy part of the final exponentiation. Used in the hard part
// of the final exponentiation. Function uses formulas in
// Granger/Scott (PKC2010).
func (e *gfP12) SpecialSquares(a *gfP12, n int) *gfP12 {
func (e *gfP12) Cyclo6Squares(a *gfP12, n int) *gfP12 {
// Square first round
in := &gfP12{}
tx, ty, tz := &gfP4{}, &gfP4{}, &gfP4{}
@ -306,7 +312,7 @@ func (e *gfP12) SpecialSquares(a *gfP12, n int) *gfP12 {
}
// Special Square without value copy, will use e directly, so e can't be same as a.
func (e *gfP12) SpecialSquareNC(a *gfP12) *gfP12 {
func (e *gfP12) Cyclo6SquareNC(a *gfP12) *gfP12 {
tx, ty, tz := &gfP4{}, &gfP4{}, &gfP4{}
v0 := &e.x

View File

@ -201,20 +201,26 @@ func (e *gfP12b6) SquareNC(a *gfP12b6) *gfP12b6 {
return e
}
// Special squaring for use on elements in T_6(fp2) (after the
// easy part of the final exponentiation. Used in the hard part
// of the final exponentiation. Function uses formulas in
// Granger/Scott (PKC2010).
func (e *gfP12b6) SpecialSquare(a *gfP12b6) *gfP12b6 {
// Cyclo6Square is used in final exponentiation after easy part(a ^ ((p^2 + 1)(p^6-1))).
// Note that after the easy part of the final exponentiation,
// the resulting element lies in cyclotomic subgroup.
// "New software speed records for cryptographic pairings"
// Section 3.3, Final exponentiation
// https://cryptojedi.org/papers/dclxvi-20100714.pdf
// The fomula reference:
// Granger/Scott (PKC2010).
// Section 3.2
// https://eprint.iacr.org/2009/565.pdf
func (e *gfP12b6) Cyclo6Square(a *gfP12b6) *gfP12b6 {
tmp := &gfP12b6{}
tmp.SpecialSquareNC(a)
tmp.Cyclo6SquareNC(a)
e.x.Set(&tmp.x)
e.y.Set(&tmp.y)
return e
}
// Special Square without value copy, will use e directly, so e can't be same as a.
func (e *gfP12b6) SpecialSquareNC(a *gfP12b6) *gfP12b6 {
func (e *gfP12b6) Cyclo6SquareNC(a *gfP12b6) *gfP12b6 {
f02 := &e.y.x
f01 := &e.y.y
f00 := &e.y.z
@ -232,28 +238,28 @@ func (e *gfP12b6) SpecialSquareNC(a *gfP12b6) *gfP12b6 {
t02.Set(t10)
t10.Set(f00)
f00.Double(t00)
f00.Add(t00, t00)
t00.Add(f00, t00)
f00.Double(t01)
f00.Add(t01, t01)
t01.Add(f00, t01)
f00.Double(t02)
f00.Add(t02, t02)
t02.Add(f00, t02)
f00.Double(t10)
f00.Add(t10, t10)
t10.Add(f00, t10)
f00.Double(t11)
f00.Add(t11, t11)
t11.Add(f00, t11)
f00.Double(t12)
f00.Add(t12, t12)
t12.Add(f00, t12)
f00.Double(&a.y.z)
f00.Add(&a.y.z, &a.y.z)
f00.Neg(f00)
f01.Double(&a.y.y)
f01.Add(&a.y.y, &a.y.y)
f01.Neg(f01)
f02.Double(&a.y.x)
f02.Add(&a.y.x, &a.y.x)
f02.Neg(f02)
f10.Double(&a.x.z)
f11.Double(&a.x.y)
f12.Double(&a.x.x)
f10.Add(&a.x.z, &a.x.z)
f11.Add(&a.x.y, &a.x.y)
f12.Add(&a.x.x, &a.x.x)
f00.Add(f00, t00)
f01.Add(f01, t01)
@ -265,7 +271,7 @@ func (e *gfP12b6) SpecialSquareNC(a *gfP12b6) *gfP12b6 {
return e
}
func (e *gfP12b6) SpecialSquares(a *gfP12b6, n int) *gfP12b6 {
func (e *gfP12b6) Cyclo6Squares(a *gfP12b6, n int) *gfP12b6 {
// Square first round
in := &gfP12b6{}
f02 := &in.y.x
@ -284,28 +290,28 @@ func (e *gfP12b6) SpecialSquares(a *gfP12b6, n int) *gfP12b6 {
t02.Set(t10)
t10.Set(f00)
f00.Double(t00)
f00.Add(t00, t00)
t00.Add(f00, t00)
f00.Double(t01)
f00.Add(t01, t01)
t01.Add(f00, t01)
f00.Double(t02)
f00.Add(t02, t02)
t02.Add(f00, t02)
f00.Double(t10)
f00.Add(t10, t10)
t10.Add(f00, t10)
f00.Double(t11)
f00.Add(t11, t11)
t11.Add(f00, t11)
f00.Double(t12)
f00.Add(t12, t12)
t12.Add(f00, t12)
f00.Double(&a.y.z)
f00.Add(&a.y.z, &a.y.z)
f00.Neg(f00)
f01.Double(&a.y.y)
f01.Add(&a.y.y, &a.y.y)
f01.Neg(f01)
f02.Double(&a.y.x)
f02.Add(&a.y.x, &a.y.x)
f02.Neg(f02)
f10.Double(&a.x.z)
f11.Double(&a.x.y)
f12.Double(&a.x.x)
f10.Add(&a.x.z, &a.x.z)
f11.Add(&a.x.y, &a.x.y)
f12.Add(&a.x.x, &a.x.x)
f00.Add(f00, t00)
f01.Add(f01, t01)
@ -333,28 +339,28 @@ func (e *gfP12b6) SpecialSquares(a *gfP12b6, n int) *gfP12b6 {
t02.Set(t10)
t10.Set(f00)
f00.Double(t00)
f00.Add(t00, t00)
t00.Add(f00, t00)
f00.Double(t01)
f00.Add(t01, t01)
t01.Add(f00, t01)
f00.Double(t02)
f00.Add(t02, t02)
t02.Add(f00, t02)
f00.Double(t10)
f00.Add(t10, t10)
t10.Add(f00, t10)
f00.Double(t11)
f00.Add(t11, t11)
t11.Add(f00, t11)
f00.Double(t12)
f00.Add(t12, t12)
t12.Add(f00, t12)
f00.Double(&in.y.z)
f00.Add(&in.y.z, &in.y.z)
f00.Neg(f00)
f01.Double(&in.y.y)
f01.Add(&in.y.y, &in.y.y)
f01.Neg(f01)
f02.Double(&in.y.x)
f02.Add(&in.y.x, &in.y.x)
f02.Neg(f02)
f10.Double(&in.x.z)
f11.Double(&in.x.y)
f12.Double(&in.x.x)
f10.Add(&in.x.z, &in.x.z)
f11.Add(&in.x.y, &in.x.y)
f12.Add(&in.x.x, &in.x.x)
f00.Add(f00, t00)
f01.Add(f01, t01)
@ -374,12 +380,12 @@ func (e *gfP12b6) SpecialSquares(a *gfP12b6, n int) *gfP12b6 {
}
func gfP4Square(retX, retY, x, y *gfP2) {
retX.SquareUNC(x)
retY.SquareNC(y)
retX.SquareU(x)
retY.Square(y)
retY.Add(retX, retY)
retX.MulNC(x, y)
retX.Double(retX)
retX.Mul(x, y)
retX.Add(retX, retX)
}
func (c *gfP12b6) Exp(a *gfP12b6, power *big.Int) *gfP12b6 {

View File

@ -330,7 +330,7 @@ func TestGfP12b6SpecialSquare(t *testing.T) {
got := &gfP12b6{}
expected := &gfP12b6{}
got.SpecialSquare(t1)
got.Cyclo6Square(t1)
expected.Square(t1)
if *got != *expected {
t.Errorf("not same got=%v, expected=%v", got, expected)

View File

@ -1,7 +1,7 @@
package bn256
// Use special square
func (e *gfP12) gfP12ExpU(x *gfP12) *gfP12 {
func (e *gfP12) Cyclo6PowToU(x *gfP12) *gfP12 {
// The sequence of 10 multiplications and 61 squarings is derived from the
// following addition chain generated with github.com/mmcloughlin/addchain v0.4.0.
//
@ -21,23 +21,23 @@ func (e *gfP12) gfP12ExpU(x *gfP12) *gfP12 {
var t2 = new(gfP12)
var t3 = new(gfP12)
t2.SpecialSquareNC(x)
t1.SpecialSquareNC(t2)
t2.Cyclo6SquareNC(x)
t1.Cyclo6SquareNC(t2)
z.MulNC(x, t1)
t0.MulNC(t1, z)
t2.Mul(t2, t0)
t3.MulNC(x, t2)
t3.SpecialSquares(t3, 40)
t3.Cyclo6Squares(t3, 40)
t3.Mul(t2, t3)
t3.SpecialSquares(t3, 7)
t3.Cyclo6Squares(t3, 7)
t2.Mul(t2, t3)
t1.Mul(t1, t2)
t1.SpecialSquares(t1, 4)
t1.Cyclo6Squares(t1, 4)
t0.Mul(t0, t1)
t0.SpecialSquare(t0)
t0.Cyclo6Square(t0)
t0.Mul(x, t0)
t0.SpecialSquares(t0, 6)
t0.Cyclo6Squares(t0, 6)
z.Mul(z, t0)
z.SpecialSquare(z)
z.Cyclo6Square(z)
return e
}

View File

@ -35,7 +35,7 @@ func Test_gfP12Square(t *testing.T) {
}
}
func TestSpecialSquare(t *testing.T) {
func TestCyclo6Square(t *testing.T) {
in := &gfP12{
testdataP4,
testdataP4,
@ -51,9 +51,18 @@ func TestSpecialSquare(t *testing.T) {
t2 := inv.FrobeniusP2(t1) // reuse inv
t1.Mul(t1, t2) // t1 = in ^ ((p^6 - 1) * (p^2 + 1)), the first two parts of the exponentiation
one := (&gfP12{}).SetOne()
t3 := (&gfP12{}).FrobeniusP2(t1)
t4 := (&gfP12{}).FrobeniusP2(t3)
t5 := (&gfP12{}).Invert(t3)
t5.Mul(t4, t5).Mul(t1, t5)
if *t5 != *one {
t.Errorf("t1 should be in Cyclotomic Subgroup")
}
got := &gfP12{}
expected := &gfP12{}
got.SpecialSquare(t1)
got.Cyclo6Square(t1)
expected.Square(t1)
if *got != *expected {
t.Errorf("not same got=%v, expected=%v", got, expected)
@ -74,7 +83,7 @@ func BenchmarkGfP12Square(b *testing.B) {
}
}
func BenchmarkGfP12SpecialSquare(b *testing.B) {
func BenchmarkGfP12Cyclo6Square(b *testing.B) {
in := &gfP12{
testdataP4,
testdataP4,
@ -93,7 +102,7 @@ func BenchmarkGfP12SpecialSquare(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
x2.SpecialSquare(t1)
x2.Cyclo6Square(t1)
}
}
@ -116,7 +125,7 @@ func BenchmarkGfP12SpecialSqures(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
got.SpecialSquares(in, 61)
got.Cyclo6Squares(in, 61)
}
}
@ -433,13 +442,22 @@ func BenchmarkGfP12ExpU(b *testing.B) {
testdataP4,
testdataP4,
}
// This is the p^6-Frobenius
t1 := (&gfP12{}).FrobeniusP6(x)
inv := (&gfP12{}).Invert(x)
t1.Mul(t1, inv)
t2 := inv.FrobeniusP2(t1) // reuse inv
t1.Mul(t1, t2) // t1 = in ^ ((p^6 - 1) * (p^2 + 1)), the first two parts of the exponentiation
got := &gfP12{}
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
got.gfP12ExpU(x)
got.gfP12ExpU(x)
got.gfP12ExpU(x)
got.Cyclo6PowToU(t1)
got.Cyclo6PowToU(t1)
got.Cyclo6PowToU(t1)
}
}

View File

@ -1,7 +1,7 @@
package bn256
// Use special square
func (e *gfP12b6) gfP12ExpU(x *gfP12b6) *gfP12b6 {
func (e *gfP12b6) Cyclo6PowToU(x *gfP12b6) *gfP12b6 {
// The sequence of 10 multiplications and 61 squarings is derived from the
// following addition chain generated with github.com/mmcloughlin/addchain v0.4.0.
//
@ -21,23 +21,23 @@ func (e *gfP12b6) gfP12ExpU(x *gfP12b6) *gfP12b6 {
var t2 = new(gfP12b6)
var t3 = new(gfP12b6)
t2.SpecialSquareNC(x)
t1.SpecialSquareNC(t2)
t2.Cyclo6SquareNC(x)
t1.Cyclo6SquareNC(t2)
z.MulNC(x, t1)
t0.MulNC(t1, z)
t2.Mul(t2, t0)
t3.MulNC(x, t2)
t3.SpecialSquares(t3, 40)
t3.Cyclo6Squares(t3, 40)
t3.Mul(t2, t3)
t3.SpecialSquares(t3, 7)
t3.Cyclo6Squares(t3, 7)
t2.Mul(t2, t3)
t1.Mul(t1, t2)
t1.SpecialSquares(t1, 4)
t1.Cyclo6Squares(t1, 4)
t0.Mul(t0, t1)
t0.SpecialSquare(t0)
t0.Cyclo6Square(t0)
t0.Mul(x, t0)
t0.SpecialSquares(t0, 6)
t0.Cyclo6Squares(t0, 6)
z.Mul(z, t0)
z.SpecialSquare(z)
z.Cyclo6Square(z)
return e
}

View File

@ -116,37 +116,7 @@ func (e *gfP2) Triple(a *gfP2) *gfP2 {
// c0 = a0*b0 - 2a1*b1
// c1 = (a0 + a1)(b0 + b1) - a0*b0 - a1*b1 = a0*b1 + a1*b0
func (e *gfP2) Mul(a, b *gfP2) *gfP2 {
tmp := &gfP2{}
tmp.MulNC(a, b)
gfp2Copy(e, tmp)
return e
}
// Mul without value copy, will use e directly, so e can't be same as a and b.
func (e *gfP2) MulNC(a, b *gfP2) *gfP2 {
tx := &e.x
ty := &e.y
v0, v1 := &gfP{}, &gfP{}
gfpMul(v0, &a.y, &b.y)
gfpMul(v1, &a.x, &b.x)
gfpAdd(tx, &a.x, &a.y)
gfpAdd(ty, &b.x, &b.y)
gfpMul(tx, tx, ty)
gfpSub(tx, tx, v0)
gfpSub(tx, tx, v1)
gfpSub(ty, v0, v1)
gfpSub(ty, ty, v1)
return e
}
func (e *gfP2) MulU(a, b *gfP2) *gfP2 {
tmp := &gfP2{}
tmp.MulUNC(a, b)
gfp2Copy(e, tmp)
gfp2Mul(e, a, b)
return e
}
@ -155,26 +125,8 @@ func (e *gfP2) MulU(a, b *gfP2) *gfP2 {
// (a0+a1*u)(b0+b1*u)*u=c0+c1*u, where
// c1 = (a0*b0 - 2a1*b1)u
// c0 = -2 * ((a0 + a1)(b0 + b1) - a0*b0 - a1*b1) = -2 * (a0*b1 + a1*b0)
func (e *gfP2) MulUNC(a, b *gfP2) *gfP2 {
tx := &e.x
ty := &e.y
v0, v1 := &gfP{}, &gfP{}
gfpMul(v0, &a.y, &b.y)
gfpMul(v1, &a.x, &b.x)
gfpAdd(tx, &a.x, &a.y)
gfpAdd(ty, &b.x, &b.y)
gfpMul(ty, tx, ty)
gfpSub(ty, ty, v0)
gfpSub(ty, ty, v1)
gfpDouble(ty, ty)
gfpNeg(ty, ty)
gfpSub(tx, v0, v1)
gfpSub(tx, tx, v1)
func (e *gfP2) MulU(a, b *gfP2) *gfP2 {
gfp2MulU(e, a, b)
return e
}
@ -195,57 +147,14 @@ func (e *gfP2) MulU1(a *gfP2) *gfP2 {
func (e *gfP2) Square(a *gfP2) *gfP2 {
// Complex squaring algorithm:
// (xu+y)² = y^2-2*x^2 + 2*u*x*y
tmp := &gfP2{}
tmp.SquareNC(a)
gfp2Copy(e, tmp)
return e
}
// Square without value copy, will use e directly, so e can't be same as a.
func (e *gfP2) SquareNC(a *gfP2) *gfP2 {
// Complex squaring algorithm:
// (xu+y)² = y^2-2*x^2 + 2*u*x*y
tx := &e.x
ty := &e.y
gfpAdd(ty, &a.x, &a.y)
gfpDouble(tx, &a.x)
gfpSub(tx, &a.y, tx)
gfpMul(ty, tx, ty)
gfpMul(tx, &a.x, &a.y)
gfpAdd(ty, tx, ty)
gfpDouble(tx, tx)
gfp2Square(e, a)
return e
}
func (e *gfP2) SquareU(a *gfP2) *gfP2 {
// Complex squaring algorithm:
// (xu+y)²*u = (y^2-2*x^2)u - 4*x*y
tmp := &gfP2{}
tmp.SquareUNC(a)
gfp2Copy(e, tmp)
return e
}
// SquareU without value copy, will use e directly, so e can't be same as a.
func (e *gfP2) SquareUNC(a *gfP2) *gfP2 {
// Complex squaring algorithm:
// (xu+y)²*u = (y^2-2*x^2)u - 4*x*y
tx := &e.x
ty := &e.y
gfpAdd(tx, &a.x, &a.y)
gfpDouble(ty, &a.x)
gfpSub(ty, &a.y, ty)
gfpMul(tx, tx, ty)
gfpMul(ty, &a.x, &a.y)
gfpAdd(tx, tx, ty)
gfpDouble(ty, ty)
gfpDouble(ty, ty)
gfpNeg(ty, ty)
gfp2SquareU(e, a)
return e
}

1653
sm9/bn256/gfp2_g1_amd64.s Normal file

File diff suppressed because it is too large Load Diff

36
sm9/bn256/gfp2_g1_decl.go Normal file
View File

@ -0,0 +1,36 @@
//go:build amd64 && !purego
// +build amd64,!purego
package bn256
// gfP2 multiplication.
//
//go:noescape
func gfp2Mul(c, a, b *gfP2)
// gfP2 multiplication. c = a*b*u
//
//go:noescape
func gfp2MulU(c, a, b *gfP2)
// gfP2 square.
//
//go:noescape
func gfp2Square(c, a *gfP2)
// gfP2 square and mult u.
//
//go:noescape
func gfp2SquareU(c, a *gfP2)
// Point doubling. Sets res = in + in. in can be the point at infinity.
//
//go:noescape
func curvePointDouble(c, a *curvePoint)
// Point addition. Sets res = in1 + in2. Returns one if the two input points
// were equal and zero otherwise. If in1 or in2 are the point at infinity, res
// and the return value are undefined.
//
//go:noescape
func curvePointAdd(c, a, b *curvePoint) int

View File

@ -0,0 +1,193 @@
//go:build (!amd64) || purego
// +build !amd64 purego
package bn256
func gfp2Mul(c, a, b *gfP2) {
tmp := &gfP2{}
tx := &tmp.x
ty := &tmp.y
v0, v1 := &gfP{}, &gfP{}
gfpMul(v0, &a.y, &b.y)
gfpMul(v1, &a.x, &b.x)
gfpAdd(tx, &a.x, &a.y)
gfpAdd(ty, &b.x, &b.y)
gfpMul(tx, tx, ty)
gfpSub(tx, tx, v0)
gfpSub(tx, tx, v1)
gfpSub(ty, v0, v1)
gfpSub(ty, ty, v1)
gfp2Copy(c, tmp)
}
func gfp2MulU(c, a, b *gfP2) {
tmp := &gfP2{}
tx := &tmp.x
ty := &tmp.y
v0, v1 := &gfP{}, &gfP{}
gfpMul(v0, &a.y, &b.y)
gfpMul(v1, &a.x, &b.x)
gfpAdd(tx, &a.x, &a.y)
gfpAdd(ty, &b.x, &b.y)
gfpMul(ty, tx, ty)
gfpSub(ty, ty, v0)
gfpSub(ty, ty, v1)
gfpDouble(ty, ty)
gfpNeg(ty, ty)
gfpSub(tx, v0, v1)
gfpSub(tx, tx, v1)
gfp2Copy(c, tmp)
}
func gfp2Square(c, a *gfP2) {
tmp := &gfP2{}
tx := &tmp.x
ty := &tmp.y
gfpAdd(ty, &a.x, &a.y)
gfpDouble(tx, &a.x)
gfpSub(tx, &a.y, tx)
gfpMul(ty, tx, ty)
gfpMul(tx, &a.x, &a.y)
gfpAdd(ty, tx, ty)
gfpDouble(tx, tx)
gfp2Copy(c, tmp)
}
func gfp2SquareU(c, a *gfP2) {
tmp := &gfP2{}
tx := &tmp.x
ty := &tmp.y
gfpAdd(tx, &a.x, &a.y)
gfpDouble(ty, &a.x)
gfpSub(ty, &a.y, ty)
gfpMul(tx, tx, ty)
gfpMul(ty, &a.x, &a.y)
gfpAdd(tx, tx, ty)
gfpDouble(ty, ty)
gfpDouble(ty, ty)
gfpNeg(ty, ty)
gfp2Copy(c, tmp)
}
func curvePointDouble(c, a *curvePoint) {
// See http://hyperelliptic.org/EFD/g1p/auto-code/shortw/jacobian-0/doubling/dbl-2009-l.op3
A, B, C := &gfP{}, &gfP{}, &gfP{}
gfpSqr(A, &a.x, 1)
gfpSqr(B, &a.y, 1)
gfpSqr(C, B, 1)
t := &gfP{}
gfpAdd(B, &a.x, B)
gfpSqr(t, B, 1)
gfpSub(B, t, A)
gfpSub(t, B, C)
d, e := &gfP{}, &gfP{}
gfpDouble(d, t)
gfpDouble(B, A)
gfpAdd(e, B, A)
gfpSqr(A, e, 1)
gfpDouble(B, d)
gfpSub(&c.x, A, B)
gfpMul(&c.z, &a.y, &a.z)
gfpDouble(&c.z, &c.z)
gfpDouble(B, C)
gfpDouble(t, B)
gfpDouble(B, t)
gfpSub(&c.y, d, &c.x)
gfpMul(t, e, &c.y)
gfpSub(&c.y, t, B)
}
func curvePointAdd(c, a, b *curvePoint) int {
// See http://hyperelliptic.org/EFD/g1p/auto-code/shortw/jacobian-0/addition/add-2007-bl.op3
var pointEq int
// Normalize the points by replacing a = [x1:y1:z1] and b = [x2:y2:z2]
// by [u1:s1:z1·z2] and [u2:s2:z1·z2]
// where u1 = x1·z2², s1 = y1·z2³ and u1 = x2·z1², s2 = y2·z1³
z12, z22 := &gfP{}, &gfP{}
gfpSqr(z12, &a.z, 1)
gfpSqr(z22, &b.z, 1)
u1, u2 := &gfP{}, &gfP{}
gfpMul(u1, &a.x, z22)
gfpMul(u2, &b.x, z12)
t, s1 := &gfP{}, &gfP{}
gfpMul(t, &b.z, z22)
gfpMul(s1, &a.y, t)
s2 := &gfP{}
gfpMul(t, &a.z, z12)
gfpMul(s2, &b.y, t)
// Compute x = (2h)²(s²-u1-u2)
// where s = (s2-s1)/(u2-u1) is the slope of the line through
// (u1,s1) and (u2,s2). The extra factor 2h = 2(u2-u1) comes from the value of z below.
// This is also:
// 4(s2-s1)² - 4h²(u1+u2) = 4(s2-s1)² - 4h³ - 4h²(2u1)
// = r² - j - 2v
// with the notations below.
h := &gfP{}
gfpSub(h, u2, u1)
gfpDouble(t, h)
// i = 4h²
i := &gfP{}
gfpSqr(i, t, 1)
// j = 4h³
j := &gfP{}
gfpMul(j, h, i)
gfpSub(t, s2, s1)
pointEq = h.Equal(zero) & t.Equal(zero)
r := &gfP{}
gfpDouble(r, t)
v := &gfP{}
gfpMul(v, u1, i)
// t4 = 4(s2-s1)²
t4, t6 := &gfP{}, &gfP{}
gfpSqr(t4, r, 1)
gfpDouble(t, v)
gfpSub(t6, t4, j)
gfpSub(&c.x, t6, t)
// Set y = -(2h)³(s1 + s*(x/4h²-u1))
// This is also
// y = - 2·s1·j - (s2-s1)(2x - 2i·u1) = r(v-x) - 2·s1·j
gfpSub(t, v, &c.x) // t7
gfpMul(t4, s1, j) // t8
gfpDouble(t6, t4) // t9
gfpMul(t4, r, t) // t10
gfpSub(&c.y, t4, t6)
// Set z = 2(u2-u1)·z1·z2 = 2h·z1·z2
gfpAdd(t, &a.z, &b.z) // t11
gfpSqr(t4, t, 1) // t12
gfpSub(t, t4, z12) // t13
gfpSub(t4, t, z22) // t14
gfpMul(&c.z, t4, h)
return pointEq
}

View File

@ -135,10 +135,10 @@ func BenchmarkGfP2Mul(b *testing.B) {
*fromBigInt(bigFromHex("17509B092E845C1266BA0D262CBEE6ED0736A96FA347C8BD856DC76B84EBEB96")),
*fromBigInt(bigFromHex("A7CF28D519BE3DA65F3170153D278FF247EFBA98A71A08116215BBA5C999A7C7")),
}
t := &gfP2{}
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
t := &gfP2{}
t.Mul(x, y)
}
}

View File

@ -121,8 +121,8 @@ func (e *gfP4) MulNC(a, b *gfP4) *gfP4 {
tx := &e.x
ty := &e.y
v0, v1 := &gfP2{}, &gfP2{}
v0.MulNC(&a.y, &b.y)
v1.MulNC(&a.x, &b.x)
v0.Mul(&a.y, &b.y)
v1.Mul(&a.x, &b.x)
tx.Add(&a.x, &a.y)
ty.Add(&b.x, &b.y)
@ -148,8 +148,8 @@ func (e *gfP4) MulNC2(a *gfP4, x, y *gfP2) *gfP4 {
tx := &e.x
ty := &e.y
v0, v1 := &gfP2{}, &gfP2{}
v0.MulNC(&a.y, y)
v1.MulNC(&a.x, x)
v0.Mul(&a.y, y)
v1.Mul(&a.x, x)
tx.Add(&a.x, &a.y)
ty.Add(x, y)
@ -181,8 +181,8 @@ func (e *gfP4) MulVNC(a, b *gfP4) *gfP4 {
tx := &e.x
ty := &e.y
v0, v1 := &gfP2{}, &gfP2{}
v0.MulNC(&a.y, &b.y)
v1.MulNC(&a.x, &b.x)
v0.Mul(&a.y, &b.y)
v1.Mul(&a.x, &b.x)
tx.Add(&a.x, &a.y)
ty.Add(&b.x, &b.y)
@ -227,11 +227,11 @@ func (e *gfP4) SquareNC(a *gfP4) *gfP4 {
tx := &e.x
ty := &e.y
tx.SquareUNC(&a.x)
ty.SquareNC(&a.y)
tx.SquareU(&a.x)
ty.Square(&a.y)
ty.Add(tx, ty)
tx.MulNC(&a.x, &a.y)
tx.Mul(&a.x, &a.y)
tx.Add(tx, tx)
return e
@ -250,8 +250,8 @@ func (e *gfP4) SquareV(a *gfP4) *gfP4 {
func (e *gfP4) SquareVNC(a *gfP4) *gfP4 {
tx := &e.x
ty := &e.y
tx.SquareUNC(&a.x)
ty.SquareNC(&a.y)
tx.SquareU(&a.x)
ty.Square(&a.y)
tx.Add(tx, ty)
ty.MulU(&a.x, &a.y)
@ -269,15 +269,15 @@ func (e *gfP4) Invert(a *gfP4) *gfP4 {
t3 := &gfP2{}
t3.SquareUNC(&a.x)
t1.SquareNC(&a.y)
t3.SquareU(&a.x)
t1.Square(&a.y)
t3.Sub(t3, t1)
t3.Invert(t3)
t1.Mul(&a.y, t3)
t1.Neg(t1)
t2.MulNC(&a.x, t3)
t2.Mul(&a.x, t3)
gfp4Copy(e, tmp)
return e

View File

@ -119,9 +119,9 @@ func (e *gfP6) MulNC(a, b *gfP6) *gfP6 {
ty := &e.y
tz := &e.z
t, v0, v1, v2 := &gfP2{}, &gfP2{}, &gfP2{}, &gfP2{}
v0.MulNC(&a.z, &b.z)
v1.MulNC(&a.y, &b.y)
v2.MulNC(&a.x, &b.x)
v0.Mul(&a.z, &b.z)
v1.Mul(&a.y, &b.y)
v2.Mul(&a.x, &b.x)
t.Add(&a.y, &a.x)
tz.Add(&b.y, &b.x)
@ -185,26 +185,26 @@ func (e *gfP6) SquareNC(a *gfP6) *gfP6 {
tz := &e.z
t, v0, v1, v2 := &gfP2{}, &gfP2{}, &gfP2{}, &gfP2{}
v0.SquareNC(&a.z)
v1.SquareNC(&a.y)
v2.SquareNC(&a.x)
v0.Square(&a.z)
v1.Square(&a.y)
v2.Square(&a.x)
t.Add(&a.y, &a.x)
tz.SquareNC(t)
tz.Square(t)
tz.Sub(tz, v1)
tz.Sub(tz, v2)
tz.MulU1(tz)
tz.Add(tz, v0)
t.Add(&a.z, &a.y)
ty.SquareNC(t)
ty.Square(t)
ty.Sub(ty, v0)
ty.Sub(ty, v1)
t.MulU1(v2)
ty.Add(ty, t)
t.Add(&a.z, &a.x)
tx.SquareNC(t)
tx.Square(t)
tx.Sub(tx, v0)
tx.Add(tx, v1)
tx.Sub(tx, v2)
@ -233,19 +233,19 @@ func (e *gfP6) Invert(a *gfP6) *gfP6 {
// See "Implementing cryptographic pairings", M. Scott, section 3.2.
// ftp://136.206.11.249/pub/crypto/pairings.pdf
t1 := (&gfP2{}).MulUNC(&a.x, &a.y)
A := (&gfP2{}).SquareNC(&a.z)
t1 := (&gfP2{}).MulU(&a.x, &a.y)
A := (&gfP2{}).Square(&a.z)
A.Sub(A, t1)
B := (&gfP2{}).SquareUNC(&a.x)
B := (&gfP2{}).SquareU(&a.x)
t1.Mul(&a.y, &a.z)
B.Sub(B, t1)
C := (&gfP2{}).SquareNC(&a.y)
C := (&gfP2{}).Square(&a.y)
t1.Mul(&a.x, &a.z)
C.Sub(C, t1)
F := (&gfP2{}).MulUNC(C, &a.y)
F := (&gfP2{}).MulU(C, &a.y)
t1.Mul(A, &a.z)
F.Add(F, t1)
t1.MulU(B, &a.x)

View File

@ -67,7 +67,7 @@
CMOVQCC b2, a2 \
CMOVQCC b3, a3
TEXT ·gfpNeg(SB),0,$0-16
TEXT ·gfpNeg(SB),NOSPLIT,$0-16
MOVQ ·p2+0(SB), R8
MOVQ ·p2+8(SB), R9
MOVQ ·p2+16(SB), R10
@ -85,7 +85,7 @@ TEXT ·gfpNeg(SB),0,$0-16
storeBlock(R8,R9,R10,R11, 0(DI))
RET
TEXT ·gfpAdd(SB),0,$0-24
TEXT ·gfpAdd(SB),NOSPLIT,$0-24
MOVQ a+8(FP), DI
MOVQ b+16(FP), SI
@ -104,7 +104,7 @@ TEXT ·gfpAdd(SB),0,$0-24
storeBlock(R8,R9,R10,R11, 0(DI))
RET
TEXT ·gfpDouble(SB),0,$0-16
TEXT ·gfpDouble(SB),NOSPLIT,$0-16
MOVQ a+0(FP), DI
MOVQ b+8(FP), SI
@ -122,7 +122,7 @@ TEXT ·gfpDouble(SB),0,$0-16
storeBlock(R8,R9,R10,R11, 0(DI))
RET
TEXT ·gfpTriple(SB),0,$0-16
TEXT ·gfpTriple(SB),NOSPLIT,$0-16
MOVQ a+0(FP), DI
MOVQ b+8(FP), SI
@ -149,7 +149,7 @@ TEXT ·gfpTriple(SB),0,$0-16
storeBlock(R8,R9,R10,R11, 0(DI))
RET
TEXT ·gfpSub(SB),0,$0-24
TEXT ·gfpSub(SB),NOSPLIT,$0-24
MOVQ a+8(FP), DI
MOVQ b+16(FP), SI
@ -180,7 +180,7 @@ TEXT ·gfpSub(SB),0,$0-24
storeBlock(R8,R9,R10,R11, 0(DI))
RET
TEXT ·gfpMul(SB),0,$0-24
TEXT ·gfpMul(SB),NOSPLIT,$0-24
MOVQ in1+8(FP), x_ptr
MOVQ in2+16(FP), y_ptr

View File

@ -56,7 +56,8 @@ func Test_gfpSqr(t *testing.T) {
gfpSqr(ret, x, 1)
pMinusOne.Mul(pMinusOne, pMinusOne)
pMinusOne.Mod(pMinusOne, p)
if *ret != *fromBigInt(pMinusOne) {
expected := fromBigInt(pMinusOne)
if *ret != *expected {
t.Errorf("bad sqr")
}
// p + 1

View File

@ -17,6 +17,11 @@ var twistB = &gfP2{
*zero,
}
var threeTwistB = &gfP2{
*newGFp(3 * 5),
*zero,
}
// twistGen is the generator of group G₂.
var twistGen = &twistPoint{
gfP2{
@ -58,7 +63,7 @@ func NewTwistGenerator() *twistPoint {
func (c *twistPoint) polynomial(x *gfP2) *gfP2 {
x3 := &gfP2{}
x3.SquareNC(x).Mul(x3, x).Add(x3, twistB)
x3.Square(x).Mul(x3, x).Add(x3, twistB)
return x3
}
@ -70,7 +75,7 @@ func (c *twistPoint) IsOnCurve() bool {
}
y2 := &gfP2{}
y2.SquareNC(&c.y)
y2.Square(&c.y)
x3 := c.polynomial(&c.x)
return y2.Equal(x3) == 1
@ -87,92 +92,79 @@ func (c *twistPoint) IsInfinity() bool {
return c.z.IsZero()
}
func (c *twistPoint) Add(a, b *twistPoint) {
// For additional comments, see the same function in curve.go.
func (c *twistPoint) Add(p1, p2 *twistPoint) {
// Complete addition formula for a = 0 from "Complete addition formulas for
// prime order elliptic curves" (https://eprint.iacr.org/2015/1060), §3.2.
// Algorithm 7: Complete, projective point addition for prime order j-invariant 0 short Weierstrass curves.
if a.IsInfinity() {
c.Set(b)
return
}
if b.IsInfinity() {
c.Set(a)
return
}
t0, t1, t2, t3, t4 := new(gfP2), new(gfP2), new(gfP2), new(gfP2), new(gfP2)
x3, y3, z3 := new(gfP2), new(gfP2), new(gfP2)
t0.Mul(&p1.x, &p2.x) // t0 := X1X2
t1.Mul(&p1.y, &p2.y) // t1 := Y1Y2
t2.Mul(&p1.z, &p2.z) // t2 := Z1Z2
t3.Add(&p1.x, &p1.y) // t3 := X1 + Y1
t4.Add(&p2.x, &p2.y) // t4 := X2 + Y2
t3.Mul(t3, t4) // t3 := t3 * t4 = (X1 + Y1) * (X2 + Y2)
t4.Add(t0, t1) // t4 := t0 + t1
t3.Sub(t3, t4) // t3 := t3 - t4 = X1Y2 + X2Y1
t4.Add(&p1.y, &p1.z) // t4 := Y1 + Z1
x3.Add(&p2.y, &p2.z) // X3 := Y2 + Z2
t4.Mul(t4, x3) // t4 := t4 * X3 = (Y1 + Z1)(Y2 + Z2)
x3.Add(t1, t2) // X3 := t1 + t2
t4.Sub(t4, x3) // t4 := t4 - X3 = Y1Z2 + Y2Z1
x3.Add(&p1.x, &p1.z) // X3 := X1 + Z1
y3.Add(&p2.x, &p2.z) // Y3 := X2 + Z2
x3.Mul(x3, y3) // X3 := X3 * Y3
y3.Add(t0, t2) // Y3 := t0 + t2
y3.Sub(x3, y3) // Y3 := X3 - Y3 = X1Z2 + X2Z1
t0.Triple(t0) // t0 := t0 + t0 + t0 = 3X1X2
t2.Mul(threeTwistB, t2) // t2 := 3b * t2 = 3bZ1Z2
z3.Add(t1, t2) // Z3 := t1 + t2 = Y1Y2 + 3bZ1Z2
t1.Sub(t1, t2) // t1 := t1 - t2 = Y1Y2 - 3bZ1Z2
y3.Mul(threeTwistB, y3) // Y3 = 3b * Y3 = 3b(X1Z2 + X2Z1)
x3.Mul(t4, y3) // X3 := t4 * Y3 = 3b(X1Z2 + X2Z1)(Y1Z2 + Y2Z1)
t2.Mul(t3, t1) // t2 := t3 * t1 = (X1Y2 + X2Y1)(Y1Y2 - 3bZ1Z2)
x3.Sub(t2, x3) // X3 := t2 - X3 = (X1Y2 + X2Y1)(Y1Y2 - 3bZ1Z2) - 3b(Y1Z2 + Y2Z1)(X1Z2 + X2Z1)
y3.Mul(y3, t0) // Y3 := Y3 * t0 = 9bX1X2(X1Z2 + X2Z1)
t1.Mul(t1, z3) // t1 := t1 * Z3 = (Y1Y2 + 3bZ1Z2)(Y1Y2 - 3bZ1Z2)
y3.Add(t1, y3) // Y3 := t1 + Y3 = (Y1Y2 + 3bZ1Z2)(Y1Y2 - 3bZ1Z2) + 9bX1X2(X1Z2 + X2Z1)
t0.Mul(t0, t3) // t0 := t0 * t3 = 3X1X2(X1Y2 + X2Y1)
z3.Mul(z3, t4) // Z3 := Z3 * t4 = (Y1Z2 + Y2Z1)(Y1Y2 + 3bZ1Z2)
z3.Add(z3, t0) // Z3 := Z3 + t0 = (Y1Z2 + Y2Z1)(Y1Y2 + 3bZ1Z2) + 3X1X2(X1Y2 + X2Y1)
// See http://hyperelliptic.org/EFD/g1p/auto-code/shortw/jacobian-0/addition/add-2007-bl.op3
z12 := (&gfP2{}).SquareNC(&a.z)
z22 := (&gfP2{}).SquareNC(&b.z)
u1 := (&gfP2{}).MulNC(&a.x, z22)
u2 := (&gfP2{}).MulNC(&b.x, z12)
t := (&gfP2{}).MulNC(&b.z, z22)
s1 := (&gfP2{}).MulNC(&a.y, t)
t.Mul(&a.z, z12)
s2 := (&gfP2{}).MulNC(&b.y, t)
h := (&gfP2{}).Sub(u2, u1)
xEqual := h.IsZero()
t.Double(h)
i := (&gfP2{}).SquareNC(t)
j := (&gfP2{}).MulNC(h, i)
t.Sub(s2, s1)
yEqual := t.IsZero()
if xEqual && yEqual {
c.Double(a)
return
}
r := (&gfP2{}).Double(t)
v := (&gfP2{}).MulNC(u1, i)
t4 := (&gfP2{}).SquareNC(r)
t.Double(v)
t6 := (&gfP2{}).Sub(t4, j)
c.x.Sub(t6, t)
t.Sub(v, &c.x) // t7
t4.Mul(s1, j) // t8
t6.Double(t4) // t9
t4.Mul(r, t) // t10
c.y.Sub(t4, t6)
t.Add(&a.z, &b.z) // t11
t4.Square(t) // t12
t.Sub(t4, z12) // t13
t4.Sub(t, z22) // t14
c.z.Mul(t4, h)
c.x.Set(x3)
c.y.Set(y3)
c.z.Set(z3)
}
func (c *twistPoint) Double(a *twistPoint) {
// See http://hyperelliptic.org/EFD/g1p/auto-code/shortw/jacobian-0/doubling/dbl-2009-l.op3
A := (&gfP2{}).SquareNC(&a.x)
B := (&gfP2{}).SquareNC(&a.y)
C := (&gfP2{}).SquareNC(B)
func (c *twistPoint) Double(p *twistPoint) {
// Complete addition formula for a = 0 from "Complete addition formulas for
// prime order elliptic curves" (https://eprint.iacr.org/2015/1060), §3.2.
// Algorithm 9: Exception-free point doubling for prime order j-invariant 0 short Weierstrass curves.
t0, t1, t2 := new(gfP2), new(gfP2), new(gfP2)
x3, y3, z3 := new(gfP2), new(gfP2), new(gfP2)
t := (&gfP2{}).Add(&a.x, B)
t2 := (&gfP2{}).SquareNC(t)
t.Sub(t2, A)
t2.Sub(t, C)
d := (&gfP2{}).Double(t2)
t.Double(A)
e := (&gfP2{}).Add(t, A)
f := (&gfP2{}).SquareNC(e)
t0.Square(&p.y) // t0 := Y^2
z3.Double(t0) // Z3 := t0 + t0
z3.Double(z3) // Z3 := Z3 + Z3
z3.Double(z3) // Z3 := Z3 + Z3
t1.Mul(&p.y, &p.z) // t1 := YZ
t2.Square(&p.z) // t0 := Z^2
t2.Mul(threeTwistB, t2) // t2 := 3b * t2 = 3bZ^2
x3.Mul(t2, z3) // X3 := t2 * Z3
y3.Add(t0, t2) // Y3 := t0 + t2
z3.Mul(t1, z3) // Z3 := t1 * Z3
t2.Triple(t2) // t2 := t2 + t2 + t2
t0.Sub(t0, t2) // t0 := t0 - t2
y3.Mul(t0, y3) // t0 := t0 * Y3
y3.Add(x3, y3) // Y3 := X3 + Y3
t1.Mul(&p.x, &p.y) // t1 := XY
x3.Mul(t0, t1) // X3 := t0 * t1
x3.Double(x3) // X3 := X3 + X3
t.Double(d)
c.x.Sub(f, t)
c.z.Mul(&a.y, &a.z)
c.z.Double(&c.z)
t.Double(C)
t2.Double(t)
t.Double(t2)
c.y.Sub(d, &c.x)
t2.Mul(e, &c.y)
c.y.Sub(t2, t)
c.x.Set(x3)
c.y.Set(y3)
c.z.Set(z3)
}
func (c *twistPoint) Mul(a *twistPoint, scalar *big.Int) {
@ -190,7 +182,36 @@ func (c *twistPoint) Mul(a *twistPoint, scalar *big.Int) {
c.Set(sum)
}
// MakeAffine reverses the Projective transform.
// A = 1/Z1
// X3 = A*X1
// Y3 = A*Y1
// Z3 = 1
func (c *twistPoint) MakeAffine() {
// TODO: do we need to change it to constant-time implementation?
if c.z.IsOne() {
return
} else if c.z.IsZero() {
c.x.SetZero()
c.y.SetOne()
c.t.SetZero()
return
}
zInv := &gfP2{}
zInv.Invert(&c.z)
c.x.Mul(&c.x, zInv)
c.y.Mul(&c.y, zInv)
c.z.SetOne()
c.t.SetOne()
}
// MakeAffine reverses the Jacobian transform.
// the Jacobian coordinates are (x1, y1, z1)
// where x = x1/z1² and y = y1/z1³.
func (c *twistPoint) AffineFromJacobian() {
if c.z.IsOne() {
return
} else if c.z.IsZero() {
@ -201,8 +222,8 @@ func (c *twistPoint) MakeAffine() {
}
zInv := (&gfP2{}).Invert(&c.z)
t := (&gfP2{}).MulNC(&c.y, zInv)
zInv2 := (&gfP2{}).SquareNC(zInv)
t := (&gfP2{}).Mul(&c.y, zInv)
zInv2 := (&gfP2{}).Square(zInv)
c.y.Mul(t, zInv2)
t.Mul(&c.x, zInv2)
c.x.Set(t)

View File

@ -28,7 +28,7 @@ func TestAddNeg(t *testing.T) {
func Test_TwistFrobeniusP(t *testing.T) {
ret1, ret2 := &twistPoint{}, &twistPoint{}
ret1.Frobenius(twistGen)
ret1.MakeAffine()
ret1.AffineFromJacobian()
ret2.x.Conjugate(&twistGen.x)
ret2.x.MulScalar(&ret2.x, betaToNegPPlus1Over3)
@ -49,12 +49,15 @@ func Test_TwistFrobeniusP(t *testing.T) {
func Test_TwistFrobeniusP2(t *testing.T) {
ret1, ret2 := &twistPoint{}, &twistPoint{}
ret1.Frobenius(twistGen)
ret1.AffineFromJacobian()
ret1.Frobenius(ret1)
ret1.AffineFromJacobian()
if !ret1.IsOnCurve() {
t.Errorf("point should be on curve")
}
ret2.FrobeniusP2(twistGen)
ret2.AffineFromJacobian()
if !ret2.IsOnCurve() {
t.Errorf("point should be on curve")
}
@ -77,7 +80,7 @@ func Test_TwistFrobeniusP2_Case2(t *testing.T) {
}
ret2.FrobeniusP2(twistGen)
ret2.MakeAffine()
ret2.AffineFromJacobian()
if !ret2.IsOnCurve() {
t.Errorf("point should be on curve")
}
@ -100,7 +103,7 @@ func Test_TwistNegFrobeniusP2_Case2(t *testing.T) {
}
ret2.NegFrobeniusP2(twistGen)
ret2.MakeAffine()
ret2.AffineFromJacobian()
if !ret2.IsOnCurve() {
t.Errorf("point should be on curve")
}