initial version of SQIsign

Co-authored-by: Jorge Chavez-Saab <jorgechavezsaab@gmail.com>
Co-authored-by: Maria Corte-Real Santos <36373796+mariascrs@users.noreply.github.com>
Co-authored-by: Luca De Feo <github@defeo.lu>
Co-authored-by: Jonathan Komada Eriksen <jonathan.eriksen97@gmail.com>
Co-authored-by: Basil Hess <bhe@zurich.ibm.com>
Co-authored-by: Antonin Leroux <18654258+tonioecto@users.noreply.github.com>
Co-authored-by: Patrick Longa <plonga@microsoft.com>
Co-authored-by: Lorenz Panny <lorenz@yx7.cc>
Co-authored-by: Francisco Rodríguez-Henríquez <francisco.rodriguez@tii.ae>
Co-authored-by: Sina Schaeffler <108983332+syndrakon@users.noreply.github.com>
Co-authored-by: Benjamin Wesolowski <19474926+Calodeon@users.noreply.github.com>
This commit is contained in:
SQIsign team
2023-06-01 00:00:00 +00:00
committed by Lorenz Panny
commit 28ff420dd0
285 changed files with 70301 additions and 0 deletions

View File

@@ -0,0 +1,18 @@
#include "ec-tests.h"
int main(int argc, char* argv[])
{
if (argc < 3) {
printf("Please enter an argument: 'test' or 'bench' and <reps>\n");
exit(1);
}
if (!strcmp(argv[1], "test")) {
TEST_LOOPS = atoi(argv[2]);
return !(ec_test() & dlog_test());
} else if (!strcmp(argv[1], "bench")) {
BENCH_LOOPS = atoi(argv[2]);
return !(ec_run() & dlog_run());
} else {
exit(1);
}
}

View File

@@ -0,0 +1,142 @@
#include <assert.h>
#include <time.h>
#include <stdio.h>
#include <fp2.h>
#include <inttypes.h>
static int BENCH_LOOPS = 1000; // Number of iterations per bench
static int TEST_LOOPS = 512; // Number of iterations per test
bool fp2_isequal(fp2_t a, fp2_t b){
return fp_is_equal(a.re, b.re) && fp_is_equal(a.im, b.im);
}
bool fp2_isone(fp2_t a){
fp_t one;
bool res = 1;
fp_mont_setone(one);
for(int i = 0; i < NWORDS_FIELD; i++){
res = res && (a.re[i] == one[i]);
res = res && (a.im[i] == 0);
}
return res;
}
void fp2_print(char *name, fp2_t const a){
fp2_t b;
fp2_set(&b, 1);
fp2_mul(&b, &b, &a);
printf("%s = 0x", name);
for(int i = NWORDS_FIELD - 1; i >=0; i--)
printf("%016" PRIx64, b.re[i]);
printf(" + i*0x");
for(int i = NWORDS_FIELD - 1; i >=0; i--)
printf("%016" PRIx64, b.im[i]);
printf("\n");
}
// 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]);
}
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;
fp2_t a, b, c, d;
fp_t e;
for (i = 0; i < TEST_LOOPS; i++)
{
printf("[%3d%%] Testing fp2_t arithmetic", 100 * i / (int)TEST_LOOPS);
fflush(stdout);
printf("\r\x1b[K");
// Random elements of fp
fp2_random(&a);
fp2_random(&b);
fp2_copy(&c, &a);
c.re[0] += 1;
fp2_copy(&d, &b);
d.re[0] -= 1;
assert(fp2_isequal(a,b) == 0); // different values check --> (a != b)
assert(fp2_isequal(c,c) == 1); // equal values check --> 1 (c == c)
// Testing neg
fp2_set(&b, 0);
fp2_copy(&c, &a);
fp2_neg(&a, &a);
fp2_sub(&c, &b, &c);
assert(fp2_isequal(a,c) == 1);
fp_mont_setone(a.re);fp_set(a.im,0); // Now a == 1
fp2_set(&b, 0); // Now b == 0
assert(fp2_is_zero(&a) == 0);
assert(fp2_is_zero(&b) == 1);
// testing c - c
fp2_sub(&d, &c, &c);
assert(fp2_is_zero(&d) == 1);
// tetsing c * 0
fp2_mul(&d, &c, &b);
assert(fp2_is_zero(&d) == 1);
// tetsing c * 1 ... recall, in Montgomery domain R mod p plays the role of the 1
fp_mont_setone(a.re);fp_set(a.im,0);
fp2_mul(&d, &c, &a);
assert(fp2_isequal(d, c) == 1);
// fp_set(e, 1); // Now e == 1
// fp2_pow(d, e, c);
// assert(fp2_isequal(d, c) == 1);
// fp_set(e, 0); // Now e == 0
// fp2_pow(d, e, c);
// assert(fp2_isone(d) == 1);
// fp2_set(a, 1); // Now e == R mod p
// fp_random(e);
// fp2_pow(d, e, a);
// assert(fp2_isone(d) == 1);
// Testing 1/a by computing (1/a) x a
fp2_random(&a);
fp2_copy(&b, &a);
fp2_inv(&a);
fp2_mul(&c, &a, &b);
assert(fp2_isone(c) == 1);
fp2_random(&a);
fp2_sqr(&b, &a);
assert( fp2_is_square(&b) );
};
if(TEST_LOOPS){
printf("[%2d%%] Tested fp2_t arithmetic:\tNo errors!\n", 100 * i /TEST_LOOPS);
}
printf("-- All tests passed.\n");
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,386 @@
#include <time.h>
#include <assert.h>
#include <stdio.h>
#include "ec.h"
#include "isog.h"
#include "test-basis.h"
#include <bench.h>
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;
}

View File

@@ -0,0 +1,445 @@
#include <poly.h>
#include <assert.h>
#include <stdio.h>
bool fp2_isequal(fp2_t a, fp2_t b){
return fp_is_equal(a.re, b.re) && fp_is_equal(a.im, b.im);
}
// 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]);
}
void slow_mul(poly h, poly f, int lenf, poly g, int leng){
// Computes h = f*g by school method
fp2_t a, b;
int nf, ng, e;
int lenh = lenf + leng - 1;
if(lenh <= 0){
return;
}
fp2_t fg[lenh];
if (leng > lenf){
slow_mul(h, g, leng, f, lenf);
return;
}
for(e = 0; e < lenh; e++){
if (lenf - 1 < e){
nf = lenf - 1;
}
else{
nf = e;
}
ng = e - nf;
fp2_set(&a, 0);
while( (ng < leng) & (nf >= 0) ){
fp2_mul(&b, &f[nf], &g[ng]);
fp2_add(&a, &a, &b);
nf--;
ng++;
}
fp2_copy(&fg[e], &a);
}
for(e = 0; e < lenh; e++){
fp2_copy(&h[e], &fg[e]);
}
return;
}
int main(){
fp2_t fp2_0, fp2_1;
#define nmax 16
int nf, ng, n, e;
fp2_set(&fp2_0, 0);
fp_mont_setone(fp2_1.re);fp_set(fp2_1.im,0);
//TEST MULTIPLICATION BY 0
for(nf = 2; nf < nmax; nf++){
fp2_t f[nf], h[nf-1];
printf("[%3d%%] Testing multiplication by 0", 100 * nf / nmax);
fflush(stdout);
printf("\r\x1b[K");
for(e = 0; e < nf; e++){
fp2_random(&f[e]);
}
poly_mul(h, f, nf, f, 0);
for(e = 0; e < nf-1; e++){
assert(fp2_is_zero(&h[e])==1);
}
poly_mul(h, f, 0, f, nf);
for(e = 0; e < nf-1; e++){
assert(fp2_is_zero(&h[e])==1);
}
}
printf("[%3d%%] Tested multiplication by 0:\t\tNo errors!\n", 100 * nf / nmax);
//TEST FOR f, g, h DISJOINT MEMORY SPACES
for(nf = 1; nf < nmax; nf++){
printf("[%3d%%] Testing multiplication", 100 * nf / nmax);
fflush(stdout);
printf("\r\x1b[K");
for(ng = 1; ng < nmax; ng++){
fp2_t f[nf]; //Random length nf poly
for(e = 0; e < nf; e++){
fp2_random(&f[e]);
}
fp2_t g[ng]; // Random length ng poly
for(e = 0; e < ng; e++){
fp2_random(&g[e]);
}
fp2_t h[nf+ng-1];// Compute product
poly_mul(h, f, nf, g, ng);
fp2_t fg[nf+ng-1]; // Compute the product by school method
slow_mul(fg, f, nf, g, ng);
for(e = 0; e < nf + ng - 1; e++){ // Verify answer term by term
assert(fp2_isequal(h[e], fg[e])==1);
}
}
}
printf("[%3d%%] Tested multiplication:\t\t\tNo errors!\n", 100 * nf / nmax);
// TEST FOR f, g CONTIGIOUS AND RESULT SAVED OVER THEM
for(nf = 1; nf < nmax; nf++){
printf("[%3d%%] Testing multiplication in place", 100 * nf / nmax);
fflush(stdout);
printf("\r\x1b[K");
for(ng = 1; ng < nmax; ng++){
fp2_t h[nf+ng];
//Random length nf poly
for(e = 0; e < nf; e++){
fp2_random(&h[e]);
}
// Random length ng poly
for(e = 0; e < ng; e++){
fp2_random(&h[e+nf]);
}
// Compute the product
fp2_t fg[nf+ng-1];
slow_mul(fg, h, nf, &(h[nf]), ng); // School method
poly_mul(h, h, nf, &(h[nf]), ng); // Karatsuba method
for(e = 0; e < nf + ng - 1; e++){ // Verify answer term by term
assert(fp2_isequal(h[e], fg[e])==1);
}
}
}
printf("[%3d%%] Tested multiplication in place:\t\tNo errors!\n", 100 * nf / nmax);
//TEST FOR MULTIPLICATION MOD X^N BY 0
for(nf = 2; nf < nmax; nf++){
fp2_t f[nf];
printf("[%3d%%] Testing mul mod x^n by 0", 100 * nf / nmax);
fflush(stdout);
printf("\r\x1b[K");
for(e = 0; e < nf; e++){
fp2_random(&f[e]);
}
for(n = 1; n < nmax; n++){
fp2_t h[n];
poly_mul_low(h, n, f, nf, f, 0);
for(e = 0; e < n; e++){
assert(fp2_is_zero(&h[e])==1);
}
poly_mul_low(h, n, f, 0, f, nf);
for(e = 0; e < n; e++){
assert(fp2_is_zero(&h[e])==1);
}
}
}
printf("[%3d%%] Tested mul mod x^n by 0:\t\t\tNo errors!\n", 100 * nf / nmax);
//TEST FOR MULTIPLICATION MOD X^N
for(nf = 1; nf < nmax; nf++){
printf("[%3d%%] Testing mul mod x^n", 100 * nf / nmax);
fflush(stdout);
printf("\r\x1b[K");
for(ng = 1; ng < nmax; ng++){
fp2_t f[nf], g[ng], fg[nf+ng-1];
poly h;
//Get random polynomials
for(e = 0; e < nf; e++){
fp2_random(&f[e]);
}
for(e = 0; e < ng; e++){
fp2_random(&g[e]);
}
//Save regular result to fg
slow_mul(fg, f, nf, g, ng);
//Compute result mod x^n
for(n = 1; n < 2*nmax; n++){
h = malloc(sizeof(fp2_t)*n);
poly_mul_low(h, n, f, nf, g, ng);
//Compare with expected
e = 0;
while(e < nf+ng-1 && e < n){
assert(fp2_isequal(h[e], fg[e]) == 1);
e++;
}
while(e < n){
assert(fp2_is_zero(&h[e]) == 1);
e++;
}
free(h);
}
}
}
printf("[%3d%%] Tested mul mod x^n:\t\t\tNo errors!\n", 100 * nf / nmax);
//TEST FOR POLY_MUL_MIDDLE
for(nf = 1; nf < 2*nmax; nf+=1){
fp2_t f[nf];
printf("[%3d%%] Testing poly_mul_middle", 100 * nf / (2*nmax));
fflush(stdout);
printf("\r\x1b[K");
for(ng = (nf+1)>>1; ng < (nf+1)-((nf+1)>>1); ng++){
// This runs from floor((nf+1)/2) to ceil((nf+1)/2)
fp2_t g[ng];
for(e = 0; e < nf; e++){
fp2_random(&f[e]);
}
for(e = 0; e < ng; e++){
fp2_random(&g[e]);
}
fp2_t h[nf+ng-1];
slow_mul(h, g, ng, f, nf);
poly_mul_middle(g, g, ng, f, nf);
for(e = 0; e < ng; e++){
assert(fp2_isequal(h[e+nf-ng], g[e])==1);
}
}
}
printf("[%3d%%] Tested poly_mul_middle:\t\t\tNo errors!\n", 100 * nf / (2*nmax));
// TEST FOR SELF RECIPROCAL MULTIPLICATION
for(nf = 1; nf < nmax; nf++){
printf("[%3d%%] Testing self reciprocal mul", 100 * nf / nmax);
fflush(stdout);
printf("\r\x1b[K");
for(ng = 1; ng < nmax; ng++){
fp2_t f[nf], g[ng], h[nf+ng-1], fg[nf+ng-1];
// Get random palyndromes
for(e = 0; e < (nf>>1); e++){
fp2_random(&f[e]);
fp2_copy(&f[nf-1-e], &f[e]);
}
if(nf & 1){
fp2_random(&f[nf>>1]);
}
for(e = 0; e < (ng>>1); e++){
fp2_random(&g[e]);
fp2_copy(&g[ng-1-e], &g[e]);
}
if(ng & 1){
fp2_random(&g[ng>>1]);
}
// Compute products
poly_mul_selfreciprocal(h, g, ng, f, nf);
slow_mul(fg, g, ng, f, nf);
// Compare
for(e = 0; e < nf+ng-1; e++){
assert(fp2_isequal(fg[e], h[e])==1);
}
}
}
printf("[%3d%%] Tested self reciprocal mul:\t\tNo errors!\n", 100 * nf / nmax);
// TEST FOR PRODUCT TREES
int tree_size, iteration, i;
int len, *DEG, LENF;
poly *H, *F, h;
for(tree_size = 1; tree_size < nmax; tree_size++){
printf("[%3d%%] Testing product tree:\t\t\tSize %d out of %d", 100 * tree_size / nmax, tree_size, nmax-1);
fflush(stdout);
printf("\r\x1b[K");
i = 0;
while((1<<i) < tree_size){
i++;
}
DEG = malloc(sizeof(int)*((1<<(i+2))-1));
H = malloc(sizeof(poly)*((1<<(i+2))-1));
F = malloc(sizeof(poly)*tree_size);
h = malloc(sizeof(fp2_t)*(nmax+1)*tree_size);
for(iteration = 0; iteration < nmax + 1 - tree_size ; iteration++){
// Generate random list of polynomials
LENF = (rand() % nmax)+1;
for(i = 0; i < tree_size; i++){
F[i] = malloc(sizeof(fp2_t)*LENF);
for(e = 0; e < LENF; e++){
fp2_random(&F[i][e]);
}
}
product_tree(H, DEG, 0, F, LENF, tree_size);
// Build product of all polynomials manually
len = LENF;
//for(e = 0; e < LENF[0]; e++){
for(e = 0; e < LENF; e++){
fp2_copy(&h[e], &F[0][e]);
}
for(i = 1; i < tree_size; i++){
poly_mul(h, h, len, F[i], LENF);
len += LENF-1;
}
// Compare to root
assert (len == DEG[0]+1);
for(e = 0; e < len; e++){
assert(fp2_isequal(H[0][e], h[e])==1);
}
clear_tree(H, 0, tree_size);
for(i = 0; i < tree_size; i++){
free(F[i]);
}
}
free(DEG);
free(H);
free(F);
free(h);
}
printf("[%3d%%] Tested product tree:\t\t\tNo errors!\n", 100 * tree_size / nmax);
// TEST FOR SELF RECIPROCAL PRODUCT TREES
for(tree_size = 1; tree_size < nmax; tree_size++){
printf("[%3d%%] Testing selfreciprocal product tree:\tSize %d out of %d", 100 * tree_size / nmax, tree_size, nmax-1);
fflush(stdout);
printf("\r\x1b[K");
i = 0;
while((1<<i) < tree_size){
i++;
}
DEG = malloc(sizeof(int)*((1<<(i+2))-1));
H = malloc(sizeof(poly)*((1<<(i+2))-1));
F = malloc(sizeof(poly)*tree_size);
h = malloc(sizeof(fp2_t)*(nmax+1)*tree_size);
for(iteration = 0; iteration < nmax + 1 - tree_size ; iteration++){
// Generate random list of polynomials
LENF = (rand() % nmax)+1;;
for(i = 0; i < tree_size; i++){
F[i] = malloc(sizeof(fp2_t)*LENF);
for(e = 0; e < (LENF>>1); e++){
fp2_random(&F[i][e]);
fp2_copy(&F[i][LENF-1-e], &F[i][e]);
}
if(LENF & 1){
fp2_random(&F[i][(LENF>>1)]);
}
}
product_tree_selfreciprocal(H, DEG, 0, F, LENF, tree_size);
// Build product of all polynomials manually
len = LENF;
for(e = 0; e < LENF; e++){
fp2_copy(&h[e], &F[0][e]);
}
for(i = 1; i < tree_size; i++){
poly_mul(h, h, len, F[i], LENF);
len += LENF-1;
}
// Compare to root
assert (len == DEG[0]+1);
for(e = 0; e < len; e++){
assert(fp2_isequal(H[0][e], h[e])==1);
}
clear_tree(H, 0, tree_size);
for(i = 0; i < tree_size; i++){
free(F[i]);
}
}
free(DEG);
free(H);
free(F);
free(h);
}
printf("[%3d%%] Tested selfreciprocal product tree:\tNo errors!\n", 100 * tree_size / nmax);
printf("-- All tests passed.\n");
return 0;
}

View File

@@ -0,0 +1,461 @@
#include "poly.h"
#include <assert.h>
#include <stdio.h>
#define nmax 32
bool fp2_isequal(fp2_t a, fp2_t b){
return fp_is_equal(a.re, b.re) && fp_is_equal(a.im, b.im);
}
// 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]);
}
int main(){
fp2_t fp2_0, fp2_1;
fp2_set(&fp2_0, 0);
fp_mont_setone(fp2_1.re);fp_set(fp2_1.im,0);
int lenf, leng, n, e, iteration, array_size, tree_size, i, root, brother, *DEG, LENF;
poly f, g, h, f_rev, f_rev_inv, *F, *H, *R, g1, g2, REM1, REM2, G1, G2, G1_rev, G2_rev, R0;
fp2_t c, *A, *C, ratio, A0;
f_rev_inv = 0;
// TEST FOR RECIPROCAL
for(lenf = 1; lenf < nmax; lenf++)
{
printf("[%3d%%] Testing reciprocals", 100 * lenf / nmax);
fflush(stdout);
printf("\r\x1b[K");
// Get random poly
f = malloc(sizeof(fp2_t)*lenf);
for(e = 0; e < lenf; e++)
fp2_random(&f[e]);
for(n = 1; n < nmax; n++)
{
// Get the reciprocal and multiply them
h = malloc(sizeof(fp2_t)*n);
memset(h, 0, sizeof(fp2_t)*n);
reciprocal(h, &c, f, lenf, n);
poly_mul_low(h, n, f, lenf, h, n);
// Compare with expected
assert(fp2_isequal(h[0],c));
for(e = 1; e < n; e++)
assert(fp2_is_zero(&h[e]));
free(h);
}
free(f);
}
printf("[%3d%%] Tested reciprocals:\t\tNo errors!\n", 100 * lenf / nmax);
// TEST FOR REDUCTION
for(lenf = 2; lenf < nmax; lenf++)
{
printf("[%3d%%] Testing polynomial reduction", 100 * lenf / nmax);
fflush(stdout);
printf("\r\x1b[K");
// Get random poly for the mod
f = malloc(sizeof(fp2_t)*lenf);
f_rev = malloc(sizeof(fp2_t)*lenf);
for(e = 0; e < lenf; e++)
{
fp2_random(&f[e]);
fp2_copy(&f_rev[lenf-1-e], &f[e]);
}
for(leng = 1; leng < nmax; leng++)
{
// Get random poly to reduce
g = malloc(sizeof(fp2_t)*leng);
for(e = 0; e < leng; e++){
fp2_random(&g[e]);
}
// Get reverse-inverse mod x^(leng-lenf+1)
if(leng >= lenf)
{
f_rev_inv = malloc(sizeof(fp2_t)*(leng-lenf+1));
reciprocal(f_rev_inv, &c, f_rev, lenf, leng-lenf+1);
}
else{
fp_mont_setone(c.re);fp_set(c.im,0);
}
// Compute the reduction
h = malloc(sizeof(fp2_t)*(lenf-1));
poly_redc(h, g, leng, f, lenf, f_rev_inv, c);
// Reduce manually
int leng_red = leng;
fp2_t scale, f_e;
while(leng_red >= lenf)
{
fp2_copy(&scale, &f[lenf-1]);
fp2_inv(&scale);
fp2_mul(&scale, &scale, &g[leng_red-1]);
for(e = 0; e < lenf; e++)
{
fp2_mul(&f_e, &f[e], &scale);
fp2_sub(&g[e+leng_red-lenf], &g[e+leng_red-lenf], &f_e);
}
leng_red--;
}
// Rescale manual result
if( leng < lenf){
fp_mont_setone(scale.re);fp_set(scale.im,0);
}
else
if(lenf == 2 && leng == 3)
{
fp2_sqr(&scale, &f[1]);
fp2_add(&scale, &scale, &scale);
}
else
fp2_copy(&scale, &c);
for(e = 0; e < leng_red; e++)
fp2_mul(&g[e], &g[e], &scale);
// Comapre results
for(e = leng_red-1; e >= 0; e--)
assert(fp2_isequal(h[e], g[e]));
for(e = leng_red; e < lenf-1; e++)
assert(fp2_is_zero(&h[e]));
free(g);
free(h);
if(leng >= lenf)
free(f_rev_inv);
}
free(f);
free(f_rev);
}
printf("[%3d%%] Tested polynomial reduction:\tNo errors!\n", 100 * lenf / nmax);
// TEST FOR RECIPROCAL TREES
for(tree_size = 3; tree_size < nmax; tree_size++)
{
printf("[%3d%%] Testing reciprocal tree:\t\tTree size %d out of %d", 100 * tree_size / nmax, tree_size, nmax);
fflush(stdout);
printf("\r\x1b[K");
// Compute size of arrays
i = 0;
while((1<<i) < tree_size){
i++;
}
array_size = (1<<(i+2))-1;
DEG = malloc(sizeof(int)*array_size);
H = malloc(sizeof(poly)*array_size);
R = malloc(sizeof(poly)*array_size);
F = malloc(sizeof(poly)*tree_size);
A = malloc(sizeof(fp2_t)*array_size);
// Get random polys
LENF = 2;
for(i = 0; i < tree_size; i++)
{
F[i] = malloc(sizeof(fp2_t)*LENF);
for(e = 0; e < LENF; e++){
fp2_random(&F[i][e]);
}
}
// Get product tree then reciprocal tree
product_tree(H, DEG, 0, F, LENF, tree_size);
leng = DEG[0]+1+(rand() % nmax);
reciprocal_tree(R, A, leng, H, DEG, 0, tree_size);
// Check the root
root = 0;
lenf = leng-DEG[root];
f = malloc(sizeof(fp2_t)*lenf);
for(e = 0; e < DEG[root]+1 && e < lenf; e++){
fp2_copy(&f[e], &H[root][DEG[root]-e]);
}
for(e = DEG[root]+1; e < lenf; e++){
fp2_set(&f[e], 0);
}
poly_mul_low(f, lenf, f, lenf, R[root], lenf);
assert(fp2_isequal(f[0], A[root]));
for(e = 1; e < lenf; e++){
assert(fp2_is_zero(&f[e]));
}
free(f);
// Perform random walks
for(iteration = 0; iteration < nmax - tree_size; iteration++)
{
root = 0;
n = tree_size;
while(n > 1)
{
if(rand() & 1)
{
root = 2*root+1;
n = n - (n>>1);
}
else
{
root = 2*root+2;
n = n>>1;
}
brother = root - 1 + 2*(root & 1);
// Check current node
if(DEG[root] > 2)
{
lenf = DEG[brother];
f = malloc(sizeof(fp2_t)*lenf);
for(e = 0; e < DEG[root]+1 && e < lenf; e++){
fp2_copy(&f[e], &H[root][DEG[root]-e]);
}
for(e = DEG[root]+1; e < lenf; e++){
fp2_set(&f[e], 0);
}
poly_mul_low(f, lenf, f, lenf, R[root], lenf);
assert(fp2_isequal(f[0], A[root]));
for(e = 1; e < lenf; e++){
assert(fp2_is_zero(&f[e]));
}
free(f);
}
}
}
// Clean up
for(i = 0; i < tree_size; i++)
free(F[i]);
clear_tree(H, 0, tree_size);
clear_tree(R, 0, tree_size);
free(F);
free(H);
free(R);
free(A);
free(DEG);
}
printf("[%3d%%] Tested reciprocal tree:\t\tNo errors!\n", 100 * tree_size / nmax);
// TEST FOR REMAINDERS
for(tree_size = 2; tree_size < nmax; tree_size++)
{
printf("[%3d%%] Testing batched remainders:\t\tTree size %d out of %d", 100 * tree_size / nmax, tree_size, nmax);
fflush(stdout);
printf("\r\x1b[K");
// Compute size of arrays
i = 0;
while((1<<i) < tree_size)
i++;
array_size = (1<<(i+2))-1;
DEG = malloc(sizeof(int)*array_size);
H = malloc(sizeof(poly)*array_size);
R = malloc(sizeof(poly)*array_size);
F = malloc(sizeof(poly)*tree_size);
A = malloc(sizeof(fp2_t)*array_size);
REM1 = malloc(sizeof(fp2_t)*array_size);
REM2 = malloc(sizeof(fp2_t)*array_size);
C = malloc(sizeof(fp2_t)*tree_size);
// Get random polys
LENF = 2;
for(i = 0; i < tree_size; i++)
{
F[i] = malloc(sizeof(fp2_t)*LENF);
for(e = 0; e < LENF; e++)
fp2_random(&F[i][e]);
}
// Get product tree, reciprocal tree, and remainders
product_tree(H, DEG, 0, F, LENF, tree_size);
leng = DEG[0]+1+(rand() % nmax);
g1 = malloc(sizeof(fp2_t)*leng);
g2 = malloc(sizeof(fp2_t)*leng);
for(e = 0; e < leng; e++)
{
fp2_random(&g1[e]);
fp2_random(&g2[e]);
}
reciprocal_tree(R, A, leng, H, DEG, 0, tree_size);
multieval_unscaled(REM1, g1, leng, R, (const fp2_t*)A, H, DEG, 0, tree_size);
multieval_unscaled(REM2, g2, leng, R, (const fp2_t*)A, H, DEG, 0, tree_size);
for(i = 0; i < tree_size; i++)
{
// Get ratio of the remainder
fp2_inv(&REM1[i]);
fp2_mul(&ratio, &REM1[i], &REM2[i]);
// Compute remainders manually
f_rev = malloc(sizeof(fp2_t)*LENF);
f_rev_inv = malloc(sizeof(fp2_t)*(leng-LENF+1));
h = malloc(sizeof(fp2_t)*(LENF-1));
for(e = 0; e < LENF; e++)
fp2_copy(&f_rev[e], &F[i][LENF-1-e]);
reciprocal(f_rev_inv, &c, f_rev, LENF, leng-LENF+1);
poly_redc(h, g1, leng, F[i], LENF, f_rev_inv, c);
fp2_copy(&REM1[i], &h[0]);
poly_redc(h, g2, leng, F[i], LENF, f_rev_inv, c);
fp2_copy(&REM2[i], &h[0]);
free(f_rev);
free(f_rev_inv);
free(h);
// Compare results
fp2_inv(&REM1[i]);
fp2_mul(&REM1[i], &REM1[i], &REM2[i]);
assert(fp2_isequal(REM1[i], ratio));
}
// Clean up
for(i = 0; i < tree_size; i++)
free(F[i]);
free(g1);
free(g2);
clear_tree(H, 0, tree_size);
clear_tree(R, 0, tree_size);
free(F);
free(H);
free(R);
free(A);
free(DEG);
free(REM1);
free(REM2);
free(C);
}
printf("[%3d%%] Tested batched remainders:\tNo errors!\n", 100 * tree_size / nmax);
// TEST FOR SCALED REMAINDER TREE
for(tree_size = 1; tree_size < nmax; tree_size++)
{
printf("[%3d%%] Testing scaled remainder tree:\tTree size %d out of %d", 100 * tree_size / nmax, tree_size, nmax);
fflush(stdout);
printf("\r\x1b[K");
// Compute size of arrays
i = 0;
while((1<<i) < tree_size)
i++;
array_size = (1<<(i+2))-1;
DEG = malloc(sizeof(int)*array_size);
H = malloc(sizeof(poly)*array_size);
F = malloc(sizeof(poly)*tree_size);
REM1 = malloc(sizeof(fp2_t)*array_size);
REM2 = malloc(sizeof(fp2_t)*array_size);
// Get random polys
LENF = 2;
for(i = 0; i < tree_size; i++)
{
F[i] = malloc(sizeof(fp2_t)*LENF);
for(e = 0; e < LENF; e++)
fp2_random(&F[i][e]);
}
// Get random polys to reduce
product_tree(H, DEG, 0, F, LENF, tree_size);
leng = DEG[0]+1+(rand() % nmax);
g1 = malloc(sizeof(fp2_t)*leng);
g2 = malloc(sizeof(fp2_t)*leng);
for(e = 0; e < leng; e++)
{
fp2_random(&g1[e]);
fp2_random(&g2[e]);
}
// Get the required initial nodes
G1 = malloc(sizeof(fp2_t)*DEG[0]);
G2 = malloc(sizeof(fp2_t)*DEG[0]);
G1_rev = malloc(sizeof(fp2_t)*DEG[0]);
G2_rev = malloc(sizeof(fp2_t)*DEG[0]);
R0 = malloc(sizeof(fp2_t)*(leng));
f_rev = malloc(sizeof(fp2_t)*(DEG[0]+1));
for(e = 0; e < DEG[0]+1; e++)
fp2_copy(&f_rev[e], &H[0][DEG[0]-e]);
if( DEG[0] > leng-DEG[0])
reciprocal(R0, &A0, f_rev, DEG[0]+1, DEG[0]);
else
reciprocal(R0, &A0, f_rev, DEG[0]+1, leng-DEG[0]);
poly_redc(G1, g1, leng, H[0], DEG[0]+1, R0, A0);
poly_redc(G2, g2, leng, H[0], DEG[0]+1, R0, A0);
for(e = 0; e < DEG[0]; e++)
{
fp2_copy(&G1_rev[e], &G1[DEG[0]-1-e]);
fp2_copy(&G2_rev[e], &G2[DEG[0]-1-e]);
}
poly_mul_middle(G1_rev, G1_rev, DEG[0], R0, DEG[0]);
poly_mul_middle(G2_rev, G2_rev, DEG[0], R0, DEG[0]);
for(e = 0; e < DEG[0]; e++)
{
fp2_copy(&G1[e], &G1_rev[DEG[0]-1-e]);
fp2_copy(&G2[e], &G2_rev[DEG[0]-1-e]);
}
free(G1_rev);free(G2_rev);free(R0);free(f_rev);
// Compute the scaled remainder trees
multieval_scaled(REM1, G1, H, DEG, 0, tree_size);
multieval_scaled(REM2, G2, H, DEG, 0, tree_size);
for(i = 0; i < tree_size; i++)
{
// Get ratio of the remainder
fp2_inv(&REM1[i]);
fp2_mul(&ratio, &REM1[i], &REM2[i]);
// Compute remainders manually
f_rev = malloc(sizeof(fp2_t)*LENF);
f_rev_inv = malloc(sizeof(fp2_t)*(leng-LENF+1));
h = malloc(sizeof(fp2_t)*(LENF-1));
for(e = 0; e < LENF; e++)
fp2_copy(&f_rev[e], &F[i][LENF-1-e]);
reciprocal(f_rev_inv, &c, f_rev, LENF, leng-LENF+1);
poly_redc(h, g1, leng, F[i], LENF, f_rev_inv, c);
fp2_copy(&REM1[i], &h[0]);
poly_redc(h, g2, leng, F[i], LENF, f_rev_inv, c);
fp2_copy(&REM2[i], &h[0]);
free(f_rev);free(f_rev_inv);free(h);
// Compare results
fp2_inv(&REM1[i]);
fp2_mul(&REM1[i], &REM1[i], &REM2[i]);
assert(fp2_isequal(REM1[i], ratio));
}
// Clean up
for(i = 0; i < tree_size; i++)
free(F[i]);
free(F);free(g1);free(g2);free(G1);free(G2);
clear_tree(H, 0, tree_size);free(H);free(DEG);
free(REM1);free(REM2);
}
printf("[%3d%%] Tested scaled remainder tree:\tNo errors!\n", 100 * tree_size / nmax);
printf("-- All tests passed.\n");
}

View File

@@ -0,0 +1,75 @@
#include "test_extras.h"
#include <bench.h>
// Global constants
extern const digit_t p[NWORDS_FIELD];
extern const digit_t R2[NWORDS_FIELD];
#if 0
int64_t cpucycles(void)
{ // Access system counter for benchmarking
unsigned int hi, lo;
asm volatile ("rdtsc\n\t" : "=a" (lo), "=d"(hi));
return ((int64_t)lo) | (((int64_t)hi) << 32);
}
#endif
int compare_words(digit_t* a, digit_t* b, unsigned int nwords)
{ // Comparing "nword" elements, a=b? : (1) a>b, (0) a=b, (-1) a<b
// SECURITY NOTE: this function does not have constant-time execution. TO BE USED FOR TESTING ONLY.
int i;
for (i = nwords-1; i >= 0; i--)
{
if (a[i] > b[i]) return 1;
else if (a[i] < b[i]) return -1;
}
return 0;
}
void sub_test(digit_t* out, digit_t* a, digit_t* b, unsigned int nwords)
{ // Subtraction without borrow, out = a-b where a>b
// SECURITY NOTE: this function does not have constant-time execution. It is for TESTING ONLY.
unsigned int i;
digit_t res, carry, borrow = 0;
for (i = 0; i < nwords; i++)
{
res = a[i] - b[i];
carry = (a[i] < b[i]);
out[i] = res - borrow;
borrow = carry || (res < borrow);
}
}
void fprandom_test(digit_t* a)
{ // Generating a pseudo-random field element in [0, p-1]
// SECURITY NOTE: distribution is not fully uniform. TO BE USED FOR TESTING ONLY.
unsigned int i, diff = 256-254, nwords = NWORDS_FIELD;
unsigned char* string = NULL;
string = (unsigned char*)a;
for (i = 0; i < sizeof(digit_t)*nwords; i++) {
*(string + i) = (unsigned char)rand(); // Obtain 256-bit number
}
a[nwords-1] &= (((digit_t)(-1) << diff) >> diff);
while (compare_words((digit_t*)p, a, nwords) < 1) { // Force it to [0, modulus-1]
sub_test(a, a, (digit_t*)p, nwords);
}
}
void fp2random_test(fp2_t* a)
{ // Generating a pseudo-random element in GF(p^2)
// SECURITY NOTE: distribution is not fully uniform. TO BE USED FOR TESTING ONLY.
fprandom_test(a->re);
fprandom_test(a->im);
}

View File

@@ -0,0 +1,29 @@
#ifndef TEST_EXTRAS_H
#define TEST_EXTRAS_H
#include <time.h>
#include <stdlib.h>
#include <fp.h>
#include <fp2.h>
#include <curve_extras.h>
#define PASSED 0
#define FAILED 1
// Access system counter for benchmarking
//int64_t cpucycles(void);
// Comparing "nword" elements, a=b? : (1) a!=b, (0) a=b
int compare_words(digit_t* a, digit_t* b, unsigned int nwords);
// Multiprecision subtraction for testing, assumes a > b
void sub_test(digit_t* out, digit_t* a, digit_t* b, unsigned int nwords);
// Generating a pseudo-random field element in [0, p-1]
void fprandom_test(digit_t* a);
// Generating a pseudo-random element in GF(p^2)
void fp2random_test(fp2_t* a);
#endif

View File

@@ -0,0 +1,298 @@
#include<time.h>
#include <stdio.h>
#include <assert.h>
#include <inttypes.h>
#include "isog.h"
#include "sdacs.h"
#include "ec.h"
#include "test-basis.h"
void random_scalar(fp_t k, const uint8_t j)
{
for(int i = 0; i < NWORDS_FIELD; i++)
k[i] = rand();
}
// 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);
}
// 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()
{
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, B, T;
fp2_set(&A.x, 0);
fp_mont_setone(A.z.re);fp_set(A.z.im,0);
// fp2_add(&A.x, &A.z, &A.x); // 1
// fp2_add(&A.x, &A.x, &A.x); // 2
// fp2_add(&A.x, &A.z, &A.x); // 3
// fp2_add(&A.x, &A.x, &A.x); // 6
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, RA, RB;
// 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) );
// ======================================================================================================
// 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) );
// --------------------------------------------------------------
fp_t m;
random_scalar(m, 0);
ladder3pt(&RA, m, &PA, &QA, &PQA, &A);
for (i = 0; i < P_LEN; i++)
{
printf("// Processing the %d-th prime:\t", i + 1);
printf("%2d%%", 100 * i / (int)P_LEN);
fflush(stdout);
printf("\r\x1b[K");
copy_point(&T, &RA);
for (j = (i+1); j < P_LEN; j++)
xMULv2(&T, &T, &(TORSION_ODD_PRIMES[j]), p_plus_minus_bitlength[j], &A);
assert( !isinfinity(T) );
kps(i, T, A);
if (TORSION_ODD_PRIMES[i] > gap)
printf("[\033[0;31m%7" PRId64 "\033[0m] (#I: %3d, #J: %3d, #K: %3d) \n", TORSION_ODD_PRIMES[i], sI, sJ, sK);
else
printf("[\033[0;31m%7" PRId64 "\033[0m] --------------------------- \n", TORSION_ODD_PRIMES[i]);
xisog(&B, i, A);
xeval(&PB, i, PB, A);
coeff(&a, B);
assert( !isinfinity(PB) );
assert( !isrational(PB, a) );
xeval(&RA, i, RA, A);
assert( (!isinfinity(RA) && (i < (P_LEN - 1))) || (isinfinity(RA) && (i == (P_LEN - 1))) );
assert( (isrational(RA, a) && (i < (P_LEN - 1))) || (isinfinity(RA) && (i == (P_LEN - 1))) );
copy_point(&A, &B);
// Verifying the order of the image point of PA has been reduced
copy_point(&T, &RA);
for (j = (i+1); j < P_LEN; j++)
xMULv2(&T, &T, &(TORSION_ODD_PRIMES[j]), p_plus_minus_bitlength[j], &A);
assert( isinfinity(T) );
kps_clear(i);
};
fp2_set(&A.x, 0);
fp_mont_setone(A.z.re);fp_set(A.z.im,0);
// fp2_add(&A.x, &A.z, &A.x); // 1
// fp2_add(&A.x, &A.x, &A.x); // 2
// fp2_add(&A.x, &A.z, &A.x); // 3
// fp2_add(&A.x, &A.x, &A.x); // 6
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) );
coeff(&a, A);
// 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) );
// ======================================================================================================
// Recall, PA, QA, and PQA are expeted to be N-order points, but we require to ensure they are of order N
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) );
random_scalar(m, 1);
ladder3pt(&RB, m, &PB, &QB, &PQB, &A);
for (i = P_LEN; i < (P_LEN+M_LEN); i++)
{
printf("// Processing the %d-th prime:\t", i + 1);
printf("%2d%%", 100 * i / (int)(P_LEN+M_LEN));
fflush(stdout);
printf("\r\x1b[K");
copy_point(&T, &RB);
for (j = (i+1); j < (P_LEN+M_LEN); j++)
xMULv2(&T, &T, &(TORSION_ODD_PRIMES[j]), p_plus_minus_bitlength[j], &A);
assert( !isinfinity(T) );
kps(i, T, A);
if (TORSION_ODD_PRIMES[i] > gap)
printf("[\033[0;31m%7" PRId64 "\033[0m] (#I: %3d, #J: %3d, #K: %3d) \n", TORSION_ODD_PRIMES[i], sI, sJ, sK);
else
printf("[\033[0;31m%7" PRId64 "\033[0m] --------------------------- \n", TORSION_ODD_PRIMES[i]);
xisog(&B, i, A);
xeval(&PA, i, PA, A);
coeff(&a, B);
assert( !isinfinity(PA) );
assert( isrational(PA, a) );
xeval(&RB, i, RB, A);
assert( (!isinfinity(RB) && (i < (P_LEN + M_LEN - 1))) || (isinfinity(RB) && (i == (P_LEN + M_LEN - 1))) );
assert( (!isrational(RB, a) && (i < (P_LEN + M_LEN - 1))) || (isinfinity(RB) && (i == (P_LEN + M_LEN - 1))) );
copy_point(&A, &B);
// Verifying the order of the image point of PB has been reduced
copy_point(&T, &RB);
for (j = (i+1); j < (P_LEN+M_LEN); j++)
xMULv2(&T, &T, &(TORSION_ODD_PRIMES[j]), p_plus_minus_bitlength[j], &A);
assert( isinfinity(T) );
kps_clear(i);
};
printf("-- All tests passed!\n");
return 0;
}