diff --git a/tests/t-cv25519.c b/tests/t-cv25519.c
index 0de50a02..b4126f4c 100644
--- a/tests/t-cv25519.c
+++ b/tests/t-cv25519.c
@@ -1,650 +1,683 @@
/* t-cv25519.c - Check the cv25519 crypto
* Copyright (C) 2016 g10 Code GmbH
*
* This file is part of Libgcrypt.
*
* Libgcrypt is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* Libgcrypt is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, see .
*/
#ifdef HAVE_CONFIG_H
#include
#endif
#include
#include
#include
#include
#include
#include
#include "stopwatch.h"
#define PGM "t-cv25519"
#include "t-common.h"
#define N_TESTS 18
+static int in_fips_mode = 0;
static void
print_mpi (const char *text, gcry_mpi_t a)
{
gcry_error_t err;
char *buf;
void *bufaddr = &buf;
err = gcry_mpi_aprint (GCRYMPI_FMT_HEX, bufaddr, NULL, a);
if (err)
fprintf (stderr, "%s: [error printing number: %s]\n",
text, gpg_strerror (err));
else
{
fprintf (stderr, "%s: %s\n", text, buf);
gcry_free (buf);
}
}
static void
show_note (const char *format, ...)
{
va_list arg_ptr;
if (!verbose && getenv ("srcdir"))
fputs (" ", stderr); /* To align above "PASS: ". */
else
fprintf (stderr, "%s: ", PGM);
va_start (arg_ptr, format);
vfprintf (stderr, format, arg_ptr);
if (*format && format[strlen(format)-1] != '\n')
putc ('\n', stderr);
va_end (arg_ptr);
}
/* Convert STRING consisting of hex characters into its binary
representation and return it as an allocated buffer. The valid
length of the buffer is returned at R_LENGTH. The string is
delimited by end of string. The function returns NULL on
error. */
static void *
hex2buffer (const char *string, size_t *r_length)
{
const char *s;
unsigned char *buffer;
size_t length;
buffer = xmalloc (strlen(string)/2+1);
length = 0;
for (s=string; *s; s +=2 )
{
if (!hexdigitp (s) || !hexdigitp (s+1))
return NULL; /* Invalid hex digits. */
((unsigned char*)buffer)[length++] = xtoi_2 (s);
}
*r_length = length;
return buffer;
}
static void
reverse_buffer (unsigned char *buffer, unsigned int length)
{
unsigned int tmp, i;
for (i=0; i < length/2; i++)
{
tmp = buffer[i];
buffer[i] = buffer[length-1-i];
buffer[length-1-i] = tmp;
}
}
/*
* Test X25519 functionality through higher layer crypto routines.
*
* Input: K (as hex string), U (as hex string), R (as hex string)
*
* where R is expected result of X25519 (K, U).
*
* It calls gcry_pk_encrypt with Curve25519 private key and let
* it compute X25519.
*/
static void
test_cv_hl (int testno, const char *k_str, const char *u_str,
const char *result_str)
{
gpg_error_t err;
void *buffer = NULL;
size_t buflen;
gcry_sexp_t s_pk = NULL;
gcry_mpi_t mpi_k = NULL;
gcry_sexp_t s_data = NULL;
gcry_sexp_t s_result = NULL;
gcry_sexp_t s_tmp = NULL;
unsigned char *res = NULL;
size_t res_len;
if (verbose > 1)
info ("Running test %d\n", testno);
if (!(buffer = hex2buffer (k_str, &buflen)) || buflen != 32)
{
fail ("error building s-exp for test %d, %s: %s",
testno, "k", "invalid hex string");
goto leave;
}
reverse_buffer (buffer, buflen);
if ((err = gcry_mpi_scan (&mpi_k, GCRYMPI_FMT_USG, buffer, buflen, NULL)))
{
fail ("error converting MPI for test %d: %s", testno, gpg_strerror (err));
goto leave;
}
if ((err = gcry_sexp_build (&s_data, NULL, "%m", mpi_k)))
{
fail ("error building s-exp for test %d, %s: %s",
testno, "data", gpg_strerror (err));
goto leave;
}
xfree (buffer);
if (!(buffer = hex2buffer (u_str, &buflen)) || buflen != 32)
{
fail ("error building s-exp for test %d, %s: %s",
testno, "u", "invalid hex string");
goto leave;
}
/*
* The procedure of decodeUCoordinate will be done internally
* by _gcry_ecc_mont_decodepoint. So, we just put the little-endian
* binary to build S-exp.
*
* We could add the prefix 0x40, but libgcrypt also supports
* format with no prefix. So, it is OK not to put the prefix.
*/
if ((err = gcry_sexp_build (&s_pk, NULL,
"(public-key"
" (ecc"
" (curve \"Curve25519\")"
" (flags djb-tweak)"
" (q%b)))", (int)buflen, buffer)))
{
fail ("error building s-exp for test %d, %s: %s",
testno, "pk", gpg_strerror (err));
goto leave;
}
xfree (buffer);
buffer = NULL;
- if ((err = gcry_pk_encrypt (&s_result, s_data, s_pk)))
+ err = gcry_pk_encrypt (&s_result, s_data, s_pk);
+ if (in_fips_mode)
+ {
+ if (!err)
+ fail ("gcry_pk_encrypt is not expected to work in FIPS mode for test %d",
+ testno);
+ if (verbose > 1)
+ info ("not executed in FIPS mode\n");
+ goto leave;
+ }
+ if (err)
fail ("gcry_pk_encrypt failed for test %d: %s", testno,
gpg_strerror (err));
s_tmp = gcry_sexp_find_token (s_result, "s", 0);
if (!s_tmp || !(res = gcry_sexp_nth_buffer (s_tmp, 1, &res_len)))
fail ("gcry_pk_encrypt failed for test %d: %s", testno, "missing value");
else
{
char *r, *r0;
int i;
/* To skip the prefix 0x40, for-loop start with i=1 */
r0 = r = xmalloc (2*(res_len)+1);
if (!r0)
{
fail ("memory allocation for test %d", testno);
goto leave;
}
for (i=1; i < res_len; i++, r += 2)
snprintf (r, 3, "%02x", res[i]);
if (strcmp (result_str, r0))
{
fail ("gcry_pk_encrypt failed for test %d: %s",
testno, "wrong value returned");
info (" expected: '%s'", result_str);
info (" got: '%s'", r0);
}
xfree (r0);
}
leave:
xfree (res);
gcry_mpi_release (mpi_k);
gcry_sexp_release (s_tmp);
gcry_sexp_release (s_result);
gcry_sexp_release (s_data);
gcry_sexp_release (s_pk);
xfree (buffer);
}
/*
* Test X25519 functionality through the API for X25519.
*
* Input: K (as hex string), U (as hex string), R (as hex string)
*
* where R is expected result of X25519 (K, U).
*
*/
static void
test_cv_x25519 (int testno, const char *k_str, const char *u_str,
const char *result_str)
{
gpg_error_t err;
void *scalar = NULL;
void *point = NULL;
size_t buflen;
unsigned char result[32];
char result_hex[65];
int i;
int algo = GCRY_ECC_CURVE25519;
unsigned int keylen;
if (verbose > 1)
info ("Running test %d\n", testno);
if (!(keylen = gcry_ecc_get_algo_keylen (algo)))
{
fail ("error getting keylen for test %d", testno);
goto leave;
}
if (keylen != 32)
{
fail ("error invalid keylen for test %d", testno);
goto leave;
}
if (!(scalar = hex2buffer (k_str, &buflen)) || buflen != keylen)
{
fail ("error of input for test %d, %s: %s",
testno, "k", "invalid hex string");
goto leave;
}
if (!(point = hex2buffer (u_str, &buflen)) || buflen != keylen)
{
fail ("error of input for test %d, %s: %s",
testno, "u", "invalid hex string");
goto leave;
}
- if ((err = gcry_ecc_mul_point (algo, result, scalar, point)))
+ err = gcry_ecc_mul_point (algo, result, scalar, point);
+ if (in_fips_mode)
+ {
+ if (!err)
+ fail ("gcry_ecc_mul_point is not expected to work in FIPS mode for test %d",
+ testno);
+ if (verbose > 1)
+ info ("not executed in FIPS mode\n");
+ goto leave;
+ }
+ if (err)
fail ("gcry_ecc_mul_point failed for test %d: %s", testno,
gpg_strerror (err));
for (i=0; i < keylen; i++)
snprintf (&result_hex[i*2], 3, "%02x", result[i]);
if (strcmp (result_str, result_hex))
{
fail ("gcry_ecc_mul_point failed for test %d: %s",
testno, "wrong value returned");
info (" expected: '%s'", result_str);
info (" got: '%s'", result_hex);
}
leave:
xfree (scalar);
xfree (point);
}
static void
test_cv (int testno, const char *k_str, const char *u_str,
const char *result_str)
{
test_cv_hl (testno, k_str, u_str, result_str);
test_cv_x25519 (testno, k_str, u_str, result_str);
}
/*
* Test iterative X25519 computation through lower layer MPI routines.
*
* Input: K (as hex string), ITER, R (as hex string)
*
* where R is expected result of iterating X25519 by ITER times.
*
*/
static void
test_it (int testno, const char *k_str, int iter, const char *result_str)
{
gcry_ctx_t ctx;
gpg_error_t err;
void *buffer = NULL;
size_t buflen;
gcry_mpi_t mpi_k = NULL;
gcry_mpi_t mpi_x = NULL;
gcry_mpi_point_t P = NULL;
gcry_mpi_point_t Q;
int i;
gcry_mpi_t mpi_kk = NULL;
if (verbose > 1)
info ("Running test %d: iteration=%d\n", testno, iter);
gcry_mpi_ec_new (&ctx, NULL, "Curve25519");
+ if (in_fips_mode)
+ {
+ if (ctx)
+ fail ("gcry_mpi_ec_new should fail in FIPS mode for test %d",
+ testno);
+ if (verbose > 1)
+ info ("not executed in FIPS mode\n");
+ return;
+ }
Q = gcry_mpi_point_new (0);
if (!(buffer = hex2buffer (k_str, &buflen)) || buflen != 32)
{
fail ("error scanning MPI for test %d, %s: %s",
testno, "k", "invalid hex string");
goto leave;
}
reverse_buffer (buffer, buflen);
if ((err = gcry_mpi_scan (&mpi_x, GCRYMPI_FMT_USG, buffer, buflen, NULL)))
{
fail ("error scanning MPI for test %d, %s: %s",
testno, "x", gpg_strerror (err));
goto leave;
}
xfree (buffer);
buffer = NULL;
P = gcry_mpi_point_set (NULL, mpi_x, NULL, GCRYMPI_CONST_ONE);
mpi_k = gcry_mpi_copy (mpi_x);
if (debug)
print_mpi ("k", mpi_k);
for (i = 0; i < iter; i++)
{
/*
* Another variant of decodeScalar25519 thing.
*/
mpi_kk = gcry_mpi_set (mpi_kk, mpi_k);
gcry_mpi_set_bit (mpi_kk, 254);
gcry_mpi_clear_bit (mpi_kk, 255);
gcry_mpi_clear_bit (mpi_kk, 0);
gcry_mpi_clear_bit (mpi_kk, 1);
gcry_mpi_clear_bit (mpi_kk, 2);
gcry_mpi_ec_mul (Q, mpi_kk, P, ctx);
P = gcry_mpi_point_set (P, mpi_k, NULL, GCRYMPI_CONST_ONE);
gcry_mpi_ec_get_affine (mpi_k, NULL, Q, ctx);
if (debug)
print_mpi ("k", mpi_k);
}
{
unsigned char res[32];
char *r, *r0;
gcry_mpi_print (GCRYMPI_FMT_USG, res, 32, NULL, mpi_k);
reverse_buffer (res, 32);
r0 = r = xmalloc (65);
if (!r0)
{
fail ("memory allocation for test %d", testno);
goto leave;
}
for (i=0; i < 32; i++, r += 2)
snprintf (r, 3, "%02x", res[i]);
if (strcmp (result_str, r0))
{
fail ("curv25519 failed for test %d: %s",
testno, "wrong value returned");
info (" expected: '%s'", result_str);
info (" got: '%s'", r0);
}
xfree (r0);
}
leave:
gcry_mpi_release (mpi_kk);
gcry_mpi_release (mpi_k);
gcry_mpi_point_release (P);
gcry_mpi_release (mpi_x);
xfree (buffer);
gcry_mpi_point_release (Q);
gcry_ctx_release (ctx);
}
/*
* X-coordinate of generator of the Curve25519.
*/
#define G_X "0900000000000000000000000000000000000000000000000000000000000000"
/*
* Test Diffie-Hellman in RFC-7748.
*
* Note that it's not like the ECDH of OpenPGP, where we use
* ephemeral public key.
*/
static void
test_dh (int testno, const char *a_priv_str, const char *a_pub_str,
const char *b_priv_str, const char *b_pub_str,
const char *result_str)
{
/* Test A for private key corresponds to public key. */
test_cv (testno, a_priv_str, G_X, a_pub_str);
/* Test B for private key corresponds to public key. */
test_cv (testno, b_priv_str, G_X, b_pub_str);
/* Test DH with A's private key and B's public key. */
test_cv (testno, a_priv_str, b_pub_str, result_str);
/* Test DH with B's private key and A's public key. */
test_cv (testno, b_priv_str, a_pub_str, result_str);
}
static void
check_cv25519 (void)
{
int ntests;
info ("Checking Curve25519.\n");
ntests = 0;
/*
* Values are cited from RFC-7748: 5.2. Test Vectors.
* Following two tests are for the first type test.
*/
test_cv (1,
"a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4",
"e6db6867583030db3594c1a424b15f7c726624ec26b3353b10a903a6d0ab1c4c",
"c3da55379de9c6908e94ea4df28d084f32eccf03491c71f754b4075577a28552");
ntests++;
test_cv (2,
"4b66e9d4d1b4673c5ad22691957d6af5c11b6421e0ea01d42ca4169e7918ba0d",
"e5210f12786811d3f4b7959d0538ae2c31dbe7106fc03c3efc4cd549c715a493",
"95cbde9476e8907d7aade45cb4b873f88b595a68799fa152e6f8f7647aac7957");
ntests++;
/*
* Additional test. Value is from second type test.
*/
test_cv (3,
G_X,
G_X,
"422c8e7a6227d7bca1350b3e2bb7279f7897b87bb6854b783c60e80311ae3079");
ntests++;
/*
* Following two tests are for the second type test,
* with one iteration and 1,000 iterations. (1,000,000 iterations
* takes too long.)
*/
test_it (4,
G_X,
1,
"422c8e7a6227d7bca1350b3e2bb7279f7897b87bb6854b783c60e80311ae3079");
ntests++;
test_it (5,
G_X,
1000,
"684cf59ba83309552800ef566f2f4d3c1c3887c49360e3875f2eb94d99532c51");
ntests++;
/*
* Last test is from: 6. Diffie-Hellman, 6.1. Curve25519
*/
test_dh (6,
/* Alice's private key, a */
"77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a",
/* Alice's public key, X25519(a, 9) */
"8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a",
/* Bob's private key, b */
"5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb",
/* Bob's public key, X25519(b, 9) */
"de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f",
/* Their shared secret, K */
"4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742");
ntests++;
/* Seven tests which results 0. */
test_cv (7,
"a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4",
"0000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000000");
ntests++;
test_cv (8,
"a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4",
"0100000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000000");
ntests++;
test_cv (9,
"a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4",
"e0eb7a7c3b41b8ae1656e3faf19fc46ada098deb9c32b1fd866205165f49b800",
"0000000000000000000000000000000000000000000000000000000000000000");
ntests++;
test_cv (10,
"a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4",
"5f9c95bca3508c24b1d0b1559c83ef5b04445cc4581c8e86d8224eddd09f1157",
"0000000000000000000000000000000000000000000000000000000000000000");
ntests++;
test_cv (11,
"a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4",
"ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
"0000000000000000000000000000000000000000000000000000000000000000");
ntests++;
test_cv (12,
"a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4",
"edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
"0000000000000000000000000000000000000000000000000000000000000000");
ntests++;
test_cv (13,
"a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4",
"eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
"0000000000000000000000000000000000000000000000000000000000000000");
ntests++;
/* Five tests which resulted 0 if decodeUCoordinate didn't change MSB. */
test_cv (14,
"a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4",
"cdeb7a7c3b41b8ae1656e3faf19fc46ada098deb9c32b1fd866205165f49b880",
"7ce548bc4919008436244d2da7a9906528fe3a6d278047654bd32d8acde9707b");
ntests++;
test_cv (15,
"a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4",
"4c9c95bca3508c24b1d0b1559c83ef5b04445cc4581c8e86d8224eddd09f11d7",
"e17902e989a034acdf7248260e2c94cdaf2fe1e72aaac7024a128058b6189939");
ntests++;
test_cv (16,
"a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4",
"d9ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"ea6e6ddf0685c31e152d5818441ac9ac8db1a01f3d6cb5041b07443a901e7145");
ntests++;
test_cv (17,
"a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4",
"daffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"845ddce7b3a9b3ee01a2f1fd4282ad293310f7a232cbc5459fb35d94bccc9d05");
ntests++;
test_cv (18,
"a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4",
"dbffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"6989e2cb1cea159acf121b0af6bf77493189c9bd32c2dac71669b540f9488247");
ntests++;
if (ntests != N_TESTS)
fail ("did %d tests but expected %d", ntests, N_TESTS);
else if ((ntests % 256))
show_note ("%d tests done\n", ntests);
}
int
main (int argc, char **argv)
{
int last_argc = -1;
if (argc)
{ argc--; argv++; }
while (argc && last_argc != argc )
{
last_argc = argc;
if (!strcmp (*argv, "--"))
{
argc--; argv++;
break;
}
else if (!strcmp (*argv, "--help"))
{
fputs ("usage: " PGM " [options]\n"
"Options:\n"
" --verbose print timings etc.\n"
" --debug flyswatter\n",
stdout);
exit (0);
}
else if (!strcmp (*argv, "--verbose"))
{
verbose++;
argc--; argv++;
}
else if (!strcmp (*argv, "--debug"))
{
verbose += 2;
debug++;
argc--; argv++;
}
else if (!strncmp (*argv, "--", 2))
die ("unknown option '%s'", *argv);
}
xgcry_control ((GCRYCTL_DISABLE_SECMEM, 0));
if (!gcry_check_version (GCRYPT_VERSION))
die ("version mismatch\n");
if (debug)
xgcry_control ((GCRYCTL_SET_DEBUG_FLAGS, 1u , 0));
xgcry_control ((GCRYCTL_ENABLE_QUICK_RANDOM, 0));
xgcry_control ((GCRYCTL_INITIALIZATION_FINISHED, 0));
+ if (gcry_fips_mode_active ())
+ in_fips_mode = 1;
+
start_timer ();
check_cv25519 ();
stop_timer ();
info ("All tests completed in %s. Errors: %d\n",
elapsed_time (1), error_count);
return !!error_count;
}
diff --git a/tests/t-ed25519.c b/tests/t-ed25519.c
index a5271c25..567bc797 100644
--- a/tests/t-ed25519.c
+++ b/tests/t-ed25519.c
@@ -1,497 +1,507 @@
/* t-ed25519.c - Check the Ed25519 crypto
* Copyright (C) 2013 g10 Code GmbH
*
* This file is part of Libgcrypt.
*
* Libgcrypt is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* Libgcrypt is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, see .
*/
#ifdef HAVE_CONFIG_H
#include
#endif
#include
#include
#include
#include
#include
#include
#include "stopwatch.h"
#define PGM "t-ed25519"
#include "t-common.h"
#define N_TESTS 1026
static int sign_with_pk;
static int no_verify;
static int custom_data_file;
+static int in_fips_mode = 0;
static void
show_note (const char *format, ...)
{
va_list arg_ptr;
if (!verbose && getenv ("srcdir"))
fputs (" ", stderr); /* To align above "PASS: ". */
else
fprintf (stderr, "%s: ", PGM);
va_start (arg_ptr, format);
vfprintf (stderr, format, arg_ptr);
if (*format && format[strlen(format)-1] != '\n')
putc ('\n', stderr);
va_end (arg_ptr);
}
static void
show_sexp (const char *prefix, gcry_sexp_t a)
{
char *buf;
size_t size;
fprintf (stderr, "%s: ", PGM);
if (prefix)
fputs (prefix, stderr);
size = gcry_sexp_sprint (a, GCRYSEXP_FMT_ADVANCED, NULL, 0);
buf = xmalloc (size);
gcry_sexp_sprint (a, GCRYSEXP_FMT_ADVANCED, buf, size);
fprintf (stderr, "%.*s", (int)size, buf);
gcry_free (buf);
}
/* Prepend FNAME with the srcdir environment variable's value and
* return an allocated filename. */
char *
prepend_srcdir (const char *fname)
{
static const char *srcdir;
char *result;
if (!srcdir && !(srcdir = getenv ("srcdir")))
srcdir = ".";
result = xmalloc (strlen (srcdir) + 1 + strlen (fname) + 1);
strcpy (result, srcdir);
strcat (result, "/");
strcat (result, fname);
return result;
}
/* Read next line but skip over empty and comment lines. Caller must
xfree the result. */
static char *
read_textline (FILE *fp, int *lineno)
{
char line[4096];
char *p;
do
{
if (!fgets (line, sizeof line, fp))
{
if (feof (fp))
return NULL;
die ("error reading input line: %s\n", strerror (errno));
}
++*lineno;
p = strchr (line, '\n');
if (!p)
die ("input line %d not terminated or too long\n", *lineno);
*p = 0;
for (p--;p > line && my_isascii (*p) && isspace (*p); p--)
*p = 0;
}
while (!*line || *line == '#');
/* if (debug) */
/* info ("read line: '%s'\n", line); */
return xstrdup (line);
}
/* Copy the data after the tag to BUFFER. BUFFER will be allocated as
needed. */
static void
copy_data (char **buffer, const char *line, int lineno)
{
const char *s;
xfree (*buffer);
*buffer = NULL;
s = strchr (line, ':');
if (!s)
{
fail ("syntax error at input line %d", lineno);
return;
}
for (s++; my_isascii (*s) && isspace (*s); s++)
;
*buffer = xstrdup (s);
}
/* Convert STRING consisting of hex characters into its binary
representation and return it as an allocated buffer. The valid
length of the buffer is returned at R_LENGTH. The string is
delimited by end of string. The function returns NULL on
error. */
static void *
hex2buffer (const char *string, size_t *r_length)
{
const char *s;
unsigned char *buffer;
size_t length;
buffer = xmalloc (strlen(string)/2+1);
length = 0;
for (s=string; *s; s +=2 )
{
if (!hexdigitp (s) || !hexdigitp (s+1))
return NULL; /* Invalid hex digits. */
((unsigned char*)buffer)[length++] = xtoi_2 (s);
}
*r_length = length;
return buffer;
}
static void
hexdowncase (char *string)
{
char *p;
for (p=string; *p; p++)
if (my_isascii (*p))
*p = tolower (*p);
}
static void
one_test (int testno, const char *sk, const char *pk,
const char *msg, const char *sig)
{
gpg_error_t err;
int i;
char *p;
void *buffer = NULL;
void *buffer2 = NULL;
size_t buflen, buflen2;
gcry_sexp_t s_tmp, s_tmp2;
gcry_sexp_t s_sk = NULL;
gcry_sexp_t s_pk = NULL;
gcry_sexp_t s_msg= NULL;
gcry_sexp_t s_sig= NULL;
unsigned char *sig_r = NULL;
unsigned char *sig_s = NULL;
char *sig_rs_string = NULL;
size_t sig_r_len, sig_s_len;
if (verbose > 1)
info ("Running test %d\n", testno);
if (!(buffer = hex2buffer (sk, &buflen)))
{
fail ("error building s-exp for test %d, %s: %s",
testno, "sk", "invalid hex string");
goto leave;
}
if (!(buffer2 = hex2buffer (pk, &buflen2)))
{
fail ("error building s-exp for test %d, %s: %s",
testno, "pk", "invalid hex string");
goto leave;
}
if (sign_with_pk)
err = gcry_sexp_build (&s_sk, NULL,
"(private-key"
" (ecc"
" (curve \"Ed25519\")"
" (flags eddsa)"
" (q %b)"
" (d %b)))",
(int)buflen2, buffer2,
(int)buflen, buffer);
else
err = gcry_sexp_build (&s_sk, NULL,
"(private-key"
" (ecc"
" (curve \"Ed25519\")"
" (flags eddsa)"
" (d %b)))",
(int)buflen, buffer);
if (err)
{
fail ("error building s-exp for test %d, %s: %s",
testno, "sk", gpg_strerror (err));
goto leave;
}
if ((err = gcry_sexp_build (&s_pk, NULL,
"(public-key"
" (ecc"
" (curve \"Ed25519\")"
" (flags eddsa)"
" (q %b)))", (int)buflen2, buffer2)))
{
fail ("error building s-exp for test %d, %s: %s",
testno, "pk", gpg_strerror (err));
goto leave;
}
xfree (buffer);
if (!(buffer = hex2buffer (msg, &buflen)))
{
fail ("error building s-exp for test %d, %s: %s",
testno, "msg", "invalid hex string");
goto leave;
}
if ((err = gcry_sexp_build (&s_msg, NULL,
"(data"
" (flags eddsa)"
" (hash-algo sha512)"
" (value %b))", (int)buflen, buffer)))
{
fail ("error building s-exp for test %d, %s: %s",
testno, "msg", gpg_strerror (err));
goto leave;
}
- if ((err = gcry_pk_sign (&s_sig, s_msg, s_sk)))
+ err = gcry_pk_sign (&s_sig, s_msg, s_sk);
+ if (in_fips_mode)
+ {
+ if (!err)
+ fail ("gcry_pk_sign is not expected to work in FIPS mode for test %d",
+ testno);
+ if (verbose > 1)
+ info ("not executed in FIPS mode\n");
+ goto leave;
+ }
+ if (err)
fail ("gcry_pk_sign failed for test %d: %s", testno, gpg_strerror (err));
if (debug)
show_sexp ("sig=", s_sig);
s_tmp2 = NULL;
s_tmp = gcry_sexp_find_token (s_sig, "sig-val", 0);
if (s_tmp)
{
s_tmp2 = s_tmp;
s_tmp = gcry_sexp_find_token (s_tmp2, "eddsa", 0);
if (s_tmp)
{
gcry_sexp_release (s_tmp2);
s_tmp2 = s_tmp;
s_tmp = gcry_sexp_find_token (s_tmp2, "r", 0);
if (s_tmp)
{
sig_r = gcry_sexp_nth_buffer (s_tmp, 1, &sig_r_len);
gcry_sexp_release (s_tmp);
}
s_tmp = gcry_sexp_find_token (s_tmp2, "s", 0);
if (s_tmp)
{
sig_s = gcry_sexp_nth_buffer (s_tmp, 1, &sig_s_len);
gcry_sexp_release (s_tmp);
}
}
}
gcry_sexp_release (s_tmp2); s_tmp2 = NULL;
if (!sig_r || !sig_s)
fail ("gcry_pk_sign failed for test %d: %s", testno, "r or s missing");
else
{
sig_rs_string = xmalloc (2*(sig_r_len + sig_s_len)+1);
p = sig_rs_string;
*p = 0;
for (i=0; i < sig_r_len; i++, p += 2)
snprintf (p, 3, "%02x", sig_r[i]);
for (i=0; i < sig_s_len; i++, p += 2)
snprintf (p, 3, "%02x", sig_s[i]);
if (strcmp (sig_rs_string, sig))
{
fail ("gcry_pk_sign failed for test %d: %s",
testno, "wrong value returned");
info (" expected: '%s'", sig);
info (" got: '%s'", sig_rs_string);
}
}
if (!no_verify)
if ((err = gcry_pk_verify (s_sig, s_msg, s_pk)))
fail ("gcry_pk_verify failed for test %d: %s",
testno, gpg_strerror (err));
leave:
gcry_sexp_release (s_sig);
gcry_sexp_release (s_sk);
gcry_sexp_release (s_pk);
gcry_sexp_release (s_msg);
xfree (buffer);
xfree (buffer2);
xfree (sig_r);
xfree (sig_s);
xfree (sig_rs_string);
}
static void
check_ed25519 (const char *fname)
{
FILE *fp;
int lineno, ntests;
char *line;
int testno;
char *sk, *pk, *msg, *sig;
info ("Checking Ed25519.\n");
fp = fopen (fname, "r");
if (!fp)
die ("error opening '%s': %s\n", fname, strerror (errno));
testno = 0;
sk = pk = msg = sig = NULL;
lineno = ntests = 0;
while ((line = read_textline (fp, &lineno)))
{
if (!strncmp (line, "TST:", 4))
testno = atoi (line+4);
else if (!strncmp (line, "SK:", 3))
copy_data (&sk, line, lineno);
else if (!strncmp (line, "PK:", 3))
copy_data (&pk, line, lineno);
else if (!strncmp (line, "MSG:", 4))
copy_data (&msg, line, lineno);
else if (!strncmp (line, "SIG:", 4))
copy_data (&sig, line, lineno);
else
fail ("unknown tag at input line %d", lineno);
xfree (line);
if (testno && sk && pk && msg && sig)
{
hexdowncase (sig);
one_test (testno, sk, pk, msg, sig);
ntests++;
if (!(ntests % 256))
show_note ("%d of %d tests done\n", ntests, N_TESTS);
xfree (pk); pk = NULL;
xfree (sk); sk = NULL;
xfree (msg); msg = NULL;
xfree (sig); sig = NULL;
}
}
xfree (pk);
xfree (sk);
xfree (msg);
xfree (sig);
if (ntests != N_TESTS && !custom_data_file)
fail ("did %d tests but expected %d", ntests, N_TESTS);
else if ((ntests % 256))
show_note ("%d tests done\n", ntests);
fclose (fp);
}
int
main (int argc, char **argv)
{
int last_argc = -1;
char *fname = NULL;
if (argc)
{ argc--; argv++; }
while (argc && last_argc != argc )
{
last_argc = argc;
if (!strcmp (*argv, "--"))
{
argc--; argv++;
break;
}
else if (!strcmp (*argv, "--help"))
{
fputs ("usage: " PGM " [options]\n"
"Options:\n"
" --verbose print timings etc.\n"
" --debug flyswatter\n"
" --sign-with-pk also use the public key for signing\n"
" --no-verify skip the verify test\n"
" --data FNAME take test data from file FNAME\n",
stdout);
exit (0);
}
else if (!strcmp (*argv, "--verbose"))
{
verbose++;
argc--; argv++;
}
else if (!strcmp (*argv, "--debug"))
{
verbose += 2;
debug++;
argc--; argv++;
}
else if (!strcmp (*argv, "--sign-with-pk"))
{
sign_with_pk = 1;
argc--; argv++;
}
else if (!strcmp (*argv, "--no-verify"))
{
no_verify = 1;
argc--; argv++;
}
else if (!strcmp (*argv, "--data"))
{
argc--; argv++;
if (argc)
{
xfree (fname);
fname = xstrdup (*argv);
argc--; argv++;
}
}
else if (!strncmp (*argv, "--", 2))
die ("unknown option '%s'", *argv);
}
if (!fname)
fname = prepend_srcdir ("t-ed25519.inp");
else
custom_data_file = 1;
xgcry_control ((GCRYCTL_DISABLE_SECMEM, 0));
if (!gcry_check_version (GCRYPT_VERSION))
die ("version mismatch\n");
if (debug)
xgcry_control ((GCRYCTL_SET_DEBUG_FLAGS, 1u , 0));
xgcry_control ((GCRYCTL_ENABLE_QUICK_RANDOM, 0));
xgcry_control ((GCRYCTL_INITIALIZATION_FINISHED, 0));
- /* Ed25519 isn't supported in fips mode */
- if (gcry_fips_mode_active())
- return 77;
+ if (gcry_fips_mode_active ())
+ in_fips_mode = 1;
start_timer ();
check_ed25519 (fname);
stop_timer ();
xfree (fname);
info ("All tests completed in %s. Errors: %d\n",
elapsed_time (1), error_count);
return !!error_count;
}
diff --git a/tests/t-ed448.c b/tests/t-ed448.c
index 1f445ffc..f38cd10c 100644
--- a/tests/t-ed448.c
+++ b/tests/t-ed448.c
@@ -1,537 +1,547 @@
/* t-ed448.c - Check the Ed448 crypto
* Copyright (C) 2020 g10 Code GmbH
*
* This file is part of Libgcrypt.
*
* Libgcrypt is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* Libgcrypt is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, see .
*/
#ifdef HAVE_CONFIG_H
#include
#endif
#include
#include
#include
#include
#include
#include
#include "stopwatch.h"
#define PGM "t-ed448"
#include "t-common.h"
#define N_TESTS 11
static int sign_with_pk;
static int no_verify;
static int custom_data_file;
+static int in_fips_mode = 0;
static void
show_note (const char *format, ...)
{
va_list arg_ptr;
if (!verbose && getenv ("srcdir"))
fputs (" ", stderr); /* To align above "PASS: ". */
else
fprintf (stderr, "%s: ", PGM);
va_start (arg_ptr, format);
vfprintf (stderr, format, arg_ptr);
if (*format && format[strlen(format)-1] != '\n')
putc ('\n', stderr);
va_end (arg_ptr);
}
static void
show_sexp (const char *prefix, gcry_sexp_t a)
{
char *buf;
size_t size;
fprintf (stderr, "%s: ", PGM);
if (prefix)
fputs (prefix, stderr);
size = gcry_sexp_sprint (a, GCRYSEXP_FMT_ADVANCED, NULL, 0);
buf = xmalloc (size);
gcry_sexp_sprint (a, GCRYSEXP_FMT_ADVANCED, buf, size);
fprintf (stderr, "%.*s", (int)size, buf);
gcry_free (buf);
}
/* Prepend FNAME with the srcdir environment variable's value and
* return an allocated filename. */
char *
prepend_srcdir (const char *fname)
{
static const char *srcdir;
char *result;
if (!srcdir && !(srcdir = getenv ("srcdir")))
srcdir = ".";
result = xmalloc (strlen (srcdir) + 1 + strlen (fname) + 1);
strcpy (result, srcdir);
strcat (result, "/");
strcat (result, fname);
return result;
}
/* Read next line but skip over empty and comment lines. Caller must
xfree the result. */
static char *
read_textline (FILE *fp, int *lineno)
{
char line[4096];
char *p;
do
{
if (!fgets (line, sizeof line, fp))
{
if (feof (fp))
return NULL;
die ("error reading input line: %s\n", strerror (errno));
}
++*lineno;
p = strchr (line, '\n');
if (!p)
die ("input line %d not terminated or too long\n", *lineno);
*p = 0;
for (p--;p > line && my_isascii (*p) && isspace (*p); p--)
*p = 0;
}
while (!*line || *line == '#');
/* if (debug) */
/* info ("read line: '%s'\n", line); */
return xstrdup (line);
}
/* Copy the data after the tag to BUFFER. BUFFER will be allocated as
needed. */
static void
copy_data (char **buffer, const char *line, int lineno)
{
const char *s;
xfree (*buffer);
*buffer = NULL;
s = strchr (line, ':');
if (!s)
{
fail ("syntax error at input line %d", lineno);
return;
}
for (s++; my_isascii (*s) && isspace (*s); s++)
;
*buffer = xstrdup (s);
}
/* Convert STRING consisting of hex characters into its binary
representation and return it as an allocated buffer. The valid
length of the buffer is returned at R_LENGTH. The string is
delimited by end of string. The function returns NULL on
error. */
static void *
hex2buffer (const char *string, size_t *r_length)
{
const char *s;
unsigned char *buffer;
size_t length;
buffer = xmalloc (strlen(string)/2+1);
length = 0;
for (s=string; *s; s +=2 )
{
if (!hexdigitp (s) || !hexdigitp (s+1))
return NULL; /* Invalid hex digits. */
((unsigned char*)buffer)[length++] = xtoi_2 (s);
}
*r_length = length;
return buffer;
}
static void
hexdowncase (char *string)
{
char *p;
for (p=string; *p; p++)
if (my_isascii (*p))
*p = tolower (*p);
}
static void
one_test (int testno, int ph, const char *sk, const char *pk,
const char *msg, const char *ctx, const char *sig)
{
gpg_error_t err;
int i;
char *p;
void *buffer = NULL;
void *buffer2 = NULL;
size_t buflen, buflen2;
gcry_sexp_t s_tmp, s_tmp2;
gcry_sexp_t s_sk = NULL;
gcry_sexp_t s_pk = NULL;
gcry_sexp_t s_msg= NULL;
gcry_sexp_t s_sig= NULL;
unsigned char *sig_r = NULL;
unsigned char *sig_s = NULL;
char *sig_rs_string = NULL;
size_t sig_r_len, sig_s_len;
if (verbose > 1)
info ("Running test %d %d\n", testno, ph);
if (!(buffer = hex2buffer (sk, &buflen)))
{
fail ("error building s-exp for test %d, %s: %s",
testno, "sk", "invalid hex string");
goto leave;
}
if (!(buffer2 = hex2buffer (pk, &buflen2)))
{
fail ("error building s-exp for test %d, %s: %s",
testno, "pk", "invalid hex string");
goto leave;
}
if (sign_with_pk)
err = gcry_sexp_build (&s_sk, NULL,
"(private-key"
" (ecc"
" (curve \"Ed448\")"
" (q %b)"
" (d %b)))",
(int)buflen2, buffer2,
(int)buflen, buffer);
else
err = gcry_sexp_build (&s_sk, NULL,
"(private-key"
" (ecc"
" (curve \"Ed448\")"
" (d %b)))",
(int)buflen, buffer);
if (err)
{
fail ("error building s-exp for test %d, %s: %s",
testno, "sk", gpg_strerror (err));
goto leave;
}
if ((err = gcry_sexp_build (&s_pk, NULL,
"(public-key"
" (ecc"
" (curve \"Ed448\")"
" (q %b)))", (int)buflen2, buffer2)))
{
fail ("error building s-exp for test %d, %s: %s",
testno, "pk", gpg_strerror (err));
goto leave;
}
xfree (buffer);
if (!(buffer = hex2buffer (msg, &buflen)))
{
fail ("error building s-exp for test %d, %s: %s",
testno, "msg", "invalid hex string");
goto leave;
}
if (ctx)
{
xfree (buffer2);
if (!(buffer2 = hex2buffer (ctx, &buflen2)))
{
fail ("error building s-exp for test %d, %s: %s",
testno, "ctx", "invalid hex string");
goto leave;
}
if ((err = gcry_sexp_build (&s_msg, NULL,
ph ?
"(data"
" (flags prehash)"
" (label %b)"
" (value %b))"
:
"(data"
" (label %b)"
" (value %b))",
(int)buflen2, buffer2,
(int)buflen, buffer)))
{
fail ("error building s-exp for test %d, %s: %s",
testno, "msg", gpg_strerror (err));
goto leave;
}
}
else
{
if ((err = gcry_sexp_build (&s_msg, NULL,
ph ?
"(data"
" (flags prehash)"
" (value %b))"
:
"(data"
" (value %b))", (int)buflen, buffer)))
{
fail ("error building s-exp for test %d, %s: %s",
testno, "msg", gpg_strerror (err));
goto leave;
}
}
- if ((err = gcry_pk_sign (&s_sig, s_msg, s_sk)))
+ err = gcry_pk_sign (&s_sig, s_msg, s_sk);
+ if (in_fips_mode)
+ {
+ if (!err)
+ fail ("gcry_pk_sign is not expected to work in FIPS mode for test %d",
+ testno);
+ if (verbose > 1)
+ info ("not executed in FIPS mode\n");
+ goto leave;
+ }
+ if (err)
fail ("gcry_pk_sign failed for test %d: %s", testno, gpg_strerror (err));
if (debug)
show_sexp ("sig=", s_sig);
s_tmp2 = NULL;
s_tmp = gcry_sexp_find_token (s_sig, "sig-val", 0);
if (s_tmp)
{
s_tmp2 = s_tmp;
s_tmp = gcry_sexp_find_token (s_tmp2, "eddsa", 0);
if (s_tmp)
{
gcry_sexp_release (s_tmp2);
s_tmp2 = s_tmp;
s_tmp = gcry_sexp_find_token (s_tmp2, "r", 0);
if (s_tmp)
{
sig_r = gcry_sexp_nth_buffer (s_tmp, 1, &sig_r_len);
gcry_sexp_release (s_tmp);
}
s_tmp = gcry_sexp_find_token (s_tmp2, "s", 0);
if (s_tmp)
{
sig_s = gcry_sexp_nth_buffer (s_tmp, 1, &sig_s_len);
gcry_sexp_release (s_tmp);
}
}
}
gcry_sexp_release (s_tmp2); s_tmp2 = NULL;
if (!sig_r || !sig_s)
fail ("gcry_pk_sign failed for test %d: %s", testno, "r or s missing");
else
{
sig_rs_string = xmalloc (2*(sig_r_len + sig_s_len)+1);
p = sig_rs_string;
*p = 0;
for (i=0; i < sig_r_len; i++, p += 2)
snprintf (p, 3, "%02x", sig_r[i]);
for (i=0; i < sig_s_len; i++, p += 2)
snprintf (p, 3, "%02x", sig_s[i]);
if (strcmp (sig_rs_string, sig))
{
fail ("gcry_pk_sign failed for test %d: %s",
testno, "wrong value returned");
info (" expected: '%s'", sig);
info (" got: '%s'", sig_rs_string);
}
}
if (!no_verify)
if ((err = gcry_pk_verify (s_sig, s_msg, s_pk)))
fail ("gcry_pk_verify failed for test %d: %s",
testno, gpg_strerror (err));
leave:
gcry_sexp_release (s_sig);
gcry_sexp_release (s_sk);
gcry_sexp_release (s_pk);
gcry_sexp_release (s_msg);
xfree (buffer);
xfree (buffer2);
xfree (sig_r);
xfree (sig_s);
xfree (sig_rs_string);
}
static void
check_ed448 (const char *fname)
{
FILE *fp;
int lineno, ntests;
char *line;
int testno;
int ph;
char *sk, *pk, *msg, *ctx, *sig;
info ("Checking Ed448.\n");
fp = fopen (fname, "r");
if (!fp)
die ("error opening '%s': %s\n", fname, strerror (errno));
testno = 0;
ph = 0;
sk = pk = msg = ctx = sig = NULL;
lineno = ntests = 0;
while ((line = read_textline (fp, &lineno)))
{
if (!strncmp (line, "TST:", 4))
testno = atoi (line+4);
else if (!strncmp (line, "PH:", 3))
ph = atoi (line+3);
else if (!strncmp (line, "SK:", 3))
copy_data (&sk, line, lineno);
else if (!strncmp (line, "PK:", 3))
copy_data (&pk, line, lineno);
else if (!strncmp (line, "MSG:", 4))
copy_data (&msg, line, lineno);
else if (!strncmp (line, "CTX:", 4))
copy_data (&ctx, line, lineno);
else if (!strncmp (line, "SIG:", 4))
copy_data (&sig, line, lineno);
else
fail ("unknown tag at input line %d", lineno);
xfree (line);
if (testno && sk && pk && msg && sig)
{
hexdowncase (sig);
one_test (testno, ph, sk, pk, msg, ctx, sig);
ntests++;
if (!(ntests % 256))
show_note ("%d of %d tests done\n", ntests, N_TESTS);
ph = 0;
xfree (pk); pk = NULL;
xfree (sk); sk = NULL;
xfree (msg); msg = NULL;
xfree (ctx); ctx = NULL;
xfree (sig); sig = NULL;
}
}
xfree (pk);
xfree (sk);
xfree (msg);
xfree (ctx);
xfree (sig);
if (ntests != N_TESTS && !custom_data_file)
fail ("did %d tests but expected %d", ntests, N_TESTS);
else if ((ntests % 256))
show_note ("%d tests done\n", ntests);
fclose (fp);
}
int
main (int argc, char **argv)
{
int last_argc = -1;
char *fname = NULL;
if (argc)
{ argc--; argv++; }
while (argc && last_argc != argc )
{
last_argc = argc;
if (!strcmp (*argv, "--"))
{
argc--; argv++;
break;
}
else if (!strcmp (*argv, "--help"))
{
fputs ("usage: " PGM " [options]\n"
"Options:\n"
" --verbose print timings etc.\n"
" --debug flyswatter\n"
" --sign-with-pk also use the public key for signing\n"
" --no-verify skip the verify test\n"
" --data FNAME take test data from file FNAME\n",
stdout);
exit (0);
}
else if (!strcmp (*argv, "--verbose"))
{
verbose++;
argc--; argv++;
}
else if (!strcmp (*argv, "--debug"))
{
verbose += 2;
debug++;
argc--; argv++;
}
else if (!strcmp (*argv, "--sign-with-pk"))
{
sign_with_pk = 1;
argc--; argv++;
}
else if (!strcmp (*argv, "--no-verify"))
{
no_verify = 1;
argc--; argv++;
}
else if (!strcmp (*argv, "--data"))
{
argc--; argv++;
if (argc)
{
xfree (fname);
fname = xstrdup (*argv);
argc--; argv++;
}
}
else if (!strncmp (*argv, "--", 2))
die ("unknown option '%s'", *argv);
}
if (!fname)
fname = prepend_srcdir ("t-ed448.inp");
else
custom_data_file = 1;
xgcry_control ((GCRYCTL_DISABLE_SECMEM, 0));
if (!gcry_check_version (GCRYPT_VERSION))
die ("version mismatch\n");
if (debug)
xgcry_control ((GCRYCTL_SET_DEBUG_FLAGS, 1u , 0));
xgcry_control ((GCRYCTL_ENABLE_QUICK_RANDOM, 0));
xgcry_control ((GCRYCTL_INITIALIZATION_FINISHED, 0));
- /* Ed448 isn't supported in fips mode */
- if (gcry_fips_mode_active())
- return 77;
+ if (gcry_fips_mode_active ())
+ in_fips_mode = 1;
start_timer ();
check_ed448 (fname);
stop_timer ();
xfree (fname);
info ("All tests completed in %s. Errors: %d\n",
elapsed_time (1), error_count);
return !!error_count;
}
diff --git a/tests/t-x448.c b/tests/t-x448.c
index 5c3cbeb9..cc4b10fc 100644
--- a/tests/t-x448.c
+++ b/tests/t-x448.c
@@ -1,593 +1,626 @@
/* t-x448.c - Check the X488 computation
* Copyright (C) 2019 g10 Code GmbH
*
* This file is part of Libgcrypt.
*
* Libgcrypt is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* Libgcrypt is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, see .
* SPDX-License-Identifier: LGPL-2.1+
*/
#ifdef HAVE_CONFIG_H
#include
#endif
#include
#include
#include
#include
#include
#include
#include "stopwatch.h"
#define PGM "t-x448"
#include "t-common.h"
#define N_TESTS 9
+static int in_fips_mode = 0;
static void
print_mpi (const char *text, gcry_mpi_t a)
{
gcry_error_t err;
char *buf;
void *bufaddr = &buf;
err = gcry_mpi_aprint (GCRYMPI_FMT_HEX, bufaddr, NULL, a);
if (err)
fprintf (stderr, "%s: [error printing number: %s]\n",
text, gpg_strerror (err));
else
{
fprintf (stderr, "%s: %s\n", text, buf);
gcry_free (buf);
}
}
static void
show_note (const char *format, ...)
{
va_list arg_ptr;
if (!verbose && getenv ("srcdir"))
fputs (" ", stderr); /* To align above "PASS: ". */
else
fprintf (stderr, "%s: ", PGM);
va_start (arg_ptr, format);
vfprintf (stderr, format, arg_ptr);
if (*format && format[strlen(format)-1] != '\n')
putc ('\n', stderr);
va_end (arg_ptr);
}
/* Convert STRING consisting of hex characters into its binary
representation and return it as an allocated buffer. The valid
length of the buffer is returned at R_LENGTH. The string is
delimited by end of string. The function returns NULL on
error. */
static void *
hex2buffer (const char *string, size_t *r_length)
{
const char *s;
unsigned char *buffer;
size_t length;
buffer = xmalloc (strlen(string)/2+1);
length = 0;
for (s=string; *s; s +=2 )
{
if (!hexdigitp (s) || !hexdigitp (s+1))
return NULL; /* Invalid hex digits. */
((unsigned char*)buffer)[length++] = xtoi_2 (s);
}
*r_length = length;
return buffer;
}
static void
reverse_buffer (unsigned char *buffer, unsigned int length)
{
unsigned int tmp, i;
for (i=0; i < length/2; i++)
{
tmp = buffer[i];
buffer[i] = buffer[length-1-i];
buffer[length-1-i] = tmp;
}
}
/*
* Test X448 functionality through higher layer crypto routines.
*
* Input: K (as hex string), U (as hex string), R (as hex string)
*
* where R is expected result of X448 (K, U).
*
*/
static void
test_cv_hl (int testno, const char *k_str, const char *u_str,
const char *result_str)
{
gpg_error_t err;
void *buffer = NULL;
size_t buflen;
gcry_sexp_t s_pk = NULL;
gcry_mpi_t mpi_k = NULL;
gcry_sexp_t s_data = NULL;
gcry_sexp_t s_result = NULL;
gcry_sexp_t s_tmp = NULL;
unsigned char *res = NULL;
size_t res_len;
if (verbose > 1)
info ("Running test %d\n", testno);
if (!(buffer = hex2buffer (k_str, &buflen)) || buflen != 56)
{
fail ("error building s-exp for test %d, %s: %s",
testno, "k", "invalid hex string");
goto leave;
}
mpi_k = gcry_mpi_set_opaque (NULL, buffer, buflen*8);
if ((err = gcry_sexp_build (&s_data, NULL, "%m", mpi_k)))
{
fail ("error building s-exp for test %d, %s: %s",
testno, "data", gpg_strerror (err));
goto leave;
}
if (!(buffer = hex2buffer (u_str, &buflen)) || buflen != 56)
{
fail ("error building s-exp for test %d, %s: %s",
testno, "u", "invalid hex string");
goto leave;
}
/*
* The procedure of decodeUCoordinate will be done internally
* by _gcry_ecc_mont_decodepoint. So, we just put the little-endian
* binary to build S-exp.
*
* We could add the prefix 0x40, but libgcrypt also supports
* format with no prefix. So, it is OK not to put the prefix.
*/
if ((err = gcry_sexp_build (&s_pk, NULL,
"(public-key"
" (ecc"
" (curve \"X448\")"
" (q%b)))", (int)buflen, buffer)))
{
fail ("error building s-exp for test %d, %s: %s",
testno, "pk", gpg_strerror (err));
goto leave;
}
xfree (buffer);
buffer = NULL;
- if ((err = gcry_pk_encrypt (&s_result, s_data, s_pk)))
- fail ("gcry_pk_encrypt failed for test %d: %s", testno,
+ err = gcry_pk_encrypt (&s_result, s_data, s_pk);
+ if (in_fips_mode)
+ {
+ if (!err)
+ fail ("gcry_pk_encrypt is not expected to work in FIPS mode for test %d",
+ testno);
+ if (verbose > 1)
+ info ("not executed in FIPS mode\n");
+ goto leave;
+ }
+ if (err)
+ fail ("gcry_pk_encrypt goto leavefailed for test %d: %s", testno,
gpg_strerror (err));
s_tmp = gcry_sexp_find_token (s_result, "s", 0);
if (!s_tmp || !(res = gcry_sexp_nth_buffer (s_tmp, 1, &res_len)))
fail ("gcry_pk_encrypt failed for test %d: %s", testno, "missing value");
else
{
char *r, *r0;
int i;
r0 = r = xmalloc (2*(res_len)+1);
if (!r0)
{
fail ("memory allocation for test %d", testno);
goto leave;
}
for (i=0; i < res_len; i++, r += 2)
snprintf (r, 3, "%02x", res[i]);
if (strcmp (result_str, r0))
{
fail ("gcry_pk_encrypt failed for test %d: %s",
testno, "wrong value returned");
info (" expected: '%s'", result_str);
info (" got: '%s'", r0);
}
xfree (r0);
}
leave:
xfree (res);
gcry_mpi_release (mpi_k);
gcry_sexp_release (s_tmp);
gcry_sexp_release (s_result);
gcry_sexp_release (s_data);
gcry_sexp_release (s_pk);
xfree (buffer);
}
/*
* Test X448 functionality through the API for X448.
*
* Input: K (as hex string), U (as hex string), R (as hex string)
*
* where R is expected result of X448 (K, U).
*
*/
static void
test_cv_x448 (int testno, const char *k_str, const char *u_str,
const char *result_str)
{
gpg_error_t err;
void *scalar;
void *point = NULL;
size_t buflen;
unsigned char result[56];
char result_hex[113];
int i;
if (verbose > 1)
info ("Running test %d\n", testno);
if (!(scalar = hex2buffer (k_str, &buflen)) || buflen != 56)
{
fail ("error building s-exp for test %d, %s: %s",
testno, "k", "invalid hex string");
goto leave;
}
if (!(point = hex2buffer (u_str, &buflen)) || buflen != 56)
{
fail ("error building s-exp for test %d, %s: %s",
testno, "u", "invalid hex string");
goto leave;
}
- if ((err = gcry_ecc_mul_point (GCRY_ECC_CURVE448, result, scalar, point)))
+ err = gcry_ecc_mul_point (GCRY_ECC_CURVE448, result, scalar, point);
+ if (in_fips_mode)
+ {
+ if (err != GPG_ERR_NOT_SUPPORTED)
+ fail ("gcry_ecc_mul_point is not expected to work in FIPS mode for test %d: %s",
+ testno, gpg_strerror (err));
+ if (verbose > 1)
+ info ("not executed in FIPS mode\n");
+ goto leave;
+ }
+ if (err)
fail ("gcry_ecc_mul_point failed for test %d: %s", testno,
gpg_strerror (err));
for (i=0; i < 56; i++)
snprintf (&result_hex[i*2], 3, "%02x", result[i]);
if (strcmp (result_str, result_hex))
{
fail ("gcry_ecc_mul_point failed for test %d: %s",
testno, "wrong value returned");
info (" expected: '%s'", result_str);
info (" got: '%s'", result_hex);
}
leave:
xfree (scalar);
xfree (point);
}
static void
test_cv (int testno, const char *k_str, const char *u_str,
const char *result_str)
{
test_cv_hl (testno, k_str, u_str, result_str);
test_cv_x448 (testno, k_str, u_str, result_str);
}
/*
* Test iterative X448 computation through lower layer MPI routines.
*
* Input: K (as hex string), ITER, R (as hex string)
*
* where R is expected result of iterating X448 by ITER times.
*
*/
static void
test_it (int testno, const char *k_str, int iter, const char *result_str)
{
- gcry_ctx_t ctx;
+ gcry_ctx_t ctx = NULL;
gpg_error_t err;
void *buffer = NULL;
size_t buflen;
gcry_mpi_t mpi_k = NULL;
gcry_mpi_t mpi_x = NULL;
gcry_mpi_point_t P = NULL;
gcry_mpi_point_t Q;
int i;
gcry_mpi_t mpi_kk = NULL;
if (verbose > 1)
info ("Running test %d: iteration=%d\n", testno, iter);
gcry_mpi_ec_new (&ctx, NULL, "X448");
+ if (in_fips_mode)
+ {
+ if (ctx)
+ fail ("gcry_mpi_ec_new should fail in FIPS mode for test %d",
+ testno);
+ if (verbose > 1)
+ info ("not executed in FIPS mode\n");
+ return;
+ }
Q = gcry_mpi_point_new (0);
if (!(buffer = hex2buffer (k_str, &buflen)) || buflen != 56)
{
fail ("error scanning MPI for test %d, %s: %s",
testno, "k", "invalid hex string");
goto leave;
}
reverse_buffer (buffer, buflen);
if ((err = gcry_mpi_scan (&mpi_x, GCRYMPI_FMT_USG, buffer, buflen, NULL)))
{
fail ("error scanning MPI for test %d, %s: %s",
testno, "x", gpg_strerror (err));
goto leave;
}
xfree (buffer);
buffer = NULL;
P = gcry_mpi_point_set (NULL, mpi_x, NULL, GCRYMPI_CONST_ONE);
mpi_k = gcry_mpi_copy (mpi_x);
if (debug)
print_mpi ("k", mpi_k);
for (i = 0; i < iter; i++)
{
/*
* Another variant of decodeScalar448 thing.
*/
mpi_kk = gcry_mpi_set (mpi_kk, mpi_k);
gcry_mpi_set_bit (mpi_kk, 447);
gcry_mpi_clear_bit (mpi_kk, 0);
gcry_mpi_clear_bit (mpi_kk, 1);
gcry_mpi_ec_mul (Q, mpi_kk, P, ctx);
P = gcry_mpi_point_set (P, mpi_k, NULL, GCRYMPI_CONST_ONE);
gcry_mpi_ec_get_affine (mpi_k, NULL, Q, ctx);
if (debug)
print_mpi ("k", mpi_k);
}
{
unsigned char res[56];
char *r, *r0;
gcry_mpi_print (GCRYMPI_FMT_USG, res, 56, NULL, mpi_k);
reverse_buffer (res, 56);
r0 = r = xmalloc (113);
if (!r0)
{
fail ("memory allocation for test %d", testno);
goto leave;
}
for (i=0; i < 56; i++, r += 2)
snprintf (r, 3, "%02x", res[i]);
if (strcmp (result_str, r0))
{
fail ("X448 failed for test %d: %s",
testno, "wrong value returned");
info (" expected: '%s'", result_str);
info (" got: '%s'", r0);
}
xfree (r0);
}
leave:
gcry_mpi_release (mpi_kk);
gcry_mpi_release (mpi_k);
gcry_mpi_point_release (P);
gcry_mpi_release (mpi_x);
xfree (buffer);
gcry_mpi_point_release (Q);
gcry_ctx_release (ctx);
}
/*
* X-coordinate of generator of the X448.
*/
#define G_X ("0500000000000000000000000000000000000000000000000000000000000000" \
"000000000000000000000000000000000000000000000000")
/*
* Test Diffie-Hellman in RFC-7748.
*
* Note that it's not like the ECDH of OpenPGP, where we use
* ephemeral public key.
*/
static void
test_dh (int testno, const char *a_priv_str, const char *a_pub_str,
const char *b_priv_str, const char *b_pub_str,
const char *result_str)
{
/* Test A for private key corresponds to public key. */
test_cv (testno, a_priv_str, G_X, a_pub_str);
/* Test B for private key corresponds to public key. */
test_cv (testno, b_priv_str, G_X, b_pub_str);
/* Test DH with A's private key and B's public key. */
test_cv (testno, a_priv_str, b_pub_str, result_str);
/* Test DH with B's private key and A's public key. */
test_cv (testno, b_priv_str, a_pub_str, result_str);
}
static void
check_x448 (void)
{
int ntests;
info ("Checking X448.\n");
ntests = 0;
/*
* Values are cited from RFC-7748: 5.2. Test Vectors.
* Following two tests are for the first type test.
*/
test_cv (1,
"3d262fddf9ec8e88495266fea19a34d28882acef045104d0d1aae121"
"700a779c984c24f8cdd78fbff44943eba368f54b29259a4f1c600ad3",
"06fce640fa3487bfda5f6cf2d5263f8aad88334cbd07437f020f08f9"
"814dc031ddbdc38c19c6da2583fa5429db94ada18aa7a7fb4ef8a086",
"ce3e4ff95a60dc6697da1db1d85e6afbdf79b50a2412d7546d5f239f"
"e14fbaadeb445fc66a01b0779d98223961111e21766282f73dd96b6f");
ntests++;
test_cv (2,
"203d494428b8399352665ddca42f9de8fef600908e0d461cb021f8c5"
"38345dd77c3e4806e25f46d3315c44e0a5b4371282dd2c8d5be3095f",
"0fbcc2f993cd56d3305b0b7d9e55d4c1a8fb5dbb52f8e9a1e9b6201b"
"165d015894e56c4d3570bee52fe205e28a78b91cdfbde71ce8d157db",
"884a02576239ff7a2f2f63b2db6a9ff37047ac13568e1e30fe63c4a7"
"ad1b3ee3a5700df34321d62077e63633c575c1c954514e99da7c179d");
ntests++;
/*
* Additional test. Value is from second type test.
*/
test_cv (3,
G_X,
G_X,
"3f482c8a9f19b01e6c46ee9711d9dc14fd4bf67af30765c2ae2b846a"
"4d23a8cd0db897086239492caf350b51f833868b9bc2b3bca9cf4113");
ntests++;
/*
* Following two tests are for the second type test,
* with one iteration and 1,000 iterations. (1,000,000 iterations
* takes too long.)
*/
test_it (4,
G_X,
1,
"3f482c8a9f19b01e6c46ee9711d9dc14fd4bf67af30765c2ae2b846a"
"4d23a8cd0db897086239492caf350b51f833868b9bc2b3bca9cf4113");
ntests++;
test_it (5,
G_X,
1000,
"aa3b4749d55b9daf1e5b00288826c467274ce3ebbdd5c17b975e09d4"
"af6c67cf10d087202db88286e2b79fceea3ec353ef54faa26e219f38");
ntests++;
/*
* Last test is from: 6. Diffie-Hellman, 6.2. Curve448
*/
test_dh (6,
/* Alice's private key, a */
"9a8f4925d1519f5775cf46b04b5800d4ee9ee8bae8bc5565d498c28d"
"d9c9baf574a9419744897391006382a6f127ab1d9ac2d8c0a598726b",
/* Alice's public key, X448(a, 5) */
"9b08f7cc31b7e3e67d22d5aea121074a273bd2b83de09c63faa73d2c"
"22c5d9bbc836647241d953d40c5b12da88120d53177f80e532c41fa0",
/* Bob's private key, b */
"1c306a7ac2a0e2e0990b294470cba339e6453772b075811d8fad0d1d"
"6927c120bb5ee8972b0d3e21374c9c921b09d1b0366f10b65173992d",
/* Bob's public key, X448(b, 5) */
"3eb7a829b0cd20f5bcfc0b599b6feccf6da4627107bdb0d4f345b430"
"27d8b972fc3e34fb4232a13ca706dcb57aec3dae07bdc1c67bf33609",
/* Their shared secret, K */
"07fff4181ac6cc95ec1c16a94a0f74d12da232ce40a77552281d282b"
"b60c0b56fd2464c335543936521c24403085d59a449a5037514a879d");
ntests++;
/* three tests which results 0. */
test_cv (7,
"3d262fddf9ec8e88495266fea19a34d28882acef045104d0d1aae121"
"700a779c984c24f8cdd78fbff44943eba368f54b29259a4f1c600ad3",
"00000000000000000000000000000000000000000000000000000000"
"00000000000000000000000000000000000000000000000000000000",
"00000000000000000000000000000000000000000000000000000000"
"00000000000000000000000000000000000000000000000000000000");
ntests++;
test_cv (8,
"3d262fddf9ec8e88495266fea19a34d28882acef045104d0d1aae121"
"700a779c984c24f8cdd78fbff44943eba368f54b29259a4f1c600ad3",
"01000000000000000000000000000000000000000000000000000000"
"00000000000000000000000000000000000000000000000000000000",
"00000000000000000000000000000000000000000000000000000000"
"00000000000000000000000000000000000000000000000000000000");
ntests++;
test_cv (9,
"3d262fddf9ec8e88495266fea19a34d28882acef045104d0d1aae121"
"700a779c984c24f8cdd78fbff44943eba368f54b29259a4f1c600ad3",
"feffffffffffffffffffffffffffffffffffffffffffffffffffffff"
"feffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"00000000000000000000000000000000000000000000000000000000"
"00000000000000000000000000000000000000000000000000000000");
ntests++;
if (ntests != N_TESTS)
fail ("did %d tests but expected %d", ntests, N_TESTS);
else if ((ntests % 256))
show_note ("%d tests done\n", ntests);
}
int
main (int argc, char **argv)
{
int last_argc = -1;
if (argc)
{ argc--; argv++; }
while (argc && last_argc != argc )
{
last_argc = argc;
if (!strcmp (*argv, "--"))
{
argc--; argv++;
break;
}
else if (!strcmp (*argv, "--help"))
{
fputs ("usage: " PGM " [options]\n"
"Options:\n"
" --verbose print timings etc.\n"
" --debug flyswatter\n",
stdout);
exit (0);
}
else if (!strcmp (*argv, "--verbose"))
{
verbose++;
argc--; argv++;
}
else if (!strcmp (*argv, "--debug"))
{
verbose += 2;
debug++;
argc--; argv++;
}
else if (!strncmp (*argv, "--", 2))
die ("unknown option '%s'", *argv);
}
xgcry_control ((GCRYCTL_DISABLE_SECMEM, 0));
if (!gcry_check_version (GCRYPT_VERSION))
die ("version mismatch\n");
if (debug)
xgcry_control ((GCRYCTL_SET_DEBUG_FLAGS, 1u , 0));
xgcry_control ((GCRYCTL_ENABLE_QUICK_RANDOM, 0));
xgcry_control ((GCRYCTL_INITIALIZATION_FINISHED, 0));
+ if (gcry_fips_mode_active ())
+ in_fips_mode = 1;
+
start_timer ();
check_x448 ();
stop_timer ();
info ("All tests completed in %s. Errors: %d\n",
elapsed_time (1), error_count);
return !!error_count;
}