|
- 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
- }
-
- func (params *ProjectiveCurveParameters) FromAffine(a *ExtensionFieldElement) {
- params.A = *a
- params.C = oneExtensionField
- }
-
- type CachedCurveParameters struct {
- Aplus2C ExtensionFieldElement
- C4 ExtensionFieldElement
- }
-
- type CachedTripleCurveParameters struct {
- Aminus2C ExtensionFieldElement
- C2 ExtensionFieldElement
- }
-
- // = 256
- var const256 = ExtensionFieldElement{
- A: Fp751Element{0x249ad67, 0x0, 0x0, 0x0, 0x0, 0x730000000000000, 0x738154969973da8b, 0x856657c146718c7f, 0x461860e4e363a697, 0xf9fd6510bba838cd, 0x4e1a3c3f06993c0c, 0x55abef5b75c7},
- B: Fp751Element{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
- }
-
- // Recover the curve parameters from three points on the curve.
- func RecoverCurveParameters(affine_xP, affine_xQ, affine_xQmP *ExtensionFieldElement) ProjectiveCurveParameters {
- var curveParams ProjectiveCurveParameters
- var t0, t1 ExtensionFieldElement
- t0.One() // = 1
- t1.Mul(affine_xP, affine_xQ) // = x_P * x_Q
- t0.Sub(&t0, &t1) // = 1 - x_P * x_Q
- t1.Mul(affine_xP, affine_xQmP) // = x_P * x_{Q-P}
- t0.Sub(&t0, &t1) // = 1 - x_P * x_Q - x_P * x_{Q-P}
- t1.Mul(affine_xQ, affine_xQmP) // = x_Q * x_{Q-P}
- t0.Sub(&t0, &t1) // = 1 - x_P * x_Q - x_P * x_{Q-P} - x_Q * x_{Q-P}
- curveParams.A.Square(&t0) // = (1 - x_P * x_Q - x_P * x_{Q-P} - x_Q * x_{Q-P})^2
- t1.Mul(&t1, affine_xP) // = x_P * x_Q * x_{Q-P}
- t1.Add(&t1, &t1) // = 2 * x_P * x_Q * x_{Q-P}
- curveParams.C.Add(&t1, &t1) // = 4 * x_P * x_Q * x_{Q-P}
- t0.Add(affine_xP, affine_xQ) // = x_P + x_Q
- t0.Add(&t0, affine_xQmP) // = x_P + x_Q + x_{Q-P}
- t1.Mul(&curveParams.C, &t0) // = 4 * x_P * x_Q * x_{Q-P} * (x_P + x_Q + x_{Q-P})
- curveParams.A.Sub(&curveParams.A, &t1) // = (1 - x_P * x_Q - x_P * x_{Q-P} - x_Q * x_{Q-P})^2 - 4 * x_P * x_Q * x_{Q-P} * (x_P + x_Q + x_{Q-P})
-
- return curveParams
- }
-
- // Compute the j-invariant (not the J-invariant) of the given curve.
- func (curveParams *ProjectiveCurveParameters) JInvariant() ExtensionFieldElement {
- var v0, v1, v2, v3 ExtensionFieldElement
- A := &curveParams.A
- C := &curveParams.C
- v0.Square(C) // C^2
- v1.Square(A) // A^2
- v2.Add(&v0, &v0) // 2C^2
- v3.Add(&v2, &v0) // 3C^2
- v2.Add(&v2, &v2) // 4C^2
- v2.Sub(&v1, &v2) // A^2 - 4C^2
- v1.Sub(&v1, &v3) // A^2 - 3C^2
- v3.Square(&v1) // (A^2 - 3C^2)^2
- v3.Mul(&v3, &v1) // (A^2 - 3C^2)^3
- v0.Square(&v0) // C^4
- v3.Mul(&v3, &const256) // 256(A^2 - 3C^2)^3
- v2.Mul(&v2, &v0) // C^4(A^2 - 4C^2)
- v2.Inv(&v2) // 1/C^4(A^2 - 4C^2)
- v0.Mul(&v3, &v2) // 256(A^2 - 3C^2)^3 / C^4(A^2 - 4C^2)
- return v0
- }
-
- // Compute cached parameters A + 2C, 4C.
- func (curve *ProjectiveCurveParameters) cachedParams() CachedCurveParameters {
- var cached CachedCurveParameters
- cached.Aplus2C.Add(&curve.C, &curve.C) // = 2*C
- cached.C4.Add(&cached.Aplus2C, &cached.Aplus2C) // = 4*C
- cached.Aplus2C.Add(&cached.Aplus2C, &curve.A) // = 2*C + A
- return cached
- }
-
- // Compute cached parameters A - 2C, 2C.
- func (curve *ProjectiveCurveParameters) cachedTripleParams() CachedTripleCurveParameters {
- var cached CachedTripleCurveParameters
- cached.C2.Add(&curve.C, &curve.C) // = 2*C
- cached.Aminus2C.Sub(&curve.A, &cached.C2) // = A- 2*C
- return cached
- }
-
- // 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
- }
-
- // A point on the projective line P^1(F_p).
- //
- // This represents a point on the (Kummer line) of the prime-field subgroup of
- // the base curve E_0(F_p), defined by E_0 : y^2 = x^3 + x.
- type ProjectivePrimeFieldPoint struct {
- X PrimeFieldElement
- Z PrimeFieldElement
- }
-
- func (point *ProjectivePoint) FromAffinePrimeField(x *PrimeFieldElement) {
- point.X.A = x.A
- point.X.B = zeroExtensionField.B
- point.Z = oneExtensionField
- }
-
- func (point *ProjectivePoint) FromAffine(x *ExtensionFieldElement) {
- point.X = *x
- point.Z = oneExtensionField
- }
-
- func (point *ProjectivePrimeFieldPoint) FromAffine(x *PrimeFieldElement) {
- point.X = *x
- point.Z = onePrimeField
- }
-
- func (point *ProjectivePoint) ToAffine() *ExtensionFieldElement {
- affine_x := new(ExtensionFieldElement)
- affine_x.Inv(&point.Z).Mul(affine_x, &point.X)
- return affine_x
- }
-
- func (point *ProjectivePrimeFieldPoint) ToAffine() *PrimeFieldElement {
- affine_x := new(PrimeFieldElement)
- 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 (lhs *ProjectivePrimeFieldPoint) VartimeEq(rhs *ProjectivePrimeFieldPoint) bool {
- var t0, t1 PrimeFieldElement
- 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)
- }
-
- func ProjectivePrimeFieldPointConditionalSwap(xP, xQ *ProjectivePrimeFieldPoint, choice uint8) {
- PrimeFieldConditionalSwap(&xP.X, &xQ.X, choice)
- PrimeFieldConditionalSwap(&xP.Z, &xQ.Z, choice)
- }
-
- // 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), 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 *ProjectivePrimeFieldPoint) Add(xP, xQ, xPmQ *ProjectivePrimeFieldPoint) *ProjectivePrimeFieldPoint {
- // Algorithm 1 of Costello-Smith.
- var v0, v1, v2, v3, v4 PrimeFieldElement
- 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, curve *CachedCurveParameters) *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, &curve.C4) // 4C(X-Z)^2
- xQ.X.Mul(&v1, &v2) // 4C(X+Z)^2(X-Z)^2
- v3.Mul(&xz4, &curve.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 xP = x(P) and cached curve parameter aPlus2Over4 = (a+2)/4, compute xQ = x([2]P).
- //
- // Note that we don't use projective curve coefficients here because we only
- // ever use a fixed curve (in our case, the base curve E_0).
- //
- // Returns xQ to allow chaining. Safe to overlap xP, xQ.
- func (xQ *ProjectivePrimeFieldPoint) Double(xP *ProjectivePrimeFieldPoint, aPlus2Over4 *PrimeFieldElement) *ProjectivePrimeFieldPoint {
- // Algorithm 2 of Costello-Smith
- var v1, v2, v3, xz4 PrimeFieldElement
- 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
- xQ.X.Mul(&v1, &v2) // (X+Z)^2(X-Z)^2
- v3.Mul(&xz4, aPlus2Over4) // 4XZ((a+2)/4)
- v3.Add(&v3, &v2) // 4XZ((a+2)/4) + (X-Z)^2
- xQ.Z.Mul(&v3, &xz4) // (4XZ((a+2)/4) + (X-Z)^2)4XZ
- // Now (xQ.x : xQ.z)
- // = ((X+Z)^2(X-Z)^2 : (4XZ((a + 2)/4) + (X-Z)^2)4XZ )
- return xQ
- }
-
- // Given xP = x(P), xQ = x(Q), and xPmQ = x(P-Q), compute xPaddQ = x(P+Q) and x2P = x(2P).
- func xDblAdd(curve *CachedCurveParameters, xP, xQ, xPmQ *ProjectivePoint) (x2P, xPaddQ ProjectivePoint) {
- var A, AA, B, BB, C, D, E, DA, CB, t0, t1 ExtensionFieldElement
- x1, z1 := &xPmQ.X, &xPmQ.Z
- x2, z2 := &xP.X, &xP.Z
- x3, z3 := &xQ.X, &xQ.Z
-
- A.Add(x2, z2) // A = x2+z2
- B.Sub(x2, z2) // B = x2-z2
- C.Add(x3, z3) // C = x3+z3
- D.Sub(x3, z3) // D = x3-z3
-
- AA.Square(&A) // AA = A^2
- BB.Square(&B) // BB = B^2
-
- E.Sub(&AA, &BB) // E = AA-BB
- BB.Mul(&BB, &curve.C4) // BB = (4C)*BB
- DA.Mul(&D, &A) // DA = D*A
- CB.Mul(&C, &B) // CB = C*B
-
- t1.Add(&DA, &CB) // t1 = DA+CB
- t0.Sub(&DA, &CB) // t0 = DA-CB
- t1.Square(&t1) // t1 = t1^2
- t0.Square(&t0) // t0 = t0^2
-
- xPaddQ.X.Mul(z1, &t1) // z5 = z1*t1
- xPaddQ.Z.Mul(x1, &t0) // x5 = x1*t0
-
- x2P.X.Mul(&AA, &BB) // x4 = AA*(4C)*BB
- x2P.Z.Mul(&curve.Aplus2C, &E) // z4 = (A+2C)*E
- x2P.Z.Add(&x2P.Z, &BB) // z4 = (4C)*BB+(A+2C)*E
- x2P.Z.Mul(&x2P.Z, &E) // z4 = E*((4C)*BB+(A+2C)*E)
- return
- }
-
- // Given xP = x(P), xQ = x(Q), and xPmQ = x(P-Q), compute xPaddQ = x(P+Q) and x2P = x(2P).
- // Assumes that the Z-xoordinate of PmQ is equal to 1.
- func xDblAdd_primefield(aPlus2Over4 *PrimeFieldElement, xP, xQ, xPmQ *ProjectivePrimeFieldPoint) (x2P, xPaddQ ProjectivePrimeFieldPoint) {
- var A, AA, B, BB, C, D, E, DA, CB, t0, t1 PrimeFieldElement
- x1 := &xPmQ.X
- x2, z2 := &xP.X, &xP.Z
- x3, z3 := &xQ.X, &xQ.Z
-
- A.Add(x2, z2) // A = x2+z2
- B.Sub(x2, z2) // B = x2-z2
- C.Add(x3, z3) // C = x3+z3
- D.Sub(x3, z3) // D = x3-z3
-
- AA.Square(&A) // AA = A^2
- BB.Square(&B) // BB = B^2
-
- E.Sub(&AA, &BB) // E = AA-BB
- DA.Mul(&D, &A) // DA = D*A
- CB.Mul(&C, &B) // CB = C*B
-
- t1.Add(&DA, &CB) // t1 = DA+CB
- t0.Sub(&DA, &CB) // t0 = DA-CB
- t1.Square(&t1) // t1 = t1^2
- t0.Square(&t0) // t0 = t0^2
-
- xPaddQ.X = t1 // z5 = z1*t1
- xPaddQ.Z.Mul(x1, &t0) // x5 = x1*t0
-
- x2P.X.Mul(&AA, &BB) // x4 = AA*(4C)*BB
- x2P.Z.Mul(aPlus2Over4, &E) // z4 = (A+2C)*E
- x2P.Z.Add(&x2P.Z, &BB) // z4 = (4C)*BB+(A+2C)*E
- x2P.Z.Mul(&x2P.Z, &E) // z4 = E*((4C)*BB+(A+2C)*E)
- return
- }
-
- // Given the curve parameters, xP = x(P), and k >= 0, compute xQ = x([2^k]P).
- //
- // Returns xQ to allow chaining. Safe to overlap xP, xQ.
- func (xQ *ProjectivePoint) Pow2k(curve *ProjectiveCurveParameters, xP *ProjectivePoint, k uint32) *ProjectivePoint {
- cachedParams := curve.cachedParams()
- *xQ = *xP
- for i := uint32(0); i < k; i++ {
- xQ.Double(xQ, &cachedParams)
- }
-
- return xQ
- }
-
- // Uses the efficient Montgomery tripling formulas from FLOR-SIDH-x64
- // Given xP = x(P) and cached tripling curve parameters Aminus2C = A - 2*C, C2 = 2*C, compute xQ = x([3]P).
- // Returns xQ to allow chaining. Safe to overlap xP, xQ.
- // Reference: A faster SW implementation of SIDH (github.com/armfazh/flor-sidh-x64).
- func (xQ *ProjectivePoint) Triple(xP *ProjectivePoint, curve *CachedTripleCurveParameters) *ProjectivePoint {
- var t0, t1, t2, t3, t4, t5 ExtensionFieldElement
- x1, z1 := &xP.X, &xP.Z
- t0.Square(x1) // t0 = x1^2
- t1.Square(z1) // t1 = z1^2
- t2.Add(x1, z1) // t2 = x1+z1
- t2.Square(&t2) // t2 = t2^2
- t3.Add(&t0, &t1) // t3 = t0+t1
- t4.Sub(&t2, &t3) // t4 = t2-t3
- t5.Mul(&curve.Aminus2C, &t4) // t5 = (A-2C)*t4
- t2.Mul(&curve.C2, &t2) // t2 = (2C)*t2
- t5.Add(&t5, &t2) // t5 = t2+t5
- t5.Add(&t5, &t5) // t5 = t5+t5
- t5.Add(&t5, &t5) // t5 = t5+t5
- t0.Mul(&t0, &t5) // t0 = t0*t5
- t1.Mul(&t1, &t5) // t1 = t1*t5
- t4.Sub(&t3, &t4) // t4 = t3-t4
- t2.Mul(&t2, &t4) // t2 = t2*t4
- t0.Sub(&t2, &t0) // t0 = t2-t0
- t1.Sub(&t2, &t1) // t1 = t2-t1
- t0.Square(&t0) // t0 = t0^2
- t1.Square(&t1) // t1 = t1^2
- xQ.X.Mul(x1, &t1) // x3 = x1*t1
- xQ.Z.Mul(z1, &t0) // z3 = z1*t0
- return xQ
- }
-
- // Given the curve parameters, xP = x(P), and k >= 0, compute xQ = x([3^k]P).
- //
- // Returns xQ to allow chaining. Safe to overlap xP, xQ.
- func (xQ *ProjectivePoint) Pow3k(curve *ProjectiveCurveParameters, xP *ProjectivePoint, k uint32) *ProjectivePoint {
- cachedParams := curve.cachedTripleParams()
- *xQ = *xP
- for i := uint32(0); i < k; i++ {
- xQ.Triple(xQ, &cachedParams)
- }
-
- 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 {
- cachedParams := curve.cachedParams()
- var x0, x1, tmp ProjectivePoint
-
- 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, &cachedParams)
- 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
- }
-
- // Given x(P) and a scalar m in little-endian bytes, compute x([m]P), x([m+1]P) using the
- // Montgomery ladder. This is described in Algorithm 8 of Costello-Smith.
- //
- // The extra value x([m+1]P) is returned to allow y-coordinate recovery;
- // otherwise, it can be ignored.
- //
- // 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.
- func ScalarMultPrimeField(aPlus2Over4 *PrimeFieldElement, xP *ProjectivePrimeFieldPoint, scalar []uint8) (ProjectivePrimeFieldPoint, ProjectivePrimeFieldPoint) {
- var x0, x1 ProjectivePrimeFieldPoint
-
- 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
- ProjectivePrimeFieldPointConditionalSwap(&x0, &x1, (bit ^ prevBit))
- x0, x1 = xDblAdd_primefield(aPlus2Over4, &x0, &x1, xP)
- prevBit = bit
- }
- }
- // now prevBit is the lowest bit of the scalar
- ProjectivePrimeFieldPointConditionalSwap(&x0, &x1, prevBit)
- return x0, x1
- }
-
- // Given P = (x_P, y_P) in affine coordinates, as well as projective points
- // x(Q), x(R) = x(P+Q), all in the prime-field subgroup of the starting curve
- // E_0(F_p), use the Okeya-Sakurai coordinate recovery strategy to recover Q =
- // (X_Q : Y_Q : Z_Q).
- //
- // This is Algorithm 5 of Costello-Smith, with the constants a = 0, b = 1 hardcoded.
- func OkeyaSakuraiCoordinateRecovery(affine_xP, affine_yP *PrimeFieldElement, xQ, xR *ProjectivePrimeFieldPoint) (X_Q, Y_Q, Z_Q PrimeFieldElement) {
- var v1, v2, v3, v4 PrimeFieldElement
- v1.Mul(affine_xP, &xQ.Z) // = x_P*Z_Q
- v2.Add(&xQ.X, &v1) // = X_Q + x_P*Z_Q
- v3.Sub(&xQ.X, &v1).Square(&v3) // = (X_Q - x_P*Z_Q)^2
- v3.Mul(&v3, &xR.X) // = X_R*(X_Q - x_P*Z_Q)^2
- // Skip setting v1 = 2a*Z_Q (step 6) since we hardcode a = 0
- // Skip adding v1 to v2 (step 7) since v1 is zero
- v4.Mul(affine_xP, &xQ.X) // = x_P*X_Q
- v4.Add(&v4, &xQ.Z) // = x_P*X_Q + Z_Q
- v2.Mul(&v2, &v4) // = (x_P*X_Q + Z_Q)*(X_Q + x_P*Z_Q)
- // Skip multiplication by v1 (step 11) since v1 is zero
- // Skip subtracting v1 from v2 (step 12) since v1 is zero
- v2.Mul(&v2, &xR.Z) // = (x_P*X_Q + Z_Q)*(X_Q + x_P*Z_Q)*Z_R
- Y_Q.Sub(&v2, &v3) // = (x_P*X_Q + Z_Q)*(X_Q + x_P*Z_Q)*Z_R - X_R*(X_Q - x_P*Z_Q)^2
- v1.Add(affine_yP, affine_yP) // = 2b*y_P
- v1.Mul(&v1, &xQ.Z).Mul(&v1, &xR.Z) // = 2b*y_P*Z_Q*Z_R
- X_Q.Mul(&v1, &xQ.X) // = 2b*y_P*Z_Q*Z_R*X_Q
- Z_Q.Mul(&v1, &xQ.Z) // = 2b*y_P*Z_Q^2*Z_R
-
- return
- }
-
- // Given x(P), x(Q), x(P-Q), as well as a scalar m in little-endian bytes,
- // compute x(P + [m]Q) using the "three-point ladder" of de Feo, Jao, and Plut.
- //
- // Safe to overlap the source with the destination.
- //
- // 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.
- //
- // The algorithm, as described in de Feo-Jao-Plut, is as follows:
- //
- // (x0, x1, x2) <--- (x(O), x(Q), x(P))
- //
- // for i = |m| down to 0, indexing the bits of m:
- // Invariant: (x0, x1, x2) == (x( [t]Q ), x( [t+1]Q ), x( P + [t]Q ))
- // where t = m//2^i is the high bits of m, starting at i
- // if m_i == 0:
- // (x0, x1, x2) <--- (xDBL(x0), xADD(x1, x0, x(Q)), xADD(x2, x0, x(P)))
- // Invariant: (x0, x1, x2) == (x( [2t]Q ), x( [2t+1]Q ), x( P + [2t]Q ))
- // == (x( [t']Q ), x( [t'+1]Q ), x( P + [t']Q ))
- // where t' = m//2^{i-1} is the high bits of m, starting at i-1
- // if m_i == 1:
- // (x0, x1, x2) <--- (xADD(x1, x0, x(Q)), xDBL(x1), xADD(x2, x1, x(P-Q)))
- // Invariant: (x0, x1, x2) == (x( [2t+1]Q ), x( [2t+2]Q ), x( P + [2t+1]Q ))
- // == (x( [t']Q ), x( [t'+1]Q ), x( P + [t']Q ))
- // where t' = m//2^{i-1} is the high bits of m, starting at i-1
- // return x2
- //
- // Notice that the roles of (x0,x1) and (x(P), x(P-Q)) swap depending on the
- // current bit of the scalar. Instead of swapping which operations we do, we
- // can swap variable names, producing the following uniform algorithm:
- //
- // (x0, x1, x2) <--- (x(O), x(Q), x(P))
- // (y0, y1) <--- (x(P), x(P-Q))
- //
- // for i = |m| down to 0, indexing the bits of m:
- // (x0, x1) <--- SWAP( m_{i+1} xor m_i, (x0,x1) )
- // (y0, y1) <--- SWAP( m_{i+1} xor m_i, (y0,y1) )
- // (x0, x1, x2) <--- ( xDBL(x0), xADD(x1,x0,x(Q)), xADD(x2, x0, y0) )
- //
- // return x2
- //
- func (xR *ProjectivePoint) ThreePointLadder(curve *ProjectiveCurveParameters, xP, xQ, xPmQ *ProjectivePoint, scalar []uint8) *ProjectivePoint {
- cachedParams := curve.cachedParams()
- var x0, x1, x2, y0, y1, tmp ProjectivePoint
-
- // (x0, x1, x2) <--- (x(O), x(Q), x(P))
- x0.X.One()
- x0.Z.Zero()
- x1 = *xQ
- x2 = *xP
- // (y0, y1) <--- (x(P), x(P-Q))
- y0 = *xP
- y1 = *xPmQ
-
- // 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))
- ProjectivePointConditionalSwap(&y0, &y1, (bit ^ prevBit))
- tmp, x2 = xDblAdd(&cachedParams, &x0, &x2, &y0)
- x1.Add(&x1, &x0, xQ) // = xADD(x1, x0, x(Q))
- x0 = tmp
- prevBit = bit
- }
- }
-
- *xR = x2
- return xR
- }
-
- /**
- Update: This is the right-to-left method for computing the x-coordinate of P+[k]Q.
-
- Desc: This function replaces the ThreePointLadder function and improves the running
- time by performing 1 Addition + 1 Doubling per bit of k.
-
- Reference: A faster SW implementation of SIDH (github.com/armfazh/flor-sidh-x64).
- */
- func (xR *ProjectivePoint) R2L(curve *ProjectiveCurveParameters, xP, xQ, xPmQ *ProjectivePoint, scalar []uint8) *ProjectivePoint {
- cachedParams := curve.cachedParams()
- var R0, R2, R1 ProjectivePoint
-
- R1 = *xP
- R2 = *xPmQ
- R0 = *xQ
-
- // Iterate over the bits of the scalar, bottom to top
- prevBit := uint8(0)
- for i := 0; i < len(scalar); i++ {
- scalarByte := scalar[i]
- for j := 0; j < 8; j++ {
- bit := (scalarByte >> uint(j)) & 0x1
- ProjectivePointConditionalSwap(&R1, &R2, (bit ^ prevBit))
- R0, R2 = xDblAdd(&cachedParams, &R0, &R2, &R1)
- prevBit = bit
- }
- }
- ProjectivePointConditionalSwap(&R1, &R2, prevBit)
- *xR = R1
- return xR
- }
-
- // Given the affine x-coordinate affine_xP of P, compute the x-coordinate
- // x(\tau(P)-P) of \tau(P)-P.
- func DistortAndDifference(affine_xP *PrimeFieldElement) ProjectivePoint {
- var xR ProjectivePoint
- var t0, t1 PrimeFieldElement
- t0.Square(affine_xP) // = x_P^2
- t1.One().Add(&t1, &t0) // = x_P^2 + 1
- xR.X.B = t1.A // = 0 + (x_P^2 + 1)*i
- t0.Add(affine_xP, affine_xP) // = 2*x_P
- xR.Z.A = t0.A // = 2*x_P + 0*i
-
- return xR
- }
-
- // Given an affine point P = (x_P, y_P) in the prime-field subgroup of the
- // starting curve E_0(F_p), together with a secret scalar m, compute x(P+[m]Q),
- // where Q = \tau(P) is the image of P under the distortion map described
- // below.
- //
- // The computation uses basically the same strategy as the
- // Costello-Longa-Naehrig implementation:
- //
- // 1. Use the standard Montgomery ladder to compute x([m]Q), x([m+1]Q)
- //
- // 2. Use Okeya-Sakurai coordinate recovery to recover [m]Q from Q, x([m]Q),
- // x([m+1]Q)
- //
- // 3. Use P and [m]Q to compute x(P + [m]Q)
- //
- // The distortion map \tau is defined as
- //
- // \tau : E_0(F_{p^2}) ---> E_0(F_{p^2})
- //
- // \tau : (x,y) |---> (-x, iy).
- //
- // The image of the distortion map is the _trace-zero_ subgroup of E_0(F_{p^2})
- // defined by Tr(P) = P + \pi_p(P) = id, where \pi_p((x,y)) = (x^p, y^p) is the
- // p-power Frobenius map. To see this, take P = (x,y) \in E_0(F_{p^2}). Then
- // Tr(P) = id if and only if \pi_p(P) = -P, so that
- //
- // -P = (x, -y) = (x^p, y^p) = \pi_p(P);
- //
- // we have x^p = x if and only if x \in F_p, while y^p = -y if and only if y =
- // i*y' for y' \in F_p.
- //
- // Thus (excepting the identity) every point in the trace-zero subgroup is of
- // the form \tau((x,y)) = (-x,i*y) for (x,y) \in E_0(F_p).
- //
- // Since the Montgomery ladder only uses the x-coordinate, and the x-coordinate
- // is always in the prime subfield, we can compute x([m]Q), x([m+1]Q) entirely
- // in the prime subfield.
- //
- // The affine form of the relation for Okeya-Sakurai coordinate recovery is
- // given on p. 13 of Costello-Smith:
- //
- // y_Q = ((x_P*x_Q + 1)*(x_P + x_Q + 2*a) - 2*a - x_R*(x_P - x_Q)^2)/(2*b*y_P),
- //
- // where R = Q + P and a,b are the Montgomery parameters. In our setting
- // (a,b)=(0,1) and our points are P=Q, Q=[m]Q, P+Q=[m+1]Q, so this becomes
- //
- // y_{mQ} = ((x_Q*x_{mQ} + 1)*(x_Q + x_{mQ}) - x_{m1Q}*(x_Q - x_{mQ})^2)/(2*y_Q)
- //
- // y_{mQ} = ((1 - x_P*x_{mQ})*(x_{mQ} - x_P) - x_{m1Q}*(x_P + x_{mQ})^2)/(2*y_P*i)
- //
- // y_{mQ} = i*((1 - x_P*x_{mQ})*(x_{mQ} - x_P) - x_{m1Q}*(x_P + x_{mQ})^2)/(-2*y_P)
- //
- // since (x_Q, y_Q) = (-x_P, y_P*i). In projective coordinates this is
- //
- // Y_{mQ}' = ((Z_{mQ} - x_P*X_{mQ})*(X_{mQ} - x_P*Z_{mQ})*Z_{m1Q}
- // - X_{m1Q}*(X_{mQ} + x_P*Z_{mQ})^2)
- //
- // with denominator
- //
- // Z_{mQ}' = (-2*y_P*Z_{mQ}*Z_{m1Q})*Z_{mQ}.
- //
- // Setting
- //
- // X_{mQ}' = (-2*y_P*Z_{mQ}*Z_{m1Q})*X_{mQ}
- //
- // gives [m]Q = (X_{mQ}' : i*Y_{mQ}' : Z_{mQ}') with X,Y,Z all in F_p. (Here
- // the ' just denotes that we've added extra terms to the denominators during
- // the computation of Y)
- //
- // To compute the x-coordinate x(P+[m]Q) from P and [m]Q, we use the affine
- // addition formulas of section 2.2 of Costello-Smith. We're only interested
- // in the x-coordinate, giving
- //
- // X_R = Z_{mQ}*(i*Y_{mQ} - y_P*Z_{mQ})^2 - (x_P*Z_{mQ} + X_{mQ})*(X_{mQ} - x_P*Z_{mQ})^2
- //
- // Z_R = Z_{mQ}*(X_{mQ} - x_P*Z_{mQ})^2.
- //
- // Notice that although X_R \in F_{p^2}, we can split the computation into
- // coordinates X_R = X_{R,a} + X_{R,b}*i as
- //
- // (i*Y_{mQ} - y_P*Z_{mQ})^2 = (y_P*Z_{mQ})^2 - Y_{mQ}^2 - 2*y_P*Z_{mQ}*Y_{mQ}*i,
- //
- // giving
- //
- // X_{R,a} = Z_{mQ}*((y_P*Z_{mQ})^2 - Y_{mQ}^2)
- // - (x_P*Z_{mQ} + X_{mQ})*(X_{mQ} - x_P*Z_{mQ})^2
- //
- // X_{R,b} = -2*y_P*Y_{mQ}*Z_{mQ}^2
- //
- // Z_R = Z_{mQ}*(X_{mQ} - x_P*Z_{mQ})^2.
- //
- // These formulas could probably be combined with the formulas for y-recover
- // and computed more efficiently, but efficiency isn't the biggest concern
- // here, since the bulk of the cost is already in the ladder.
- func SecretPoint(affine_xP, affine_yP *PrimeFieldElement, scalar []uint8) ProjectivePoint {
- var xQ ProjectivePrimeFieldPoint
- xQ.FromAffine(affine_xP)
- xQ.X.Neg(&xQ.X)
-
- // Compute x([m]Q) = (X_{mQ} : Z_{mQ}), x([m+1]Q) = (X_{m1Q} : Z_{m1Q})
- var xmQ, xm1Q = ScalarMultPrimeField(&E0_aPlus2Over4, &xQ, scalar)
-
- // Now perform coordinate recovery:
- // [m]Q = (X_{mQ} : Y_{mQ}*i : Z_{mQ})
- var XmQ, YmQ, ZmQ PrimeFieldElement
- var t0, t1 PrimeFieldElement
-
- // Y_{mQ} = (Z_{mQ} - x_P*X_{mQ})*(X_{mQ} - x_P*Z_{mQ})*Z_{m1Q}
- // - X_{m1Q}*(X_{mQ} + x_P*Z_{mQ})^2
- t0.Mul(affine_xP, &xmQ.X) // = x_P*X_{mQ}
- YmQ.Sub(&xmQ.Z, &t0) // = Z_{mQ} - x_P*X_{mQ}
- t1.Mul(affine_xP, &xmQ.Z) // = x_P*Z_{mQ}
- t0.Sub(&xmQ.X, &t1) // = X_{mQ} - x_P*Z_{mQ}
- YmQ.Mul(&YmQ, &t0) // = (Z_{mQ} - x_P*X_{mQ})*(X_{mQ} - x_P*Z_{mQ})
- YmQ.Mul(&YmQ, &xm1Q.Z) // = (Z_{mQ} - x_P*X_{mQ})*(X_{mQ} - x_P*Z_{mQ})*Z_{m1Q}
- t1.Add(&t1, &xmQ.X).Square(&t1) // = (X_{mQ} + x_P*Z_{mQ})^2
- t1.Mul(&t1, &xm1Q.X) // = X_{m1Q}*(X_{mQ} + x_P*Z_{mQ})^2
- YmQ.Sub(&YmQ, &t1) // = Y_{mQ}
-
- // Z_{mQ} = -2*(Z_{mQ}^2 * Z_{m1Q} * y_P)
- t0.Mul(&xmQ.Z, &xm1Q.Z).Mul(&t0, affine_yP) // = Z_{mQ} * Z_{m1Q} * y_P
- t0.Neg(&t0) // = -1*(Z_{mQ} * Z_{m1Q} * y_P)
- t0.Add(&t0, &t0) // = -2*(Z_{mQ} * Z_{m1Q} * y_P)
- ZmQ.Mul(&xmQ.Z, &t0) // = -2*(Z_{mQ}^2 * Z_{m1Q} * y_P)
-
- // We added terms to the denominator Z_{mQ}, so multiply them to X_{mQ}
- // X_{mQ} = -2*X_{mQ}*Z_{mQ}*Z_{m1Q}*y_P
- XmQ.Mul(&xmQ.X, &t0)
-
- // Now compute x(P + [m]Q) = (X_Ra + i*X_Rb : Z_R)
- var XRa, XRb, ZR PrimeFieldElement
-
- XRb.Square(&ZmQ).Mul(&XRb, &YmQ) // = Y_{mQ} * Z_{mQ}^2
- XRb.Mul(&XRb, affine_yP) // = Y_{mQ} * y_P * Z_{mQ}^2
- XRb.Add(&XRb, &XRb) // = 2 * Y_{mQ} * y_P * Z_{mQ}^2
- XRb.Neg(&XRb) // = -2 * Y_{mQ} * y_P * Z_{mQ}^2
-
- t0.Mul(affine_yP, &ZmQ).Square(&t0) // = (y_P * Z_{mQ})^2
- t1.Square(&YmQ) // = Y_{mQ}^2
- XRa.Sub(&t0, &t1) // = (y_P * Z_{mQ})^2 - Y_{mQ}^2
- XRa.Mul(&XRa, &ZmQ) // = Z_{mQ}*((y_P * Z_{mQ})^2 - Y_{mQ}^2)
- t0.Mul(affine_xP, &ZmQ) // = x_P * Z_{mQ}
- t1.Add(&XmQ, &t0) // = X_{mQ} + x_P*Z_{mQ}
- t0.Sub(&XmQ, &t0) // = X_{mQ} - x_P*Z_{mQ}
- t0.Square(&t0) // = (X_{mQ} - x_P*Z_{mQ})^2
- t1.Mul(&t1, &t0) // = (X_{mQ} + x_P*Z_{mQ})*(X_{mQ} - x_P*Z_{mQ})^2
- XRa.Sub(&XRa, &t1) // = Z_{mQ}*((y_P*Z_{mQ})^2 - Y_{mQ}^2) - (X_{mQ} + x_P*Z_{mQ})*(X_{mQ} - x_P*Z_{mQ})^2
-
- ZR.Mul(&ZmQ, &t0) // = Z_{mQ}*(X_{mQ} - x_P*Z_{mQ})^2
-
- var xR ProjectivePoint
- xR.X.A = XRa.A
- xR.X.B = XRb.A
- xR.Z.A = ZR.A
-
- return xR
- }
|