crypto/fipsmodule/ec/p521.c (376 lines of code) (raw):
/*
------------------------------------------------------------------------------------
Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0 OR ISC
------------------------------------------------------------------------------------
*/
// Implementation of P-521 that uses Fiat-crypto for the field arithmetic
// found in third_party/fiat, similarly to p256.c and p384.c
#include <openssl/bn.h>
#include <openssl/ec.h>
#include <openssl/err.h>
#include <openssl/mem.h>
#include "../bn/internal.h"
#include "../cpucap/internal.h"
#include "../delocate.h"
#include "internal.h"
#include "ec_nistp.h"
#if !defined(OPENSSL_SMALL)
#if defined(EC_NISTP_USE_S2N_BIGNUM)
# include "../../../third_party/s2n-bignum/s2n-bignum_aws-lc.h"
#else
# if defined(EC_NISTP_USE_64BIT_LIMB)
# include "../../../third_party/fiat/p521_64.h"
# else
# include "../../../third_party/fiat/p521_32.h"
# endif
#endif
#if defined(EC_NISTP_USE_S2N_BIGNUM)
#define P521_NLIMBS (9)
typedef uint64_t p521_limb_t;
typedef uint64_t p521_felem[P521_NLIMBS]; // field element
static const p521_limb_t p521_felem_one[P521_NLIMBS] = {
0x0000000000000001, 0x0000000000000000,
0x0000000000000000, 0x0000000000000000,
0x0000000000000000, 0x0000000000000000,
0x0000000000000000, 0x0000000000000000,
0x0000000000000000};
// The field characteristic p.
static const p521_limb_t p521_felem_p[P521_NLIMBS] = {
0xffffffffffffffff, 0xffffffffffffffff,
0xffffffffffffffff, 0xffffffffffffffff,
0xffffffffffffffff, 0xffffffffffffffff,
0xffffffffffffffff, 0xffffffffffffffff,
0x1ff};
// s2n-bignum implementation of field arithmetic
#define p521_felem_add(out, in0, in1) bignum_add_p521(out, in0, in1)
#define p521_felem_sub(out, in0, in1) bignum_sub_p521(out, in0, in1)
#define p521_felem_opp(out, in0) bignum_neg_p521(out, in0)
#define p521_felem_to_bytes(out, in0) bignum_tolebytes_p521(out, in0)
#define p521_felem_from_bytes(out, in0) bignum_fromlebytes_p521(out, in0)
#define p521_felem_mul(out, in0, in1) bignum_mul_p521_selector(out, in0, in1)
#define p521_felem_sqr(out, in0) bignum_sqr_p521_selector(out, in0)
#else // EC_NISTP_USE_S2N_BIGNUM
#if defined(EC_NISTP_USE_64BIT_LIMB)
// In the 64-bit case Fiat-crypto represents a field element by 9 58-bit digits.
#define P521_NLIMBS (9)
typedef uint64_t p521_felem[P521_NLIMBS]; // field element
typedef uint64_t p521_limb_t;
// One in Fiat-crypto's representation (58-bit digits).
static const p521_limb_t p521_felem_one[P521_NLIMBS] = {
0x0000000000000001, 0x0000000000000000,
0x0000000000000000, 0x0000000000000000,
0x0000000000000000, 0x0000000000000000,
0x0000000000000000, 0x0000000000000000,
0x0000000000000000};
// The field characteristic p in Fiat-crypto's representation (58-bit digits).
static const p521_limb_t p521_felem_p[P521_NLIMBS] = {
0x03ffffffffffffff, 0x03ffffffffffffff,
0x03ffffffffffffff, 0x03ffffffffffffff,
0x03ffffffffffffff, 0x03ffffffffffffff,
0x03ffffffffffffff, 0x03ffffffffffffff,
0x01ffffffffffffff};
#else // 64BIT; else 32BIT
// In the 32-bit case Fiat-crypto represents a field element by 19 digits
// with the following bit sizes:
// [28, 27, 28, 27, 28, 27, 27, 28, 27, 28, 27, 28, 27, 27, 28, 27, 28, 27, 27].
#define P521_NLIMBS (19)
typedef uint32_t p521_felem[P521_NLIMBS]; // field element
typedef uint32_t p521_limb_t;
// One in Fiat-crypto's representation.
static const p521_limb_t p521_felem_one[P521_NLIMBS] = {
0x0000001, 0x0000000, 0x0000000, 0x0000000,
0x0000000, 0x0000000, 0x0000000, 0x0000000,
0x0000000, 0x0000000, 0x0000000, 0x0000000,
0x0000000, 0x0000000, 0x0000000, 0x0000000,
0x0000000, 0x0000000, 0x0000000};
// The field characteristic p in Fiat-crypto's representation.
static const p521_limb_t p521_felem_p[P521_NLIMBS] = {
0xfffffff, 0x7ffffff, 0xfffffff, 0x7ffffff,
0xfffffff, 0x7ffffff, 0x7ffffff, 0xfffffff,
0x7ffffff, 0xfffffff, 0x7ffffff, 0xfffffff,
0x7ffffff, 0x7ffffff, 0xfffffff, 0x7ffffff,
0xfffffff, 0x7ffffff, 0x7ffffff};
#endif // 64BIT
// Fiat-crypto implementation of field arithmetic
#define p521_felem_add(out, in0, in1) fiat_secp521r1_carry_add(out, in0, in1)
#define p521_felem_sub(out, in0, in1) fiat_secp521r1_carry_sub(out, in0, in1)
#define p521_felem_opp(out, in0) fiat_secp521r1_carry_opp(out, in0)
#define p521_felem_mul(out, in0, in1) fiat_secp521r1_carry_mul(out, in0, in1)
#define p521_felem_sqr(out, in0) fiat_secp521r1_carry_square(out, in0)
#define p521_felem_to_bytes(out, in0) fiat_secp521r1_to_bytes(out, in0)
#define p521_felem_from_bytes(out, in0) fiat_secp521r1_from_bytes(out, in0)
#endif // EC_NISTP_USE_S2N_BIGNUM
// The wrapper functions are needed for FIPS static build.
// Otherwise, initializing ec_nistp_meth with pointers to s2n-bignum
// functions directly generates :got: references that are also thought
// to be local_target by the delocator.
static inline void p521_felem_add_wrapper(ec_nistp_felem_limb *c,
const ec_nistp_felem_limb *a,
const ec_nistp_felem_limb *b) {
p521_felem_add(c, a, b);
}
static inline void p521_felem_sub_wrapper(ec_nistp_felem_limb *c,
const ec_nistp_felem_limb *a,
const ec_nistp_felem_limb *b) {
p521_felem_sub(c, a, b);
}
static inline void p521_felem_neg_wrapper(ec_nistp_felem_limb *c,
const ec_nistp_felem_limb *a) {
p521_felem_opp(c, a);
}
static p521_limb_t p521_felem_nz(const p521_limb_t in1[P521_NLIMBS]) {
p521_limb_t is_not_zero = 0;
for (int i = 0; i < P521_NLIMBS; i++) {
is_not_zero |= in1[i];
}
#if defined(EC_NISTP_USE_S2N_BIGNUM)
return is_not_zero;
#else
// Fiat-crypto functions may return p (the field characteristic)
// instead of 0 in some cases, so we also check for that.
p521_limb_t is_not_p = 0;
for (int i = 0; i < P521_NLIMBS; i++) {
is_not_p |= (in1[i] ^ p521_felem_p[i]);
}
return ~(constant_time_is_zero_w(is_not_p) |
constant_time_is_zero_w(is_not_zero));
#endif
}
// NOTE: the input and output are in little-endian representation.
static void p521_from_generic(p521_felem out, const EC_FELEM *in) {
#ifdef OPENSSL_BIG_ENDIAN
uint8_t tmp[P521_EC_FELEM_BYTES];
bn_words_to_little_endian(tmp, P521_EC_FELEM_BYTES, in->words, P521_EC_FELEM_WORDS);
p521_felem_from_bytes(out, tmp);
#else
p521_felem_from_bytes(out, (const uint8_t *)in->words);
#endif
}
// NOTE: the input and output are in little-endian representation.
static void p521_to_generic(EC_FELEM *out, const p521_felem in) {
// |p521_felem_to_bytes| function will write the result to the first 66 bytes
// of |out| which is exactly how many bytes are needed to represent a 521-bit
// element.
// The number of BN_ULONGs to represent a 521-bit value is 9 and 17, when
// BN_ULONG is 64-bit and 32-bit, respectively. Nine 64-bit BN_ULONGs
// translate to 72 bytes, which means that we have to make sure that the
// extra 6 bytes are zeroed out. To avoid confusion over 32 vs. 64 bit
// systems and Fiat's vs. ours representation we zero out the whole element.
#ifdef OPENSSL_BIG_ENDIAN
uint8_t tmp[P521_EC_FELEM_BYTES];
p521_felem_to_bytes(tmp, in);
bn_little_endian_to_words(out->words, P521_EC_FELEM_WORDS, tmp, P521_EC_FELEM_BYTES);
#else
OPENSSL_memset((uint8_t*)out->words, 0, sizeof(out->words));
// Convert the element to bytes.
p521_felem_to_bytes((uint8_t *)out->words, in);
#endif
}
// Finite field inversion using Fermat Little Theorem.
// The code is autogenerated by the ECCKiila project:
// https://arxiv.org/abs/2007.11481
static void p521_felem_inv(p521_felem output, const p521_felem t1) {
#if defined(EC_NISTP_USE_S2N_BIGNUM)
bignum_inv_p521(output, t1);
#else
/* temporary variables */
p521_felem acc, t2, t4, t8, t16, t32, t64;
p521_felem t128, t256, t512, t516, t518, t519;
p521_felem_sqr(acc, t1);
p521_felem_mul(t2, acc, t1);
p521_felem_sqr(acc, t2);
p521_felem_sqr(acc, acc);
p521_felem_mul(t4, acc, t2);
p521_felem_sqr(acc, t4);
for (int i = 0; i < 3; i++) {
p521_felem_sqr(acc, acc);
}
p521_felem_mul(t8, acc, t4);
p521_felem_sqr(acc, t8);
for (int i = 0; i < 7; i++) {
p521_felem_sqr(acc, acc);
}
p521_felem_mul(t16, acc, t8);
p521_felem_sqr(acc, t16);
for (int i = 0; i < 15; i++) {
p521_felem_sqr(acc, acc);
}
p521_felem_mul(t32, acc, t16);
p521_felem_sqr(acc, t32);
for (int i = 0; i < 31; i++) {
p521_felem_sqr(acc, acc);
}
p521_felem_mul(t64, acc, t32);
p521_felem_sqr(acc, t64);
for (int i = 0; i < 63; i++) {
p521_felem_sqr(acc, acc);
}
p521_felem_mul(t128, acc, t64);
p521_felem_sqr(acc, t128);
for (int i = 0; i < 127; i++) {
p521_felem_sqr(acc, acc);
}
p521_felem_mul(t256, acc, t128);
p521_felem_sqr(acc, t256);
for (int i = 0; i < 255; i++) {
p521_felem_sqr(acc, acc);
}
p521_felem_mul(t512, acc, t256);
p521_felem_sqr(acc, t512);
for (int i = 0; i < 3; i++) {
p521_felem_sqr(acc, acc);
}
p521_felem_mul(t516, acc, t4);
p521_felem_sqr(acc, t516);
p521_felem_sqr(acc, acc);
p521_felem_mul(t518, acc, t2);
p521_felem_sqr(acc, t518);
p521_felem_mul(t519, acc, t1);
p521_felem_sqr(acc, t519);
p521_felem_sqr(acc, acc);
p521_felem_mul(output, acc, t1);
#endif
}
static void p521_point_double(p521_felem x_out,
p521_felem y_out,
p521_felem z_out,
const p521_felem x_in,
const p521_felem y_in,
const p521_felem z_in) {
#if defined(EC_NISTP_USE_S2N_BIGNUM)
ec_nistp_felem_limb in[P521_NLIMBS * 3];
ec_nistp_felem_limb out[P521_NLIMBS * 3];
ec_nistp_coordinates_to_point(in, x_in, y_in, z_in, P521_NLIMBS);
p521_jdouble_selector(out, in);
ec_nistp_point_to_coordinates(x_out, y_out, z_out, out, P521_NLIMBS);
#else
ec_nistp_point_double(p521_methods(), x_out, y_out, z_out, x_in, y_in, z_in);
#endif
}
// p521_point_add calculates (x1, y1, z1) + (x2, y2, z2)
//
// The method is taken from:
// http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.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/davidben/fiat-crypto/blob/c7b95f62b2a54b559522573310e9b487327d219a/src/Curves/Weierstrass/Jacobian.v#L467>
// <https://github.com/davidben/fiat-crypto/blob/c7b95f62b2a54b559522573310e9b487327d219a/src/Curves/Weierstrass/Jacobian.v#L544>
static void p521_point_add(p521_felem x3, p521_felem y3, p521_felem z3,
const p521_felem x1,
const p521_felem y1,
const p521_felem z1,
const int mixed,
const p521_felem x2,
const p521_felem y2,
const p521_felem z2) {
ec_nistp_point_add(p521_methods(), x3, y3, z3, x1, y1, z1, mixed, x2, y2, z2);
}
#include "p521_table.h"
#if defined(EC_NISTP_USE_S2N_BIGNUM)
DEFINE_METHOD_FUNCTION(ec_nistp_meth, p521_methods) {
out->felem_num_limbs = P521_NLIMBS;
out->felem_num_bits = 521;
out->felem_add = p521_felem_add_wrapper;
out->felem_sub = p521_felem_sub_wrapper;
out->felem_mul = bignum_mul_p521_selector;
out->felem_sqr = bignum_sqr_p521_selector;
out->felem_neg = p521_felem_neg_wrapper;
out->felem_nz = p521_felem_nz;
out->felem_one = p521_felem_one;
out->point_dbl = p521_point_double;
out->point_add = p521_point_add;
out->scalar_mul_base_table = (const ec_nistp_felem_limb*) p521_g_pre_comp;
}
#else
DEFINE_METHOD_FUNCTION(ec_nistp_meth, p521_methods) {
out->felem_num_limbs = P521_NLIMBS;
out->felem_num_bits = 521;
out->felem_add = fiat_secp521r1_carry_add;
out->felem_sub = fiat_secp521r1_carry_sub;
out->felem_mul = fiat_secp521r1_carry_mul;
out->felem_sqr = fiat_secp521r1_carry_square;
out->felem_neg = fiat_secp521r1_carry_opp;
out->felem_nz = p521_felem_nz;
out->felem_one = p521_felem_one;
out->point_dbl = p521_point_double;
out->point_add = p521_point_add;
out->scalar_mul_base_table = (const ec_nistp_felem_limb*) p521_g_pre_comp;
}
#endif
// OPENSSL EC_METHOD FUNCTIONS
// Takes the Jacobian coordinates (X, Y, Z) of a point and returns:
// (X', Y') = (X/Z^2, Y/Z^3).
static int ec_GFp_nistp521_point_get_affine_coordinates(
const EC_GROUP *group, const EC_JACOBIAN *point,
EC_FELEM *x_out, EC_FELEM *y_out) {
if (constant_time_declassify_w(ec_GFp_simple_is_at_infinity(group, point))) {
OPENSSL_PUT_ERROR(EC, EC_R_POINT_AT_INFINITY);
return 0;
}
p521_felem z1, z2;
p521_from_generic(z1, &point->Z);
p521_felem_inv(z2, z1);
p521_felem_sqr(z2, z2);
if (x_out != NULL) {
p521_felem x;
p521_from_generic(x, &point->X);
p521_felem_mul(x, x, z2);
p521_to_generic(x_out, x);
}
if (y_out != NULL) {
p521_felem y;
p521_from_generic(y, &point->Y);
p521_felem_sqr(z2, z2); // z^-4
p521_felem_mul(y, y, z1); // y * z
p521_felem_mul(y, y, z2); // y * z^-3
p521_to_generic(y_out, y);
}
return 1;
}
static void ec_GFp_nistp521_add(const EC_GROUP *group, EC_JACOBIAN *r,
const EC_JACOBIAN *a, const EC_JACOBIAN *b) {
p521_felem x1, y1, z1, x2, y2, z2;
p521_from_generic(x1, &a->X);
p521_from_generic(y1, &a->Y);
p521_from_generic(z1, &a->Z);
p521_from_generic(x2, &b->X);
p521_from_generic(y2, &b->Y);
p521_from_generic(z2, &b->Z);
p521_point_add(x1, y1, z1, x1, y1, z1, 0 /* both Jacobian */, x2, y2, z2);
p521_to_generic(&r->X, x1);
p521_to_generic(&r->Y, y1);
p521_to_generic(&r->Z, z1);
}
static void ec_GFp_nistp521_dbl(const EC_GROUP *group, EC_JACOBIAN *r,
const EC_JACOBIAN *a) {
p521_felem x, y, z;
p521_from_generic(x, &a->X);
p521_from_generic(y, &a->Y);
p521_from_generic(z, &a->Z);
p521_point_double(x, y, z, x, y, z);
p521_to_generic(&r->X, x);
p521_to_generic(&r->Y, y);
p521_to_generic(&r->Z, z);
}
// Multiplication of an arbitrary point by a scalar, r = [scalar]P.
static void ec_GFp_nistp521_point_mul(const EC_GROUP *group, EC_JACOBIAN *r,
const EC_JACOBIAN *p,
const EC_SCALAR *scalar) {
p521_felem res[3] = {{0}, {0}, {0}}, tmp[3] = {{0}, {0}, {0}};
p521_from_generic(tmp[0], &p->X);
p521_from_generic(tmp[1], &p->Y);
p521_from_generic(tmp[2], &p->Z);
#if defined(EC_NISTP_USE_S2N_BIGNUM)
p521_jscalarmul_selector((uint64_t*)res, scalar->words, (uint64_t*)tmp);
#else
ec_nistp_scalar_mul(p521_methods(), res[0], res[1], res[2], tmp[0], tmp[1], tmp[2], scalar);
#endif
p521_to_generic(&r->X, res[0]);
p521_to_generic(&r->Y, res[1]);
p521_to_generic(&r->Z, res[2]);
}
// Multiplication of the base point G of P-521 curve with the given scalar.
static void ec_GFp_nistp521_point_mul_base(const EC_GROUP *group,
EC_JACOBIAN *r,
const EC_SCALAR *scalar) {
p521_felem res[3] = {{0}, {0}, {0}};
ec_nistp_scalar_mul_base(p521_methods(), res[0], res[1], res[2], scalar);
p521_to_generic(&r->X, res[0]);
p521_to_generic(&r->Y, res[1]);
p521_to_generic(&r->Z, res[2]);
}
// Computes [g_scalar]G + [p_scalar]P, where G is the base point of the P-521
// curve, and P is the given point |p|.
// Note: this function is NOT constant-time.
static void ec_GFp_nistp521_point_mul_public(const EC_GROUP *group,
EC_JACOBIAN *r,
const EC_SCALAR *g_scalar,
const EC_JACOBIAN *p,
const EC_SCALAR *p_scalar) {
p521_felem res[3] = {{0}, {0}, {0}}, tmp[3] = {{0}, {0}, {0}};
p521_from_generic(tmp[0], &p->X);
p521_from_generic(tmp[1], &p->Y);
p521_from_generic(tmp[2], &p->Z);
ec_nistp_scalar_mul_public(p521_methods(), res[0], res[1], res[2], g_scalar, tmp[0], tmp[1], tmp[2], p_scalar);
// Copy the result to the output.
p521_to_generic(&r->X, res[0]);
p521_to_generic(&r->Y, res[1]);
p521_to_generic(&r->Z, res[2]);
}
static void ec_GFp_nistp521_felem_mul(const EC_GROUP *group, EC_FELEM *r,
const EC_FELEM *a, const EC_FELEM *b) {
p521_felem felem1, felem2, felem3;
p521_from_generic(felem1, a);
p521_from_generic(felem2, b);
p521_felem_mul(felem3, felem1, felem2);
p521_to_generic(r, felem3);
}
static void ec_GFp_nistp521_felem_sqr(const EC_GROUP *group, EC_FELEM *r,
const EC_FELEM *a) {
p521_felem felem1, felem2;
p521_from_generic(felem1, a);
p521_felem_sqr(felem2, felem1);
p521_to_generic(r, felem2);
}
DEFINE_METHOD_FUNCTION(EC_METHOD, EC_GFp_nistp521_method) {
out->point_get_affine_coordinates =
ec_GFp_nistp521_point_get_affine_coordinates;
out->add = ec_GFp_nistp521_add;
out->dbl = ec_GFp_nistp521_dbl;
out->mul = ec_GFp_nistp521_point_mul;
out->mul_base = ec_GFp_nistp521_point_mul_base;
out->mul_public = ec_GFp_nistp521_point_mul_public;
out->felem_mul = ec_GFp_nistp521_felem_mul;
out->felem_sqr = ec_GFp_nistp521_felem_sqr;
out->felem_to_bytes = ec_GFp_simple_felem_to_bytes;
out->felem_from_bytes = ec_GFp_simple_felem_from_bytes;
out->scalar_inv0_montgomery = ec_simple_scalar_inv0_montgomery;
out->scalar_to_montgomery_inv_vartime =
ec_simple_scalar_to_montgomery_inv_vartime;
out->cmp_x_coordinate = ec_GFp_simple_cmp_x_coordinate;
}
// ----------------------------------------------------------------------------
// Analysis of the doubling case occurrence in the Joye-Tunstall recoding:
// see the analysis at the bottom of the |p384.c| file.
#endif // !defined(OPENSSL_SMALL)