#include #include #include #include "ec.h" #include "isog.h" #include "test-basis.h" #include static int BENCH_LOOPS = 1000; // Number of iterations per bench static int TEST_LOOPS = 128; // Number of iterations per test // void random_scalar(fp_t k, const uint8_t j) // { // // To implement a better random function (We must use some of the SHAKE family functions) // do // { // randombytes((void *)k, keyspace_bytes[j]); // } while (fp_issmaller((uint64_t *)k, keyspace_size[j])); // } // VERY NOT SECURE (testing only) void fp2_random(fp2_t *a){ for(int i = 0; i < NWORDS_FIELD; i++){ a->re[i] = rand(); a->im[i] = rand(); } // Normalize fp2_t one; fp_mont_setone(one.re);fp_set(one.im,0); fp2_mul(&*a, &*a, &one); // Update seed srand((unsigned) a->re[0]); } // Affine Montgomery coefficient computation (A + 2C : 4C) --> A/C void coeff(fp2_t *B, ec_point_t const A) { fp2_t t; fp2_add(&t, &A.x, &A.x); // (2 * A24) fp2_sub(&t, &t, &A.z); // (2 * A24) - C24 fp2_copy(&*B, &A.z); fp2_inv(&*B); // 1 / (C24) fp2_add(&t, &t, &t); // 4*A = 2[(2 * A24) - C24] fp2_mul(&*B, &t, &*B); // A/C = 2[(2 * A24) - C24] / C24 } // Determines if point is fp2-rational (if not, then it must be a zero trace point) uint8_t isrational(ec_point_t const T, fp2_t const a) { fp2_t XT, tmp, aux, YT_squared; fp2_copy(&XT, &T.z); fp2_inv(&XT); fp2_mul(&XT, &XT, &T.x); fp2_sqr(&tmp, &XT); fp2_mul(&aux, &tmp, &XT); fp2_mul(&tmp, &tmp, &a); fp2_add(&YT_squared, &tmp, &aux); fp2_add(&YT_squared, &YT_squared, &XT); return fp2_is_square(&YT_squared); } // ladder3pt computes x(P + [m]Q) void ladder3pt(ec_point_t* R, fp_t const m, ec_point_t const* P, ec_point_t const* Q, ec_point_t const* PQ, ec_point_t const* A) { ec_point_t X0, X1, X2; copy_point(&X0, Q); copy_point(&X1, P); copy_point(&X2, PQ); int i,j; uint64_t t; for (i = 0; i < NWORDS_FIELD; i++) { t = 1; for (j = 0 ; j < 64; j++) { swap_points(&X1, &X2, -((t & m[i]) == 0)); xDBLADD(&X0, &X1, &X0, &X1, &X2, A); swap_points(&X1, &X2, -((t & m[i]) == 0)); t <<= 1; }; }; copy_point(R, &X1); } // For computing [(p + 1) / l_i]P, i:=0, ..., (N - 1) void cofactor_multiples(ec_point_t P[], ec_point_t const* A, size_t lower, size_t upper) { assert(lower < upper); if (upper - lower == 1) return ; int i; size_t mid = lower + (upper - lower + 1) / 2; copy_point(&(P[mid]), &(P[lower])); for (i = lower; i < (int)mid; i++) xMULv2(&(P[mid]), &(P[mid]), &(TORSION_ODD_PRIMES[i]), p_plus_minus_bitlength[i], A); for (i = (int)mid; i < (int)upper; i++) xMULv2(&(P[lower]), &(P[lower]), &(TORSION_ODD_PRIMES[i]), p_plus_minus_bitlength[i], A); cofactor_multiples(P, A, lower, mid); cofactor_multiples(P, A, mid, upper); } // The projective x-coordinate point (X : Z) at infinity is such that Z == 0 static inline int isinfinity(ec_point_t const P) { return fp2_is_zero(&P.z); } int main(int argc, char* argv[]) { if (argc > 1) { TEST_LOOPS = atoi(argv[1]); } fp2_t fp2_0, fp2_1; fp2_set(&fp2_0, 0); fp_mont_setone(fp2_1.re);fp_set(fp2_1.im,0); int i, j; ec_point_t A; fp2_set(&A.x, 0); fp_mont_setone(A.z.re);fp_set(A.z.im,0); fp2_add(&A.z, &A.z, &A.z); // 2C fp2_add(&A.x, &A.x, &A.z); // A' + 2C fp2_add(&A.z, &A.z, &A.z); // 4C // Just to ensure the projective curve coeffientes are different from zero assert( !fp2_is_zero(&A.x) & !fp2_is_zero(&A.x) ); fp2_t a; coeff(&a, A); ec_point_t PA, QA, PQA, PB, QB, PQB; // Writing the public projective x-coordinate points into Montogmery domain fp2_tomont(&(PA.x), &(xPA)); fp_mont_setone(PA.z.re);fp_set(PA.z.im,0); fp2_tomont(&(QA.x), &(xQA)); fp_mont_setone(QA.z.re);fp_set(QA.z.im,0); fp2_tomont(&(PQA.x), &(xPQA)); fp_mont_setone(PQA.z.re);fp_set(PQA.z.im,0); assert( isrational(PA, a) ); assert( isrational(QA, a) ); assert( isrational(PQA, a) ); // ====================================================================================================== // Recall, PA, QA, and PQA are expeted to be N-order points, but we require to ensure they are of order N for (j = 0; j < P_LEN; j++) { for (i = 1; i < TORSION_ODD_POWERS[j]; i++) { xMULv2(&PA, &PA, &(TORSION_ODD_PRIMES[j]), p_plus_minus_bitlength[j], &A); xMULv2(&QA, &QA, &(TORSION_ODD_PRIMES[j]), p_plus_minus_bitlength[j], &A); xMULv2(&PQA, &PQA, &(TORSION_ODD_PRIMES[j]), p_plus_minus_bitlength[j], &A); assert( isrational(PA, a) ); assert( isrational(QA, a) ); assert( isrational(PQA, a) ); }; }; assert( !isinfinity(PA) ); assert( !isinfinity(QA) ); assert( !isinfinity(PQA) ); ec_point_t P[P_LEN + M_LEN], Q[P_LEN + M_LEN], PQ[P_LEN + M_LEN]; copy_point(&(P[0]), &PA); cofactor_multiples(P, &A, 0, P_LEN); copy_point(&(Q[0]), &QA); cofactor_multiples(Q, &A, 0, P_LEN); copy_point(&(PQ[0]), &PQA); cofactor_multiples(PQ, &A, 0, P_LEN); for (j = 0; j < P_LEN; j++) { // x(PA) assert( !isinfinity(P[j]) ); // It must be different from the point at infinity assert( isrational(P[j], a) ); xMULv2(&P[j], &P[j], &(TORSION_ODD_PRIMES[j]), p_plus_minus_bitlength[j], &A); assert( isinfinity(P[j]) ); // It must be now the point at infinity // x(QA) assert( !isinfinity(Q[j]) ); // It must be different from the point at infinity assert( isrational(Q[j], a) ); xMULv2(&Q[j], &Q[j], &(TORSION_ODD_PRIMES[j]), p_plus_minus_bitlength[j], &A); assert( isinfinity(Q[j]) ); // It must be now the point at infinity // x(PQA) assert( !isinfinity(PQ[j]) ); // It must be different from the point at infinity assert( isrational(PQ[j], a) ); xMULv2(&PQ[j], &PQ[j], &(TORSION_ODD_PRIMES[j]), p_plus_minus_bitlength[j], &A); assert( isinfinity(PQ[j]) ); // It must be now the point at infinity }; // Writing the public projective x-coordinate points into Montogmery domain fp2_tomont(&(PB.x), &(xPB)); fp_mont_setone(PB.z.re);fp_set(PB.z.im,0); fp2_tomont(&(QB.x), &(xQB)); fp_mont_setone(QB.z.re);fp_set(QB.z.im,0); fp2_tomont(&(PQB.x), &(xPQB)); fp_mont_setone(PQB.z.re);fp_set(PQB.z.im,0); assert( !isrational(PB, a) ); assert( !isrational(QB, a) ); assert( !isrational(PQB, a) ); // ====================================================================================================== // Recall, PB, QB, and PQB are expeted to be M-order points, but we require to ensure they are of order M for (j = P_LEN; j < (P_LEN + M_LEN); j++) { for (i = 1; i < TORSION_ODD_POWERS[j]; i++) { xMULv2(&PB, &PB, &(TORSION_ODD_PRIMES[j]), p_plus_minus_bitlength[j], &A); xMULv2(&QB, &QB, &(TORSION_ODD_PRIMES[j]), p_plus_minus_bitlength[j], &A); xMULv2(&PQB, &PQB, &(TORSION_ODD_PRIMES[j]), p_plus_minus_bitlength[j], &A); assert( !isrational(PB, a) ); assert( !isrational(QB, a) ); assert( !isrational(PQB, a) ); }; }; assert( !isinfinity(PB) ); assert( !isinfinity(QB) ); assert( !isinfinity(PQB) ); copy_point(&(P[P_LEN]), &PB); cofactor_multiples(P, &A, P_LEN, P_LEN + M_LEN); copy_point(&(Q[P_LEN]), &QB); cofactor_multiples(Q, &A, P_LEN, P_LEN + M_LEN); copy_point(&(PQ[P_LEN]), &PQB); cofactor_multiples(PQ, &A, P_LEN, P_LEN + M_LEN); for (j = P_LEN; j < (P_LEN+M_LEN); j++) { // x(PB) assert( !isinfinity(P[j]) ); // It must be different from the point at infinity assert( !isrational(P[j], a) ); xMULv2(&P[j], &P[j], &(TORSION_ODD_PRIMES[j]), p_plus_minus_bitlength[j], &A); assert( isinfinity(P[j]) ); // It must be now the point at infinity // x(QB) assert( !isinfinity(Q[j]) ); // It must be different from the point at infinity assert( !isrational(Q[j], a) ); xMULv2(&Q[j], &Q[j], &(TORSION_ODD_PRIMES[j]), p_plus_minus_bitlength[j], &A); assert( isinfinity(Q[j]) ); // It must be now the point at infinity // x(PQB) assert( !isinfinity(PQ[j]) ); // It must be different from the point at infinity assert( !isrational(PQ[j], a) ); xMULv2(&PQ[j], &PQ[j], &(TORSION_ODD_PRIMES[j]), p_plus_minus_bitlength[j], &A); assert( isinfinity(PQ[j]) ); // It must be now the point at infinity }; fp2_t m; // Writing the public projective x-coordinate points into Montogmery domain fp2_tomont(&(PA.x), &(xPA)); fp_mont_setone(PA.z.re);fp_set(PA.z.im,0); fp2_tomont(&(QA.x), &(xQA)); fp_mont_setone(QA.z.re);fp_set(QA.z.im,0); fp2_tomont(&(PQA.x), &(xPQA)); fp_mont_setone(PQA.z.re);fp_set(PQA.z.im,0); assert( isrational(PA, a) ); assert( isrational(QA, a) ); assert( isrational(PQA, a) ); fp2_tomont(&(PB.x), &(xPB)); fp_mont_setone(PB.z.re);fp_set(PB.z.im,0); fp2_tomont(&(QB.x), &(xQB)); fp_mont_setone(QB.z.re);fp_set(QB.z.im,0); fp2_tomont(&(PQB.x), &(xPQB)); fp_mont_setone(PQB.z.re);fp_set(PQB.z.im,0); assert( !isrational(PB, a) ); assert( !isrational(QB, a) ); assert( !isrational(PQB, a) ); ec_point_t R[P_LEN + M_LEN]; int k; for (j = 0; j < TEST_LOOPS; j++) { printf("[%3d%%] Testing EC differential arithmetic", 100 * j / TEST_LOOPS); fflush(stdout); printf("\r\x1b[K"); fp2_random(&m); ladder3pt(&(R[0]), m.re, &PA, &QA, &PQA, &A); assert( isrational(R[0], a) ); for (k = 0; k < P_LEN; k++) { for (i = 1; i < TORSION_ODD_POWERS[k]; i++) { xMULv2(&R[0], &R[0], &(TORSION_ODD_PRIMES[k]), p_plus_minus_bitlength[k], &A); assert( isrational(R[0], a) ); }; }; cofactor_multiples(R, &A, 0, P_LEN); for (i = 0; i < P_LEN; i++) { assert( !isinfinity(R[i]) ); // It must be different from the point at infinity assert( isrational(R[i], a) ); xMULv2(&R[i], &R[i], &(TORSION_ODD_PRIMES[i]), p_plus_minus_bitlength[i], &A); assert( isinfinity(R[i]) ); // It must be now the point at infinity }; fp2_random(&m); ladder3pt(&(R[P_LEN]), m.re, &PB, &QB, &PQB, &A); assert( !isrational(R[P_LEN], a) ); for (k = P_LEN; k < (P_LEN+M_LEN); k++) { for (i = 1; i < TORSION_ODD_POWERS[k]; i++) { xMULv2(&R[P_LEN], &R[P_LEN], &(TORSION_ODD_PRIMES[k]), p_plus_minus_bitlength[k], &A); assert( !isrational(R[P_LEN], a) ); }; }; cofactor_multiples(R, &A, P_LEN, P_LEN + M_LEN); for (i = P_LEN; i < (P_LEN+M_LEN); i++) { assert( !isinfinity(R[i]) ); // It must be different from the point at infinity assert( !isrational(R[i], a) ); xMULv2(&R[i], &R[i], &(TORSION_ODD_PRIMES[i]), p_plus_minus_bitlength[i], &A); assert( isinfinity(R[i]) ); // It must be now the point at infinity }; }; if(TEST_LOOPS) printf("[%3d%%] Tested EC differential arithmetic:\tNo errors!\n", 100 * j / TEST_LOOPS); printf("-- All tests passed.\n"); // BENCHMARK xDBLv2 unsigned long long cycles, cycles1, cycles2; cycles = 0; ec_point_t PP[TEST_LOOPS], EE[TEST_LOOPS]; for(int i = 0; i < TEST_LOOPS; i++){ fp2_random(&PP[i].x); fp2_random(&PP[i].z); fp2_random(&EE[i].x); fp2_random(&EE[i].z); } cycles1 = cpucycles(); for(int i = 0; i < TEST_LOOPS; i++){ xDBLv2(&PP[i], &PP[i], &EE[i]); } cycles2 = cpucycles(); cycles = cycles+(cycles2-cycles1); printf("xDBLv2 bench: %7lld cycles\n", cycles/TEST_LOOPS); // BENCHMARK xIsog4 cycles = 0; ec_point_t KK0[TEST_LOOPS], KK1[TEST_LOOPS], KK2[TEST_LOOPS]; for(int i = 0; i < TEST_LOOPS; i++){ fp2_random(&KK0[i].x); fp2_random(&KK0[i].z); fp2_random(&KK1[i].x); fp2_random(&KK1[i].z); fp2_random(&KK2[i].x); fp2_random(&KK2[i].z); } cycles1 = cpucycles(); for(int i = 0; i < TEST_LOOPS; i++){ fp2_t t0, t1; fp2_add(&t0, &PP[i].x, &PP[i].z); fp2_sub(&t1, &PP[i].x, &PP[i].z); fp2_mul(&(EE[i].x), &t0, &KK1[i].x); fp2_mul(&(EE[i].z), &t1, &KK2[i].x); fp2_mul(&t0, &t0, &t1); fp2_mul(&t0, &t0, &KK0[i].x); fp2_add(&t1, &(EE[i].x), &(EE[i].z)); fp2_sub(&(EE[i].z), &(EE[i].x), &(EE[i].z)); fp2_sqr(&t1, &t1); fp2_sqr(&(EE[i].z), &(EE[i].z)); fp2_add(&(EE[i].x), &t0, &t1); fp2_sub(&t0, &(EE[i].z), &t0); fp2_mul(&(EE[i].x), &(EE[i].x), &t1); fp2_mul(&(EE[i].z), &(EE[i].z), &t0); } cycles2 = cpucycles(); cycles = cycles+(cycles2-cycles1); printf("xeval_4 bench: %7lld cycles\n", cycles/TEST_LOOPS); return 0; }