github.com/cloudflare/circl@v1.5.0/dh/sidh/internal/p503/curve.go (about)

     1  // Code generated by go generate; DO NOT EDIT.
     2  // This file was generated by robots.
     3  
     4  package p503
     5  
     6  import (
     7  	"errors"
     8  	. "github.com/cloudflare/circl/dh/sidh/internal/common"
     9  	"math"
    10  )
    11  
    12  // Stores isogeny 3 curve constants
    13  type isogeny3 struct {
    14  	K1 Fp2
    15  	K2 Fp2
    16  }
    17  
    18  // Stores isogeny 4 curve constants
    19  type isogeny4 struct {
    20  	isogeny3
    21  	K3 Fp2
    22  }
    23  
    24  // Computes j-invariant for a curve y2=x3+A/Cx+x with A,C in F_(p^2). Result
    25  // is returned in jBytes buffer, encoded in little-endian format. Caller
    26  // provided jBytes buffer has to be big enough to j-invariant value. In case
    27  // of SIDH, buffer size must be at least size of shared secret.
    28  // Implementation corresponds to Algorithm 9 from SIKE.
    29  func Jinvariant(cparams *ProjectiveCurveParameters, j *Fp2) {
    30  	var t0, t1 Fp2
    31  
    32  	sqr(j, &cparams.A)   // j  = A^2
    33  	sqr(&t1, &cparams.C) // t1 = C^2
    34  	add(&t0, &t1, &t1)   // t0 = t1 + t1
    35  	sub(&t0, j, &t0)     // t0 = j - t0
    36  	sub(&t0, &t0, &t1)   // t0 = t0 - t1
    37  	sub(j, &t0, &t1)     // t0 = t0 - t1
    38  	sqr(&t1, &t1)        // t1 = t1^2
    39  	mul(j, j, &t1)       // j = j * t1
    40  	add(&t0, &t0, &t0)   // t0 = t0 + t0
    41  	add(&t0, &t0, &t0)   // t0 = t0 + t0
    42  	sqr(&t1, &t0)        // t1 = t0^2
    43  	mul(&t0, &t0, &t1)   // t0 = t0 * t1
    44  	add(&t0, &t0, &t0)   // t0 = t0 + t0
    45  	add(&t0, &t0, &t0)   // t0 = t0 + t0
    46  	inv(j, j)            // j  = 1/j
    47  	mul(j, &t0, j)       // j  = t0 * j
    48  }
    49  
    50  // Given affine points x(P), x(Q) and x(Q-P) in a extension field F_{p^2}, function
    51  // recovers projective coordinate A of a curve. This is Algorithm 10 from SIKE.
    52  func RecoverCoordinateA(curve *ProjectiveCurveParameters, xp, xq, xr *Fp2) {
    53  	var t0, t1 Fp2
    54  
    55  	add(&t1, xp, xq)                        // t1 = Xp + Xq
    56  	mul(&t0, xp, xq)                        // t0 = Xp * Xq
    57  	mul(&curve.A, xr, &t1)                  // A  = X(q-p) * t1
    58  	add(&curve.A, &curve.A, &t0)            // A  = A + t0
    59  	mul(&t0, &t0, xr)                       // t0 = t0 * X(q-p)
    60  	sub(&curve.A, &curve.A, &params.OneFp2) // A  = A - 1
    61  	add(&t0, &t0, &t0)                      // t0 = t0 + t0
    62  	add(&t1, &t1, xr)                       // t1 = t1 + X(q-p)
    63  	add(&t0, &t0, &t0)                      // t0 = t0 + t0
    64  	sqr(&curve.A, &curve.A)                 // A  = A^2
    65  	inv(&t0, &t0)                           // t0 = 1/t0
    66  	mul(&curve.A, &curve.A, &t0)            // A  = A * t0
    67  	sub(&curve.A, &curve.A, &t1)            // A  = A - t1
    68  }
    69  
    70  // Computes equivalence (A:C) ~ (A+2C : A-2C)
    71  func CalcCurveParamsEquiv3(cparams *ProjectiveCurveParameters) CurveCoefficientsEquiv {
    72  	var coef CurveCoefficientsEquiv
    73  	var c2 Fp2
    74  
    75  	add(&c2, &cparams.C, &cparams.C)
    76  	// A24p = A+2*C
    77  	add(&coef.A, &cparams.A, &c2)
    78  	// A24m = A-2*C
    79  	sub(&coef.C, &cparams.A, &c2)
    80  	return coef
    81  }
    82  
    83  // Computes equivalence (A:C) ~ (A+2C : 4C)
    84  func CalcCurveParamsEquiv4(cparams *ProjectiveCurveParameters) CurveCoefficientsEquiv {
    85  	var coefEq CurveCoefficientsEquiv
    86  
    87  	add(&coefEq.C, &cparams.C, &cparams.C)
    88  	// A24p = A+2C
    89  	add(&coefEq.A, &cparams.A, &coefEq.C)
    90  	// C24 = 4*C
    91  	add(&coefEq.C, &coefEq.C, &coefEq.C)
    92  	return coefEq
    93  }
    94  
    95  // Helper function for RightToLeftLadder(). Returns A+2C / 4.
    96  func CalcAplus2Over4(cparams *ProjectiveCurveParameters) (ret Fp2) {
    97  	var tmp Fp2
    98  
    99  	// 2C
   100  	add(&tmp, &cparams.C, &cparams.C)
   101  	// A+2C
   102  	add(&ret, &cparams.A, &tmp)
   103  	// 1/4C
   104  	add(&tmp, &tmp, &tmp)
   105  	inv(&tmp, &tmp)
   106  	// A+2C/4C
   107  	mul(&ret, &ret, &tmp)
   108  	return
   109  }
   110  
   111  // Recovers (A:C) curve parameters from projectively equivalent (A+2C:A-2C).
   112  func RecoverCurveCoefficients3(cparams *ProjectiveCurveParameters, coefEq *CurveCoefficientsEquiv) {
   113  	add(&cparams.A, &coefEq.A, &coefEq.C)
   114  	// cparams.A = 2*(A+2C+A-2C) = 4A
   115  	add(&cparams.A, &cparams.A, &cparams.A)
   116  	// cparams.C = (A+2C-A+2C) = 4C
   117  	sub(&cparams.C, &coefEq.A, &coefEq.C)
   118  	return
   119  }
   120  
   121  // Recovers (A:C) curve parameters from projectively equivalent (A+2C:4C).
   122  func RecoverCurveCoefficients4(cparams *ProjectiveCurveParameters, coefEq *CurveCoefficientsEquiv) {
   123  	// cparams.C = (4C)*1/2=2C
   124  	mul(&cparams.C, &coefEq.C, &params.HalfFp2)
   125  	// cparams.A = A+2C - 2C = A
   126  	sub(&cparams.A, &coefEq.A, &cparams.C)
   127  	// cparams.C = 2C * 1/2 = C
   128  	mul(&cparams.C, &cparams.C, &params.HalfFp2)
   129  }
   130  
   131  // Combined coordinate doubling and differential addition. Takes projective points
   132  // P,Q,Q-P and (A+2C)/4C curve E coefficient. Returns 2*P and P+Q calculated on E.
   133  // Function is used only by RightToLeftLadder. Corresponds to Algorithm 5 of SIKE
   134  func xDbladd(P, Q, QmP *ProjectivePoint, a24 *Fp2) (dblP, PaQ ProjectivePoint) {
   135  	var t0, t1, t2 Fp2
   136  
   137  	xQmP, zQmP := &QmP.X, &QmP.Z
   138  	xPaQ, zPaQ := &PaQ.X, &PaQ.Z
   139  	x2P, z2P := &dblP.X, &dblP.Z
   140  	xP, zP := &P.X, &P.Z
   141  	xQ, zQ := &Q.X, &Q.Z
   142  
   143  	add(&t0, xP, zP)      // t0   = Xp+Zp
   144  	sub(&t1, xP, zP)      // t1   = Xp-Zp
   145  	sqr(x2P, &t0)         // 2P.X = t0^2
   146  	sub(&t2, xQ, zQ)      // t2   = Xq-Zq
   147  	add(xPaQ, xQ, zQ)     // Xp+q = Xq+Zq
   148  	mul(&t0, &t0, &t2)    // t0   = t0 * t2
   149  	mul(z2P, &t1, &t1)    // 2P.Z = t1 * t1
   150  	mul(&t1, &t1, xPaQ)   // t1   = t1 * Xp+q
   151  	sub(&t2, x2P, z2P)    // t2   = 2P.X - 2P.Z
   152  	mul(x2P, x2P, z2P)    // 2P.X = 2P.X * 2P.Z
   153  	mul(xPaQ, a24, &t2)   // Xp+q = A24 * t2
   154  	sub(zPaQ, &t0, &t1)   // Zp+q = t0 - t1
   155  	add(z2P, xPaQ, z2P)   // 2P.Z = Xp+q + 2P.Z
   156  	add(xPaQ, &t0, &t1)   // Xp+q = t0 + t1
   157  	mul(z2P, z2P, &t2)    // 2P.Z = 2P.Z * t2
   158  	sqr(zPaQ, zPaQ)       // Zp+q = Zp+q ^ 2
   159  	sqr(xPaQ, xPaQ)       // Xp+q = Xp+q ^ 2
   160  	mul(zPaQ, xQmP, zPaQ) // Zp+q = Xq-p * Zp+q
   161  	mul(xPaQ, zQmP, xPaQ) // Xp+q = Zq-p * Xp+q
   162  	return
   163  }
   164  
   165  // Given the curve parameters, xP = x(P), computes xP = x([2^k]P)
   166  // Safe to overlap xP, x2P.
   167  func Pow2k(xP *ProjectivePoint, params *CurveCoefficientsEquiv, k uint32) {
   168  	var t0, t1 Fp2
   169  
   170  	x, z := &xP.X, &xP.Z
   171  	for i := uint32(0); i < k; i++ {
   172  		sub(&t0, x, z)           // t0  = Xp - Zp
   173  		add(&t1, x, z)           // t1  = Xp + Zp
   174  		sqr(&t0, &t0)            // t0  = t0 ^ 2
   175  		sqr(&t1, &t1)            // t1  = t1 ^ 2
   176  		mul(z, &params.C, &t0)   // Z2p = C24 * t0
   177  		mul(x, z, &t1)           // X2p = Z2p * t1
   178  		sub(&t1, &t1, &t0)       // t1  = t1 - t0
   179  		mul(&t0, &params.A, &t1) // t0  = A24+ * t1
   180  		add(z, z, &t0)           // Z2p = Z2p + t0
   181  		mul(z, z, &t1)           // Zp  = Z2p * t1
   182  	}
   183  }
   184  
   185  // Given the curve parameters, xP = x(P), and k >= 0, compute xP = x([3^k]P).
   186  //
   187  // Safe to overlap xP, xR.
   188  func Pow3k(xP *ProjectivePoint, params *CurveCoefficientsEquiv, k uint32) {
   189  	var t0, t1, t2, t3, t4, t5, t6 Fp2
   190  
   191  	x, z := &xP.X, &xP.Z
   192  	for i := uint32(0); i < k; i++ {
   193  		sub(&t0, x, z)           // t0  = Xp - Zp
   194  		sqr(&t2, &t0)            // t2  = t0^2
   195  		add(&t1, x, z)           // t1  = Xp + Zp
   196  		sqr(&t3, &t1)            // t3  = t1^2
   197  		add(&t4, &t1, &t0)       // t4  = t1 + t0
   198  		sub(&t0, &t1, &t0)       // t0  = t1 - t0
   199  		sqr(&t1, &t4)            // t1  = t4^2
   200  		sub(&t1, &t1, &t3)       // t1  = t1 - t3
   201  		sub(&t1, &t1, &t2)       // t1  = t1 - t2
   202  		mul(&t5, &t3, &params.A) // t5  = t3 * A24+
   203  		mul(&t3, &t3, &t5)       // t3  = t5 * t3
   204  		mul(&t6, &t2, &params.C) // t6  = t2 * A24-
   205  		mul(&t2, &t2, &t6)       // t2  = t2 * t6
   206  		sub(&t3, &t2, &t3)       // t3  = t2 - t3
   207  		sub(&t2, &t5, &t6)       // t2  = t5 - t6
   208  		mul(&t1, &t2, &t1)       // t1  = t2 * t1
   209  		add(&t2, &t3, &t1)       // t2  = t3 + t1
   210  		sqr(&t2, &t2)            // t2  = t2^2
   211  		mul(x, &t2, &t4)         // X3p = t2 * t4
   212  		sub(&t1, &t3, &t1)       // t1  = t3 - t1
   213  		sqr(&t1, &t1)            // t1  = t1^2
   214  		mul(z, &t1, &t0)         // Z3p = t1 * t0
   215  	}
   216  }
   217  
   218  // Set (y1, y2, y3)  = (1/x1, 1/x2, 1/x3).
   219  //
   220  // All xi, yi must be distinct.
   221  func Fp2Batch3Inv(x1, x2, x3, y1, y2, y3 *Fp2) {
   222  	var x1x2, t Fp2
   223  
   224  	mul(&x1x2, x1, x2) // x1*x2
   225  	mul(&t, &x1x2, x3) // 1/(x1*x2*x3)
   226  	inv(&t, &t)
   227  	mul(y1, &t, x2) // 1/x1
   228  	mul(y1, y1, x3)
   229  	mul(y2, &t, x1) // 1/x2
   230  	mul(y2, y2, x3)
   231  	mul(y3, &t, &x1x2) // 1/x3
   232  }
   233  
   234  // Scalarmul3Pt is a right-to-left point multiplication that given the
   235  // x-coordinate of P, Q and P-Q calculates the x-coordinate of R=Q+[scalar]P.
   236  // nbits must be smaller or equal to len(scalar).
   237  func ScalarMul3Pt(cparams *ProjectiveCurveParameters, P, Q, PmQ *ProjectivePoint, nbits uint, scalar []uint8) ProjectivePoint {
   238  	var R0, R2, R1 ProjectivePoint
   239  	aPlus2Over4 := CalcAplus2Over4(cparams)
   240  	R1 = *P
   241  	R2 = *PmQ
   242  	R0 = *Q
   243  
   244  	// Iterate over the bits of the scalar, bottom to top
   245  	prevBit := uint8(0)
   246  	for i := uint(0); i < nbits; i++ {
   247  		bit := (scalar[i>>3] >> (i & 7) & 1)
   248  		swap := prevBit ^ bit
   249  		prevBit = bit
   250  		cswap(&R1.X, &R1.Z, &R2.X, &R2.Z, swap)
   251  		R0, R2 = xDbladd(&R0, &R2, &R1, &aPlus2Over4)
   252  	}
   253  	cswap(&R1.X, &R1.Z, &R2.X, &R2.Z, prevBit)
   254  	return R1
   255  }
   256  
   257  // Given a three-torsion point p = x(PB) on the curve E_(A:C), construct the
   258  // three-isogeny phi : E_(A:C) -> E_(A:C)/<P_3> = E_(A':C').
   259  //
   260  // Input: (XP_3: ZP_3), where P_3 has exact order 3 on E_A/C
   261  // Output:
   262  //   - Curve coordinates (A' + 2C', A' - 2C') corresponding to E_A'/C' = A_E/C/<P3>
   263  //   - Isogeny phi with constants in F_p^2
   264  func (phi *isogeny3) GenerateCurve(p *ProjectivePoint) CurveCoefficientsEquiv {
   265  	var t0, t1, t2, t3, t4 Fp2
   266  	var coefEq CurveCoefficientsEquiv
   267  	K1, K2 := &phi.K1, &phi.K2
   268  
   269  	sub(K1, &p.X, &p.Z)            // K1 = XP3 - ZP3
   270  	sqr(&t0, K1)                   // t0 = K1^2
   271  	add(K2, &p.X, &p.Z)            // K2 = XP3 + ZP3
   272  	sqr(&t1, K2)                   // t1 = K2^2
   273  	add(&t2, &t0, &t1)             // t2 = t0 + t1
   274  	add(&t3, K1, K2)               // t3 = K1 + K2
   275  	sqr(&t3, &t3)                  // t3 = t3^2
   276  	sub(&t3, &t3, &t2)             // t3 = t3 - t2
   277  	add(&t2, &t1, &t3)             // t2 = t1 + t3
   278  	add(&t3, &t3, &t0)             // t3 = t3 + t0
   279  	add(&t4, &t3, &t0)             // t4 = t3 + t0
   280  	add(&t4, &t4, &t4)             // t4 = t4 + t4
   281  	add(&t4, &t1, &t4)             // t4 = t1 + t4
   282  	mul(&coefEq.C, &t2, &t4)       // A24m = t2 * t4
   283  	add(&t4, &t1, &t2)             // t4 = t1 + t2
   284  	add(&t4, &t4, &t4)             // t4 = t4 + t4
   285  	add(&t4, &t0, &t4)             // t4 = t0 + t4
   286  	mul(&t4, &t3, &t4)             // t4 = t3 * t4
   287  	sub(&t0, &t4, &coefEq.C)       // t0 = t4 - A24m
   288  	add(&coefEq.A, &coefEq.C, &t0) // A24p = A24m + t0
   289  	return coefEq
   290  }
   291  
   292  // Given a 3-isogeny phi and a point pB = x(PB), compute x(QB), the x-coordinate
   293  // of the image QB = phi(PB) of PB under phi : E_(A:C) -> E_(A':C').
   294  //
   295  // The output xQ = x(Q) is then a point on the curve E_(A':C'); the curve
   296  // parameters are returned by the GenerateCurve function used to construct phi.
   297  func (phi *isogeny3) EvaluatePoint(p *ProjectivePoint) {
   298  	var t0, t1, t2 Fp2
   299  	K1, K2 := &phi.K1, &phi.K2
   300  	px, pz := &p.X, &p.Z
   301  
   302  	add(&t0, px, pz)   // t0 = XQ + ZQ
   303  	sub(&t1, px, pz)   // t1 = XQ - ZQ
   304  	mul(&t0, K1, &t0)  // t2 = K1 * t0
   305  	mul(&t1, K2, &t1)  // t1 = K2 * t1
   306  	add(&t2, &t0, &t1) // t2 = t0 + t1
   307  	sub(&t0, &t1, &t0) // t0 = t1 - t0
   308  	sqr(&t2, &t2)      // t2 = t2 ^ 2
   309  	sqr(&t0, &t0)      // t0 = t0 ^ 2
   310  	mul(px, px, &t2)   // XQ'= XQ * t2
   311  	mul(pz, pz, &t0)   // ZQ'= ZQ * t0
   312  }
   313  
   314  // Given a four-torsion point p = x(PB) on the curve E_(A:C), construct the
   315  // four-isogeny phi : E_(A:C) -> E_(A:C)/<P_4> = E_(A':C').
   316  //
   317  // Input: (XP_4: ZP_4), where P_4 has exact order 4 on E_A/C
   318  // Output:
   319  //   - Curve coordinates (A' + 2C', 4C') corresponding to E_A'/C' = A_E/C/<P4>
   320  //   - Isogeny phi with constants in F_p^2
   321  func (phi *isogeny4) GenerateCurve(p *ProjectivePoint) CurveCoefficientsEquiv {
   322  	var coefEq CurveCoefficientsEquiv
   323  	xp4, zp4 := &p.X, &p.Z
   324  	K1, K2, K3 := &phi.K1, &phi.K2, &phi.K3
   325  
   326  	sub(K2, xp4, zp4)
   327  	add(K3, xp4, zp4)
   328  	sqr(K1, zp4)
   329  	add(K1, K1, K1)
   330  	sqr(&coefEq.C, K1)
   331  	add(K1, K1, K1)
   332  	sqr(&coefEq.A, xp4)
   333  	add(&coefEq.A, &coefEq.A, &coefEq.A)
   334  	sqr(&coefEq.A, &coefEq.A)
   335  	return coefEq
   336  }
   337  
   338  // Given a 4-isogeny phi and a point xP = x(P), compute x(Q), the x-coordinate
   339  // of the image Q = phi(P) of P under phi : E_(A:C) -> E_(A':C').
   340  //
   341  // Input: Isogeny returned by GenerateCurve and point q=(Qx,Qz) from E0_A/C
   342  // Output: Corresponding point q from E1_A'/C', where E1 is 4-isogenous to E0
   343  func (phi *isogeny4) EvaluatePoint(p *ProjectivePoint) {
   344  	var t0, t1 Fp2
   345  	xq, zq := &p.X, &p.Z
   346  	K1, K2, K3 := &phi.K1, &phi.K2, &phi.K3
   347  
   348  	add(&t0, xq, zq)
   349  	sub(&t1, xq, zq)
   350  	mul(xq, &t0, K2)
   351  	mul(zq, &t1, K3)
   352  	mul(&t0, &t0, &t1)
   353  	mul(&t0, &t0, K1)
   354  	add(&t1, xq, zq)
   355  	sub(zq, xq, zq)
   356  	sqr(&t1, &t1)
   357  	sqr(zq, zq)
   358  	add(xq, &t0, &t1)
   359  	sub(&t0, zq, &t0)
   360  	mul(xq, xq, &t1)
   361  	mul(zq, zq, &t0)
   362  }
   363  
   364  // PublicKeyValidation preforms public key/ciphertext validation using the CLN test.
   365  // CLN test: Check that P and Q are both of order 3^e3 and they generate the torsion E_A[3^e3]
   366  // A countermeasure for remote timing attacks on SIKE; suggested by https://eprint.iacr.org/2022/054.pdf
   367  // Any curve E_A (SIKE 434, 503, 751) that passes CLN test is supersingular.
   368  // Input: The public key / ciphertext P, Q, PmQ. The projective coordinate A of the curve defined by (P, Q, PmQ)
   369  // Outputs: Whether (P,Q,PmQ) follows the CLN test
   370  func PublicKeyValidation(cparams *ProjectiveCurveParameters, P, Q, PmQ *ProjectivePoint, nbits uint) error {
   371  
   372  	var PmQX, PmQZ Fp2
   373  	FromMontgomery(&PmQX, &PmQ.X)
   374  	FromMontgomery(&PmQZ, &PmQ.Z)
   375  
   376  	// PmQ is not point T or O
   377  	if (isZero(&PmQX) == 1) || (isZero(&PmQZ) == 1) {
   378  		return errors.New("curve: PmQ is invalid")
   379  	}
   380  
   381  	cparam := CalcCurveParamsEquiv3(cparams)
   382  
   383  	// Compute e_3 = log3(2^(nbits+1))
   384  	var e3 uint32
   385  	e3_float := float64(int(nbits)+1) / math.Log2(3)
   386  	e3 = uint32(e3_float)
   387  
   388  	// Verify that P and Q generate E_A[3^e_3] by checking: [3^(e_3-1)]P != [+-3^(e_3-1)]Q
   389  	var test_P, test_Q ProjectivePoint
   390  	test_P = *P
   391  	test_Q = *Q
   392  
   393  	Pow3k(&test_P, &cparam, e3-1)
   394  	Pow3k(&test_Q, &cparam, e3-1)
   395  
   396  	var PZ, QZ Fp2
   397  	FromMontgomery(&PZ, &test_P.Z)
   398  	FromMontgomery(&QZ, &test_Q.Z)
   399  
   400  	// P, Q are not of full order 3^e_3
   401  	if (isZero(&PZ) == 1) || (isZero(&QZ) == 1) {
   402  		return errors.New("curve: ciphertext/public key are not of full order 3^e3")
   403  	}
   404  
   405  	// PX/PZ = affine(PX)
   406  	// QX/QZ = affine(QX)
   407  	// If PX/PZ = QX/QZ, we have P=+-Q
   408  	var PXQZ_PZQX_fromMont, PXQZ_PZQX, PXQZ, PZQX Fp2
   409  	mul(&PXQZ, &test_P.X, &test_Q.Z)
   410  	mul(&PZQX, &test_P.Z, &test_Q.X)
   411  	sub(&PXQZ_PZQX, &PXQZ, &PZQX)
   412  	FromMontgomery(&PXQZ_PZQX_fromMont, &PXQZ_PZQX)
   413  
   414  	// [3^(e_3-1)]P == [+-3^(e_3-1)]Q
   415  	if isZero(&PXQZ_PZQX_fromMont) == 1 {
   416  		return errors.New("curve: ciphertext/public key are not linearly independent")
   417  	}
   418  
   419  	// Check that Ord(P) = Ord(Q) = 3^(e_3)
   420  	Pow3k(&test_P, &cparam, 1)
   421  	Pow3k(&test_Q, &cparam, 1)
   422  
   423  	FromMontgomery(&PZ, &test_P.Z)
   424  	FromMontgomery(&QZ, &test_Q.Z)
   425  
   426  	// P, Q are not of correct order 3^e_3
   427  	if (isZero(&PZ) == 0) || (isZero(&QZ) == 0) {
   428  		return errors.New("curve: ciphertext/public key are not of correct order 3^e3")
   429  	}
   430  	return nil
   431  }