blob: 6ce9471a5519152e8296a095025c32b93d2a9803 [file]
// Copyright 2020 The BoringSSL Authors
// Copyright 2014-2016 The OpenSSL Project Authors. All Rights Reserved.
// Copyright (c) 2014, Intel Corporation. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <openssl/base.h>
#include <openssl/bn.h>
#include <openssl/ec.h>
#include <openssl/mem.h>
#include <assert.h>
#include <iterator>
#include "../../internal.h"
#include "../delocate.h"
#include "internal.h"
#include "p256_internal.h"
using namespace bssl;
static void fiat_p256_opp_conditional(fiat_p256_felem x, crypto_word_t c) {
alignas(32) fiat_p256_felem n;
fiat_p256_opp(n, x);
for (size_t i = 0; i < P256_LIMBS; ++i) {
x[i] = br_cmov(c, n[i], x[i]);
}
}
// bit returns the `i`th bit in `in`.
static crypto_word_t bit(const uint8_t in[32], int i) {
if (i < 0 || i >= 256) {
return 0;
}
return (in[i >> 3] >> (i & 7)) & 1;
}
// point multiplication
static void fiat_p256_select_point_16(fiat_p256_felem dst[3],
const fiat_p256_felem src[16][3],
size_t i) {
OPENSSL_memset(dst, 0, 3 * sizeof(fiat_p256_felem));
OPENSSL_CLANG_PRAGMA("clang loop unroll_count(4)")
for (size_t j = 0; j < 16; j++) {
constant_time_conditional_memxor(dst, &src[j], 3 * sizeof(fiat_p256_felem),
constant_time_eq_w(i, j));
}
}
// Precompute multiples of `p`. p_pre_comp[i] is (i+1) * `p`.
static void p256_point_mul(fiat_p256_felem out[3], const fiat_p256_felem p[3],
const uint8_t s[32]) {
alignas(32) fiat_p256_felem p_pre_comp[16][3];
OPENSSL_memcpy(p_pre_comp[0], p, sizeof(p_pre_comp[0]));
for (size_t j = 2; j <= 16; ++j) {
if (j & 1) {
p256_point_add_vartime_if_doubling((uintptr_t)p_pre_comp[j - 1],
(uintptr_t)p_pre_comp[j - 2],
(uintptr_t)p_pre_comp[0]);
} else {
p256_point_double((uintptr_t)p_pre_comp[j - 1],
(uintptr_t)p_pre_comp[(j - 1) / 2]);
}
}
alignas(32) fiat_p256_felem ret[3];
bool ret_is_zero = true;
for (size_t i = 51; i < 52; i--) {
if (!ret_is_zero) {
for (size_t k = 4; k < 5; k--) {
p256_point_double((uintptr_t)ret, (uintptr_t)ret);
}
}
crypto_word_t bits = 0;
OPENSSL_CLANG_PRAGMA("clang loop unroll(full)")
for (size_t k = 5; k < 6; k--) {
bits |= bit(s, i * 5 - 1 + k) << k;
}
crypto_word_t sign, digit;
ec_GFp_nistp_recode_scalar_bits(&sign, &digit, bits);
alignas(32) fiat_p256_felem t[3];
fiat_p256_select_point_16(t, p_pre_comp, digit - 1);
fiat_p256_opp_conditional(t[1], sign);
if (!ret_is_zero) {
p256_point_add_vartime_if_doubling((uintptr_t)ret, (uintptr_t)ret,
(uintptr_t)t);
} else {
OPENSSL_memcpy(ret, t, sizeof(ret));
ret_is_zero = false;
}
}
// The first loop iteration will initialize `ret`.
assert(!ret_is_zero);
OPENSSL_memcpy(out, ret, sizeof(ret));
}
// point_mul_public
#include "./p256_table.h"
static void ec_GFp_nistp256_point_mul_public(const EC_GROUP *group,
EC_JACOBIAN *r,
const EC_SCALAR *gs,
const EC_JACOBIAN *p,
const EC_SCALAR *ps) {
const uint8_t *g_scalar = (uint8_t *)gs->words;
int8_t p_wNAF[257] = {0};
alignas(32) fiat_p256_felem p_pre_comp[1 << (4 - 1)][3];
OPENSSL_memcpy(p_pre_comp[0][2], &p->Z, 32);
if (!p256_point_iszero((uintptr_t)p_pre_comp[0])) {
ec_compute_wNAF(group, p_wNAF, ps, /*bits=*/256, /*w=*/4);
// Precompute multiples of `p`. p_pre_comp[i] is (2*i+1) * `p`.
OPENSSL_memcpy(p_pre_comp[0][0], &p->X, 32);
OPENSSL_memcpy(p_pre_comp[0][1], &p->Y, 32);
alignas(32) fiat_p256_felem p2[3];
p256_point_double((uintptr_t)p2, (uintptr_t)p_pre_comp[0]);
for (size_t i = 1; i < std::size(p_pre_comp); i++) {
p256_point_add_nz_nz_neq((uintptr_t)p_pre_comp[i],
(uintptr_t)p_pre_comp[i - 1], (uintptr_t)p2);
}
}
alignas(32) fiat_p256_felem ret[3] = {};
bool ret_is_zero = true; // Save some point operations, avoid 0+Q
for (int i = 256; i >= 0; i--) {
if (!ret_is_zero) {
p256_point_double((uintptr_t)ret, (uintptr_t)ret);
}
if (i <= 31) {
OPENSSL_CLANG_PRAGMA("clang loop unroll(full)")
for (size_t j = 1; j < 2; j--) {
crypto_word_t bits = 0;
OPENSSL_CLANG_PRAGMA("clang loop unroll(full)")
for (size_t k = 3; k < 4; k--) {
bits |= bit(g_scalar, i + j * 32 + k * 64) << k;
}
if (bits != 0) {
if (!ret_is_zero) {
alignas(32) fiat_p256_felem t[3];
fiat_p256_set_one(t[2]);
OPENSSL_memcpy(t, fiat_p256_g_pre_comp[j][bits - 1], 64);
p256_point_add_affinenz_conditional_vartime_if_doubling(
(uintptr_t)ret, (uintptr_t)ret, (uintptr_t)t, 1);
ret_is_zero = p256_point_iszero((uintptr_t)ret);
} else {
OPENSSL_memcpy(ret, fiat_p256_g_pre_comp[j][bits - 1],
sizeof(fiat_p256_g_pre_comp[j][bits - 1]));
fiat_p256_set_one(ret[2]);
ret_is_zero = false;
}
}
}
}
int digit = p_wNAF[i];
if (digit != 0) {
assert(digit & 1);
size_t idx = (size_t)(digit < 0 ? (-digit) >> 1 : digit >> 1);
fiat_p256_felem t[3];
OPENSSL_memcpy(t, p_pre_comp[idx], sizeof(t));
if (digit < 0) {
fiat_p256_opp(t[1], t[1]);
}
if (!ret_is_zero) {
p256_point_add_vartime_if_doubling((uintptr_t)ret, (uintptr_t)ret,
(uintptr_t)t);
ret_is_zero = p256_point_iszero((uintptr_t)ret);
} else {
OPENSSL_memcpy(ret, t, sizeof(ret));
ret_is_zero = false;
}
}
}
OPENSSL_memcpy(&r->X, ret[0], 32);
OPENSSL_memcpy(&r->Y, ret[1], 32);
OPENSSL_memcpy(&r->Z, ret[2], 32);
}
// p256_point_mul_base (small and full)
// fiat_p256_select_point_affine selects the `i`th point from a precomputation
// table and copies it to `dst`. If `n<=i`, the output is (0, 0).
static void fiat_p256_select_point_affine(fiat_p256_felem dst[2],
const fiat_p256_felem src[/*n*/][2],
size_t i, size_t n) {
OPENSSL_memset(dst, 0, 2 * sizeof(fiat_p256_felem));
OPENSSL_CLANG_PRAGMA("clang loop unroll_count(8)")
for (size_t j = 0; j < n; j++) {
constant_time_conditional_memxor(dst, &src[j], 2 * sizeof(fiat_p256_felem),
constant_time_eq_w(i, j));
}
}
static void fiat_p256_conditional_zero_or_one(fiat_p256_felem out,
crypto_word_t c) {
fiat_p256_set_one(out);
crypto_word_t mask = value_barrier_w(~constant_time_is_zero_w(c));
for (size_t i = 0; i < P256_LIMBS; ++i) {
out[i] &= mask;
}
}
#if defined(OPENSSL_SMALL)
#ifdef __clang__
__attribute__((noinline)) // Do not inline into unrolled loop below.
#endif
static void fiat_p256_select_point_affine_15(
fiat_p256_felem dst[2], const fiat_p256_felem src[/*n*/][2], size_t i) {
fiat_p256_select_point_affine(dst, src, i, 15); // Intended to be inlined.
}
static void p256_point_mul_base(fiat_p256_felem ret[3], const uint8_t s[32]) {
bool ret_is_zero = true; // Save two point operations in the first round.
alignas(32) fiat_p256_felem t[3];
fiat_p256_set_one(t[2]);
for (size_t i = 31; i < 32; i--) {
if (!ret_is_zero) {
p256_point_double((uintptr_t)ret, (uintptr_t)ret);
}
OPENSSL_CLANG_PRAGMA("clang loop unroll(full)")
for (size_t j = 1; j < 2; j--) {
crypto_word_t bits = 0;
OPENSSL_CLANG_PRAGMA("clang loop unroll(full)")
for (size_t k = 3; k < 4; k--) {
bits |= bit(s, i + 32 * j + 64 * k) << k;
}
fiat_p256_select_point_affine_15(t, fiat_p256_g_pre_comp[j], bits - 1);
if (!ret_is_zero) {
p256_point_add_affinenz_conditional_vartime_if_doubling(
(uintptr_t)ret, (uintptr_t)ret, (uintptr_t)t, (uintptr_t)bits);
} else {
OPENSSL_memcpy(ret, t, sizeof(t));
fiat_p256_conditional_zero_or_one(ret[2], bits);
ret_is_zero = false;
}
}
}
// The first loop iteration will initialize `ret`.
assert(!ret_is_zero);
}
#else // defined(OPENSSL_SMALL)
/*
* Originally written by Shay Gueron (1, 2), and Vlad Krasnov (1)
* (1) Intel Corporation, Israel Development Center, Haifa, Israel
* (2) University of Haifa, Israel
*
* Reference:
* S.Gueron and V.Krasnov, "Fast Prime Field Elliptic Curve Cryptography with
* 256 Bit Primes"
*/
// Precomputed tables for the default generator
typedef fiat_p256_felem PRECOMP256_ROW[64][2];
#include "p256-nistz-table.h"
#ifdef __clang__
__attribute__((noinline)) // Do not inline into unrolled loop below.
#endif
static void fiat_p256_select_point_affine_16(fiat_p256_felem dst[2],
const fiat_p256_felem src[16][2],
size_t i) {
fiat_p256_select_point_affine(dst, src, i, 16);
}
#ifdef __clang__
__attribute__((noinline)) // Do not inline into unrolled loop below.
#endif
static void fiat_p256_select_point_affine_64(fiat_p256_felem dst[2],
const fiat_p256_felem src[64][2],
size_t i) {
fiat_p256_select_point_affine(dst, src, i, 64);
}
// See `ec_GFp_nistp_recode_scalar_bits` in util.c for details
static crypto_word_t booth_recode_w7(crypto_word_t in) {
crypto_word_t s, d;
s = ~((in >> 7) - 1);
d = (1 << 8) - in - 1;
d = (d & s) | (in & ~s);
d = (d >> 1) + (d & 1);
return (d << 1) + (s & 1);
}
static void p256_point_mul_base(fiat_p256_felem ret[3], const uint8_t s[32]) {
bool ret_is_zero = true;
alignas(32) fiat_p256_felem t[3];
fiat_p256_set_one(t[2]);
for (size_t i = 36; i < 37; i--) {
// Load 7-bit windows, plus one bit below the window for `booth_recode_w7`,
// i.e. 8 bits from 7 * i - 1 to 7 * (i + 1).
constexpr size_t kMask = (1 << (7 + 1)) - 1;
crypto_word_t wvalue;
if (i == 0) {
wvalue = (s[0] << 1) & kMask;
} else {
size_t first_bit = 7 * i - 1;
size_t idx = first_bit / 8;
// The window may span two bytes.
wvalue =
s[idx] | (static_cast<crypto_word_t>(idx < 31 ? s[idx + 1] : 0) << 8);
wvalue = (wvalue >> (first_bit % 8)) & kMask;
}
wvalue = booth_recode_w7(wvalue);
if (i == 36) {
// The last window has only 4 bits instead of the full 7.
declassify_assert((wvalue >> 1) <= 16);
fiat_p256_select_point_affine_16(t, ecp_nistz256_precomputed[i],
(wvalue >> 1) - 1);
} else {
fiat_p256_select_point_affine_64(t, ecp_nistz256_precomputed[i],
(wvalue >> 1) - 1);
}
fiat_p256_opp_conditional(t[1], wvalue & 1);
if (!ret_is_zero) {
p256_point_add_affinenz_conditional_vartime_if_doubling(
(uintptr_t)ret, (uintptr_t)ret, (uintptr_t)t, wvalue >> 1);
} else {
OPENSSL_memcpy(ret, t, sizeof(t));
fiat_p256_conditional_zero_or_one(ret[2], wvalue >> 1);
ret_is_zero = false;
}
}
// The first loop iteration will initialize `ret`.
assert(!ret_is_zero);
}
#endif // !OPENSSL_SMALL
// FIELD-ELEMENT INVERSION
// fiat_p256_inv_square calculates `out` = `in`^{-2}
//
// Based on Fermat's Little Theorem:
// a^p = a (mod p)
// a^{p-1} = 1 (mod p)
// a^{p-3} = a^{-2} (mod p)
static void fiat_p256_inv_square(fiat_p256_felem out,
const fiat_p256_felem in) {
// This implements the addition chain described in
// https://briansmith.org/ecc-inversion-addition-chains-01#p256_field_inversion
fiat_p256_felem x2, x3, x6, x12, x15, x30, x32;
p256_coord_sqr(x2, in); // 2^2 - 2^1
p256_coord_mul(x2, x2, in); // 2^2 - 2^0
p256_coord_sqr(x3, x2); // 2^3 - 2^1
p256_coord_mul(x3, x3, in); // 2^3 - 2^0
p256_coord_sqr(x6, x3);
for (int i = 1; i < 3; i++) {
p256_coord_sqr(x6, x6); // 2^6 - 2^3
}
p256_coord_mul(x6, x6, x3); // 2^6 - 2^0
p256_coord_sqr(x12, x6);
for (int i = 1; i < 6; i++) {
p256_coord_sqr(x12, x12); // 2^12 - 2^6
}
p256_coord_mul(x12, x12, x6); // 2^12 - 2^0
p256_coord_sqr(x15, x12);
for (int i = 1; i < 3; i++) {
p256_coord_sqr(x15, x15); // 2^15 - 2^3
}
p256_coord_mul(x15, x15, x3); // 2^15 - 2^0
p256_coord_sqr(x30, x15);
for (int i = 1; i < 15; i++) {
p256_coord_sqr(x30, x30); // 2^30 - 2^15
}
p256_coord_mul(x30, x30, x15); // 2^30 - 2^0
p256_coord_sqr(x32, x30);
p256_coord_sqr(x32, x32); // 2^32 - 2^2
p256_coord_mul(x32, x32, x2); // 2^32 - 2^0
fiat_p256_felem ret;
p256_coord_sqr(ret, x32);
for (int i = 1; i < 31 + 1; i++) {
p256_coord_sqr(ret, ret); // 2^64 - 2^32
}
p256_coord_mul(ret, ret, in); // 2^64 - 2^32 + 2^0
for (int i = 0; i < 96 + 32; i++) {
p256_coord_sqr(ret, ret); // 2^192 - 2^160 + 2^128
}
p256_coord_mul(ret, ret, x32); // 2^192 - 2^160 + 2^128 + 2^32 - 2^0
for (int i = 0; i < 32; i++) {
p256_coord_sqr(ret, ret); // 2^224 - 2^192 + 2^160 + 2^64 - 2^32
}
p256_coord_mul(ret, ret, x32); // 2^224 - 2^192 + 2^160 + 2^64 - 2^0
for (int i = 0; i < 30; i++) {
p256_coord_sqr(ret, ret); // 2^254 - 2^222 + 2^190 + 2^94 - 2^30
}
p256_coord_mul(ret, ret, x30); // 2^254 - 2^222 + 2^190 + 2^94 - 2^0
p256_coord_sqr(ret, ret);
p256_coord_sqr(out, ret); // 2^256 - 2^224 + 2^192 + 2^96 - 2^2
}
// Arithmetic modulo curve order
static void p256_order_inv0(const EC_GROUP *group, EC_SCALAR *out,
const EC_SCALAR *in) {
// table[i] stores a power of `in` corresponding to the matching enum value.
enum {
// The following indices specify the power in binary.
i_1 = 0,
i_10,
i_11,
i_101,
i_111,
i_1010,
i_1111,
i_10101,
i_101010,
i_101111,
// The following indices specify 2^N-1, or N ones in a row.
i_x6,
i_x8,
i_x16,
i_x32
};
BN_ULONG table[15][P256_LIMBS];
// https://briansmith.org/ecc-inversion-addition-chains-01#p256_scalar_inversion
// This code uses specialized field arithmetic and saves 12 sqr and 13 mul.
// Pre-calculate powers.
OPENSSL_memcpy(table[i_1], in->words, P256_LIMBS * sizeof(BN_ULONG));
p256_order_sqr(group, table[i_10], table[i_1], 1);
p256_order_mul(group, table[i_11], table[i_1], table[i_10]);
p256_order_mul(group, table[i_101], table[i_11], table[i_10]);
p256_order_mul(group, table[i_111], table[i_101], table[i_10]);
p256_order_sqr(group, table[i_1010], table[i_101], 1);
p256_order_mul(group, table[i_1111], table[i_1010], table[i_101]);
p256_order_sqr(group, table[i_10101], table[i_1010], 1);
p256_order_mul(group, table[i_10101], table[i_10101], table[i_1]);
p256_order_sqr(group, table[i_101010], table[i_10101], 1);
p256_order_mul(group, table[i_101111], table[i_101010], table[i_101]);
p256_order_mul(group, table[i_x6], table[i_101010], table[i_10101]);
p256_order_sqr(group, table[i_x8], table[i_x6], 2);
p256_order_mul(group, table[i_x8], table[i_x8], table[i_11]);
p256_order_sqr(group, table[i_x16], table[i_x8], 8);
p256_order_mul(group, table[i_x16], table[i_x16], table[i_x8]);
p256_order_sqr(group, table[i_x32], table[i_x16], 16);
p256_order_mul(group, table[i_x32], table[i_x32], table[i_x16]);
// Compute `in` raised to the order-2.
p256_order_sqr(group, out->words, table[i_x32], 64);
p256_order_mul(group, out->words, out->words, table[i_x32]);
static const struct {
uint8_t p, i;
} kChain[27] = {{32, i_x32}, {6, i_101111}, {5, i_111}, {4, i_11},
{5, i_1111}, {5, i_10101}, {4, i_101}, {3, i_101},
{3, i_101}, {5, i_111}, {9, i_101111}, {6, i_1111},
{2, i_1}, {5, i_1}, {6, i_1111}, {5, i_111},
{4, i_111}, {5, i_111}, {5, i_101}, {3, i_11},
{10, i_101111}, {2, i_11}, {5, i_11}, {5, i_11},
{3, i_1}, {7, i_10101}, {6, i_1111}};
for (const auto &step : kChain) {
p256_order_sqr(group, out->words, out->words, step.p);
p256_order_mul(group, out->words, out->words, table[step.i]);
}
}
static int p256_order_mont_inv_vartime(const EC_GROUP *group, EC_SCALAR *out,
const EC_SCALAR *in) {
#if !defined(OPENSSL_NO_ASM) && \
(defined(OPENSSL_X86_64) || defined(OPENSSL_AARCH64))
#if defined(OPENSSL_X86_64)
if (!CRYPTO_is_AVX_capable()) {
// No AVX support; fallback to generic code.
return ec_simple_scalar_to_montgomery_inv_vartime(group, out, in);
}
#endif
assert(group->order.N.width == P256_LIMBS);
if (!beeu_mod_inverse_vartime(out->words, in->words, group->order.N.d)) {
return 0;
}
// The result should be returned in the Montgomery domain.
ec_scalar_to_montgomery(group, out, out);
return 1;
#else
return ec_simple_scalar_to_montgomery_inv_vartime(group, out, in);
#endif
}
// OPENSSL EC_METHOD WRAPPERS
static void fiat_p256_from_generic(fiat_p256_felem out, const EC_FELEM *in) {
OPENSSL_memcpy(out, in->words, 32);
}
static void fiat_p256_to_generic(EC_FELEM *out, const fiat_p256_felem in) {
OPENSSL_memcpy(out->words, in, 32);
}
// Takes the Jacobian coordinates (X, Y, Z) of a point and returns (X', Y') =
// (X/Z^2, Y/Z^3).
static int ec_GFp_nistp256_point_get_affine_coordinates(
const EC_GROUP *group, const EC_JACOBIAN *point, EC_FELEM *x_out,
EC_FELEM *y_out) {
if (constant_time_declassify_int(
ec_GFp_simple_is_at_infinity(group, point))) {
OPENSSL_PUT_ERROR(EC, EC_R_POINT_AT_INFINITY);
return 0;
}
fiat_p256_felem z1, z2;
fiat_p256_from_generic(z1, &point->Z);
fiat_p256_inv_square(z2, z1);
if (x_out != nullptr) {
fiat_p256_felem x;
fiat_p256_from_generic(x, &point->X);
p256_coord_mul(x, x, z2);
fiat_p256_to_generic(x_out, x);
}
if (y_out != nullptr) {
fiat_p256_felem y;
fiat_p256_from_generic(y, &point->Y);
p256_coord_sqr(z2, z2); // z^-4
p256_coord_mul(y, y, z1); // y * z
p256_coord_mul(y, y, z2); // y * z^-3
fiat_p256_to_generic(y_out, y);
}
return 1;
}
static int ec_GFp_nistp256_cmp_x_coordinate(const EC_GROUP *group,
const EC_JACOBIAN *p,
const EC_SCALAR *r) {
if (ec_GFp_simple_is_at_infinity(group, p)) {
return 0;
}
// We wish to compare X/Z^2 with r. This is equivalent to comparing X with
// r*Z^2. Note that X and Z are represented in Montgomery form, while r is
// not.
fiat_p256_felem Z2_mont;
fiat_p256_from_generic(Z2_mont, &p->Z);
p256_coord_mul(Z2_mont, Z2_mont, Z2_mont);
fiat_p256_felem r_Z2;
OPENSSL_memcpy(r_Z2, r->words, 32); // r < order < p, so this is valid.
p256_coord_mul(r_Z2, r_Z2, Z2_mont);
fiat_p256_felem X;
fiat_p256_from_generic(X, &p->X);
fiat_p256_from_montgomery(X, X);
if (OPENSSL_memcmp(&r_Z2, &X, sizeof(r_Z2)) == 0) {
return 1;
}
// During signing the x coefficient is reduced modulo the group order.
// Therefore there is a small possibility, less than 1/2^128, that group_order
// < p.x < P. in that case we need not only to compare against `r` but also to
// compare against r+group_order.
assert(group->field.N.width == group->order.N.width);
EC_FELEM tmp;
BN_ULONG carry =
bn_add_words(tmp.words, r->words, group->order.N.d, group->field.N.width);
if (carry == 0 &&
bn_less_than_words(tmp.words, group->field.N.d, group->field.N.width)) {
fiat_p256_from_generic(r_Z2, &tmp);
p256_coord_mul(r_Z2, r_Z2, Z2_mont);
if (OPENSSL_memcmp(&r_Z2, &X, sizeof(r_Z2)) == 0) {
return 1;
}
}
return 0;
}
static void ec_GFp_nistp256_point_mul(const EC_GROUP *group, EC_JACOBIAN *r,
const EC_JACOBIAN *p,
const EC_SCALAR *scalar) {
fiat_p256_felem t[3];
fiat_p256_from_generic(t[0], &p->X);
fiat_p256_from_generic(t[1], &p->Y);
fiat_p256_from_generic(t[2], &p->Z);
p256_point_mul(t, t, (uint8_t *)scalar->words);
fiat_p256_to_generic(&r->X, t[0]);
fiat_p256_to_generic(&r->Y, t[1]);
fiat_p256_to_generic(&r->Z, t[2]);
}
static void ec_GFp_nistp256_point_mul_base(const EC_GROUP *group,
EC_JACOBIAN *r, const EC_SCALAR *s) {
alignas(32) fiat_p256_felem ret[3];
p256_point_mul_base(ret, (uint8_t *)s->words);
fiat_p256_to_generic(&r->X, ret[0]);
fiat_p256_to_generic(&r->Y, ret[1]);
fiat_p256_to_generic(&r->Z, ret[2]);
}
static void ec_GFp_nistp256_add(const EC_GROUP *group, EC_JACOBIAN *r,
const EC_JACOBIAN *a, const EC_JACOBIAN *b) {
fiat_p256_felem p[3], q[3];
fiat_p256_from_generic(p[0], &a->X);
fiat_p256_from_generic(p[1], &a->Y);
fiat_p256_from_generic(p[2], &a->Z);
fiat_p256_from_generic(q[0], &b->X);
fiat_p256_from_generic(q[1], &b->Y);
fiat_p256_from_generic(q[2], &b->Z);
p256_point_add_vartime_if_doubling((uintptr_t)p, (uintptr_t)p, (uintptr_t)q);
fiat_p256_to_generic(&r->X, p[0]);
fiat_p256_to_generic(&r->Y, p[1]);
fiat_p256_to_generic(&r->Z, p[2]);
}
static void ec_GFp_nistp256_dbl(const EC_GROUP *group, EC_JACOBIAN *r,
const EC_JACOBIAN *a) {
fiat_p256_felem p[3];
fiat_p256_from_generic(p[0], &a->X);
fiat_p256_from_generic(p[1], &a->Y);
fiat_p256_from_generic(p[2], &a->Z);
p256_point_double((uintptr_t)p, (uintptr_t)p);
fiat_p256_to_generic(&r->X, p[0]);
fiat_p256_to_generic(&r->Y, p[1]);
fiat_p256_to_generic(&r->Z, p[2]);
}
BSSL_NAMESPACE_BEGIN
DEFINE_METHOD_FUNCTION(EC_METHOD, EC_GFp_nistp256_method) {
out->point_get_affine_coordinates =
ec_GFp_nistp256_point_get_affine_coordinates;
out->add = ec_GFp_nistp256_add;
out->dbl = ec_GFp_nistp256_dbl;
out->mul = ec_GFp_nistp256_point_mul;
out->mul_base = ec_GFp_nistp256_point_mul_base;
out->mul_public = ec_GFp_nistp256_point_mul_public;
out->scalar_inv0_montgomery = p256_order_inv0;
out->scalar_to_montgomery_inv_vartime = p256_order_mont_inv_vartime;
out->cmp_x_coordinate = ec_GFp_nistp256_cmp_x_coordinate;
}
BSSL_NAMESPACE_END