#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: // // As a sanity check, a proof that these points form a commutative group: // // 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: // // // // 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: // // // // 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); }