mte/unikernel/duniverse/mirage-crypto/ec/native/point_operations.h

264 lines
8 KiB
C
Raw Normal View History

2025-11-11 02:07:51 +01:00
#define MAKE_FN_NAME1(x,y) x ## y
#define MAKE_FN_NAME(x,y) MAKE_FN_NAME1(x,y)
#define fe_one MAKE_FN_NAME(CURVE_DESCRIPTION,_set_one)
#define fe_add MAKE_FN_NAME(CURVE_DESCRIPTION,_add)
#define fe_sub MAKE_FN_NAME(CURVE_DESCRIPTION,_sub)
#define fe_mul MAKE_FN_NAME(CURVE_DESCRIPTION,_mul)
#define fe_sqr MAKE_FN_NAME(CURVE_DESCRIPTION,_square)
#define fe_nonzero MAKE_FN_NAME(CURVE_DESCRIPTION,_nonzero)
#define fe_selectznz MAKE_FN_NAME(CURVE_DESCRIPTION,_selectznz)
#define fe_from_bytes MAKE_FN_NAME(CURVE_DESCRIPTION,_from_bytes)
#define fe_to_mont MAKE_FN_NAME(CURVE_DESCRIPTION,_to_montgomery)
typedef WORD fe[LIMBS];
static WORD fe_nz(const WORD in1[LIMBS]) {
WORD ret;
fe_nonzero(&ret, in1);
return ret;
}
static void fe_copy(WORD out[LIMBS], const WORD in1[LIMBS]) {
for (int i = 0; i < LIMBS; i++) {
out[i] = in1[i];
}
}
static void fe_cmovznz(WORD out[LIMBS], WORD t, const WORD z[LIMBS],
const WORD nz[LIMBS]) {
fe_selectznz(out, !!t, z, nz);
}
// Group operations
// ----------------
//
// Building on top of the field operations we have the operations on the
// elliptic curve group itself. Points on the curve are represented in Jacobian
// coordinates.
//
// Both operations were transcribed to Coq and proven to correspond to naive
// implementations using Affine coordinates, for all suitable fields. In the
// Coq proofs, issues of constant-time execution and memory layout (aliasing)
// conventions were not considered. Specification of affine coordinates:
// <https://github.com/mit-plv/fiat-crypto/blob/79f8b5f39ed609339f0233098dee1a3c4e6b3080/src/Spec/WeierstrassCurve.v#L28>
// As a sanity check, a proof that these points form a commutative group:
// <https://github.com/mit-plv/fiat-crypto/blob/79f8b5f39ed609339f0233098dee1a3c4e6b3080/src/Curves/Weierstrass/AffineProofs.v#L33>
// point_double calculates 2*(x_in, y_in, z_in)
//
// The method is taken from:
// http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#doubling-dbl-2001-b
//
// Coq transcription and correctness proof:
// <https://github.com/mit-plv/fiat-crypto/blob/79f8b5f39ed609339f0233098dee1a3c4e6b3080/src/Curves/Weierstrass/Jacobian.v#L93>
// <https://github.com/mit-plv/fiat-crypto/blob/79f8b5f39ed609339f0233098dee1a3c4e6b3080/src/Curves/Weierstrass/Jacobian.v#L201>
//
// Outputs can equal corresponding inputs, i.e., x_out == x_in is allowed.
// while x_out == y_in is not (maybe this works, but it's not tested).
static void point_double(fe x_out, fe y_out, fe z_out,
const fe x_in, const fe y_in, const fe z_in) {
fe delta, gamma, beta, ftmp, ftmp2, tmptmp, alpha, fourbeta;
// delta = z^2
fe_sqr(delta, z_in);
// gamma = y^2
fe_sqr(gamma, y_in);
// beta = x*gamma
fe_mul(beta, x_in, gamma);
// alpha = 3*(x-delta)*(x+delta)
fe_sub(ftmp, x_in, delta);
fe_add(ftmp2, x_in, delta);
fe_add(tmptmp, ftmp2, ftmp2);
fe_add(ftmp2, ftmp2, tmptmp);
fe_mul(alpha, ftmp, ftmp2);
// x' = alpha^2 - 8*beta
fe_sqr(x_out, alpha);
fe_add(fourbeta, beta, beta);
fe_add(fourbeta, fourbeta, fourbeta);
fe_add(tmptmp, fourbeta, fourbeta);
fe_sub(x_out, x_out, tmptmp);
// z' = (y + z)^2 - gamma - delta
fe_add(delta, gamma, delta);
fe_add(ftmp, y_in, z_in);
fe_sqr(z_out, ftmp);
fe_sub(z_out, z_out, delta);
// y' = alpha*(4*beta - x') - 8*gamma^2
fe_sub(y_out, fourbeta, x_out);
fe_add(gamma, gamma, gamma);
fe_sqr(gamma, gamma);
fe_mul(y_out, alpha, y_out);
fe_add(gamma, gamma, gamma);
fe_sub(y_out, y_out, gamma);
}
// point_add calculates (x1, y1, z1) + (x2, y2, z2)
//
// The method is taken from:
// http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#addition-add-2007-bl,
// adapted for mixed addition (z2 = 1, or z2 = 0 for the point at infinity).
//
// Coq transcription and correctness proof:
// <https://github.com/mit-plv/fiat-crypto/blob/79f8b5f39ed609339f0233098dee1a3c4e6b3080/src/Curves/Weierstrass/Jacobian.v#L135>
// <https://github.com/mit-plv/fiat-crypto/blob/79f8b5f39ed609339f0233098dee1a3c4e6b3080/src/Curves/Weierstrass/Jacobian.v#L205>
//
// This function includes a branch for checking whether the two input points
// are equal, (while not equal to the point at infinity). This case never
// happens during single point multiplication, so there is no timing leak for
// ECDH or ECDSA signing.
static void point_add(fe x3, fe y3, fe z3, const fe x1,
const fe y1, const fe z1, const int mixed,
const fe x2, const fe y2, const fe z2) {
fe x_out, y_out, z_out;
WORD z1nz = fe_nz(z1);
WORD z2nz = fe_nz(z2);
// z1z1 = z1z1 = z1**2
fe z1z1; fe_sqr(z1z1, z1);
fe u1, s1, two_z1z2;
if (!mixed) {
// z2z2 = z2**2
fe z2z2; fe_sqr(z2z2, z2);
// u1 = x1*z2z2
fe_mul(u1, x1, z2z2);
// two_z1z2 = (z1 + z2)**2 - (z1z1 + z2z2) = 2z1z2
fe_add(two_z1z2, z1, z2);
fe_sqr(two_z1z2, two_z1z2);
fe_sub(two_z1z2, two_z1z2, z1z1);
fe_sub(two_z1z2, two_z1z2, z2z2);
// s1 = y1 * z2**3
fe_mul(s1, z2, z2z2);
fe_mul(s1, s1, y1);
} else {
// We'll assume z2 = 1 (special case z2 = 0 is handled later).
// u1 = x1*z2z2
fe_copy(u1, x1);
// two_z1z2 = 2z1z2
fe_add(two_z1z2, z1, z1);
// s1 = y1 * z2**3
fe_copy(s1, y1);
}
// u2 = x2*z1z1
fe u2; fe_mul(u2, x2, z1z1);
// h = u2 - u1
fe h; fe_sub(h, u2, u1);
WORD xneq = fe_nz(h);
// z_out = two_z1z2 * h
fe_mul(z_out, h, two_z1z2);
// z1z1z1 = z1 * z1z1
fe z1z1z1; fe_mul(z1z1z1, z1, z1z1);
// s2 = y2 * z1**3
fe s2; fe_mul(s2, y2, z1z1z1);
// r = (s2 - s1)*2
fe r;
fe_sub(r, s2, s1);
fe_add(r, r, r);
WORD yneq = fe_nz(r);
if (!xneq && !yneq && z1nz && z2nz) {
point_double(x3, y3, z3, x1, y1, z1);
return;
}
// I = (2h)**2
fe i;
fe_add(i, h, h);
fe_sqr(i, i);
// J = h * I
fe j; fe_mul(j, h, i);
// V = U1 * I
fe v; fe_mul(v, u1, i);
// x_out = r**2 - J - 2V
fe_sqr(x_out, r);
fe_sub(x_out, x_out, j);
fe_sub(x_out, x_out, v);
fe_sub(x_out, x_out, v);
// y_out = r(V-x_out) - 2 * s1 * J
fe_sub(y_out, v, x_out);
fe_mul(y_out, y_out, r);
fe s1j;
fe_mul(s1j, s1, j);
fe_sub(y_out, y_out, s1j);
fe_sub(y_out, y_out, s1j);
fe_cmovznz(x_out, z1nz, x2, x_out);
fe_cmovznz(x3, z2nz, x1, x_out);
fe_cmovznz(y_out, z1nz, y2, y_out);
fe_cmovznz(y3, z2nz, y1, y_out);
fe_cmovznz(z_out, z1nz, z2, z_out);
fe_cmovznz(z3, z2nz, z1, z_out);
}
/* Use a sliding window optimization method for scalar multiplication
Hard-coded window size = 4
Implementation inspired from Go's crypto library
https://github.com/golang/go/blob/a5cd894318677359f6d07ee74f9004d28b4d164c/src/crypto/internal/nistec/p256.go#L317
*/
/* Select the n-th element of the table
without leaking information about [n] */
static void table_select(fe out_x, fe out_y, fe out_z, size_t index, uint8_t n) {
fe x, y, z = {0};
fe_one(x); fe_one(y);
for(uint8_t i = 1 ; i < 16 ; ++i) {
WORD cond = i ^ n;
fe_cmovznz(x, cond, generator_table[index][n - 1][0], x);
fe_cmovznz(y, cond, generator_table[index][n - 1][1], y);
fe_cmovznz(z, cond, generator_table[index][n - 1][2], z);
}
fe_copy(out_x, x);
fe_copy(out_y, y);
fe_copy(out_z, z);
}
/* Returns [kG] by decomposing [k] in binary form, and adding
[2^0G * k_0 + 2^1G * k_1 + ...] in constant time using
pre-computed values of 2^iG */
static void scalar_mult_base(fe x2, fe y2, fe z2,
const uint8_t* scalar, size_t len) {
// P = 0
fe p_x, p_y, p_z = {0};
fe_one(p_x);
fe_one(p_y);
size_t index = 0;
for(size_t i = 0 ; i < len ; ++i) {
fe s_x, s_y, s_z;
uint8_t window = scalar[i] & 0xf;
table_select(s_x, s_y, s_z, index, window);
point_add(p_x, p_y, p_z, p_x, p_y, p_z, 0, s_x, s_y, s_z);
index++;
window = scalar[i] >> 4;
table_select(s_x, s_y, s_z, index, window);
point_add(p_x, p_y, p_z, p_x, p_y, p_z, 0, s_x, s_y, s_z);
index++;
}
fe_copy(x2, p_x);
fe_copy(y2, p_y);
fe_copy(z2, p_z);
}