boringssl/crypto/ec/p256-x86_64_test.cc
David Benjamin 4a9313a7e7 Add low-level p256-x86_64 tests.
For the most part, this is with random test data which isn't
particularly good. But we'll be able to add more interesting test
vectors as they come up.

Change-Id: I9c50db7ac2c4bf978d4901000ab32e3642aea82b
Reviewed-on: https://boringssl-review.googlesource.com/12222
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: Adam Langley <agl@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
2016-11-15 17:05:01 +00:00

512 lines
15 KiB
C++

/* 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. */
#if !defined(__STDC_FORMAT_MACROS)
#define __STDC_FORMAT_MACROS
#endif
#include <openssl/base.h>
#include <stdio.h>
#include <string.h>
#include <openssl/bn.h>
#include <openssl/mem.h>
#include "../bn/internal.h"
#include "../test/file_test.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)
static bool TestSelectW5() {
// Fill a table with some garbage input.
P256_POINT table[16];
for (size_t i = 0; i < 16; i++) {
memset(table[i].X, 3 * i, sizeof(table[i].X));
memset(table[i].Y, 3 * i + 1, sizeof(table[i].Y));
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) {
memset(&expected, 0, sizeof(expected));
} else {
expected = table[i-1];
}
if (memcmp(&val, &expected, sizeof(P256_POINT)) != 0) {
fprintf(stderr, "ecp_nistz256_select_w5(%d) gave the wrong value.\n", i);
return false;
}
}
return true;
}
static bool TestSelectW7() {
// Fill a table with some garbage input.
P256_POINT_AFFINE table[64];
for (size_t i = 0; i < 64; i++) {
memset(table[i].X, 2 * i, sizeof(table[i].X));
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) {
memset(&expected, 0, sizeof(expected));
} else {
expected = table[i-1];
}
if (memcmp(&val, &expected, sizeof(P256_POINT_AFFINE)) != 0) {
fprintf(stderr, "ecp_nistz256_select_w7(%d) gave the wrong value.\n", i);
return false;
}
}
return true;
}
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) {
t->PrintLine("Invalid length: %s", name);
return false;
}
// |byte| contains bytes in big-endian while |out| should contain |BN_ULONG|s
// in little-endian.
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 bool ExpectFieldElementsEqual(FileTest *t, const char *message,
const BN_ULONG expected[P256_LIMBS],
const BN_ULONG actual[P256_LIMBS]) {
if (memcmp(expected, actual, sizeof(BN_ULONG) * P256_LIMBS) == 0) {
return true;
}
t->PrintLine("%s", message);
t->PrintLine("Expected: %s", FieldElementToString(expected).c_str());
t->PrintLine("Actual: %s", FieldElementToString(actual).c_str());
return false;
}
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_is_negative(x.get()) ||
BN_is_negative(y.get()) ||
BN_is_negative(z.get()) ||
BN_cmp(x.get(), p.get()) >= 0 ||
BN_cmp(y.get(), p.get()) >= 0 ||
BN_cmp(z.get(), p.get()) >= 0) {
return false;
}
memset(out, 0, sizeof(P256_POINT_AFFINE));
if (BN_is_zero(z.get())) {
// The point at infinity is represented as (0, 0).
return true;
}
bssl::UniquePtr<BN_CTX> ctx(BN_CTX_new());
bssl::UniquePtr<BN_MONT_CTX> mont(BN_MONT_CTX_new());
if (!ctx || !mont ||
!BN_MONT_CTX_set(mont.get(), p.get(), ctx.get()) ||
// 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())) {
return false;
}
memcpy(out->X, x->d, sizeof(BN_ULONG) * x->top);
memcpy(out->Y, y->d, sizeof(BN_ULONG) * y->top);
return true;
}
static bool ExpectPointsEqual(FileTest *t, const char *message,
const P256_POINT_AFFINE *expected,
const P256_POINT *point) {
// There are multiple representations of the same |P256_POINT|, so convert to
// |P256_POINT_AFFINE| and compare.
P256_POINT_AFFINE affine;
if (!PointToAffine(&affine, point)) {
t->PrintLine("%s", message);
t->PrintLine("Could not convert to affine: (%s, %s, %s)",
FieldElementToString(point->X).c_str(),
FieldElementToString(point->Y).c_str(),
FieldElementToString(point->Z).c_str());
return false;
}
if (memcmp(expected, &affine, sizeof(P256_POINT_AFFINE)) != 0) {
t->PrintLine("%s", message);
t->PrintLine("Expected: (%s, %s)",
FieldElementToString(expected->X).c_str(),
FieldElementToString(expected->Y).c_str());
t->PrintLine("Actual: (%s, %s)", FieldElementToString(affine.X).c_str(),
FieldElementToString(affine.Y).c_str());
return false;
}
return true;
}
static bool TestNegate(FileTest *t) {
BN_ULONG a[P256_LIMBS], b[P256_LIMBS];
if (!GetFieldElement(t, a, "A") ||
!GetFieldElement(t, b, "B")) {
return false;
}
// Test that -A = B.
BN_ULONG ret[P256_LIMBS];
ecp_nistz256_neg(ret, a);
if (!ExpectFieldElementsEqual(t, "ecp_nistz256_neg(A) was incorrect.", b,
ret)) {
return false;
}
memcpy(ret, a, sizeof(ret));
ecp_nistz256_neg(ret, ret);
if (!ExpectFieldElementsEqual(
t, "In-place ecp_nistz256_neg(A) was incorrect.", b, ret)) {
return false;
}
// Test that -B = A.
ecp_nistz256_neg(ret, b);
if (!ExpectFieldElementsEqual(t, "ecp_nistz256_neg(B) was incorrect.", a,
ret)) {
return false;
}
memcpy(ret, b, sizeof(ret));
ecp_nistz256_neg(ret, ret);
if (!ExpectFieldElementsEqual(
t, "In-place ecp_nistz256_neg(B) was incorrect.", a, ret)) {
return false;
}
return true;
}
static bool TestMulMont(FileTest *t) {
BN_ULONG a[P256_LIMBS], b[P256_LIMBS], result[P256_LIMBS];
if (!GetFieldElement(t, a, "A") ||
!GetFieldElement(t, b, "B") ||
!GetFieldElement(t, result, "Result")) {
return false;
}
BN_ULONG ret[P256_LIMBS];
ecp_nistz256_mul_mont(ret, a, b);
if (!ExpectFieldElementsEqual(t, "ecp_nistz256_mul_mont(A, B) was incorrect.",
result, ret)) {
return false;
}
ecp_nistz256_mul_mont(ret, b, a);
if (!ExpectFieldElementsEqual(t, "ecp_nistz256_mul_mont(B, A) was incorrect.",
result, ret)) {
return false;
}
memcpy(ret, a, sizeof(ret));
ecp_nistz256_mul_mont(ret, ret, b);
if (!ExpectFieldElementsEqual(
t, "ecp_nistz256_mul_mont(ret = A, B) was incorrect.", result, ret)) {
return false;
}
memcpy(ret, a, sizeof(ret));
ecp_nistz256_mul_mont(ret, b, ret);
if (!ExpectFieldElementsEqual(
t, "ecp_nistz256_mul_mont(B, ret = A) was incorrect.", result, ret)) {
return false;
}
memcpy(ret, b, sizeof(ret));
ecp_nistz256_mul_mont(ret, a, ret);
if (!ExpectFieldElementsEqual(
t, "ecp_nistz256_mul_mont(A, ret = B) was incorrect.", result, ret)) {
return false;
}
memcpy(ret, b, sizeof(ret));
ecp_nistz256_mul_mont(ret, ret, a);
if (!ExpectFieldElementsEqual(
t, "ecp_nistz256_mul_mont(ret = B, A) was incorrect.", result, ret)) {
return false;
}
if (memcmp(a, b, sizeof(a)) == 0) {
ecp_nistz256_sqr_mont(ret, a);
if (!ExpectFieldElementsEqual(t, "ecp_nistz256_sqr_mont(A) was incorrect.",
result, ret)) {
return false;
}
memcpy(ret, a, sizeof(ret));
ecp_nistz256_sqr_mont(ret, ret);
if (!ExpectFieldElementsEqual(
t, "ecp_nistz256_sqr_mont(ret = A) was incorrect.", result, ret)) {
return false;
}
}
return true;
}
static bool TestFromMont(FileTest *t) {
BN_ULONG a[P256_LIMBS], result[P256_LIMBS];
if (!GetFieldElement(t, a, "A") ||
!GetFieldElement(t, result, "Result")) {
return false;
}
BN_ULONG ret[P256_LIMBS];
ecp_nistz256_from_mont(ret, a);
if (!ExpectFieldElementsEqual(t, "ecp_nistz256_from_mont(A) was incorrect.",
result, ret)) {
return false;
}
memcpy(ret, a, sizeof(ret));
ecp_nistz256_from_mont(ret, ret);
if (!ExpectFieldElementsEqual(
t, "ecp_nistz256_from_mont(ret = A) was incorrect.", result, ret)) {
return false;
}
return true;
}
static bool TestPointAdd(FileTest *t) {
P256_POINT a, b;
P256_POINT_AFFINE result;
if (!GetFieldElement(t, a.X, "A.X") ||
!GetFieldElement(t, a.Y, "A.Y") ||
!GetFieldElement(t, a.Z, "A.Z") ||
!GetFieldElement(t, b.X, "B.X") ||
!GetFieldElement(t, b.Y, "B.Y") ||
!GetFieldElement(t, b.Z, "B.Z") ||
!GetFieldElement(t, result.X, "Result.X") ||
!GetFieldElement(t, result.Y, "Result.Y")) {
return false;
}
P256_POINT ret;
ecp_nistz256_point_add(&ret, &a, &b);
if (!ExpectPointsEqual(t, "ecp_nistz256_point_add(A, B) was incorrect.",
&result, &ret)) {
return false;
}
ecp_nistz256_point_add(&ret, &b, &a);
if (!ExpectPointsEqual(t, "ecp_nistz256_point_add(B, A) was incorrect.",
&result, &ret)) {
return false;
}
memcpy(&ret, &a, sizeof(ret));
ecp_nistz256_point_add(&ret, &ret, &b);
if (!ExpectPointsEqual(t, "ecp_nistz256_point_add(ret = A, B) was incorrect.",
&result, &ret)) {
return false;
}
memcpy(&ret, &a, sizeof(ret));
ecp_nistz256_point_add(&ret, &b, &ret);
if (!ExpectPointsEqual(t, "ecp_nistz256_point_add(B, ret = A) was incorrect.",
&result, &ret)) {
return false;
}
memcpy(&ret, &b, sizeof(ret));
ecp_nistz256_point_add(&ret, &a, &ret);
if (!ExpectPointsEqual(t, "ecp_nistz256_point_add(ret = A, B) was incorrect.",
&result, &ret)) {
return false;
}
memcpy(&ret, &b, sizeof(ret));
ecp_nistz256_point_add(&ret, &ret, &a);
if (!ExpectPointsEqual(t, "ecp_nistz256_point_add(ret = B, A) was incorrect.",
&result, &ret)) {
return false;
}
P256_POINT_AFFINE a_affine, b_affine, infinity;
memset(&infinity, 0, sizeof(infinity));
if (!PointToAffine(&a_affine, &a) ||
!PointToAffine(&b_affine, &b)) {
return false;
}
// ecp_nistz256_point_add_affine does not work when a == b unless doubling the
// point at infinity.
if (memcmp(&a_affine, &b_affine, sizeof(a_affine)) != 0 ||
memcmp(&a_affine, &infinity, sizeof(a_affine)) == 0) {
ecp_nistz256_point_add_affine(&ret, &a, &b_affine);
if (!ExpectPointsEqual(t,
"ecp_nistz256_point_add_affine(A, B) was incorrect.",
&result, &ret)) {
return false;
}
memcpy(&ret, &a, sizeof(ret));
ecp_nistz256_point_add_affine(&ret, &ret, &b_affine);
if (!ExpectPointsEqual(
t, "ecp_nistz256_point_add_affine(ret = A, B) was incorrect.",
&result, &ret)) {
return false;
}
ecp_nistz256_point_add_affine(&ret, &b, &a_affine);
if (!ExpectPointsEqual(t,
"ecp_nistz256_point_add_affine(B, A) was incorrect.",
&result, &ret)) {
return false;
}
memcpy(&ret, &b, sizeof(ret));
ecp_nistz256_point_add_affine(&ret, &ret, &a_affine);
if (!ExpectPointsEqual(
t, "ecp_nistz256_point_add_affine(ret = B, A) was incorrect.",
&result, &ret)) {
return false;
}
}
if (memcmp(&a, &b, sizeof(a)) == 0) {
ecp_nistz256_point_double(&ret, &a);
if (!ExpectPointsEqual(t, "ecp_nistz256_point_double(A) was incorrect.",
&result, &ret)) {
return false;
}
ret = a;
ecp_nistz256_point_double(&ret, &ret);
if (!ExpectPointsEqual(
t, "In-place ecp_nistz256_point_double(A) was incorrect.", &result,
&ret)) {
return false;
}
}
return true;
}
int main(int argc, char **argv) {
if (argc != 2) {
fprintf(stderr, "%s TEST_FILE\n", argv[0]);
return 1;
}
if (!TestSelectW5() ||
!TestSelectW7()) {
return 1;
}
return FileTestMain([](FileTest *t, void *) -> bool {
if (t->GetParameter() == "Negate") {
return TestNegate(t);
}
if (t->GetParameter() == "MulMont") {
return TestMulMont(t);
}
if (t->GetParameter() == "FromMont") {
return TestFromMont(t);
}
if (t->GetParameter() == "PointAdd") {
return TestPointAdd(t);
}
t->PrintLine("Unknown test type: %s", t->GetParameter().c_str());
return false;
}, nullptr, argv[1]);
}
#else
int main() {
printf("PASS\n");
return 0;
}
#endif