|
|
|
/* Copyright (c) 2016, Google Inc.
|
|
|
|
*
|
|
|
|
* Permission to use, copy, modify, and/or distribute this software for any
|
|
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
|
|
* copyright notice and this permission notice appear in all copies.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
|
|
|
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
|
|
|
|
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
|
|
|
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
|
|
|
|
|
|
|
|
#include <openssl/base.h>
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#include <gtest/gtest.h>
|
|
|
|
|
|
|
|
#include <openssl/bn.h>
|
|
|
|
#include <openssl/ec.h>
|
|
|
|
#include <openssl/mem.h>
|
|
|
|
#include <openssl/nid.h>
|
|
|
|
|
|
|
|
#include "internal.h"
|
|
|
|
#include "../bn/internal.h"
|
|
|
|
#include "../../internal.h"
|
|
|
|
#include "../../test/abi_test.h"
|
|
|
|
#include "../../test/file_test.h"
|
|
|
|
#include "../../test/test_util.h"
|
|
|
|
#include "p256-x86_64.h"
|
|
|
|
|
|
|
|
|
|
|
|
// Disable tests if BORINGSSL_SHARED_LIBRARY is defined. These tests need access
|
|
|
|
// to internal functions.
|
|
|
|
#if !defined(OPENSSL_NO_ASM) && defined(OPENSSL_X86_64) && \
|
|
|
|
!defined(OPENSSL_SMALL) && !defined(BORINGSSL_SHARED_LIBRARY)
|
|
|
|
|
|
|
|
TEST(P256_X86_64Test, SelectW5) {
|
|
|
|
// Fill a table with some garbage input.
|
|
|
|
alignas(64) P256_POINT table[16];
|
|
|
|
for (size_t i = 0; i < 16; i++) {
|
|
|
|
OPENSSL_memset(table[i].X, 3 * i, sizeof(table[i].X));
|
|
|
|
OPENSSL_memset(table[i].Y, 3 * i + 1, sizeof(table[i].Y));
|
|
|
|
OPENSSL_memset(table[i].Z, 3 * i + 2, sizeof(table[i].Z));
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = 0; i <= 16; i++) {
|
|
|
|
P256_POINT val;
|
|
|
|
ecp_nistz256_select_w5(&val, table, i);
|
|
|
|
|
|
|
|
P256_POINT expected;
|
|
|
|
if (i == 0) {
|
|
|
|
OPENSSL_memset(&expected, 0, sizeof(expected));
|
|
|
|
} else {
|
|
|
|
expected = table[i-1];
|
|
|
|
}
|
|
|
|
|
|
|
|
EXPECT_EQ(Bytes(reinterpret_cast<const char *>(&expected), sizeof(expected)),
|
|
|
|
Bytes(reinterpret_cast<const char *>(&val), sizeof(val)));
|
|
|
|
}
|
|
|
|
|
|
|
|
// This is a constant-time function, so it is only necessary to instrument one
|
|
|
|
// index for ABI checking.
|
|
|
|
P256_POINT val;
|
|
|
|
CHECK_ABI(ecp_nistz256_select_w5, &val, table, 7);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(P256_X86_64Test, SelectW7) {
|
|
|
|
// Fill a table with some garbage input.
|
|
|
|
alignas(64) P256_POINT_AFFINE table[64];
|
|
|
|
for (size_t i = 0; i < 64; i++) {
|
|
|
|
OPENSSL_memset(table[i].X, 2 * i, sizeof(table[i].X));
|
|
|
|
OPENSSL_memset(table[i].Y, 2 * i + 1, sizeof(table[i].Y));
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = 0; i <= 64; i++) {
|
|
|
|
P256_POINT_AFFINE val;
|
|
|
|
ecp_nistz256_select_w7(&val, table, i);
|
|
|
|
|
|
|
|
P256_POINT_AFFINE expected;
|
|
|
|
if (i == 0) {
|
|
|
|
OPENSSL_memset(&expected, 0, sizeof(expected));
|
|
|
|
} else {
|
|
|
|
expected = table[i-1];
|
|
|
|
}
|
|
|
|
|
|
|
|
EXPECT_EQ(Bytes(reinterpret_cast<const char *>(&expected), sizeof(expected)),
|
|
|
|
Bytes(reinterpret_cast<const char *>(&val), sizeof(val)));
|
|
|
|
}
|
|
|
|
|
|
|
|
// This is a constant-time function, so it is only necessary to instrument one
|
|
|
|
// index for ABI checking.
|
|
|
|
P256_POINT_AFFINE val;
|
|
|
|
CHECK_ABI(ecp_nistz256_select_w7, &val, table, 42);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(P256_X86_64Test, BEEU) {
|
Check static CPU capabilities on x86.
On Arm, our CRYPTO_is_*_capable functions check the corresponding
preprocessor symbol. This allows us to automatically drop dynamic checks
and fallback code when some capability is always avilable.
This CL does the same on x86, as well as consolidates our
OPENSSL_ia32cap_P checks in one place. Since this abstraction is
incompatible with some optimizations we do around OPENSSL_ia32cap_get()
in the FIPS module, I've marked the symbol __attribute__((const)), which
is enough to make GCC and Clang do the optimizations for us. (We already
do the same to DEFINE_BSS_GET.)
Most x86 platforms support a much wider range of capabilities, so this
is usually a no-op. But, notably, all x86_64 Mac hardware has SSSE3
available, so this allows us to statically drop an AES implementation.
(On macOS with -Wl,-dead_strip, this seems to trim 35080 bytes from the
bssl binary.) Configs like -march=native can also drop a bunch of code.
Update-Note: This CL may break build environments that incorrectly mark
some instruction as statically available. This is unlikely to happen
with vector instructions like AVX, where the compiler could freely emit
them anyway. However, instructions like AES-NI might be set incorrectly.
Change-Id: I44fd715c9887d3fda7cb4519c03bee4d4f2c7ea6
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/51548
Reviewed-by: Adam Langley <agl@google.com>
3 years ago
|
|
|
if (!CRYPTO_is_AVX_capable()) {
|
|
|
|
// No AVX support; cannot run the BEEU code.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
bssl::UniquePtr<EC_GROUP> group(
|
|
|
|
EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1));
|
|
|
|
ASSERT_TRUE(group);
|
|
|
|
|
|
|
|
BN_ULONG order_words[P256_LIMBS];
|
|
|
|
ASSERT_TRUE(
|
|
|
|
bn_copy_words(order_words, P256_LIMBS, EC_GROUP_get0_order(group.get())));
|
|
|
|
|
|
|
|
BN_ULONG in[P256_LIMBS], out[P256_LIMBS];
|
|
|
|
EC_SCALAR in_scalar, out_scalar, result;
|
|
|
|
OPENSSL_memset(in, 0, sizeof(in));
|
|
|
|
|
|
|
|
// Trying to find the inverse of zero should fail.
|
|
|
|
ASSERT_FALSE(beeu_mod_inverse_vartime(out, in, order_words));
|
|
|
|
// This is not a constant-time function, so instrument both zero and a few
|
|
|
|
// inputs below.
|
|
|
|
ASSERT_FALSE(CHECK_ABI(beeu_mod_inverse_vartime, out, in, order_words));
|
|
|
|
|
|
|
|
// kOneMont is 1, in Montgomery form.
|
|
|
|
static const BN_ULONG kOneMont[P256_LIMBS] = {
|
|
|
|
TOBN(0xc46353d, 0x039cdaaf),
|
|
|
|
TOBN(0x43190552, 0x58e8617b),
|
|
|
|
0,
|
|
|
|
0xffffffff,
|
|
|
|
};
|
|
|
|
|
|
|
|
for (BN_ULONG i = 1; i < 2000; i++) {
|
|
|
|
SCOPED_TRACE(i);
|
|
|
|
|
|
|
|
in[0] = i;
|
|
|
|
if (i >= 1000) {
|
|
|
|
in[1] = i << 8;
|
|
|
|
in[2] = i << 32;
|
|
|
|
in[3] = i << 48;
|
|
|
|
} else {
|
|
|
|
in[1] = in[2] = in[3] = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
EXPECT_TRUE(bn_less_than_words(in, order_words, P256_LIMBS));
|
|
|
|
ASSERT_TRUE(beeu_mod_inverse_vartime(out, in, order_words));
|
|
|
|
EXPECT_TRUE(bn_less_than_words(out, order_words, P256_LIMBS));
|
|
|
|
|
|
|
|
// Calculate out*in and confirm that it equals one, modulo the order.
|
|
|
|
OPENSSL_memcpy(in_scalar.bytes, in, sizeof(in));
|
|
|
|
OPENSSL_memcpy(out_scalar.bytes, out, sizeof(out));
|
|
|
|
ec_scalar_to_montgomery(group.get(), &in_scalar, &in_scalar);
|
|
|
|
ec_scalar_to_montgomery(group.get(), &out_scalar, &out_scalar);
|
|
|
|
ec_scalar_mul_montgomery(group.get(), &result, &in_scalar, &out_scalar);
|
|
|
|
|
|
|
|
EXPECT_EQ(0, OPENSSL_memcmp(kOneMont, &result, sizeof(kOneMont)));
|
|
|
|
|
|
|
|
// Invert the result and expect to get back to the original value.
|
|
|
|
ASSERT_TRUE(beeu_mod_inverse_vartime(out, out, order_words));
|
|
|
|
EXPECT_EQ(0, OPENSSL_memcmp(in, out, sizeof(in)));
|
|
|
|
|
|
|
|
if (i < 5) {
|
|
|
|
EXPECT_TRUE(CHECK_ABI(beeu_mod_inverse_vartime, out, in, order_words));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool GetFieldElement(FileTest *t, BN_ULONG out[P256_LIMBS],
|
|
|
|
const char *name) {
|
|
|
|
std::vector<uint8_t> bytes;
|
|
|
|
if (!t->GetBytes(&bytes, name)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bytes.size() != BN_BYTES * P256_LIMBS) {
|
|
|
|
ADD_FAILURE() << "Invalid length: " << name;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// |byte| contains bytes in big-endian while |out| should contain |BN_ULONG|s
|
|
|
|
// in little-endian.
|
|
|
|
OPENSSL_memset(out, 0, P256_LIMBS * sizeof(BN_ULONG));
|
|
|
|
for (size_t i = 0; i < bytes.size(); i++) {
|
|
|
|
out[P256_LIMBS - 1 - (i / BN_BYTES)] <<= 8;
|
|
|
|
out[P256_LIMBS - 1 - (i / BN_BYTES)] |= bytes[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static std::string FieldElementToString(const BN_ULONG a[P256_LIMBS]) {
|
|
|
|
std::string ret;
|
|
|
|
for (size_t i = P256_LIMBS-1; i < P256_LIMBS; i--) {
|
|
|
|
char buf[2 * BN_BYTES + 1];
|
|
|
|
BIO_snprintf(buf, sizeof(buf), BN_HEX_FMT2, a[i]);
|
|
|
|
ret += buf;
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static testing::AssertionResult ExpectFieldElementsEqual(
|
|
|
|
const char *expected_expr, const char *actual_expr,
|
|
|
|
const BN_ULONG expected[P256_LIMBS], const BN_ULONG actual[P256_LIMBS]) {
|
|
|
|
if (OPENSSL_memcmp(expected, actual, sizeof(BN_ULONG) * P256_LIMBS) == 0) {
|
|
|
|
return testing::AssertionSuccess();
|
|
|
|
}
|
|
|
|
|
|
|
|
return testing::AssertionFailure()
|
|
|
|
<< "Expected: " << FieldElementToString(expected) << " ("
|
|
|
|
<< expected_expr << ")\n"
|
|
|
|
<< "Actual: " << FieldElementToString(actual) << " (" << actual_expr
|
|
|
|
<< ")";
|
|
|
|
}
|
|
|
|
|
|
|
|
#define EXPECT_FIELD_ELEMENTS_EQUAL(a, b) \
|
|
|
|
EXPECT_PRED_FORMAT2(ExpectFieldElementsEqual, a, b)
|
|
|
|
|
|
|
|
static bool PointToAffine(P256_POINT_AFFINE *out, const P256_POINT *in) {
|
|
|
|
static const uint8_t kP[] = {
|
|
|
|
0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
|
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
|
|
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
|
|
};
|
|
|
|
|
|
|
|
bssl::UniquePtr<BIGNUM> x(BN_new()), y(BN_new()), z(BN_new());
|
|
|
|
bssl::UniquePtr<BIGNUM> p(BN_bin2bn(kP, sizeof(kP), nullptr));
|
|
|
|
if (!x || !y || !z || !p ||
|
|
|
|
!bn_set_words(x.get(), in->X, P256_LIMBS) ||
|
|
|
|
!bn_set_words(y.get(), in->Y, P256_LIMBS) ||
|
|
|
|
!bn_set_words(z.get(), in->Z, P256_LIMBS)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Coordinates must be fully-reduced.
|
|
|
|
if (BN_cmp(x.get(), p.get()) >= 0 ||
|
|
|
|
BN_cmp(y.get(), p.get()) >= 0 ||
|
|
|
|
BN_cmp(z.get(), p.get()) >= 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (BN_is_zero(z.get())) {
|
|
|
|
// The point at infinity is represented as (0, 0).
|
|
|
|
OPENSSL_memset(out, 0, sizeof(P256_POINT_AFFINE));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bssl::UniquePtr<BN_CTX> ctx(BN_CTX_new());
|
|
|
|
bssl::UniquePtr<BN_MONT_CTX> mont(
|
|
|
|
BN_MONT_CTX_new_for_modulus(p.get(), ctx.get()));
|
|
|
|
if (!ctx || !mont ||
|
|
|
|
// Invert Z.
|
|
|
|
!BN_from_montgomery(z.get(), z.get(), mont.get(), ctx.get()) ||
|
|
|
|
!BN_mod_inverse(z.get(), z.get(), p.get(), ctx.get()) ||
|
|
|
|
!BN_to_montgomery(z.get(), z.get(), mont.get(), ctx.get()) ||
|
|
|
|
// Convert (X, Y, Z) to (X/Z^2, Y/Z^3).
|
|
|
|
!BN_mod_mul_montgomery(x.get(), x.get(), z.get(), mont.get(),
|
|
|
|
ctx.get()) ||
|
|
|
|
!BN_mod_mul_montgomery(x.get(), x.get(), z.get(), mont.get(),
|
|
|
|
ctx.get()) ||
|
|
|
|
!BN_mod_mul_montgomery(y.get(), y.get(), z.get(), mont.get(),
|
|
|
|
ctx.get()) ||
|
|
|
|
!BN_mod_mul_montgomery(y.get(), y.get(), z.get(), mont.get(),
|
|
|
|
ctx.get()) ||
|
|
|
|
!BN_mod_mul_montgomery(y.get(), y.get(), z.get(), mont.get(),
|
|
|
|
ctx.get()) ||
|
|
|
|
!bn_copy_words(out->X, P256_LIMBS, x.get()) ||
|
|
|
|
!bn_copy_words(out->Y, P256_LIMBS, y.get())) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static testing::AssertionResult ExpectPointsEqual(
|
|
|
|
const char *expected_expr, const char *actual_expr,
|
|
|
|
const P256_POINT_AFFINE *expected, const P256_POINT *actual) {
|
|
|
|
// There are multiple representations of the same |P256_POINT|, so convert to
|
|
|
|
// |P256_POINT_AFFINE| and compare.
|
|
|
|
P256_POINT_AFFINE affine;
|
|
|
|
if (!PointToAffine(&affine, actual)) {
|
|
|
|
return testing::AssertionFailure()
|
|
|
|
<< "Could not convert " << actual_expr << " to affine: ("
|
|
|
|
<< FieldElementToString(actual->X) << ", "
|
|
|
|
<< FieldElementToString(actual->Y) << ", "
|
|
|
|
<< FieldElementToString(actual->Z) << ")";
|
|
|
|
}
|
|
|
|
|
|
|
|
if (OPENSSL_memcmp(expected, &affine, sizeof(P256_POINT_AFFINE)) != 0) {
|
|
|
|
return testing::AssertionFailure()
|
|
|
|
<< "Expected: (" << FieldElementToString(expected->X) << ", "
|
|
|
|
<< FieldElementToString(expected->Y) << ") (" << expected_expr
|
|
|
|
<< "; affine)\n"
|
|
|
|
<< "Actual: (" << FieldElementToString(affine.X) << ", "
|
|
|
|
<< FieldElementToString(affine.Y) << ") (" << actual_expr << ")";
|
|
|
|
}
|
|
|
|
|
|
|
|
return testing::AssertionSuccess();
|
|
|
|
}
|
|
|
|
|
|
|
|
#define EXPECT_POINTS_EQUAL(a, b) EXPECT_PRED_FORMAT2(ExpectPointsEqual, a, b)
|
|
|
|
|
|
|
|
static void TestNegate(FileTest *t) {
|
|
|
|
BN_ULONG a[P256_LIMBS], b[P256_LIMBS];
|
|
|
|
ASSERT_TRUE(GetFieldElement(t, a, "A"));
|
|
|
|
ASSERT_TRUE(GetFieldElement(t, b, "B"));
|
|
|
|
|
|
|
|
// Test that -A = B.
|
|
|
|
BN_ULONG ret[P256_LIMBS];
|
|
|
|
ecp_nistz256_neg(ret, a);
|
|
|
|
EXPECT_FIELD_ELEMENTS_EQUAL(b, ret);
|
|
|
|
|
|
|
|
OPENSSL_memcpy(ret, a, sizeof(ret));
|
|
|
|
ecp_nistz256_neg(ret, ret /* a */);
|
|
|
|
EXPECT_FIELD_ELEMENTS_EQUAL(b, ret);
|
|
|
|
|
|
|
|
// Test that -B = A.
|
|
|
|
ecp_nistz256_neg(ret, b);
|
|
|
|
EXPECT_FIELD_ELEMENTS_EQUAL(a, ret);
|
|
|
|
|
|
|
|
OPENSSL_memcpy(ret, b, sizeof(ret));
|
|
|
|
ecp_nistz256_neg(ret, ret /* b */);
|
|
|
|
EXPECT_FIELD_ELEMENTS_EQUAL(a, ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void TestMulMont(FileTest *t) {
|
|
|
|
BN_ULONG a[P256_LIMBS], b[P256_LIMBS], result[P256_LIMBS];
|
|
|
|
ASSERT_TRUE(GetFieldElement(t, a, "A"));
|
|
|
|
ASSERT_TRUE(GetFieldElement(t, b, "B"));
|
|
|
|
ASSERT_TRUE(GetFieldElement(t, result, "Result"));
|
|
|
|
|
|
|
|
BN_ULONG ret[P256_LIMBS];
|
|
|
|
ecp_nistz256_mul_mont(ret, a, b);
|
|
|
|
EXPECT_FIELD_ELEMENTS_EQUAL(result, ret);
|
|
|
|
|
|
|
|
ecp_nistz256_mul_mont(ret, b, a);
|
|
|
|
EXPECT_FIELD_ELEMENTS_EQUAL(result, ret);
|
|
|
|
|
|
|
|
OPENSSL_memcpy(ret, a, sizeof(ret));
|
|
|
|
ecp_nistz256_mul_mont(ret, ret /* a */, b);
|
|
|
|
EXPECT_FIELD_ELEMENTS_EQUAL(result, ret);
|
|
|
|
|
|
|
|
OPENSSL_memcpy(ret, a, sizeof(ret));
|
|
|
|
ecp_nistz256_mul_mont(ret, b, ret);
|
|
|
|
EXPECT_FIELD_ELEMENTS_EQUAL(result, ret);
|
|
|
|
|
|
|
|
OPENSSL_memcpy(ret, b, sizeof(ret));
|
|
|
|
ecp_nistz256_mul_mont(ret, a, ret /* b */);
|
|
|
|
EXPECT_FIELD_ELEMENTS_EQUAL(result, ret);
|
|
|
|
|
|
|
|
OPENSSL_memcpy(ret, b, sizeof(ret));
|
|
|
|
ecp_nistz256_mul_mont(ret, ret /* b */, a);
|
|
|
|
EXPECT_FIELD_ELEMENTS_EQUAL(result, ret);
|
|
|
|
|
|
|
|
if (OPENSSL_memcmp(a, b, sizeof(a)) == 0) {
|
|
|
|
ecp_nistz256_sqr_mont(ret, a);
|
|
|
|
EXPECT_FIELD_ELEMENTS_EQUAL(result, ret);
|
|
|
|
|
|
|
|
OPENSSL_memcpy(ret, a, sizeof(ret));
|
|
|
|
ecp_nistz256_sqr_mont(ret, ret /* a */);
|
|
|
|
EXPECT_FIELD_ELEMENTS_EQUAL(result, ret);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void TestFromMont(FileTest *t) {
|
|
|
|
BN_ULONG a[P256_LIMBS], result[P256_LIMBS];
|
|
|
|
ASSERT_TRUE(GetFieldElement(t, a, "A"));
|
|
|
|
ASSERT_TRUE(GetFieldElement(t, result, "Result"));
|
|
|
|
|
|
|
|
BN_ULONG ret[P256_LIMBS];
|
|
|
|
ecp_nistz256_from_mont(ret, a);
|
|
|
|
EXPECT_FIELD_ELEMENTS_EQUAL(result, ret);
|
|
|
|
|
|
|
|
OPENSSL_memcpy(ret, a, sizeof(ret));
|
|
|
|
ecp_nistz256_from_mont(ret, ret /* a */);
|
|
|
|
EXPECT_FIELD_ELEMENTS_EQUAL(result, ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void TestPointAdd(FileTest *t) {
|
|
|
|
P256_POINT a, b;
|
|
|
|
P256_POINT_AFFINE result;
|
|
|
|
ASSERT_TRUE(GetFieldElement(t, a.X, "A.X"));
|
|
|
|
ASSERT_TRUE(GetFieldElement(t, a.Y, "A.Y"));
|
|
|
|
ASSERT_TRUE(GetFieldElement(t, a.Z, "A.Z"));
|
|
|
|
ASSERT_TRUE(GetFieldElement(t, b.X, "B.X"));
|
|
|
|
ASSERT_TRUE(GetFieldElement(t, b.Y, "B.Y"));
|
|
|
|
ASSERT_TRUE(GetFieldElement(t, b.Z, "B.Z"));
|
|
|
|
ASSERT_TRUE(GetFieldElement(t, result.X, "Result.X"));
|
|
|
|
ASSERT_TRUE(GetFieldElement(t, result.Y, "Result.Y"));
|
|
|
|
|
|
|
|
P256_POINT ret;
|
|
|
|
ecp_nistz256_point_add(&ret, &a, &b);
|
|
|
|
EXPECT_POINTS_EQUAL(&result, &ret);
|
|
|
|
|
|
|
|
ecp_nistz256_point_add(&ret, &b, &a);
|
|
|
|
EXPECT_POINTS_EQUAL(&result, &ret);
|
|
|
|
|
|
|
|
OPENSSL_memcpy(&ret, &a, sizeof(ret));
|
|
|
|
ecp_nistz256_point_add(&ret, &ret /* a */, &b);
|
|
|
|
EXPECT_POINTS_EQUAL(&result, &ret);
|
|
|
|
|
|
|
|
OPENSSL_memcpy(&ret, &a, sizeof(ret));
|
|
|
|
ecp_nistz256_point_add(&ret, &b, &ret /* a */);
|
|
|
|
EXPECT_POINTS_EQUAL(&result, &ret);
|
|
|
|
|
|
|
|
OPENSSL_memcpy(&ret, &b, sizeof(ret));
|
|
|
|
ecp_nistz256_point_add(&ret, &a, &ret /* b */);
|
|
|
|
EXPECT_POINTS_EQUAL(&result, &ret);
|
|
|
|
|
|
|
|
OPENSSL_memcpy(&ret, &b, sizeof(ret));
|
|
|
|
ecp_nistz256_point_add(&ret, &ret /* b */, &a);
|
|
|
|
EXPECT_POINTS_EQUAL(&result, &ret);
|
|
|
|
|
|
|
|
P256_POINT_AFFINE a_affine, b_affine, infinity;
|
|
|
|
OPENSSL_memset(&infinity, 0, sizeof(infinity));
|
|
|
|
ASSERT_TRUE(PointToAffine(&a_affine, &a));
|
|
|
|
ASSERT_TRUE(PointToAffine(&b_affine, &b));
|
|
|
|
|
|
|
|
// ecp_nistz256_point_add_affine does not work when a == b unless doubling the
|
|
|
|
// point at infinity.
|
|
|
|
if (OPENSSL_memcmp(&a_affine, &b_affine, sizeof(a_affine)) != 0 ||
|
|
|
|
OPENSSL_memcmp(&a_affine, &infinity, sizeof(a_affine)) == 0) {
|
|
|
|
ecp_nistz256_point_add_affine(&ret, &a, &b_affine);
|
|
|
|
EXPECT_POINTS_EQUAL(&result, &ret);
|
|
|
|
|
|
|
|
OPENSSL_memcpy(&ret, &a, sizeof(ret));
|
|
|
|
ecp_nistz256_point_add_affine(&ret, &ret /* a */, &b_affine);
|
|
|
|
EXPECT_POINTS_EQUAL(&result, &ret);
|
|
|
|
|
|
|
|
ecp_nistz256_point_add_affine(&ret, &b, &a_affine);
|
|
|
|
EXPECT_POINTS_EQUAL(&result, &ret);
|
|
|
|
|
|
|
|
OPENSSL_memcpy(&ret, &b, sizeof(ret));
|
|
|
|
ecp_nistz256_point_add_affine(&ret, &ret /* b */, &a_affine);
|
|
|
|
EXPECT_POINTS_EQUAL(&result, &ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (OPENSSL_memcmp(&a, &b, sizeof(a)) == 0) {
|
|
|
|
ecp_nistz256_point_double(&ret, &a);
|
|
|
|
EXPECT_POINTS_EQUAL(&result, &ret);
|
|
|
|
|
|
|
|
ret = a;
|
|
|
|
ecp_nistz256_point_double(&ret, &ret /* a */);
|
|
|
|
EXPECT_POINTS_EQUAL(&result, &ret);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void TestOrdMulMont(FileTest *t) {
|
|
|
|
// This test works on scalars rather than field elements, but the
|
|
|
|
// representation is the same.
|
|
|
|
BN_ULONG a[P256_LIMBS], b[P256_LIMBS], result[P256_LIMBS];
|
|
|
|
ASSERT_TRUE(GetFieldElement(t, a, "A"));
|
|
|
|
ASSERT_TRUE(GetFieldElement(t, b, "B"));
|
|
|
|
ASSERT_TRUE(GetFieldElement(t, result, "Result"));
|
|
|
|
|
|
|
|
BN_ULONG ret[P256_LIMBS];
|
|
|
|
ecp_nistz256_ord_mul_mont(ret, a, b);
|
|
|
|
EXPECT_FIELD_ELEMENTS_EQUAL(result, ret);
|
|
|
|
|
|
|
|
ecp_nistz256_ord_mul_mont(ret, b, a);
|
|
|
|
EXPECT_FIELD_ELEMENTS_EQUAL(result, ret);
|
|
|
|
|
|
|
|
OPENSSL_memcpy(ret, a, sizeof(ret));
|
|
|
|
ecp_nistz256_ord_mul_mont(ret, ret /* a */, b);
|
|
|
|
EXPECT_FIELD_ELEMENTS_EQUAL(result, ret);
|
|
|
|
|
|
|
|
OPENSSL_memcpy(ret, a, sizeof(ret));
|
|
|
|
ecp_nistz256_ord_mul_mont(ret, b, ret);
|
|
|
|
EXPECT_FIELD_ELEMENTS_EQUAL(result, ret);
|
|
|
|
|
|
|
|
OPENSSL_memcpy(ret, b, sizeof(ret));
|
|
|
|
ecp_nistz256_ord_mul_mont(ret, a, ret /* b */);
|
|
|
|
EXPECT_FIELD_ELEMENTS_EQUAL(result, ret);
|
|
|
|
|
|
|
|
OPENSSL_memcpy(ret, b, sizeof(ret));
|
|
|
|
ecp_nistz256_ord_mul_mont(ret, ret /* b */, a);
|
|
|
|
EXPECT_FIELD_ELEMENTS_EQUAL(result, ret);
|
|
|
|
|
|
|
|
if (OPENSSL_memcmp(a, b, sizeof(a)) == 0) {
|
|
|
|
ecp_nistz256_ord_sqr_mont(ret, a, 1);
|
|
|
|
EXPECT_FIELD_ELEMENTS_EQUAL(result, ret);
|
|
|
|
|
|
|
|
OPENSSL_memcpy(ret, a, sizeof(ret));
|
|
|
|
ecp_nistz256_ord_sqr_mont(ret, ret /* a */, 1);
|
|
|
|
EXPECT_FIELD_ELEMENTS_EQUAL(result, ret);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(P256_X86_64Test, TestVectors) {
|
|
|
|
return FileTestGTest("crypto/fipsmodule/ec/p256-x86_64_tests.txt",
|
|
|
|
[](FileTest *t) {
|
|
|
|
if (t->GetParameter() == "Negate") {
|
|
|
|
TestNegate(t);
|
|
|
|
} else if (t->GetParameter() == "MulMont") {
|
|
|
|
TestMulMont(t);
|
|
|
|
} else if (t->GetParameter() == "FromMont") {
|
|
|
|
TestFromMont(t);
|
|
|
|
} else if (t->GetParameter() == "PointAdd") {
|
|
|
|
TestPointAdd(t);
|
|
|
|
} else if (t->GetParameter() == "OrdMulMont") {
|
|
|
|
TestOrdMulMont(t);
|
|
|
|
} else {
|
|
|
|
FAIL() << "Unknown test type:" << t->GetParameter();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
// Instrument the functions covered in TestVectors for ABI checking.
|
|
|
|
TEST(P256_X86_64Test, ABI) {
|
|
|
|
BN_ULONG a[P256_LIMBS], b[P256_LIMBS], c[P256_LIMBS];
|
|
|
|
OPENSSL_memset(a, 0x01, sizeof(a));
|
|
|
|
// These functions are all constant-time, so it is only necessary to
|
|
|
|
// instrument one call each for ABI checking.
|
|
|
|
CHECK_ABI(ecp_nistz256_neg, b, a);
|
|
|
|
CHECK_ABI(ecp_nistz256_mul_mont, c, a, b);
|
|
|
|
CHECK_ABI(ecp_nistz256_sqr_mont, c, a);
|
|
|
|
CHECK_ABI(ecp_nistz256_from_mont, c, a);
|
|
|
|
CHECK_ABI(ecp_nistz256_ord_mul_mont, c, a, b);
|
|
|
|
|
|
|
|
// Check a few different loop counts.
|
|
|
|
CHECK_ABI(ecp_nistz256_ord_sqr_mont, b, a, 1);
|
|
|
|
CHECK_ABI(ecp_nistz256_ord_sqr_mont, b, a, 3);
|
|
|
|
|
|
|
|
// Point addition has some special cases around infinity and doubling. Test a
|
|
|
|
// few different scenarios.
|
|
|
|
static const P256_POINT kA = {
|
|
|
|
{TOBN(0x60559ac7, 0xc8d0d89d), TOBN(0x6cda3400, 0x545f7e2c),
|
|
|
|
TOBN(0x9b5159e0, 0x323e6048), TOBN(0xcb8dea33, 0x27057fe6)},
|
|
|
|
{TOBN(0x81a2d3bc, 0xc93a2d53), TOBN(0x81f40762, 0xa4f33ccf),
|
|
|
|
TOBN(0xc3c3300a, 0xa8ad50ea), TOBN(0x553de89b, 0x31719830)},
|
|
|
|
{TOBN(0x3fd9470f, 0xb277d181), TOBN(0xc191b8d5, 0x6376f206),
|
|
|
|
TOBN(0xb2572c1f, 0x45eda26f), TOBN(0x4589e40d, 0xf2efc546)},
|
|
|
|
};
|
|
|
|
static const P256_POINT kB = {
|
|
|
|
{TOBN(0x3cf0b0aa, 0x92054341), TOBN(0xb949bb80, 0xdab57807),
|
|
|
|
TOBN(0x99de6814, 0xefd21b3e), TOBN(0x32ad5649, 0x7c6c6e83)},
|
|
|
|
{TOBN(0x06afaa02, 0x688399e0), TOBN(0x75f2d096, 0x2a3ce65c),
|
|
|
|
TOBN(0xf6a31eb7, 0xca0244b3), TOBN(0x57b33b7a, 0xcfeee75e)},
|
|
|
|
{TOBN(0x7617d2e0, 0xb4f1d35f), TOBN(0xa922cb10, 0x7f592b65),
|
|
|
|
TOBN(0x12fd6c7a, 0x51a2f474), TOBN(0x337d5e1e, 0xc2fc711b)},
|
|
|
|
};
|
|
|
|
// This file represents Jacobian infinity as (*, *, 0).
|
|
|
|
static const P256_POINT kInfinity = {
|
|
|
|
{TOBN(0, 0), TOBN(0, 0), TOBN(0, 0), TOBN(0, 0)},
|
|
|
|
{TOBN(0, 0), TOBN(0, 0), TOBN(0, 0), TOBN(0, 0)},
|
|
|
|
{TOBN(0, 0), TOBN(0, 0), TOBN(0, 0), TOBN(0, 0)},
|
|
|
|
};
|
|
|
|
|
|
|
|
P256_POINT p;
|
|
|
|
CHECK_ABI(ecp_nistz256_point_add, &p, &kA, &kB);
|
|
|
|
CHECK_ABI(ecp_nistz256_point_add, &p, &kA, &kA);
|
|
|
|
OPENSSL_memcpy(&p, &kA, sizeof(P256_POINT));
|
|
|
|
ecp_nistz256_neg(p.Y, p.Y);
|
|
|
|
CHECK_ABI(ecp_nistz256_point_add, &p, &kA, &p); // A + -A
|
|
|
|
CHECK_ABI(ecp_nistz256_point_add, &p, &kA, &kInfinity);
|
|
|
|
CHECK_ABI(ecp_nistz256_point_add, &p, &kInfinity, &kA);
|
|
|
|
CHECK_ABI(ecp_nistz256_point_add, &p, &kInfinity, &kInfinity);
|
|
|
|
CHECK_ABI(ecp_nistz256_point_double, &p, &kA);
|
|
|
|
CHECK_ABI(ecp_nistz256_point_double, &p, &kInfinity);
|
|
|
|
|
|
|
|
static const P256_POINT_AFFINE kC = {
|
|
|
|
{TOBN(0x7e3ad339, 0xfb3fa5f0), TOBN(0x559d669d, 0xe3a047b2),
|
|
|
|
TOBN(0x8883b298, 0x7042e595), TOBN(0xfabada65, 0x7e477f08)},
|
|
|
|
{TOBN(0xd9cfceb8, 0xda1c3e85), TOBN(0x80863761, 0x0ce6d6bc),
|
|
|
|
TOBN(0xa8409d84, 0x66034f02), TOBN(0x05519925, 0x31a68d55)},
|
|
|
|
};
|
|
|
|
// This file represents affine infinity as (0, 0).
|
|
|
|
static const P256_POINT_AFFINE kInfinityAffine = {
|
|
|
|
{TOBN(0, 0), TOBN(0, 0), TOBN(0, 0), TOBN(0, 0)},
|
|
|
|
{TOBN(0, 0), TOBN(0, 0), TOBN(0, 0), TOBN(0, 0)},
|
|
|
|
};
|
|
|
|
|
|
|
|
CHECK_ABI(ecp_nistz256_point_add_affine, &p, &kA, &kC);
|
|
|
|
CHECK_ABI(ecp_nistz256_point_add_affine, &p, &kA, &kInfinityAffine);
|
|
|
|
CHECK_ABI(ecp_nistz256_point_add_affine, &p, &kInfinity, &kInfinityAffine);
|
|
|
|
CHECK_ABI(ecp_nistz256_point_add_affine, &p, &kInfinity, &kC);
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|