// Code generated by go generate; DO NOT EDIT. // This file was generated by robots. package p434 import ( "bytes" crand "crypto/rand" . "github.com/cloudflare/circl/dh/sidh/internal/common" "io" "math" "math/rand" "testing" "time" ) func vartimeEqProjFp2(lhs, rhs *ProjectivePoint) bool { var t0, t1 Fp2 mul(&t0, &lhs.X, &rhs.Z) mul(&t1, &lhs.Z, &rhs.X) return vartimeEqFp2(&t0, &t1) } func toAffine(point *ProjectivePoint) *Fp2 { var affineX Fp2 inv(&affineX, &point.Z) mul(&affineX, &affineX, &point.X) return &affineX } func Test_jInvariant(t *testing.T) { curve := ProjectiveCurveParameters{A: curveA, C: curveC} jbufRes := make([]byte, params.SharedSecretSize) jbufExp := make([]byte, params.SharedSecretSize) var jInv Fp2 Jinvariant(&curve, &jInv) FromMontgomery(&jInv, &jInv) Fp2ToBytes(jbufRes, &jInv, params.Bytelen) jInv = expectedJ FromMontgomery(&jInv, &jInv) Fp2ToBytes(jbufExp, &jInv, params.Bytelen) if !bytes.Equal(jbufRes[:], jbufExp[:]) { t.Error("Computed incorrect j-invariant: found\n", jbufRes, "\nexpected\n", jbufExp) } } func TestProjectivePointVartimeEq(t *testing.T) { var xP ProjectivePoint xP = ProjectivePoint{X: affineXP, Z: params.OneFp2} xQ := xP // Scale xQ, which results in the same projective point mul(&xQ.X, &xQ.X, &curveA) mul(&xQ.Z, &xQ.Z, &curveA) if !vartimeEqProjFp2(&xP, &xQ) { t.Error("Expected the scaled point to be equal to the original") } } func TestPointMulVersusSage(t *testing.T) { curve := ProjectiveCurveParameters{A: curveA, C: curveC} cparams := CalcCurveParamsEquiv4(&curve) var xP ProjectivePoint // x 2 xP = ProjectivePoint{X: affineXP, Z: params.OneFp2} Pow2k(&xP, &cparams, 1) afxQ := toAffine(&xP) if !vartimeEqFp2(afxQ, &affineXP2) { t.Error("\nExpected\n", affineXP2, "\nfound\n", afxQ) } // x 4 xP = ProjectivePoint{X: affineXP, Z: params.OneFp2} Pow2k(&xP, &cparams, 2) afxQ = toAffine(&xP) if !vartimeEqFp2(afxQ, &affineXP4) { t.Error("\nExpected\n", affineXP4, "\nfound\n", afxQ) } } func TestPointMul9VersusSage(t *testing.T) { curve := ProjectiveCurveParameters{A: curveA, C: curveC} cparams := CalcCurveParamsEquiv3(&curve) var xP ProjectivePoint xP = ProjectivePoint{X: affineXP, Z: params.OneFp2} Pow3k(&xP, &cparams, 2) afxQ := toAffine(&xP) if !vartimeEqFp2(afxQ, &affineXP9) { t.Error("\nExpected\n", affineXP9, "\nfound\n", afxQ) } } func BenchmarkThreePointLadder(b *testing.B) { curve := ProjectiveCurveParameters{A: curveA, C: curveC} for n := 0; n < b.N; n++ { ScalarMul3Pt(&curve, &threePointLadderInputs[0], &threePointLadderInputs[1], &threePointLadderInputs[2], uint(len(scalar3Pt)*8), scalar3Pt[:]) } } /* ------------------------------------------------------------------------- Generate invalid public key points / ciphertext for test TestKEMInvalidPK -------------------------------------------------------------------------*/ // Left-to-right Montgomery ladder, Algorithm 4 in Costello-Smith // Input: ProjectivePoint P (xP, zP) // Output: x([scalar]P), z([scalar]P) func montgomeryLadder(cparams *ProjectiveCurveParameters, P *ProjectivePoint, scalar []uint8, random uint) ProjectivePoint { var R0, R2, R1 ProjectivePoint coefEq := CalcCurveParamsEquiv4(cparams) // for xDbl aPlus2Over4 := CalcAplus2Over4(cparams) // for xDblAdd R0 = *P // RO <- P R1 = *P Pow2k(&R1, &coefEq, 1) // R1 <- [2]P R2 = *P // R2 = R1-R0 = P prevBit := uint8(0) for i := int(random); i >= 0; i-- { bit := (scalar[i>>3] >> (i & 7) & 1) swap := prevBit ^ bit prevBit = bit cswap(&R0.X, &R0.Z, &R1.X, &R1.Z, swap) R0, R1 = xDbladd(&R0, &R1, &R2, &aPlus2Over4) } cswap(&R0.X, &R0.Z, &R1.X, &R1.Z, prevBit) return R0 } // P = P + T // From paper https://eprint.iacr.org/2017/212.pdf // The map tau_T: P->P+T is (X : Z) -> (Z : X) on Montgomery curves func tauT(P *ProjectivePoint) { P.X, P.Z = P.Z, P.X // magic! } // Construct Invalid public key tuple (P,Q) such that P and Q are linearly dependent // Simulate section 3.1.1 of paper https://eprint.iacr.org/2022/054.pdf // We only construct point P and Q because in the attacks the third point is P-Q by construction // and the countermeasure does not test it // Without loss of generality, we assume the curve is the starting curve func testInvalidPKNoneLinear(t *testing.T) { // Generate random scalar as secret secret := make([]byte, params.B.SecretByteLen) _, err := io.ReadFull(crand.Reader, secret) if err != nil { t.Error("Fail read random bytes") } var P, Q ProjectivePoint rand.Seed(time.Now().UnixNano()) random_index := rand.Intn(int(params.B.SecretByteLen-1) * 8) // Set P as a point of order 3^e3 P = ProjectivePoint{X: params.B.AffineP, Z: params.OneFp2} // Set Q = [k]P, where k = secret[:random_index] Q = montgomeryLadder(¶ms.InitCurve, &P, secret, uint(random_index)) // Make sure Q is of full order 3^e_3, var test_Q ProjectivePoint test_Q = Q var e3 uint32 e3_float := float64(int(params.B.SecretBitLen)+1) / math.Log2(3) e3 = uint32(e3_float) cparam_q := CalcCurveParamsEquiv3(¶ms.InitCurve) Pow3k(&test_Q, &cparam_q, e3-1) var test_QZ Fp2 FromMontgomery(&test_QZ, &test_Q.Z) // Q are not of full order 3^e_3 for isZero(&test_QZ) == 1 { rand.Seed(time.Now().UnixNano()) random_index = rand.Intn(int(params.B.SecretByteLen-1) * 8) Q = montgomeryLadder(¶ms.InitCurve, &P, secret, uint(random_index)) test_Q = Q Pow3k(&test_Q, &cparam_q, e3-1) FromMontgomery(&test_QZ, &test_Q.Z) } // invQz = 1/Q.Z var invQz Fp2 invQz = Q.Z inv(&invQz, &invQz) mul(&P.X, &P.X, &P.Z) mul(&Q.X, &Q.X, &invQz) var xP, xQ, xQmP ProjectivePoint xP = ProjectivePoint{X: P.X, Z: params.OneFp2} xQ = ProjectivePoint{X: Q.X, Z: params.OneFp2} xQmP = ProjectivePoint{X: params.OneFp2, Z: params.OneFp2} error_verify := PublicKeyValidation(¶ms.InitCurve, &xP, &xQ, &xQmP, params.B.SecretBitLen) if error_verify == nil { t.Errorf("\nExpect linearly dependent ciphertext to fail, index: %v scalar: %v ", random_index, secret) } } // Construct Invalid public key tuple (P,Q) such that Q = [k]P + T, where k is random and T is the point of order 2. // Simulate HB and section 3.1.2 of paper https://eprint.iacr.org/2022/054.pdf // We only construct point P and Q because in the attacks the third point is P-Q by construction // and the countermeasure does not test it // Without loss of generality, we assume the curve is the starting curve func testInvalidPKT(t *testing.T) { // Generate random scalar as secret secret := make([]byte, params.B.SecretByteLen) _, err := io.ReadFull(crand.Reader, secret) if err != nil { t.Error("Fail read random bytes") } var P, Q ProjectivePoint rand.Seed(time.Now().UnixNano()) random_index := rand.Intn(int(params.B.SecretByteLen-1) * 8) // Set P as a point of order 3^e3 P = ProjectivePoint{X: params.B.AffineP, Z: params.OneFp2} // Set Q = [k]P, where k = secret[:random_index] Q = montgomeryLadder(¶ms.InitCurve, &P, secret, uint(random_index)) // Q = [k]P + T tauT(&Q) var invQz Fp2 invQz = Q.Z inv(&invQz, &invQz) mul(&P.X, &P.X, &P.Z) mul(&Q.X, &Q.X, &invQz) var xP, xQ, xQmP ProjectivePoint xP = ProjectivePoint{X: P.X, Z: params.OneFp2} xQ = ProjectivePoint{X: Q.X, Z: params.OneFp2} xQmP = ProjectivePoint{X: params.OneFp2, Z: params.OneFp2} error_verify := PublicKeyValidation(¶ms.InitCurve, &xP, &xQ, &xQmP, params.B.SecretBitLen) if error_verify == nil { t.Errorf("\nExpect ciphertext involve point T to fail, index: %v scalar: %v ", random_index, secret) } } // Construct Invalid public key tuple (P,Q) such that P and Q are in E[2^e2] // Simulate section 3.2 of paper https://eprint.iacr.org/2022/054.pdf // We only construct point P and Q because in the attacks the third point is P-Q by construction // and the countermeasure does not test it // Without loss of generality, we assume the curve is the starting curve func testInvalidPKOrder2(t *testing.T) { // Generate random scalar as secret secret := make([]byte, params.B.SecretByteLen) _, err := io.ReadFull(crand.Reader, secret) if err != nil { t.Error("Fail read random bytes") } var P, Q ProjectivePoint P = ProjectivePoint{X: params.A.AffineP, Z: params.OneFp2} Q = ProjectivePoint{X: params.A.AffineQ, Z: params.OneFp2} rand.Seed(time.Now().UnixNano()) random_index_p := rand.Intn(int(params.A.SecretByteLen-1) * 8) random_index_q := rand.Intn(int(params.A.SecretByteLen-1) * 8) P = montgomeryLadder(¶ms.InitCurve, &P, secret, uint(random_index_p)) Q = montgomeryLadder(¶ms.InitCurve, &Q, secret, uint(random_index_q)) var invQz, invPz Fp2 invQz = Q.Z invPz = P.Z inv(&invQz, &invQz) inv(&invPz, &invPz) mul(&P.X, &P.X, &invPz) mul(&Q.X, &Q.X, &invQz) var xP, xQ, xQmP ProjectivePoint xP = ProjectivePoint{X: P.X, Z: params.OneFp2} xQ = ProjectivePoint{X: Q.X, Z: params.OneFp2} xQmP = ProjectivePoint{X: params.OneFp2, Z: params.OneFp2} error_verify := PublicKeyValidation(¶ms.InitCurve, &xP, &xQ, &xQmP, params.B.SecretBitLen) if error_verify == nil { t.Errorf("\nExpect ciphertext in torsion E[2^e2] to fail, index_p: %v index_q: %v scalar: %v ", random_index_p, random_index_q, secret) } } // Construct Invalid public key tuple (P,Q) such that P and Q are in E[3^e3] but not of full order 3^e3 // Simulate section 3.1.1 of paper https://eprint.iacr.org/2022/054.pdf // We only construct point P and Q because in the attacks the third point is P-Q by construction // and the countermeasure does not test it // Without loss of generality, we assume the curve is the starting curve func testInvalidPKFullOrder(t *testing.T) { var P, Q ProjectivePoint P = ProjectivePoint{X: params.B.AffineP, Z: params.OneFp2} Q = ProjectivePoint{X: params.B.AffineQ, Z: params.OneFp2} var e3 uint32 e3_float := float64(int(params.B.SecretBitLen)+1) / math.Log2(3) e3 = uint32(e3_float) rand.Seed(time.Now().UnixNano()) random_index_p := rand.Intn(int(e3)) random_index_q := rand.Intn(int(e3)) cparam_q := CalcCurveParamsEquiv3(¶ms.InitCurve) Pow3k(&P, &cparam_q, uint32(random_index_p)) Pow3k(&Q, &cparam_q, uint32(random_index_q)) var invQz, invPz Fp2 invQz = Q.Z invPz = P.Z inv(&invQz, &invQz) inv(&invPz, &invPz) mul(&P.X, &P.X, &invPz) mul(&Q.X, &Q.X, &invQz) var xP, xQ, xQmP ProjectivePoint xP = ProjectivePoint{X: P.X, Z: params.OneFp2} xQ = ProjectivePoint{X: Q.X, Z: params.OneFp2} xQmP = ProjectivePoint{X: params.OneFp2, Z: params.OneFp2} error_verify := PublicKeyValidation(¶ms.InitCurve, &xP, &xQ, &xQmP, params.B.SecretBitLen) if error_verify == nil { t.Errorf("\nExpect ciphertext not of full order to fail, index_p: %v index_q: %v ", random_index_p, random_index_q) } } // A trivial test case not covered by paper https://eprint.iacr.org/2022/054.pdf and HB // Countermeasure in https://eprint.iacr.org/2022/054.pdf only cares about P and Q // But if PmQ is point T or O, that can also lead to recovery of the first bit func testInvalidPmQ(t *testing.T) { var zero Fp2 var xP, xQ, xQmP ProjectivePoint xP = ProjectivePoint{X: params.A.AffineP, Z: params.OneFp2} xQ = ProjectivePoint{X: params.A.AffineQ, Z: params.OneFp2} xQmP = ProjectivePoint{X: zero, Z: params.OneFp2} error_verify := PublicKeyValidation(¶ms.InitCurve, &xP, &xQ, &xQmP, params.B.SecretBitLen) if error_verify == nil { t.Errorf("\nExpect PmQ as T to fail\n") } } // Test valid ciphertext // Where P, Q are linearly independent points of correct order 3^e3 in E[3^e3] func testValidPQ(t *testing.T) { var xP, xQ, xQmP ProjectivePoint xP = ProjectivePoint{X: params.B.AffineP, Z: params.OneFp2} xQ = ProjectivePoint{X: params.B.AffineQ, Z: params.OneFp2} xQmP = ProjectivePoint{X: params.OneFp2, Z: params.OneFp2} error_verify := PublicKeyValidation(¶ms.InitCurve, &xP, &xQ, &xQmP, params.B.SecretBitLen) if error_verify != nil { t.Errorf("\nExpect correct ciphertext to not fail\n") } } /* ------------------------------------------------------------------------- Public key / Ciphertext validation against attacks proposed in paper https://eprint.iacr.org/2022/054.pdf and HB -------------------------------------------------------------------------*/ func TestInvalidPK(t *testing.T) { t.Run("InvalidPmQ", testInvalidPmQ) t.Run("InvalidPKNoneLinear", testInvalidPKNoneLinear) t.Run("InvalidPKT", testInvalidPKT) t.Run("InvalidPKOrder2", testInvalidPKOrder2) t.Run("InvalidPKFullOrder", testInvalidPKFullOrder) t.Run("ValidPQ", testValidPQ) }