add pqm4
Some checks failed
CMake / build (OFF, AUTO, SYSTEM, x64, ref, 10, ) (push) Has been cancelled
CMake / build (ON, 32, BUILD, x64, ref, 10, .cmake/32bit.cmake) (push) Has been cancelled
CMake / build (ON, 32, SYSTEM, arm64, ref, 10, ) (push) Has been cancelled
CMake / build (ON, 32, SYSTEM, x64, ref, 10, ) (push) Has been cancelled
CMake / build (ON, AUTO, MINI, x64, ref, 10, ) (push) Has been cancelled
CMake / build (ON, AUTO, SYSTEM, arm64, ref, 10, ) (push) Has been cancelled
CMake / build (ON, AUTO, SYSTEM, x64, broadwell, 10, ) (push) Has been cancelled
CMake / build (ON, AUTO, SYSTEM, x64, ref, 10, ) (push) Has been cancelled
Big-endian s390x test (Daily Workflow) / s390-be (Debug) (push) Has been cancelled
Big-endian s390x test (Daily Workflow) / s390-be (Release) (push) Has been cancelled
Benchmarks (Daily Workflow) / benchmarks (arm64, ref, ) (push) Has been cancelled
Benchmarks (Daily Workflow) / benchmarks (x64, broadwell, 10, ) (push) Has been cancelled
Benchmarks (Daily Workflow) / benchmarks (x64, ref, ) (push) Has been cancelled
Benchmarks (Daily Workflow) / benchmarks (x64, ref, 10, .cmake/32bit.cmake) (push) Has been cancelled
Daily workflow for various checks / checks (push) Has been cancelled
Some checks failed
CMake / build (OFF, AUTO, SYSTEM, x64, ref, 10, ) (push) Has been cancelled
CMake / build (ON, 32, BUILD, x64, ref, 10, .cmake/32bit.cmake) (push) Has been cancelled
CMake / build (ON, 32, SYSTEM, arm64, ref, 10, ) (push) Has been cancelled
CMake / build (ON, 32, SYSTEM, x64, ref, 10, ) (push) Has been cancelled
CMake / build (ON, AUTO, MINI, x64, ref, 10, ) (push) Has been cancelled
CMake / build (ON, AUTO, SYSTEM, arm64, ref, 10, ) (push) Has been cancelled
CMake / build (ON, AUTO, SYSTEM, x64, broadwell, 10, ) (push) Has been cancelled
CMake / build (ON, AUTO, SYSTEM, x64, ref, 10, ) (push) Has been cancelled
Big-endian s390x test (Daily Workflow) / s390-be (Debug) (push) Has been cancelled
Big-endian s390x test (Daily Workflow) / s390-be (Release) (push) Has been cancelled
Benchmarks (Daily Workflow) / benchmarks (arm64, ref, ) (push) Has been cancelled
Benchmarks (Daily Workflow) / benchmarks (x64, broadwell, 10, ) (push) Has been cancelled
Benchmarks (Daily Workflow) / benchmarks (x64, ref, ) (push) Has been cancelled
Benchmarks (Daily Workflow) / benchmarks (x64, ref, 10, .cmake/32bit.cmake) (push) Has been cancelled
Daily workflow for various checks / checks (push) Has been cancelled
This commit is contained in:
416
src/pqm4/sqisign_lvl3/ref/basis.c
Normal file
416
src/pqm4/sqisign_lvl3/ref/basis.c
Normal file
@@ -0,0 +1,416 @@
|
||||
#include "ec.h"
|
||||
#include "fp2.h"
|
||||
#include "e0_basis.h"
|
||||
#include <assert.h>
|
||||
|
||||
uint32_t
|
||||
ec_recover_y(fp2_t *y, const fp2_t *Px, const ec_curve_t *curve)
|
||||
{ // Recover y-coordinate of a point on the Montgomery curve y^2 = x^3 + Ax^2 + x
|
||||
fp2_t t0;
|
||||
|
||||
fp2_sqr(&t0, Px);
|
||||
fp2_mul(y, &t0, &curve->A); // Ax^2
|
||||
fp2_add(y, y, Px); // Ax^2 + x
|
||||
fp2_mul(&t0, &t0, Px);
|
||||
fp2_add(y, y, &t0); // x^3 + Ax^2 + x
|
||||
// This is required, because we do not yet know that our curves are
|
||||
// supersingular so our points live on the twist with B = 1.
|
||||
return fp2_sqrt_verify(y);
|
||||
}
|
||||
|
||||
static void
|
||||
difference_point(ec_point_t *PQ, const ec_point_t *P, const ec_point_t *Q, const ec_curve_t *curve)
|
||||
{
|
||||
// Given P,Q in projective x-only, computes a deterministic choice for (P-Q)
|
||||
// Based on Proposition 3 of https://eprint.iacr.org/2017/518.pdf
|
||||
|
||||
fp2_t Bxx, Bxz, Bzz, t0, t1;
|
||||
|
||||
fp2_mul(&t0, &P->x, &Q->x);
|
||||
fp2_mul(&t1, &P->z, &Q->z);
|
||||
fp2_sub(&Bxx, &t0, &t1);
|
||||
fp2_sqr(&Bxx, &Bxx);
|
||||
fp2_mul(&Bxx, &Bxx, &curve->C); // C*(P.x*Q.x-P.z*Q.z)^2
|
||||
fp2_add(&Bxz, &t0, &t1);
|
||||
fp2_mul(&t0, &P->x, &Q->z);
|
||||
fp2_mul(&t1, &P->z, &Q->x);
|
||||
fp2_add(&Bzz, &t0, &t1);
|
||||
fp2_mul(&Bxz, &Bxz, &Bzz); // (P.x*Q.x+P.z*Q.z)(P.x*Q.z+P.z*Q.x)
|
||||
fp2_sub(&Bzz, &t0, &t1);
|
||||
fp2_sqr(&Bzz, &Bzz);
|
||||
fp2_mul(&Bzz, &Bzz, &curve->C); // C*(P.x*Q.z-P.z*Q.x)^2
|
||||
fp2_mul(&Bxz, &Bxz, &curve->C); // C*(P.x*Q.x+P.z*Q.z)(P.x*Q.z+P.z*Q.x)
|
||||
fp2_mul(&t0, &t0, &t1);
|
||||
fp2_mul(&t0, &t0, &curve->A);
|
||||
fp2_add(&t0, &t0, &t0);
|
||||
fp2_add(&Bxz, &Bxz, &t0); // C*(P.x*Q.x+P.z*Q.z)(P.x*Q.z+P.z*Q.x) + 2*A*P.x*Q.z*P.z*Q.x
|
||||
|
||||
// To ensure that the denominator is a fourth power in Fp, we normalize by
|
||||
// C*C_bar^2*(P.z)_bar^2*(Q.z)_bar^2
|
||||
fp_copy(&t0.re, &curve->C.re);
|
||||
fp_neg(&t0.im, &curve->C.im);
|
||||
fp2_sqr(&t0, &t0);
|
||||
fp2_mul(&t0, &t0, &curve->C);
|
||||
fp_copy(&t1.re, &P->z.re);
|
||||
fp_neg(&t1.im, &P->z.im);
|
||||
fp2_sqr(&t1, &t1);
|
||||
fp2_mul(&t0, &t0, &t1);
|
||||
fp_copy(&t1.re, &Q->z.re);
|
||||
fp_neg(&t1.im, &Q->z.im);
|
||||
fp2_sqr(&t1, &t1);
|
||||
fp2_mul(&t0, &t0, &t1);
|
||||
fp2_mul(&Bxx, &Bxx, &t0);
|
||||
fp2_mul(&Bxz, &Bxz, &t0);
|
||||
fp2_mul(&Bzz, &Bzz, &t0);
|
||||
|
||||
// Solving quadratic equation
|
||||
fp2_sqr(&t0, &Bxz);
|
||||
fp2_mul(&t1, &Bxx, &Bzz);
|
||||
fp2_sub(&t0, &t0, &t1);
|
||||
// No need to check if t0 is square, as per the entangled basis algorithm.
|
||||
fp2_sqrt(&t0);
|
||||
fp2_add(&PQ->x, &Bxz, &t0);
|
||||
fp2_copy(&PQ->z, &Bzz);
|
||||
}
|
||||
|
||||
// Lifts a basis x(P), x(Q), x(P-Q) assuming the curve has (A/C : 1) and the point
|
||||
// P = (X/Z : 1). For generic implementation see lift_basis()
|
||||
uint32_t
|
||||
lift_basis_normalized(jac_point_t *P, jac_point_t *Q, ec_basis_t *B, ec_curve_t *E)
|
||||
{
|
||||
assert(fp2_is_one(&B->P.z));
|
||||
assert(fp2_is_one(&E->C));
|
||||
|
||||
fp2_copy(&P->x, &B->P.x);
|
||||
fp2_copy(&Q->x, &B->Q.x);
|
||||
fp2_copy(&Q->z, &B->Q.z);
|
||||
fp2_set_one(&P->z);
|
||||
uint32_t ret = ec_recover_y(&P->y, &P->x, E);
|
||||
|
||||
// Algorithm of Okeya-Sakurai to recover y.Q in the montgomery model
|
||||
fp2_t v1, v2, v3, v4;
|
||||
fp2_mul(&v1, &P->x, &Q->z);
|
||||
fp2_add(&v2, &Q->x, &v1);
|
||||
fp2_sub(&v3, &Q->x, &v1);
|
||||
fp2_sqr(&v3, &v3);
|
||||
fp2_mul(&v3, &v3, &B->PmQ.x);
|
||||
fp2_add(&v1, &E->A, &E->A);
|
||||
fp2_mul(&v1, &v1, &Q->z);
|
||||
fp2_add(&v2, &v2, &v1);
|
||||
fp2_mul(&v4, &P->x, &Q->x);
|
||||
fp2_add(&v4, &v4, &Q->z);
|
||||
fp2_mul(&v2, &v2, &v4);
|
||||
fp2_mul(&v1, &v1, &Q->z);
|
||||
fp2_sub(&v2, &v2, &v1);
|
||||
fp2_mul(&v2, &v2, &B->PmQ.z);
|
||||
fp2_sub(&Q->y, &v3, &v2);
|
||||
fp2_add(&v1, &P->y, &P->y);
|
||||
fp2_mul(&v1, &v1, &Q->z);
|
||||
fp2_mul(&v1, &v1, &B->PmQ.z);
|
||||
fp2_mul(&Q->x, &Q->x, &v1);
|
||||
fp2_mul(&Q->z, &Q->z, &v1);
|
||||
|
||||
// Transforming to a jacobian coordinate
|
||||
fp2_sqr(&v1, &Q->z);
|
||||
fp2_mul(&Q->y, &Q->y, &v1);
|
||||
fp2_mul(&Q->x, &Q->x, &Q->z);
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
lift_basis(jac_point_t *P, jac_point_t *Q, ec_basis_t *B, ec_curve_t *E)
|
||||
{
|
||||
// Normalise the curve E such that (A : C) is (A/C : 1)
|
||||
// and the point x(P) = (X/Z : 1).
|
||||
fp2_t inverses[2];
|
||||
fp2_copy(&inverses[0], &B->P.z);
|
||||
fp2_copy(&inverses[1], &E->C);
|
||||
|
||||
fp2_batched_inv(inverses, 2);
|
||||
fp2_set_one(&B->P.z);
|
||||
fp2_set_one(&E->C);
|
||||
|
||||
fp2_mul(&B->P.x, &B->P.x, &inverses[0]);
|
||||
fp2_mul(&E->A, &E->A, &inverses[1]);
|
||||
|
||||
// Lift the basis to Jacobian points P, Q
|
||||
return lift_basis_normalized(P, Q, B, E);
|
||||
}
|
||||
|
||||
// Given an x-coordinate, determines if this is a valid
|
||||
// point on the curve. Assumes C=1.
|
||||
static uint32_t
|
||||
is_on_curve(const fp2_t *x, const ec_curve_t *curve)
|
||||
{
|
||||
assert(fp2_is_one(&curve->C));
|
||||
fp2_t t0;
|
||||
|
||||
fp2_add(&t0, x, &curve->A); // x + (A/C)
|
||||
fp2_mul(&t0, &t0, x); // x^2 + (A/C)*x
|
||||
fp2_add_one(&t0, &t0); // x^2 + (A/C)*x + 1
|
||||
fp2_mul(&t0, &t0, x); // x^3 + (A/C)*x^2 + x
|
||||
|
||||
return fp2_is_square(&t0);
|
||||
}
|
||||
|
||||
// Helper function which given a point of order k*2^n with n maximal
|
||||
// and k odd, computes a point of order 2^f
|
||||
static inline void
|
||||
clear_cofactor_for_maximal_even_order(ec_point_t *P, ec_curve_t *curve, int f)
|
||||
{
|
||||
// clear out the odd cofactor to get a point of order 2^n
|
||||
ec_mul(P, p_cofactor_for_2f, P_COFACTOR_FOR_2F_BITLENGTH, P, curve);
|
||||
|
||||
// clear the power of two to get a point of order 2^f
|
||||
for (int i = 0; i < TORSION_EVEN_POWER - f; i++) {
|
||||
xDBL_A24(P, P, &curve->A24, curve->is_A24_computed_and_normalized);
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function which finds an NQR -1 / (1 + i*b) for entangled basis generation
|
||||
static uint8_t
|
||||
find_nqr_factor(fp2_t *x, ec_curve_t *curve, const uint8_t start)
|
||||
{
|
||||
// factor = -1/(1 + i*b) for b in Fp will be NQR whenever 1 + b^2 is NQR
|
||||
// in Fp, so we find one of these and then invert (1 + i*b). We store b
|
||||
// as a u8 hint to save time in verification.
|
||||
|
||||
// We return the hint as a u8, but use (uint16_t)n to give 2^16 - 1
|
||||
// to make failure cryptographically negligible, with a fallback when
|
||||
// n > 128 is required.
|
||||
uint8_t hint;
|
||||
uint32_t found = 0;
|
||||
uint16_t n = start;
|
||||
|
||||
bool qr_b = 1;
|
||||
fp_t b, tmp;
|
||||
fp2_t z, t0, t1;
|
||||
|
||||
do {
|
||||
while (qr_b) {
|
||||
// find b with 1 + b^2 a non-quadratic residue
|
||||
fp_set_small(&tmp, (uint32_t)n * n + 1);
|
||||
qr_b = fp_is_square(&tmp);
|
||||
n++; // keeps track of b = n - 1
|
||||
}
|
||||
|
||||
// for Px := -A/(1 + i*b) to be on the curve
|
||||
// is equivalent to A^2*(z-1) - z^2 NQR for z = 1 + i*b
|
||||
// thus prevents unnecessary inversion pre-check
|
||||
|
||||
// t0 = z - 1 = i*b
|
||||
// t1 = z = 1 + i*b
|
||||
fp_set_small(&b, (uint32_t)n - 1);
|
||||
fp2_set_zero(&t0);
|
||||
fp2_set_one(&z);
|
||||
fp_copy(&z.im, &b);
|
||||
fp_copy(&t0.im, &b);
|
||||
|
||||
// A^2*(z-1) - z^2
|
||||
fp2_sqr(&t1, &curve->A);
|
||||
fp2_mul(&t0, &t0, &t1); // A^2 * (z - 1)
|
||||
fp2_sqr(&t1, &z);
|
||||
fp2_sub(&t0, &t0, &t1); // A^2 * (z - 1) - z^2
|
||||
found = !fp2_is_square(&t0);
|
||||
|
||||
qr_b = 1;
|
||||
} while (!found);
|
||||
|
||||
// set Px to -A/(1 + i*b)
|
||||
fp2_copy(x, &z);
|
||||
fp2_inv(x);
|
||||
fp2_mul(x, x, &curve->A);
|
||||
fp2_neg(x, x);
|
||||
|
||||
/*
|
||||
* With very low probability n will not fit in 7 bits.
|
||||
* We set hint = 0 which signals failure and the need
|
||||
* to generate a value on the fly during verification
|
||||
*/
|
||||
hint = n <= 128 ? n - 1 : 0;
|
||||
|
||||
return hint;
|
||||
}
|
||||
|
||||
// Helper function which finds a point x(P) = n * A
|
||||
static uint8_t
|
||||
find_nA_x_coord(fp2_t *x, ec_curve_t *curve, const uint8_t start)
|
||||
{
|
||||
assert(!fp2_is_square(&curve->A)); // Only to be called when A is a NQR
|
||||
|
||||
// when A is NQR we allow x(P) to be a multiple n*A of A
|
||||
uint8_t n = start;
|
||||
if (n == 1) {
|
||||
fp2_copy(x, &curve->A);
|
||||
} else {
|
||||
fp2_mul_small(x, &curve->A, n);
|
||||
}
|
||||
|
||||
while (!is_on_curve(x, curve)) {
|
||||
fp2_add(x, x, &curve->A);
|
||||
n++;
|
||||
}
|
||||
|
||||
/*
|
||||
* With very low probability (1/2^128), n will not fit in 7 bits.
|
||||
* In this case, we set hint = 0 which signals failure and the need
|
||||
* to generate a value on the fly during verification
|
||||
*/
|
||||
uint8_t hint = n < 128 ? n : 0;
|
||||
return hint;
|
||||
}
|
||||
|
||||
// The entangled basis generation does not allow A = 0
|
||||
// so we simply return the one we have already precomputed
|
||||
static void
|
||||
ec_basis_E0_2f(ec_basis_t *PQ2, ec_curve_t *curve, int f)
|
||||
{
|
||||
assert(fp2_is_zero(&curve->A));
|
||||
ec_point_t P, Q;
|
||||
|
||||
// Set P, Q to precomputed (X : 1) values
|
||||
fp2_copy(&P.x, &BASIS_E0_PX);
|
||||
fp2_copy(&Q.x, &BASIS_E0_QX);
|
||||
fp2_set_one(&P.z);
|
||||
fp2_set_one(&Q.z);
|
||||
|
||||
// clear the power of two to get a point of order 2^f
|
||||
for (int i = 0; i < TORSION_EVEN_POWER - f; i++) {
|
||||
xDBL_E0(&P, &P);
|
||||
xDBL_E0(&Q, &Q);
|
||||
}
|
||||
|
||||
// Set P, Q in the basis and compute x(P - Q)
|
||||
copy_point(&PQ2->P, &P);
|
||||
copy_point(&PQ2->Q, &Q);
|
||||
difference_point(&PQ2->PmQ, &P, &Q, curve);
|
||||
}
|
||||
|
||||
// Computes a basis E[2^f] = <P, Q> where the point Q is above (0 : 0)
|
||||
// and stores hints as an array for faster recomputation at a later point
|
||||
uint8_t
|
||||
ec_curve_to_basis_2f_to_hint(ec_basis_t *PQ2, ec_curve_t *curve, int f)
|
||||
{
|
||||
// Normalise (A/C : 1) and ((A + 2)/4 : 1)
|
||||
ec_normalize_curve_and_A24(curve);
|
||||
|
||||
if (fp2_is_zero(&curve->A)) {
|
||||
ec_basis_E0_2f(PQ2, curve, f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t hint;
|
||||
bool hint_A = fp2_is_square(&curve->A);
|
||||
|
||||
// Compute the points P, Q
|
||||
ec_point_t P, Q;
|
||||
|
||||
if (!hint_A) {
|
||||
// when A is NQR we allow x(P) to be a multiple n*A of A
|
||||
hint = find_nA_x_coord(&P.x, curve, 1);
|
||||
} else {
|
||||
// when A is QR we instead have to find (1 + b^2) a NQR
|
||||
// such that x(P) = -A / (1 + i*b)
|
||||
hint = find_nqr_factor(&P.x, curve, 1);
|
||||
}
|
||||
|
||||
fp2_set_one(&P.z);
|
||||
fp2_add(&Q.x, &curve->A, &P.x);
|
||||
fp2_neg(&Q.x, &Q.x);
|
||||
fp2_set_one(&Q.z);
|
||||
|
||||
// clear out the odd cofactor to get a point of order 2^f
|
||||
clear_cofactor_for_maximal_even_order(&P, curve, f);
|
||||
clear_cofactor_for_maximal_even_order(&Q, curve, f);
|
||||
|
||||
// compute PmQ, set PmQ to Q to ensure Q above (0,0)
|
||||
difference_point(&PQ2->Q, &P, &Q, curve);
|
||||
copy_point(&PQ2->P, &P);
|
||||
copy_point(&PQ2->PmQ, &Q);
|
||||
|
||||
// Finally, we compress hint_A and hint into a single bytes.
|
||||
// We choose to set the LSB of hint to hint_A
|
||||
assert(hint < 128); // We expect hint to be 7-bits in size
|
||||
return (hint << 1) | hint_A;
|
||||
}
|
||||
|
||||
// Computes a basis E[2^f] = <P, Q> where the point Q is above (0 : 0)
|
||||
// given the hints as an array for faster basis computation
|
||||
int
|
||||
ec_curve_to_basis_2f_from_hint(ec_basis_t *PQ2, ec_curve_t *curve, int f, const uint8_t hint)
|
||||
{
|
||||
// Normalise (A/C : 1) and ((A + 2)/4 : 1)
|
||||
ec_normalize_curve_and_A24(curve);
|
||||
|
||||
if (fp2_is_zero(&curve->A)) {
|
||||
ec_basis_E0_2f(PQ2, curve, f);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// The LSB of hint encodes whether A is a QR
|
||||
// The remaining 7-bits are used to find a valid x(P)
|
||||
bool hint_A = hint & 1;
|
||||
uint8_t hint_P = hint >> 1;
|
||||
|
||||
// Compute the points P, Q
|
||||
ec_point_t P, Q;
|
||||
|
||||
if (!hint_P) {
|
||||
// When hint_P = 0 it means we did not find a point in 128 attempts
|
||||
// this is very rare and we almost never expect to need this fallback
|
||||
// In either case, we can start with b = 128 to skip testing the known
|
||||
// values which will not work
|
||||
if (!hint_A) {
|
||||
find_nA_x_coord(&P.x, curve, 128);
|
||||
} else {
|
||||
find_nqr_factor(&P.x, curve, 128);
|
||||
}
|
||||
} else {
|
||||
// Otherwise we use the hint to directly find x(P) based on hint_A
|
||||
if (!hint_A) {
|
||||
// when A is NQR, we have found n such that x(P) = n*A
|
||||
fp2_mul_small(&P.x, &curve->A, hint_P);
|
||||
} else {
|
||||
// when A is QR we have found b such that (1 + b^2) is a NQR in
|
||||
// Fp, so we must compute x(P) = -A / (1 + i*b)
|
||||
fp_set_one(&P.x.re);
|
||||
fp_set_small(&P.x.im, hint_P);
|
||||
fp2_inv(&P.x);
|
||||
fp2_mul(&P.x, &P.x, &curve->A);
|
||||
fp2_neg(&P.x, &P.x);
|
||||
}
|
||||
}
|
||||
fp2_set_one(&P.z);
|
||||
|
||||
#ifndef NDEBUG
|
||||
int passed = 1;
|
||||
passed = is_on_curve(&P.x, curve);
|
||||
passed &= !fp2_is_square(&P.x);
|
||||
|
||||
if (!passed)
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
// set xQ to -xP - A
|
||||
fp2_add(&Q.x, &curve->A, &P.x);
|
||||
fp2_neg(&Q.x, &Q.x);
|
||||
fp2_set_one(&Q.z);
|
||||
|
||||
// clear out the odd cofactor to get a point of order 2^f
|
||||
clear_cofactor_for_maximal_even_order(&P, curve, f);
|
||||
clear_cofactor_for_maximal_even_order(&Q, curve, f);
|
||||
|
||||
// compute PmQ, set PmQ to Q to ensure Q above (0,0)
|
||||
difference_point(&PQ2->Q, &P, &Q, curve);
|
||||
copy_point(&PQ2->P, &P);
|
||||
copy_point(&PQ2->PmQ, &Q);
|
||||
|
||||
#ifndef NDEBUG
|
||||
passed &= test_basis_order_twof(PQ2, curve, f);
|
||||
|
||||
if (!passed)
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
return 1;
|
||||
}
|
||||
Reference in New Issue
Block a user