Mailing List Archive

[PATCH] ecc: Add Curve448.
Hello,

I'm going to push this change to master, so that we can use Curve448
encryption by GnuPG in near future.


* cipher/ecc-curves.c (curve_aliases): Add Curve448.
(domain_parms): Add domain parameter for Curve448.
* cipher/ecc-ecdh.c (_gcry_ecc_mul_point): Support Curve448.
* mpi/ec.c (ec_addm_448, ec_subm_448, ec_mulm_448): New.
(ec_mul2_448, ec_pow2_448): New.
(field_table): Add field specific routines for Curve448.
(curve25519_bad_points): It's constants.
(curve448_bad_points): New.
(bad_points_table): New.
(ec_p_init): Initialize by bad_points_table.
* tests/Makefile.am (t-cv448): New.
* tests/t-cv448.c: New.
* tests/curves.c (N_CURVES): Increment.

--

This change is to add "Curve448". In libgcrypt and GnuPG, we have an
interface with a curve name, which was introduced before X25519/X448
function.

While OID 1.3.101.111 is for X448 function on the curve, we use it
to refer the curve itself.

Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
---
cipher/ecc-curves.c | 20 +-
cipher/ecc-ecdh.c | 3 +-
mpi/ec.c | 210 +++++++++++++++-
tests/Makefile.am | 3 +-
tests/curves.c | 2 +-
tests/t-cv448.c | 602 ++++++++++++++++++++++++++++++++++++++++++++
6 files changed, 829 insertions(+), 11 deletions(-)
create mode 100644 tests/t-cv448.c

diff --git a/cipher/ecc-curves.c b/cipher/ecc-curves.c
index 85f14eff..74d6b5e5 100644
--- a/cipher/ecc-curves.c
+++ b/cipher/ecc-curves.c
@@ -54,10 +54,10 @@ static const struct
{ "Ed448", "1.3.101.113" }, /* rfc8410 */

{ "X22519", "1.3.101.110" }, /* rfc8410 */
-
- { "X448", "1.3.101.111" }, /* rfc8410 */
#endif

+ { "Curve448", "1.3.101.111" }, /* X448 in rfc8410 */
+
{ "NIST P-192", "1.2.840.10045.3.1.1" }, /* X9.62 OID */
{ "NIST P-192", "prime192v1" }, /* X9.62 name. */
{ "NIST P-192", "secp192r1" }, /* SECP name. */
@@ -157,6 +157,22 @@ static const ecc_domain_parms_t domain_parms[] =
"0x5F51E65E475F794B1FE122D388B72EB36DC2B28192839E4DD6163A5D81312C14",
"0x08"
},
+ {
+ /* (y^2 = x^3 + 156326*x^2 + x) */
+ "Curve448", 448, 0,
+ MPI_EC_MONTGOMERY, ECC_DIALECT_STANDARD,
+ "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE"
+ "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
+ "0x98A9",
+ "0x01",
+ "0x3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+ "7CCA23E9C44EDB49AED63690216CC2728DC58F552378C292AB5844F3",
+ "0x00000000000000000000000000000000000000000000000000000000"
+ "00000000000000000000000000000000000000000000000000000005",
+ "0x7D235D1295F5B1F66C98AB6E58326FCECBAE5D34F55545D060F75DC2"
+ "8DF3F6EDB8027E2346430D211312C4B150677AF76FD7223D457B5B1A",
+ "0x04"
+ },
#if 0 /* No real specs yet found. */
{
/* x^2 + y^2 = 1 + 3617x^2y^2 mod 2^414 - 17 */
diff --git a/cipher/ecc-ecdh.c b/cipher/ecc-ecdh.c
index bfd07d40..405fc142 100644
--- a/cipher/ecc-ecdh.c
+++ b/cipher/ecc-ecdh.c
@@ -88,8 +88,7 @@ _gcry_ecc_mul_point (int algo, unsigned char *result,
else if (algo == GCRY_ECC_CURVE448)
{
nbits = ECC_CURVE448_BITS;
- curve = "X448";
- return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
+ curve = "Curve448";
}
else
return gpg_error (GPG_ERR_UNKNOWN_ALGORITHM);
diff --git a/mpi/ec.c b/mpi/ec.c
index 97afbfed..abdca997 100644
--- a/mpi/ec.c
+++ b/mpi/ec.c
@@ -366,7 +366,7 @@ mpih_set_cond (mpi_ptr_t wp, mpi_ptr_t up, mpi_size_t usize, unsigned long set)
wp[i] = wp[i] ^ x;
}
}
-
+
/* Routines for 2^255 - 19. */

#define LIMB_SIZE_25519 ((256+BITS_PER_MPI_LIMB-1)/BITS_PER_MPI_LIMB)
@@ -477,7 +477,167 @@ ec_pow2_25519 (gcry_mpi_t w, const gcry_mpi_t b, mpi_ec_t ctx)
{
ec_mulm_25519 (w, b, b, ctx);
}
+
+/* Routines for 2^448 - 2^224 - 1. */
+
+#define LIMB_SIZE_448 ((448+BITS_PER_MPI_LIMB-1)/BITS_PER_MPI_LIMB)
+#define LIMB_SIZE_HALF_448 ((LIMB_SIZE_448+1)/2)
+
+static void
+ec_addm_448 (gcry_mpi_t w, gcry_mpi_t u, gcry_mpi_t v, mpi_ec_t ctx)
+{
+ mpi_ptr_t wp, up, vp;
+ mpi_size_t wsize = LIMB_SIZE_448;
+ mpi_limb_t n[LIMB_SIZE_448];
+ mpi_limb_t cy;
+
+ if (w->nlimbs != wsize || u->nlimbs != wsize || v->nlimbs != wsize)
+ log_bug ("addm_448: different sizes\n");
+
+ memset (n, 0, sizeof n);
+ up = u->d;
+ vp = v->d;
+ wp = w->d;
+
+ cy = _gcry_mpih_add_n (wp, up, vp, wsize);
+ mpih_set_cond (n, ctx->p->d, wsize, (cy != 0UL));
+ _gcry_mpih_sub_n (wp, wp, n, wsize);
+}
+
+static void
+ec_subm_448 (gcry_mpi_t w, gcry_mpi_t u, gcry_mpi_t v, mpi_ec_t ctx)
+{
+ mpi_ptr_t wp, up, vp;
+ mpi_size_t wsize = LIMB_SIZE_448;
+ mpi_limb_t n[LIMB_SIZE_448];
+ mpi_limb_t borrow;
+
+ if (w->nlimbs != wsize || u->nlimbs != wsize || v->nlimbs != wsize)
+ log_bug ("subm_448: different sizes\n");
+
+ memset (n, 0, sizeof n);
+ up = u->d;
+ vp = v->d;
+ wp = w->d;
+
+ borrow = _gcry_mpih_sub_n (wp, up, vp, wsize);
+ mpih_set_cond (n, ctx->p->d, wsize, (borrow != 0UL));
+ _gcry_mpih_add_n (wp, wp, n, wsize);
+}
+
+static void
+ec_mulm_448 (gcry_mpi_t w, gcry_mpi_t u, gcry_mpi_t v, mpi_ec_t ctx)
+{
+ mpi_ptr_t wp, up, vp;
+ mpi_size_t wsize = LIMB_SIZE_448;
+ mpi_limb_t n[LIMB_SIZE_448*2];
+ mpi_limb_t a2[LIMB_SIZE_HALF_448];
+ mpi_limb_t a3[LIMB_SIZE_HALF_448];
+ mpi_limb_t b0[LIMB_SIZE_HALF_448];
+ mpi_limb_t b1[LIMB_SIZE_HALF_448];
+ mpi_limb_t cy;
+ int i;
+#if (LIMB_SIZE_HALF_448 > LIMB_SIZE_448/2)
+ mpi_limb_t b1_rest, a3_rest;
+#endif
+
+ if (w->nlimbs != wsize || u->nlimbs != wsize || v->nlimbs != wsize)
+ log_bug ("mulm_448: different sizes\n");
+
+ up = u->d;
+ vp = v->d;
+ wp = w->d;
+
+ _gcry_mpih_mul_n (n, up, vp, wsize);
+
+ for (i = 0; i < (wsize + 1)/ 2; i++)
+ {
+ b0[i] = n[i];
+ b1[i] = n[i+wsize/2];
+ a2[i] = n[i+wsize];
+ a3[i] = n[i+wsize+wsize/2];
+ }
+
+#if (LIMB_SIZE_HALF_448 > LIMB_SIZE_448/2)
+ b0[LIMB_SIZE_HALF_448-1] &= (1UL<<32)-1;
+ a2[LIMB_SIZE_HALF_448-1] &= (1UL<<32)-1;
+
+ b1_rest = 0;
+ a3_rest = 0;
+
+ for (i = (wsize + 1)/ 2 -1; i >= 0; i--)
+ {
+ mpi_limb_t b1v, a3v;
+ b1v = b1[i];
+ a3v = a3[i];
+ b1[i] = (b1_rest<<32) | (b1v >> 32);
+ a3[i] = (a3_rest<<32) | (a3v >> 32);
+ b1_rest = b1v & ((1UL <<32)-1);
+ a3_rest = a3v & ((1UL <<32)-1);
+ }
+#endif
+
+ cy = _gcry_mpih_add_n (b0, b0, a2, LIMB_SIZE_HALF_448);
+ cy += _gcry_mpih_add_n (b0, b0, a3, LIMB_SIZE_HALF_448);
+ for (i = 0; i < (wsize + 1)/ 2; i++)
+ wp[i] = b0[i];
+#if (LIMB_SIZE_HALF_448 > LIMB_SIZE_448/2)
+ wp[LIMB_SIZE_HALF_448-1] &= ((1UL <<32)-1);
+#endif
+
+#if (LIMB_SIZE_HALF_448 > LIMB_SIZE_448/2)
+ cy = b0[LIMB_SIZE_HALF_448-1] >> 32;
+#endif
+
+ cy = _gcry_mpih_add_1 (b1, b1, LIMB_SIZE_HALF_448, cy);
+ cy += _gcry_mpih_add_n (b1, b1, a2, LIMB_SIZE_HALF_448);
+ cy += _gcry_mpih_add_n (b1, b1, a3, LIMB_SIZE_HALF_448);
+ cy += _gcry_mpih_add_n (b1, b1, a3, LIMB_SIZE_HALF_448);
+#if (LIMB_SIZE_HALF_448 > LIMB_SIZE_448/2)
+ b1_rest = 0;
+ for (i = (wsize + 1)/ 2 -1; i >= 0; i--)
+ {
+ mpi_limb_t b1v = b1[i];
+ b1[i] = (b1_rest<<32) | (b1v >> 32);
+ b1_rest = b1v & ((1UL <<32)-1);
+ }
+ wp[LIMB_SIZE_HALF_448-1] |= (b1_rest << 32);
+#endif
+ for (i = 0; i < wsize / 2; i++)
+ wp[i+(wsize + 1) / 2] = b1[i];
+
+#if (LIMB_SIZE_HALF_448 > LIMB_SIZE_448/2)
+ cy = b1[LIMB_SIZE_HALF_448-1];
+#endif
+
+ memset (n, 0, wsize * BYTES_PER_MPI_LIMB);
+
+#if (LIMB_SIZE_HALF_448 > LIMB_SIZE_448/2)
+ n[LIMB_SIZE_HALF_448-1] = cy << 32;
+#else
+ n[LIMB_SIZE_HALF_448] = cy;
+#endif
+ n[0] = cy;
+ _gcry_mpih_add_n (wp, wp, n, wsize);
+
+ memset (n, 0, wsize * BYTES_PER_MPI_LIMB);
+ cy = _gcry_mpih_sub_n (wp, wp, ctx->p->d, wsize);
+ mpih_set_cond (n, ctx->p->d, wsize, (cy != 0UL));
+ _gcry_mpih_add_n (wp, wp, n, wsize);
+}

+static void
+ec_mul2_448 (gcry_mpi_t w, gcry_mpi_t u, mpi_ec_t ctx)
+{
+ ec_addm_448 (w, u, u, ctx);
+}
+
+static void
+ec_pow2_448 (gcry_mpi_t w, const gcry_mpi_t b, mpi_ec_t ctx)
+{
+ ec_mulm_448 (w, b, b, ctx);
+}
+
struct field_table {
const char *p;

@@ -498,6 +658,15 @@ static const struct field_table field_table[] = {
ec_mul2_25519,
ec_pow2_25519
},
+ {
+ "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE"
+ "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
+ ec_addm_448,
+ ec_subm_448,
+ ec_mulm_448,
+ ec_mul2_448,
+ ec_pow2_448
+ },
{ NULL, NULL, NULL, NULL, NULL, NULL },
};

@@ -544,17 +713,37 @@ ec_get_two_inv_p (mpi_ec_t ec)
}


-static const char *curve25519_bad_points[] = {
+static const char *const curve25519_bad_points[] = {
+ "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000001",
"0x00b8495f16056286fdb1329ceb8d09da6ac49ff1fae35616aeb8413b7c7aebe0",
"0x57119fd0dd4e22d8868e1c58c45c44045bef839c55b1d0b1248c50a3bc959c5f",
"0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec",
- "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed",
"0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffee",
NULL
};

+
+static const char *const curve448_bad_points[] = {
+ "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffe"
+ "ffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
+ "0x00000000000000000000000000000000000000000000000000000000"
+ "00000000000000000000000000000000000000000000000000000000",
+ "0x00000000000000000000000000000000000000000000000000000000"
+ "00000000000000000000000000000000000000000000000000000001",
+ "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffe"
+ "fffffffffffffffffffffffffffffffffffffffffffffffffffffffe",
+ "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "00000000000000000000000000000000000000000000000000000000",
+ NULL
+};
+
+static const char *const *bad_points_table[] = {
+ curve25519_bad_points,
+ curve448_bad_points,
+};
+
static gcry_mpi_t
scanval (const char *string)
{
@@ -607,8 +796,19 @@ ec_p_init (mpi_ec_t ctx, enum gcry_mpi_ec_models model,

if (model == MPI_EC_MONTGOMERY)
{
- for (i=0; i< DIM(ctx->t.scratch) && curve25519_bad_points[i]; i++)
- ctx->t.scratch[i] = scanval (curve25519_bad_points[i]);
+ for (i=0; i< DIM(bad_points_table); i++)
+ {
+ gcry_mpi_t p_candidate = scanval (bad_points_table[i][0]);
+ int match_p = !mpi_cmp (ctx->p, p_candidate);
+ int j;
+
+ mpi_free (p_candidate);
+ if (!match_p)
+ continue;
+
+ for (j=0; i< DIM(ctx->t.scratch) && bad_points_table[i][j]; j++)
+ ctx->t.scratch[j] = scanval (bad_points_table[i][j]);
+ }
}
else
{
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 9e117970..2ae70e54 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -22,7 +22,8 @@ tests_bin = \
version t-secmem mpitests t-sexp t-convert \
t-mpi-bit t-mpi-point curves t-lock \
prime basic keygen pubkey hmac hashtest t-kdf keygrip \
- fips186-dsa aeswrap pkcs1v2 random dsa-rfc6979 t-ed25519 t-cv25519
+ fips186-dsa aeswrap pkcs1v2 random dsa-rfc6979 \
+ t-ed25519 t-cv25519 t-cv448

tests_bin_last = benchmark bench-slope

diff --git a/tests/curves.c b/tests/curves.c
index dc5ebe77..bacc1302 100644
--- a/tests/curves.c
+++ b/tests/curves.c
@@ -33,7 +33,7 @@
#include "t-common.h"

/* Number of curves defined in ../cipger/ecc.c */
-#define N_CURVES 22
+#define N_CURVES 23

/* A real world sample public key. */
static char const sample_key_1[] =
diff --git a/tests/t-cv448.c b/tests/t-cv448.c
new file mode 100644
index 00000000..85fea298
--- /dev/null
+++ b/tests/t-cv448.c
@@ -0,0 +1,602 @@
+/* t-cv448.c - Check the Curve488 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 <https://www.gnu.org/licenses/>.
+ * SPDX-License-Identifier: LGPL-2.1+
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdarg.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "stopwatch.h"
+
+#define PGM "t-cv448"
+#include "t-common.h"
+#define N_TESTS 9
+
+
+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;
+ }
+
+ 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 != 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 \"Curve448\")"
+ " (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)))
+ 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 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)))
+ 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;
+ 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, "Curve448");
+ 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));
+
+ start_timer ();
+ check_x448 ();
+ stop_timer ();
+
+ info ("All tests completed in %s. Errors: %d\n",
+ elapsed_time (1), error_count);
+ return !!error_count;
+}
--
2.20.1

_______________________________________________
Gcrypt-devel mailing list
Gcrypt-devel@gnupg.org
http://lists.gnupg.org/mailman/listinfo/gcrypt-devel
Re: [PATCH] ecc: Add Curve448. [ In reply to ]
On Wed, 25 Sep 2019 20:15, gniibe@fsij.org said:

> I'm going to push this change to master, so that we can use Curve448
> encryption by GnuPG in near future.

:-)

> - { "X448", "1.3.101.111" }, /* rfc8410 */
> #endif
>
> + { "Curve448", "1.3.101.111" }, /* X448 in rfc8410 */

Would you mind to briefly explain the name change?


Shalom-Salam,

Werner

--
Die Gedanken sind frei. Ausnahmen regelt ein Bundesgesetz.
Re: [PATCH] ecc: Add Curve448. [ In reply to ]
Werner Koch wrote:
>> - { "X448", "1.3.101.111" }, /* rfc8410 */
>> #endif
>>
>> + { "Curve448", "1.3.101.111" }, /* X448 in rfc8410 */
>
> Would you mind to briefly explain the name change?

Well, this is important point. In this patch, I kind of abuse an OID
which is defined in RFC8410 for X448 algorithm.

Perhaps, ideally for OpenPGP, we would need an independent OID for the
curve.

For other curves (than Curve448), an OID represents a specific curve.

In RFC8410, (IIUC), an OID is assigned to an algorithm (signature
algorithm, or key-agreement algorithm). It doesn not represent
a curve. That's my understanding. Please correct if I am wrong.

The name change is needed, I suppose, because key for ECDH and ECDH
packet in OpenPGP will be in big-endian, if we follow the existing way
with ECC curves (NIST ones, Brainpool ones, and Curve25519). Note that
for Curve25519, it is not in native format of X25519. So, I intend that
it will be same, that is, it will not be in native format of X448.

(How come? That's because when ECDH with Curve25519 was implemented for
OpenPGP, there was no X25519 defined.)


It is also possible to introduce new thing for X448. We can define X448
key and ECDH with X448 in OpenPGP, in the native format of X448.

I can change the patch accordingly, if the latter is preferred.

I'm open to any changes.
--

_______________________________________________
Gcrypt-devel mailing list
Gcrypt-devel@gnupg.org
http://lists.gnupg.org/mailman/listinfo/gcrypt-devel
Re: [PATCH] ecc: Add Curve448. [ In reply to ]
On Thu 2019-09-26 18:19:23 +0900, Niibe Yutaka wrote:
> In RFC8410, (IIUC), an OID is assigned to an algorithm (signature
> algorithm, or key-agreement algorithm). It doesn not represent
> a curve. That's my understanding. Please correct if I am wrong.

I believe this is correct.

> The name change is needed, I suppose, because key for ECDH and ECDH
> packet in OpenPGP will be in big-endian, if we follow the existing way
> with ECC curves (NIST ones, Brainpool ones, and Curve25519). Note that
> for Curve25519, it is not in native format of X25519. So, I intend that
> it will be same, that is, it will not be in native format of X448.

I'm not convinced that this kind of consistency is the right way to go.

In the world beyond gcrypt, points on goldilocks (curve 448) and curve
25519 have canonical a bytestring representation used by every other
implementation. Requiring consistency *within* gcrypt between 25519 and
448 means requiring inconsistency between gcrypt and the larger world
for 448. I'd rather see gcrypt use the standard representations for
as many points as possible.

> It is also possible to introduce new thing for X448. We can define X448
> key and ECDH with X448 in OpenPGP, in the native format of X448.
>
> I can change the patch accordingly, if the latter is preferred.

I lean toward using the "native" format where possible, even if that
leaves 25519 as an outlier.

--dkg
Re: [PATCH] ecc: Add Curve448. [ In reply to ]
On Mon, 30 Sep 2019 19:26, Daniel Kahn Gillmor said:

> I lean toward using the "native" format where possible, even if that
> leaves 25519 as an outlier.

I tend to agree.


Salam-Shalom,

Werner

--
Die Gedanken sind frei. Ausnahmen regelt ein Bundesgesetz.
Re: [PATCH] ecc: Add Curve448. [ In reply to ]
Hello, again,

Thanks for your suggestions.

Daniel Kahn Gillmor <dkg@fifthhorseman.net> wrote:
> I'm not convinced that this kind of consistency is the right way to go.

I see your point. If I could allow to make an excuse, my initial
motivation was minimizing the patch.

Anyway, I concur.

Thus, considering again, I'm going to modify my patch to use native
format of X448 to represent point (with the prefix 0x40), using OID of
X448.

Probably, for libgcrypt, I'll introduce new internal flag,

ECC_DIALECT_LITTLE_ENDIAN

to express that the curve prefers/requires non-default serialization
format (from the view point of existing curves of libgcrypt). Then,
code paths will be more cleaner.


Well, it reminds me about a virtue of good baseball umpire;
Don't keep further misjudgments for your own consistency.
Or, I whould quote a phrase in Chapter I of the first book of Confucius.

Thank you, as always.
--

_______________________________________________
Gcrypt-devel mailing list
Gcrypt-devel@gnupg.org
http://lists.gnupg.org/mailman/listinfo/gcrypt-devel
Re: [PATCH] ecc: Add Curve448. [ In reply to ]
On Thu 2019-10-03 15:39:46 +0900, NIIBE Yutaka wrote:
> Hello, again,
>
> Thanks for your suggestions.
>
> Daniel Kahn Gillmor <dkg@fifthhorseman.net> wrote:
>> I'm not convinced that this kind of consistency is the right way to go.
>
> I see your point. If I could allow to make an excuse, my initial
> motivation was minimizing the patch.
>
> Anyway, I concur.
>
> Thus, considering again, I'm going to modify my patch to use native
> format of X448 to represent point (with the prefix 0x40), using OID of
> X448.
>
> Probably, for libgcrypt, I'll introduce new internal flag,
>
> ECC_DIALECT_LITTLE_ENDIAN
>
> to express that the curve prefers/requires non-default serialization
> format (from the view point of existing curves of libgcrypt). Then,
> code paths will be more cleaner.

It occurs to me that if we want consistency within some future version
of gcrypt, we could introduce the same codepath for 25519 in its native
representation -- so that the invoker could use either representation.
Then we could somehow deprecate the older representation, and finally
remove it on the next major API break.

I'm not sure how to do that deprecation and removal effectively and
safely, given gcrypt's string-driven interface, but if anyone with
deeper understanding of gcrypt's API design and evolution wants to
suggest a deprecation mechanism, i'd be happy to review it.

> Well, it reminds me about a virtue of good baseball umpire;
> Don't keep further misjudgments for your own consistency.
> Or, I whould quote a phrase in Chapter I of the first book of Confucius.

:)

--dkg
Re: [PATCH] ecc: Add Curve448. [ In reply to ]
On Thu, 3 Oct 2019 06:33, Daniel Kahn Gillmor said:

> I'm not sure how to do that deprecation and removal effectively and

There won't be any deprecation of an already Libgcrypt provided curve;
if something else is required a new curve needs to be added.


Shalom-Salam,

Werner

--
Die Gedanken sind frei. Ausnahmen regelt ein Bundesgesetz.
Re: [PATCH] ecc: Add Curve448. [ In reply to ]
Hello,

It's not yet finished, but I post update of my work.

I modified my patch so that it's more friendly to X488 API.
That is:

* A scalar is fixed-size little-endian.
* A point is represented in fixed-size little-endian.
(with no prefix)

I think that current libgcrypt + GnuPG implementation have an issue
around handling of those scalar and point as an MPI. I'll describe
this issue in another mail.

--------------------------
diff --git a/cipher/ecc-curves.c b/cipher/ecc-curves.c
index 4a3139c3..1e0d3e55 100644
--- a/cipher/ecc-curves.c
+++ b/cipher/ecc-curves.c
@@ -54,9 +54,9 @@ static const struct
{ "Ed448", "1.3.101.113" }, /* rfc8410 */

{ "X22519", "1.3.101.110" }, /* rfc8410 */
+#endif

{ "X448", "1.3.101.111" }, /* rfc8410 */
-#endif

{ "NIST P-192", "1.2.840.10045.3.1.1" }, /* X9.62 OID */
{ "NIST P-192", "prime192v1" }, /* X9.62 name. */
@@ -162,6 +162,22 @@ static const ecc_domain_parms_t domain_parms[] =
* the function _gcry_ecc_fill_in_curve.
*/
},
+ {
+ /* (y^2 = x^3 + 156326*x^2 + x) */
+ "X448", 448, 0,
+ MPI_EC_MONTGOMERY, ECC_DIALECT_SAFECURVE,
+ "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE"
+ "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
+ "0x98A9",
+ "0x01",
+ "0x3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+ "7CCA23E9C44EDB49AED63690216CC2728DC58F552378C292AB5844F3",
+ "0x00000000000000000000000000000000000000000000000000000000"
+ "00000000000000000000000000000000000000000000000000000005",
+ "0x7D235D1295F5B1F66C98AB6E58326FCECBAE5D34F55545D060F75DC2"
+ "8DF3F6EDB8027E2346430D211312C4B150677AF76FD7223D457B5B1A",
+ "0x04"
+ },
#if 0 /* No real specs yet found. */
{
/* x^2 + y^2 = 1 + 3617x^2y^2 mod 2^414 - 17 */
diff --git a/cipher/ecc-ecdh.c b/cipher/ecc-ecdh.c
index bfd07d40..dbf0491e 100644
--- a/cipher/ecc-ecdh.c
+++ b/cipher/ecc-ecdh.c
@@ -38,13 +38,18 @@ static mpi_ec_t
prepare_ec (const char *curve_name, elliptic_curve_t *E)
{
mpi_ec_t ec;
+ int flags = 0;

memset (E, 0, sizeof *E);
if (_gcry_ecc_fill_in_curve (0, curve_name, E, NULL))
return NULL;

+ if (E->dialect != ECC_DIALECT_SAFECURVE)
+ flags = PUBKEY_FLAG_DJB_TWEAK;
+
ec = _gcry_mpi_ec_p_internal_new (E->model, E->dialect,
- PUBKEY_FLAG_DJB_TWEAK, E->p, E->a, E->b);
+ flags, E->p, E->a, E->b);
+ ec->h = mpi_copy (E->h);
return ec;
}

@@ -89,26 +94,30 @@ _gcry_ecc_mul_point (int algo, unsigned char *result,
{
nbits = ECC_CURVE448_BITS;
curve = "X448";
- return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
}
else
return gpg_error (GPG_ERR_UNKNOWN_ALGORITHM);

nbytes = nbits / 8;

- mpi_k = mpi_new (nbits);
ec = prepare_ec (curve, &E);
mpi_u = mpi_new (nbits);
Q = mpi_point_new (nbits);
x = mpi_new (nbits);

memcpy (buffer, scalar, nbytes);
- reverse_buffer (buffer, nbytes);
- _gcry_mpi_set_buffer (mpi_k, buffer, nbytes, 0);
+ if (algo == GCRY_ECC_CURVE25519)
+ {
+ mpi_k = mpi_new (nbits);
+ reverse_buffer (buffer, nbytes);
+ _gcry_mpi_set_buffer (mpi_k, buffer, nbytes, 0);

- for (i = 0; i < mpi_get_nbits (E.h) - 1; i++)
- mpi_clear_bit (mpi_k, i);
- mpi_set_highbit (mpi_k, mpi_get_nbits (E.p) - 1);
+ for (i = 0; i < mpi_get_nbits (E.h) - 1; i++)
+ mpi_clear_bit (mpi_k, i);
+ mpi_set_highbit (mpi_k, mpi_get_nbits (E.p) - 1);
+ }
+ else
+ mpi_k = _gcry_mpi_set_opaque_copy (NULL, buffer, nbytes*8);

if (point)
{
diff --git a/cipher/ecc-misc.c b/cipher/ecc-misc.c
index 8cdfec62..39a6581d 100644
--- a/cipher/ecc-misc.c
+++ b/cipher/ecc-misc.c
@@ -98,6 +98,7 @@ _gcry_ecc_dialect2str (enum ecc_dialects dialect)
{
case ECC_DIALECT_STANDARD: str = "Standard"; break;
case ECC_DIALECT_ED25519: str = "Ed25519"; break;
+ case ECC_DIALECT_SAFECURVE: str = "SafeCurve"; break;
}
return str;
}
diff --git a/cipher/ecc.c b/cipher/ecc.c
index 96e267fb..cedcbe8f 100644
--- a/cipher/ecc.c
+++ b/cipher/ecc.c
@@ -161,21 +161,29 @@ nist_generate_key (ECC_secret_key *sk, elliptic_curve_t *E, mpi_ec_t ctx,
random_level = GCRY_VERY_STRONG_RANDOM;

/* Generate a secret. */
- if (ctx->dialect == ECC_DIALECT_ED25519 || (flags & PUBKEY_FLAG_DJB_TWEAK))
+ if (ctx->dialect == ECC_DIALECT_ED25519
+ || ctx->dialect == ECC_DIALECT_SAFECURVE
+ || (flags & PUBKEY_FLAG_DJB_TWEAK))
{
char *rndbuf;
int len = (pbits+7)/8;
- unsigned int h;

- mpi_get_ui (&h, E->h);
- sk->d = mpi_snew (pbits);
rndbuf = _gcry_random_bytes_secure (len, random_level);
- if ((pbits % 8))
- rndbuf[0] &= (1 << (pbits % 8)) - 1;
- rndbuf[0] |= (1 << ((pbits + 7) % 8));
- rndbuf[(pbits-1)/8] &= (256 - h);
- _gcry_mpi_set_buffer (sk->d, rndbuf, len, 0);
- xfree (rndbuf);
+ if (ctx->dialect == ECC_DIALECT_SAFECURVE)
+ sk->d = mpi_set_opaque (NULL, rndbuf, len*8);
+ else
+ {
+ unsigned int h;
+
+ mpi_get_ui (&h, E->h);
+ sk->d = mpi_snew (pbits);
+ if ((pbits % 8))
+ rndbuf[0] &= (1 << (pbits % 8)) - 1;
+ rndbuf[0] |= (1 << ((pbits + 7) % 8));
+ rndbuf[(pbits-1)/8] &= (256 - h);
+ _gcry_mpi_set_buffer (sk->d, rndbuf, len, 0);
+ xfree (rndbuf);
+ }
}
else
sk->d = _gcry_dsa_gen_k (E->n, random_level);
@@ -342,22 +350,29 @@ test_ecdh_only_keys (ECC_secret_key *sk, unsigned int nbits, int flags)
point_init (&pk.Q);
point_set (&pk.Q, &sk->Q);

- if ((flags & PUBKEY_FLAG_DJB_TWEAK))
+ if (sk->E.dialect == ECC_DIALECT_SAFECURVE
+ || (flags & PUBKEY_FLAG_DJB_TWEAK))
{
char *rndbuf;
const unsigned int pbits = mpi_get_nbits (sk->E.p);
int len = (pbits+7)/8;
- unsigned int h;

- mpi_get_ui (&h, sk->E.h);
- test = mpi_new (pbits);
rndbuf = _gcry_random_bytes (len, GCRY_WEAK_RANDOM);
- if ((pbits % 8))
- rndbuf[0] &= (1 << (pbits % 8)) - 1;
- rndbuf[0] |= (1 << ((pbits + 7) % 8));
- rndbuf[(pbits-1)/8] &= (256 - h);
- _gcry_mpi_set_buffer (test, rndbuf, len, 0);
- xfree (rndbuf);
+ if (sk->E.dialect == ECC_DIALECT_SAFECURVE)
+ test = mpi_set_opaque (NULL, rndbuf, len*8);
+ else
+ {
+ unsigned int h;
+
+ mpi_get_ui (&h, sk->E.h);
+ test = mpi_new (pbits);
+ if ((pbits % 8))
+ rndbuf[0] &= (1 << (pbits % 8)) - 1;
+ rndbuf[0] |= (1 << ((pbits + 7) % 8));
+ rndbuf[(pbits-1)/8] &= (256 - h);
+ _gcry_mpi_set_buffer (test, rndbuf, len, 0);
+ xfree (rndbuf);
+ }
}
else
{
@@ -372,7 +387,7 @@ test_ecdh_only_keys (ECC_secret_key *sk, unsigned int nbits, int flags)

/* R_ = hkQ <=> R_ = hkdG */
_gcry_mpi_ec_mul_point (&R_, test, &pk.Q, ec);
- if (!(flags & PUBKEY_FLAG_DJB_TWEAK))
+ if (pk.E.dialect == ECC_DIALECT_STANDARD && !(flags & PUBKEY_FLAG_DJB_TWEAK))
_gcry_mpi_ec_mul_point (&R_, ec->h, &R_, ec);
if (_gcry_mpi_ec_get_affine (x0, NULL, &R_, ec))
log_fatal ("ecdh: Failed to get affine coordinates for hkQ\n");
@@ -380,7 +395,7 @@ test_ecdh_only_keys (ECC_secret_key *sk, unsigned int nbits, int flags)
_gcry_mpi_ec_mul_point (&R_, test, &pk.E.G, ec);
_gcry_mpi_ec_mul_point (&R_, sk->d, &R_, ec);
/* R_ = hdkG */
- if (!(flags & PUBKEY_FLAG_DJB_TWEAK))
+ if (pk.E.dialect == ECC_DIALECT_STANDARD && !(flags & PUBKEY_FLAG_DJB_TWEAK))
_gcry_mpi_ec_mul_point (&R_, ec->h, &R_, ec);

if (_gcry_mpi_ec_get_affine (x1, NULL, &R_, ec))
@@ -602,6 +617,7 @@ ecc_generate (const gcry_sexp_t genparms, gcry_sexp_t *r_skey)
}

ctx = _gcry_mpi_ec_p_internal_new (E.model, E.dialect, flags, E.p, E.a, E.b);
+ ctx->h = mpi_copy (E.h);

if (E.model == MPI_EC_MONTGOMERY)
rc = nist_generate_key (&sk, &E, ctx, flags, nbits, &Qx, NULL);
@@ -628,7 +644,9 @@ ecc_generate (const gcry_sexp_t genparms, gcry_sexp_t *r_skey)
unsigned int encpklen;

if (E.model == MPI_EC_MONTGOMERY)
- rc = _gcry_ecc_mont_encodepoint (Qx, nbits, 1, &encpk, &encpklen);
+ rc = _gcry_ecc_mont_encodepoint (Qx, nbits,
+ sk.E.dialect != ECC_DIALECT_SAFECURVE,
+ &encpk, &encpklen);
else
/* (Gx and Gy are used as scratch variables) */
rc = _gcry_ecc_eddsa_encodepoint (&sk.Q, ctx, Gx, Gy,
@@ -765,12 +783,11 @@ ecc_check_secret_key (gcry_sexp_t keyparms)

/* Extract the parameters. */
if ((flags & PUBKEY_FLAG_PARAM))
- rc = sexp_extract_param (keyparms, NULL, "-p?a?b?g?n?h?/q?+d",
+ rc = sexp_extract_param (keyparms, NULL, "-p?a?b?g?n?h?/q?",
&sk.E.p, &sk.E.a, &sk.E.b, &mpi_g, &sk.E.n,
- &sk.E.h, &mpi_q, &sk.d, NULL);
+ &sk.E.h, &mpi_q, NULL);
else
- rc = sexp_extract_param (keyparms, NULL, "/q?+d",
- &mpi_q, &sk.d, NULL);
+ rc = sexp_extract_param (keyparms, NULL, "/q?", &mpi_q, NULL);
if (rc)
goto leave;

@@ -807,8 +824,16 @@ ecc_check_secret_key (gcry_sexp_t keyparms)
? ECC_DIALECT_ED25519
: ECC_DIALECT_STANDARD);
if (!sk.E.h)
- sk.E.h = mpi_const (MPI_C_ONE);
+ sk.E.h = mpi_const (MPI_C_ONE);
}
+
+ if (sk.E.dialect == ECC_DIALECT_SAFECURVE)
+ rc = sexp_extract_param (keyparms, NULL, "/d", &sk.d, NULL);
+ else
+ rc = sexp_extract_param (keyparms, NULL, "+d", &sk.d, NULL);
+ if (rc)
+ goto leave;
+
if (DBG_CIPHER)
{
log_debug ("ecc_testkey info: %s/%s\n",
@@ -834,6 +859,7 @@ ecc_check_secret_key (gcry_sexp_t keyparms)

ec = _gcry_mpi_ec_p_internal_new (sk.E.model, sk.E.dialect, flags,
sk.E.p, sk.E.a, sk.E.b);
+ ec->h = mpi_copy (sk.E.h);

if (mpi_q)
{
@@ -939,7 +965,7 @@ ecc_sign (gcry_sexp_t *r_sig, gcry_sexp_t s_data, gcry_sexp_t keyparms)
? ECC_DIALECT_ED25519
: ECC_DIALECT_STANDARD);
if (!sk.E.h)
- sk.E.h = mpi_const (MPI_C_ONE);
+ sk.E.h = mpi_const (MPI_C_ONE);
}
if (DBG_CIPHER)
{
@@ -1107,7 +1133,7 @@ ecc_verify (gcry_sexp_t s_sig, gcry_sexp_t s_data, gcry_sexp_t s_keyparms)
? ECC_DIALECT_ED25519
: ECC_DIALECT_STANDARD);
if (!pk.E.h)
- pk.E.h = mpi_const (MPI_C_ONE);
+ pk.E.h = mpi_const (MPI_C_ONE);
}

if (DBG_CIPHER)
@@ -1261,6 +1287,7 @@ ecc_encrypt_raw (gcry_sexp_t *r_ciph, gcry_sexp_t s_data, gcry_sexp_t keyparms)
ECC_public_key pk;
mpi_ec_t ec = NULL;
int flags = 0;
+ int safecurve;

memset (&pk, 0, sizeof pk);
_gcry_pk_util_init_encoding_ctx (&ctx, PUBKEY_OP_ENCRYPT,
@@ -1277,18 +1304,6 @@ ecc_encrypt_raw (gcry_sexp_t *r_ciph, gcry_sexp_t s_data, gcry_sexp_t keyparms)
sexp_release (l1);
l1 = NULL;

- /*
- * Extract the data.
- */
- rc = _gcry_pk_util_data_to_mpi (s_data, &data, &ctx);
- if (rc)
- goto leave;
- if (mpi_is_opaque (data))
- {
- rc = GPG_ERR_INV_DATA;
- goto leave;
- }
-
/*
* Extract the key.
*/
@@ -1307,22 +1322,42 @@ ecc_encrypt_raw (gcry_sexp_t *r_ciph, gcry_sexp_t s_data, gcry_sexp_t keyparms)
/* Add missing parameters using the optional curve parameter. */
l1 = sexp_find_token (keyparms, "curve", 5);
if (l1)
+ curvename = sexp_nth_string (l1, 1);
+ /* Guess required fields if a curve parameter has not been given. */
+ if (curvename)
{
- curvename = sexp_nth_string (l1, 1);
- if (curvename)
- {
- rc = _gcry_ecc_fill_in_curve (0, curvename, &pk.E, NULL);
- if (rc)
- goto leave;
- }
+ rc = _gcry_ecc_fill_in_curve (0, curvename, &pk.E, NULL);
+ if (rc)
+ goto leave;
}
- /* Guess required fields if a curve parameter has not been given. */
- if (!curvename)
+ else
{
pk.E.model = MPI_EC_WEIERSTRASS;
pk.E.dialect = ECC_DIALECT_STANDARD;
if (!pk.E.h)
- pk.E.h = mpi_const (MPI_C_ONE);
+ pk.E.h = mpi_const (MPI_C_ONE);
+ }
+
+ if (pk.E.dialect == ECC_DIALECT_SAFECURVE)
+ {
+ ctx.flags |= PUBKEY_FLAG_RAW_FLAG;
+ safecurve = 1;
+ }
+ else if ((flags & PUBKEY_FLAG_DJB_TWEAK))
+ safecurve = 1;
+ else
+ safecurve = 0;
+
+ /*
+ * Extract the data.
+ */
+ rc = _gcry_pk_util_data_to_mpi (s_data, &data, &ctx);
+ if (rc)
+ goto leave;
+ if (pk.E.dialect != ECC_DIALECT_SAFECURVE && mpi_is_opaque (data))
+ {
+ rc = GPG_ERR_INV_DATA;
+ goto leave;
}

/*
@@ -1364,6 +1399,7 @@ ecc_encrypt_raw (gcry_sexp_t *r_ciph, gcry_sexp_t s_data, gcry_sexp_t keyparms)
/* Compute the encrypted value. */
ec = _gcry_mpi_ec_p_internal_new (pk.E.model, pk.E.dialect, flags,
pk.E.p, pk.E.a, pk.E.b);
+ ec->h = mpi_copy (pk.E.h);

/* Convert the public key. */
if (mpi_q)
@@ -1408,7 +1444,7 @@ ecc_encrypt_raw (gcry_sexp_t *r_ciph, gcry_sexp_t s_data, gcry_sexp_t keyparms)
* imported public key which might not follow the key
* generation procedure.
*/
- if (!(flags & PUBKEY_FLAG_DJB_TWEAK))
+ if (!safecurve)
{ /* It's not for X25519, then, the input data was simply wrong. */
rc = GPG_ERR_INV_DATA;
goto leave_main;
@@ -1418,7 +1454,9 @@ ecc_encrypt_raw (gcry_sexp_t *r_ciph, gcry_sexp_t s_data, gcry_sexp_t keyparms)
mpi_s = _gcry_ecc_ec2os (x, y, pk.E.p);
else
{
- rc = _gcry_ecc_mont_encodepoint (x, nbits, 1, &rawmpi, &rawmpilen);
+ rc = _gcry_ecc_mont_encodepoint (x, nbits,
+ pk.E.dialect != ECC_DIALECT_SAFECURVE,
+ &rawmpi, &rawmpilen);
if (rc)
goto leave_main;
mpi_s = mpi_new (0);
@@ -1437,7 +1475,9 @@ ecc_encrypt_raw (gcry_sexp_t *r_ciph, gcry_sexp_t s_data, gcry_sexp_t keyparms)
mpi_e = _gcry_ecc_ec2os (x, y, pk.E.p);
else
{
- rc = _gcry_ecc_mont_encodepoint (x, nbits, 1, &rawmpi, &rawmpilen);
+ rc = _gcry_ecc_mont_encodepoint (x, nbits,
+ pk.E.dialect != ECC_DIALECT_SAFECURVE,
+ &rawmpi, &rawmpilen);
if (!rc)
{
mpi_e = mpi_new (0);
@@ -1502,6 +1542,7 @@ ecc_decrypt_raw (gcry_sexp_t *r_plain, gcry_sexp_t s_data, gcry_sexp_t keyparms)
mpi_point_struct R;
gcry_mpi_t r = NULL;
int flags = 0;
+ int safecurve;

memset (&sk, 0, sizeof sk);
point_init (&kG);
@@ -1527,23 +1568,18 @@ ecc_decrypt_raw (gcry_sexp_t *r_plain, gcry_sexp_t s_data, gcry_sexp_t keyparms)
rc = _gcry_pk_util_preparse_encval (s_data, ecc_names, &l1, &ctx);
if (rc)
goto leave;
- rc = sexp_extract_param (l1, NULL, "e", &data_e, NULL);
+ rc = sexp_extract_param (l1, NULL, "/e", &data_e, NULL);
if (rc)
goto leave;
if (DBG_CIPHER)
log_printmpi ("ecc_decrypt d_e", data_e);
- if (mpi_is_opaque (data_e))
- {
- rc = GPG_ERR_INV_DATA;
- goto leave;
- }

/*
* Extract the key.
*/
- rc = sexp_extract_param (keyparms, NULL, "-p?a?b?g?n?h?+d",
+ rc = sexp_extract_param (keyparms, NULL, "-p?a?b?g?n?h?",
&sk.E.p, &sk.E.a, &sk.E.b, &mpi_g, &sk.E.n,
- &sk.E.h, &sk.d, NULL);
+ &sk.E.h, NULL);
if (rc)
goto leave;
if (mpi_g)
@@ -1557,23 +1593,29 @@ ecc_decrypt_raw (gcry_sexp_t *r_plain, gcry_sexp_t s_data, gcry_sexp_t keyparms)
sexp_release (l1);
l1 = sexp_find_token (keyparms, "curve", 5);
if (l1)
+ curvename = sexp_nth_string (l1, 1);
+ /* Guess required fields if a curve parameter has not been given. */
+ if (curvename)
{
- curvename = sexp_nth_string (l1, 1);
- if (curvename)
- {
- rc = _gcry_ecc_fill_in_curve (0, curvename, &sk.E, NULL);
- if (rc)
- goto leave;
- }
+ rc = _gcry_ecc_fill_in_curve (0, curvename, &sk.E, NULL);
+ if (rc)
+ goto leave;
}
- /* Guess required fields if a curve parameter has not been given. */
- if (!curvename)
+ else
{
sk.E.model = MPI_EC_WEIERSTRASS;
sk.E.dialect = ECC_DIALECT_STANDARD;
if (!sk.E.h)
- sk.E.h = mpi_const (MPI_C_ONE);
+ sk.E.h = mpi_const (MPI_C_ONE);
}
+
+ if (sk.E.dialect == ECC_DIALECT_SAFECURVE)
+ rc = sexp_extract_param (keyparms, NULL, "/d", &sk.d, NULL);
+ else
+ rc = sexp_extract_param (keyparms, NULL, "+d", &sk.d, NULL);
+ if (rc)
+ goto leave;
+
if (DBG_CIPHER)
{
log_debug ("ecc_decrypt info: %s/%s\n",
@@ -1596,9 +1638,14 @@ ecc_decrypt_raw (gcry_sexp_t *r_plain, gcry_sexp_t s_data, gcry_sexp_t keyparms)
goto leave;
}

+ if (sk.E.dialect == ECC_DIALECT_SAFECURVE || (flags & PUBKEY_FLAG_DJB_TWEAK))
+ safecurve = 1;
+ else
+ safecurve = 0;

ec = _gcry_mpi_ec_p_internal_new (sk.E.model, sk.E.dialect, flags,
sk.E.p, sk.E.a, sk.E.b);
+ ec->h = mpi_copy (sk.E.h);

/*
* Compute the plaintext.
@@ -1613,7 +1660,7 @@ ecc_decrypt_raw (gcry_sexp_t *r_plain, gcry_sexp_t s_data, gcry_sexp_t keyparms)
if (DBG_CIPHER)
log_printpnt ("ecc_decrypt kG", &kG, NULL);

- if ((flags & PUBKEY_FLAG_DJB_TWEAK))
+ if (safecurve)
{
/* For X25519, by its definition, validation should not be done. */
/* (Instead, we do output check.)
@@ -1682,7 +1729,9 @@ ecc_decrypt_raw (gcry_sexp_t *r_plain, gcry_sexp_t s_data, gcry_sexp_t keyparms)
unsigned char *rawmpi;
unsigned int rawmpilen;

- rc = _gcry_ecc_mont_encodepoint (x, nbits, 1, &rawmpi, &rawmpilen);
+ rc = _gcry_ecc_mont_encodepoint (x, nbits,
+ sk.E.dialect != ECC_DIALECT_SAFECURVE,
+ &rawmpi, &rawmpilen);
if (rc)
goto leave;

@@ -1877,7 +1926,7 @@ compute_keygrip (gcry_md_hd_t md, gcry_sexp_t keyparms)
if (rc)
goto leave;
}
- else if ((flags & PUBKEY_FLAG_DJB_TWEAK))
+ else if (dialect == ECC_DIALECT_SAFECURVE || (flags & PUBKEY_FLAG_DJB_TWEAK))
{
/* Remove the prefix 0x40 for keygrip computation. */
raw = mpi_get_opaque (values[6], &n);
@@ -1998,7 +2047,8 @@ _gcry_pk_ecc_get_sexp (gcry_sexp_t *r_sexp, int mode, mpi_ec_t ec)
unsigned char *encpk;
unsigned int encpklen;

- rc = _gcry_ecc_mont_encodepoint (ec->Q->x, ec->nbits, 1,
+ rc = _gcry_ecc_mont_encodepoint (ec->Q->x, ec->nbits,
+ ec->dialect != ECC_DIALECT_SAFECURVE,
&encpk, &encpklen);
if (rc)
goto leave;
diff --git a/cipher/pubkey-util.c b/cipher/pubkey-util.c
index ae0e1c46..859b54a1 100644
--- a/cipher/pubkey-util.c
+++ b/cipher/pubkey-util.c
@@ -685,7 +685,10 @@ _gcry_pk_util_data_to_mpi (gcry_sexp_t input, gcry_mpi_t *ret_mpi,
ldata = sexp_find_token (input, "data", 0);
if (!ldata)
{ /* assume old style */
- *ret_mpi = sexp_nth_mpi (input, 0, 0);
+ if ((ctx->flags & PUBKEY_FLAG_RAW_FLAG))
+ *ret_mpi = sexp_nth_mpi (input, 0, GCRYMPI_FMT_OPAQUE);
+ else
+ *ret_mpi = sexp_nth_mpi (input, 0, 0);
return *ret_mpi ? GPG_ERR_NO_ERROR : GPG_ERR_INV_OBJ;
}

diff --git a/mpi/ec.c b/mpi/ec.c
index ed936d74..8d3d7284 100644
--- a/mpi/ec.c
+++ b/mpi/ec.c
@@ -30,6 +30,7 @@
#include "ec-context.h"
#include "ec-internal.h"

+extern void reverse_buffer (unsigned char *buffer, unsigned int length);

#define point_init(a) _gcry_mpi_point_init ((a))
#define point_free(a) _gcry_mpi_point_free_parts ((a))
@@ -366,7 +367,7 @@ mpih_set_cond (mpi_ptr_t wp, mpi_ptr_t up, mpi_size_t usize, unsigned long set)
wp[i] = wp[i] ^ x;
}
}
-
+
/* Routines for 2^255 - 19. */

#define LIMB_SIZE_25519 ((256+BITS_PER_MPI_LIMB-1)/BITS_PER_MPI_LIMB)
@@ -477,7 +478,167 @@ ec_pow2_25519 (gcry_mpi_t w, const gcry_mpi_t b, mpi_ec_t ctx)
{
ec_mulm_25519 (w, b, b, ctx);
}
+
+/* Routines for 2^448 - 2^224 - 1. */
+
+#define LIMB_SIZE_448 ((448+BITS_PER_MPI_LIMB-1)/BITS_PER_MPI_LIMB)
+#define LIMB_SIZE_HALF_448 ((LIMB_SIZE_448+1)/2)
+
+static void
+ec_addm_448 (gcry_mpi_t w, gcry_mpi_t u, gcry_mpi_t v, mpi_ec_t ctx)
+{
+ mpi_ptr_t wp, up, vp;
+ mpi_size_t wsize = LIMB_SIZE_448;
+ mpi_limb_t n[LIMB_SIZE_448];
+ mpi_limb_t cy;
+
+ if (w->nlimbs != wsize || u->nlimbs != wsize || v->nlimbs != wsize)
+ log_bug ("addm_448: different sizes\n");
+
+ memset (n, 0, sizeof n);
+ up = u->d;
+ vp = v->d;
+ wp = w->d;
+
+ cy = _gcry_mpih_add_n (wp, up, vp, wsize);
+ mpih_set_cond (n, ctx->p->d, wsize, (cy != 0UL));
+ _gcry_mpih_sub_n (wp, wp, n, wsize);
+}
+
+static void
+ec_subm_448 (gcry_mpi_t w, gcry_mpi_t u, gcry_mpi_t v, mpi_ec_t ctx)
+{
+ mpi_ptr_t wp, up, vp;
+ mpi_size_t wsize = LIMB_SIZE_448;
+ mpi_limb_t n[LIMB_SIZE_448];
+ mpi_limb_t borrow;

+ if (w->nlimbs != wsize || u->nlimbs != wsize || v->nlimbs != wsize)
+ log_bug ("subm_448: different sizes\n");
+
+ memset (n, 0, sizeof n);
+ up = u->d;
+ vp = v->d;
+ wp = w->d;
+
+ borrow = _gcry_mpih_sub_n (wp, up, vp, wsize);
+ mpih_set_cond (n, ctx->p->d, wsize, (borrow != 0UL));
+ _gcry_mpih_add_n (wp, wp, n, wsize);
+}
+
+static void
+ec_mulm_448 (gcry_mpi_t w, gcry_mpi_t u, gcry_mpi_t v, mpi_ec_t ctx)
+{
+ mpi_ptr_t wp, up, vp;
+ mpi_size_t wsize = LIMB_SIZE_448;
+ mpi_limb_t n[LIMB_SIZE_448*2];
+ mpi_limb_t a2[LIMB_SIZE_HALF_448];
+ mpi_limb_t a3[LIMB_SIZE_HALF_448];
+ mpi_limb_t b0[LIMB_SIZE_HALF_448];
+ mpi_limb_t b1[LIMB_SIZE_HALF_448];
+ mpi_limb_t cy;
+ int i;
+#if (LIMB_SIZE_HALF_448 > LIMB_SIZE_448/2)
+ mpi_limb_t b1_rest, a3_rest;
+#endif
+
+ if (w->nlimbs != wsize || u->nlimbs != wsize || v->nlimbs != wsize)
+ log_bug ("mulm_448: different sizes\n");
+
+ up = u->d;
+ vp = v->d;
+ wp = w->d;
+
+ _gcry_mpih_mul_n (n, up, vp, wsize);
+
+ for (i = 0; i < (wsize + 1)/ 2; i++)
+ {
+ b0[i] = n[i];
+ b1[i] = n[i+wsize/2];
+ a2[i] = n[i+wsize];
+ a3[i] = n[i+wsize+wsize/2];
+ }
+
+#if (LIMB_SIZE_HALF_448 > LIMB_SIZE_448/2)
+ b0[LIMB_SIZE_HALF_448-1] &= (1UL<<32)-1;
+ a2[LIMB_SIZE_HALF_448-1] &= (1UL<<32)-1;
+
+ b1_rest = 0;
+ a3_rest = 0;
+
+ for (i = (wsize + 1)/ 2 -1; i >= 0; i--)
+ {
+ mpi_limb_t b1v, a3v;
+ b1v = b1[i];
+ a3v = a3[i];
+ b1[i] = (b1_rest<<32) | (b1v >> 32);
+ a3[i] = (a3_rest<<32) | (a3v >> 32);
+ b1_rest = b1v & ((1UL <<32)-1);
+ a3_rest = a3v & ((1UL <<32)-1);
+ }
+#endif
+
+ cy = _gcry_mpih_add_n (b0, b0, a2, LIMB_SIZE_HALF_448);
+ cy += _gcry_mpih_add_n (b0, b0, a3, LIMB_SIZE_HALF_448);
+ for (i = 0; i < (wsize + 1)/ 2; i++)
+ wp[i] = b0[i];
+#if (LIMB_SIZE_HALF_448 > LIMB_SIZE_448/2)
+ wp[LIMB_SIZE_HALF_448-1] &= ((1UL <<32)-1);
+#endif
+
+#if (LIMB_SIZE_HALF_448 > LIMB_SIZE_448/2)
+ cy = b0[LIMB_SIZE_HALF_448-1] >> 32;
+#endif
+
+ cy = _gcry_mpih_add_1 (b1, b1, LIMB_SIZE_HALF_448, cy);
+ cy += _gcry_mpih_add_n (b1, b1, a2, LIMB_SIZE_HALF_448);
+ cy += _gcry_mpih_add_n (b1, b1, a3, LIMB_SIZE_HALF_448);
+ cy += _gcry_mpih_add_n (b1, b1, a3, LIMB_SIZE_HALF_448);
+#if (LIMB_SIZE_HALF_448 > LIMB_SIZE_448/2)
+ b1_rest = 0;
+ for (i = (wsize + 1)/ 2 -1; i >= 0; i--)
+ {
+ mpi_limb_t b1v = b1[i];
+ b1[i] = (b1_rest<<32) | (b1v >> 32);
+ b1_rest = b1v & ((1UL <<32)-1);
+ }
+ wp[LIMB_SIZE_HALF_448-1] |= (b1_rest << 32);
+#endif
+ for (i = 0; i < wsize / 2; i++)
+ wp[i+(wsize + 1) / 2] = b1[i];
+
+#if (LIMB_SIZE_HALF_448 > LIMB_SIZE_448/2)
+ cy = b1[LIMB_SIZE_HALF_448-1];
+#endif
+
+ memset (n, 0, wsize * BYTES_PER_MPI_LIMB);
+
+#if (LIMB_SIZE_HALF_448 > LIMB_SIZE_448/2)
+ n[LIMB_SIZE_HALF_448-1] = cy << 32;
+#else
+ n[LIMB_SIZE_HALF_448] = cy;
+#endif
+ n[0] = cy;
+ _gcry_mpih_add_n (wp, wp, n, wsize);
+
+ memset (n, 0, wsize * BYTES_PER_MPI_LIMB);
+ cy = _gcry_mpih_sub_n (wp, wp, ctx->p->d, wsize);
+ mpih_set_cond (n, ctx->p->d, wsize, (cy != 0UL));
+ _gcry_mpih_add_n (wp, wp, n, wsize);
+}
+
+static void
+ec_mul2_448 (gcry_mpi_t w, gcry_mpi_t u, mpi_ec_t ctx)
+{
+ ec_addm_448 (w, u, u, ctx);
+}
+
+static void
+ec_pow2_448 (gcry_mpi_t w, const gcry_mpi_t b, mpi_ec_t ctx)
+{
+ ec_mulm_448 (w, b, b, ctx);
+}
+
struct field_table {
const char *p;

@@ -498,6 +659,15 @@ static const struct field_table field_table[] = {
ec_mul2_25519,
ec_pow2_25519
},
+ {
+ "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE"
+ "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
+ ec_addm_448,
+ ec_subm_448,
+ ec_mulm_448,
+ ec_mul2_448,
+ ec_pow2_448
+ },
{ NULL, NULL, NULL, NULL, NULL, NULL },
};

@@ -544,17 +714,37 @@ ec_get_two_inv_p (mpi_ec_t ec)
}


-static const char *curve25519_bad_points[] = {
+static const char *const curve25519_bad_points[] = {
+ "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000001",
"0x00b8495f16056286fdb1329ceb8d09da6ac49ff1fae35616aeb8413b7c7aebe0",
"0x57119fd0dd4e22d8868e1c58c45c44045bef839c55b1d0b1248c50a3bc959c5f",
"0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec",
- "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed",
"0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffee",
NULL
};

+
+static const char *const curve448_bad_points[] = {
+ "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffe"
+ "ffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
+ "0x00000000000000000000000000000000000000000000000000000000"
+ "00000000000000000000000000000000000000000000000000000000",
+ "0x00000000000000000000000000000000000000000000000000000000"
+ "00000000000000000000000000000000000000000000000000000001",
+ "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffe"
+ "fffffffffffffffffffffffffffffffffffffffffffffffffffffffe",
+ "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "00000000000000000000000000000000000000000000000000000000",
+ NULL
+};
+
+static const char *const *bad_points_table[] = {
+ curve25519_bad_points,
+ curve448_bad_points,
+};
+
static gcry_mpi_t
scanval (const char *string)
{
@@ -607,8 +797,19 @@ ec_p_init (mpi_ec_t ctx, enum gcry_mpi_ec_models model,

if (model == MPI_EC_MONTGOMERY)
{
- for (i=0; i< DIM(ctx->t.scratch) && curve25519_bad_points[i]; i++)
- ctx->t.scratch[i] = scanval (curve25519_bad_points[i]);
+ for (i=0; i< DIM(bad_points_table); i++)
+ {
+ gcry_mpi_t p_candidate = scanval (bad_points_table[i][0]);
+ int match_p = !mpi_cmp (ctx->p, p_candidate);
+ int j;
+
+ mpi_free (p_candidate);
+ if (!match_p)
+ continue;
+
+ for (j=0; i< DIM(ctx->t.scratch) && bad_points_table[i][j]; j++)
+ ctx->t.scratch[j] = scanval (bad_points_table[i][j]);
+ }
}
else
{
@@ -1570,6 +1771,7 @@ _gcry_mpi_ec_mul_point (mpi_point_t result,
mpi_point_t q1, q2, prd, sum;
unsigned long sw;
mpi_size_t rsize;
+ int scalar_copied = 0;

/* Compute scalar point multiplication with Montgomery Ladder.
Note that we don't use Y-coordinate in the points at all.
@@ -1585,6 +1787,29 @@ _gcry_mpi_ec_mul_point (mpi_point_t result,
p2.x = mpi_copy (point->x);
mpi_set_ui (p2.z, 1);

+ if (mpi_is_opaque (scalar))
+ {
+ gcry_mpi_t a;
+ unsigned int cofactor, n;
+ const unsigned int pbits = mpi_get_nbits (ctx->p);
+ unsigned char *raw;
+
+ scalar_copied = 1;
+
+ raw = _gcry_mpi_get_opaque_copy (scalar, &n);
+ mpi_get_ui (&cofactor, ctx->h);
+ reverse_buffer (raw, (n+7)/8);
+ if ((pbits % 8))
+ raw[0] &= (1 << (pbits % 8)) - 1;
+ raw[0] |= (1 << ((pbits + 7) % 8));
+ raw[(pbits-1)/8] &= (256 - cofactor);
+ a = mpi_is_secure (scalar) ? mpi_snew (pbits): mpi_new (pbits);
+ _gcry_mpi_set_buffer (a, raw, (n+7)/8, 0);
+ xfree (raw);
+
+ scalar = a;
+ }
+
point_resize (&p1, ctx);
point_resize (&p2, ctx);
point_resize (&p1_, ctx);
@@ -1634,6 +1859,8 @@ _gcry_mpi_ec_mul_point (mpi_point_t result,
point_free (&p2);
point_free (&p1_);
point_free (&p2_);
+ if (scalar_copied)
+ _gcry_mpi_release (scalar);
return;
}

diff --git a/src/mpi.h b/src/mpi.h
index aeba7f8b..ee6be2f7 100644
--- a/src/mpi.h
+++ b/src/mpi.h
@@ -266,7 +266,8 @@ enum gcry_mpi_ec_models
enum ecc_dialects
{
ECC_DIALECT_STANDARD = 0,
- ECC_DIALECT_ED25519
+ ECC_DIALECT_ED25519,
+ ECC_DIALECT_SAFECURVE
};


diff --git a/tests/Makefile.am b/tests/Makefile.am
index 9e117970..e463d8c6 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -22,7 +22,8 @@ tests_bin = \
version t-secmem mpitests t-sexp t-convert \
t-mpi-bit t-mpi-point curves t-lock \
prime basic keygen pubkey hmac hashtest t-kdf keygrip \
- fips186-dsa aeswrap pkcs1v2 random dsa-rfc6979 t-ed25519 t-cv25519
+ fips186-dsa aeswrap pkcs1v2 random dsa-rfc6979 \
+ t-ed25519 t-cv25519 t-x448

tests_bin_last = benchmark bench-slope

diff --git a/tests/curves.c b/tests/curves.c
index dc5ebe77..bacc1302 100644
--- a/tests/curves.c
+++ b/tests/curves.c
@@ -33,7 +33,7 @@
#include "t-common.h"

/* Number of curves defined in ../cipger/ecc.c */
-#define N_CURVES 22
+#define N_CURVES 23

/* A real world sample public key. */
static char const sample_key_1[] =
diff --git a/tests/t-x448.c b/tests/t-x448.c
new file mode 100644
index 00000000..5c3cbeb9
--- /dev/null
+++ b/tests/t-x448.c
@@ -0,0 +1,593 @@
+/* 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 <https://www.gnu.org/licenses/>.
+ * SPDX-License-Identifier: LGPL-2.1+
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdarg.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "stopwatch.h"
+
+#define PGM "t-x448"
+#include "t-common.h"
+#define N_TESTS 9
+
+
+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,
+ 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)))
+ 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;
+ 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");
+ 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));
+
+ start_timer ();
+ check_x448 ();
+ stop_timer ();
+
+ info ("All tests completed in %s. Errors: %d\n",
+ elapsed_time (1), error_count);
+ return !!error_count;
+}

_______________________________________________
Gcrypt-devel mailing list
Gcrypt-devel@gnupg.org
http://lists.gnupg.org/mailman/listinfo/gcrypt-devel