[libgcrypt/f20] add cleared ECC support
Tom Callaway
spot at fedoraproject.org
Mon Oct 21 16:49:48 UTC 2013
commit c57e45f04b88def6d5913f22e9f8bb08c83149db
Author: Tom Callaway <spot at fedoraproject.org>
Date: Mon Oct 21 17:49:45 2013 +0100
add cleared ECC support
curves.c | 166 ++++
ecc.c | 1658 ++++++++++++++++++++++++++++++++++++
libgcrypt-1.5.3-ecc-test-fix.patch | 12 +
libgcrypt.spec | 22 +-
4 files changed, 1852 insertions(+), 6 deletions(-)
---
diff --git a/curves.c b/curves.c
new file mode 100644
index 0000000..caead00
--- /dev/null
+++ b/curves.c
@@ -0,0 +1,166 @@
+/* curves.c - ECC curves regression tests
+ * Copyright (C) 2011 Free Software Foundation, Inc.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include "../src/gcrypt.h"
+
+/* Number of curves defined in ../cipger/ecc.c */
+#define N_CURVES 2
+
+/* A real world sample public key. */
+static char const sample_key_1[] =
+"(public-key\n"
+" (ecdsa\n"
+" (p #00FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF#)\n"
+" (a #00FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC#)\n"
+" (b #5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B#)\n"
+" (g #046B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296"
+ "4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5#)\n"
+" (n #00FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551#)\n"
+" (q #0442B927242237639A36CE9221B340DB1A9AB76DF2FE3E171277F6A4023DED146EE"
+ "86525E38CCECFF3FB8D152CC6334F70D23A525175C1BCBDDE6E023B2228770E#)\n"
+" ))";
+static char const sample_key_1_curve[] = "NIST P-256";
+static unsigned int sample_key_1_nbits = 256;
+
+/* Program option flags. */
+static int verbose;
+static int error_count;
+
+static void
+fail (const char *format, ...)
+{
+ va_list arg_ptr;
+
+ va_start (arg_ptr, format);
+ vfprintf (stderr, format, arg_ptr);
+ va_end (arg_ptr);
+ error_count++;
+}
+
+static void
+die (const char *format, ...)
+{
+ va_list arg_ptr;
+
+ va_start (arg_ptr, format);
+ vfprintf (stderr, format, arg_ptr);
+ va_end (arg_ptr);
+ exit (1);
+}
+
+
+static void
+list_curves (void)
+{
+ int idx;
+ const char *name;
+ unsigned int nbits;
+
+ for (idx=0; (name = gcry_pk_get_curve (NULL, idx, &nbits)); idx++)
+ {
+ if (verbose)
+ printf ("%s - %u bits\n", name, nbits);
+ }
+ if (idx != N_CURVES)
+ fail ("expected %d curves but got %d\n", N_CURVES, idx);
+ if (gcry_pk_get_curve (NULL, -1, NULL))
+ fail ("curve iteration failed\n");
+}
+
+
+static void
+check_matching (void)
+{
+ gpg_error_t err;
+ gcry_sexp_t key;
+ const char *name;
+ unsigned int nbits;
+
+ err = gcry_sexp_new (&key, sample_key_1, 0, 1);
+ if (err)
+ die ("parsing s-expression string failed: %s\n", gpg_strerror (err));
+ name = gcry_pk_get_curve (key, 0, &nbits);
+ if (!name)
+ fail ("curve name not found for sample_key_1\n");
+ else if (strcmp (name, sample_key_1_curve))
+ fail ("expected curve name %s but got %s for sample_key_1\n",
+ sample_key_1_curve, name);
+ else if (nbits != sample_key_1_nbits)
+ fail ("expected curve size %u but got %u for sample_key_1\n",
+ sample_key_1_nbits, nbits);
+
+ gcry_sexp_release (key);
+
+}
+
+
+static void
+check_get_params (void)
+{
+ gcry_sexp_t param;
+ const char *name;
+
+ param = gcry_pk_get_param (GCRY_PK_ECDSA, sample_key_1_curve);
+ if (!param)
+ fail ("error gerring parameters for `%s'\n", sample_key_1_curve);
+
+ name = gcry_pk_get_curve (param, 0, NULL);
+ if (!name)
+ fail ("get_param: curve name not found for sample_key_1\n");
+ else if (strcmp (name, sample_key_1_curve))
+ fail ("get_param: expected curve name %s but got %s for sample_key_1\n",
+ sample_key_1_curve, name);
+
+ gcry_sexp_release (param);
+
+}
+
+
+int
+main (int argc, char **argv)
+{
+ int debug = 0;
+
+ if (argc > 1 && !strcmp (argv[1], "--verbose"))
+ verbose = 1;
+ else if (argc > 1 && !strcmp (argv[1], "--debug"))
+ verbose = debug = 1;
+
+ if (!gcry_check_version (GCRYPT_VERSION))
+ die ("version mismatch\n");
+
+ gcry_control (GCRYCTL_DISABLE_SECMEM, 0);
+ gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
+ if (debug)
+ gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1u, 0);
+ list_curves ();
+ check_matching ();
+ check_get_params ();
+
+ return error_count ? 1 : 0;
+}
diff --git a/ecc.c b/ecc.c
new file mode 100644
index 0000000..bf63f4b
--- /dev/null
+++ b/ecc.c
@@ -0,0 +1,1658 @@
+/* ecc.c - Elliptic Curve Cryptography
+ Copyright (C) 2007, 2008, 2010, 2011 Free Software Foundation, Inc.
+
+ 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, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ USA. */
+
+/* This code is originally based on the Patch 0.1.6 for the gnupg
+ 1.4.x branch as retrieved on 2007-03-21 from
+ http://www.calcurco.cat/eccGnuPG/src/gnupg-1.4.6-ecc0.2.0beta1.diff.bz2
+ The original authors are:
+ Written by
+ Sergi Blanch i Torne <d4372211 at alumnes.eup.udl.es>,
+ Ramiro Moreno Chiral <ramiro at eup.udl.es>
+ Maintainers
+ Sergi Blanch i Torne
+ Ramiro Moreno Chiral
+ Mikael Mylnikov (mmr)
+ For use in Libgcrypt the code has been heavily modified and cleaned
+ up. In fact there is not much left of the orginally code except for
+ some variable names and the text book implementaion of the sign and
+ verification algorithms. The arithmetic functions have entirely
+ been rewritten and moved to mpi/ec.c.
+
+ ECDH encrypt and decrypt code written by Andrey Jivsov,
+*/
+
+
+/* TODO:
+
+ - If we support point compression we need to uncompress before
+ computing the keygrip
+
+ - In mpi/ec.c we use mpi_powm for x^2 mod p: Either implement a
+ special case in mpi_powm or check whether mpi_mulm is faster.
+
+ - Decide whether we should hide the mpi_point_t definition.
+*/
+
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "g10lib.h"
+#include "mpi.h"
+#include "cipher.h"
+
+/* Definition of a curve. */
+typedef struct
+{
+ gcry_mpi_t p; /* Prime specifying the field GF(p). */
+ gcry_mpi_t a; /* First coefficient of the Weierstrass equation. */
+ gcry_mpi_t b; /* Second coefficient of the Weierstrass equation. */
+ mpi_point_t G; /* Base point (generator). */
+ gcry_mpi_t n; /* Order of G. */
+ const char *name; /* Name of curve or NULL. */
+} elliptic_curve_t;
+
+
+typedef struct
+{
+ elliptic_curve_t E;
+ mpi_point_t Q; /* Q = [d]G */
+} ECC_public_key;
+
+typedef struct
+{
+ elliptic_curve_t E;
+ mpi_point_t Q;
+ gcry_mpi_t d;
+} ECC_secret_key;
+
+
+/* This tables defines aliases for curve names. */
+static const struct
+{
+ const char *name; /* Our name. */
+ const char *other; /* Other name. */
+} curve_aliases[] =
+ {
+ { "NIST P-256", "1.2.840.10045.3.1.7" }, /* From NIST SP 800-78-1. */
+ { "NIST P-256", "prime256v1" },
+ { "NIST P-256", "secp256r1" },
+
+ { "NIST P-384", "secp384r1" },
+ { "NIST P-384", "1.3.132.0.34" },
+
+ { NULL, NULL}
+ };
+
+typedef struct {
+ const char *desc; /* Description of the curve. */
+ unsigned int nbits; /* Number of bits. */
+ unsigned int fips:1; /* True if this is a FIPS140-2 approved curve. */
+ const char *p; /* Order of the prime field. */
+ const char *a, *b; /* The coefficients. */
+ const char *n; /* The order of the base point. */
+ const char *g_x, *g_y; /* Base point. */
+} ecc_domain_parms_t;
+
+/* This static table defines all available curves. */
+static const ecc_domain_parms_t domain_parms[] =
+ {
+ {
+ "NIST P-256", 256, 1,
+ "0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff",
+ "0xffffffff00000001000000000000000000000000fffffffffffffffffffffffc",
+ "0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b",
+ "0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551",
+
+ "0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296",
+ "0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5"
+ },
+ {
+ "NIST P-384", 384, 1,
+ "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe"
+ "ffffffff0000000000000000ffffffff",
+ "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe"
+ "ffffffff0000000000000000fffffffc",
+ "0xb3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875a"
+ "c656398d8a2ed19d2a85c8edd3ec2aef",
+ "0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf"
+ "581a0db248b0a77aecec196accc52973",
+
+ "0xaa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a38"
+ "5502f25dbf55296c3a545e3872760ab7",
+ "0x3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c0"
+ "0a60b1ce1d7e819d7a431d7c90ea0e5f"
+ },
+
+ { NULL, 0, 0, NULL, NULL, NULL, NULL }
+ };
+
+
+/* Registered progress function and its callback value. */
+static void (*progress_cb) (void *, const char*, int, int, int);
+static void *progress_cb_data;
+
+
+#define point_init(a) _gcry_mpi_ec_point_init ((a))
+#define point_free(a) _gcry_mpi_ec_point_free ((a))
+
+
+
+/* Local prototypes. */
+static gcry_mpi_t gen_k (gcry_mpi_t p, int security_level);
+static void test_keys (ECC_secret_key * sk, unsigned int nbits);
+static int check_secret_key (ECC_secret_key * sk);
+static gpg_err_code_t sign (gcry_mpi_t input, ECC_secret_key *skey,
+ gcry_mpi_t r, gcry_mpi_t s);
+static gpg_err_code_t verify (gcry_mpi_t input, ECC_public_key *pkey,
+ gcry_mpi_t r, gcry_mpi_t s);
+
+
+static gcry_mpi_t gen_y_2 (gcry_mpi_t x, elliptic_curve_t * base);
+
+
+
+
+void
+_gcry_register_pk_ecc_progress (void (*cb) (void *, const char *,
+ int, int, int),
+ void *cb_data)
+{
+ progress_cb = cb;
+ progress_cb_data = cb_data;
+}
+
+/* static void */
+/* progress (int c) */
+/* { */
+/* if (progress_cb) */
+/* progress_cb (progress_cb_data, "pk_ecc", c, 0, 0); */
+/* } */
+
+
+
+
+/* Set the value from S into D. */
+static void
+point_set (mpi_point_t *d, mpi_point_t *s)
+{
+ mpi_set (d->x, s->x);
+ mpi_set (d->y, s->y);
+ mpi_set (d->z, s->z);
+}
+
+
+/*
+ * Release a curve object.
+ */
+static void
+curve_free (elliptic_curve_t *E)
+{
+ mpi_free (E->p); E->p = NULL;
+ mpi_free (E->a); E->a = NULL;
+ mpi_free (E->b); E->b = NULL;
+ point_free (&E->G);
+ mpi_free (E->n); E->n = NULL;
+}
+
+
+/*
+ * Return a copy of a curve object.
+ */
+static elliptic_curve_t
+curve_copy (elliptic_curve_t E)
+{
+ elliptic_curve_t R;
+
+ R.p = mpi_copy (E.p);
+ R.a = mpi_copy (E.a);
+ R.b = mpi_copy (E.b);
+ point_init (&R.G);
+ point_set (&R.G, &E.G);
+ R.n = mpi_copy (E.n);
+
+ return R;
+}
+
+
+/* Helper to scan a hex string. */
+static gcry_mpi_t
+scanval (const char *string)
+{
+ gpg_error_t err;
+ gcry_mpi_t val;
+
+ err = gcry_mpi_scan (&val, GCRYMPI_FMT_HEX, string, 0, NULL);
+ if (err)
+ log_fatal ("scanning ECC parameter failed: %s\n", gpg_strerror (err));
+ return val;
+}
+
+
+
+
+
+/****************
+ * Solve the right side of the equation that defines a curve.
+ */
+static gcry_mpi_t
+gen_y_2 (gcry_mpi_t x, elliptic_curve_t *base)
+{
+ gcry_mpi_t three, x_3, axb, y;
+
+ three = mpi_alloc_set_ui (3);
+ x_3 = mpi_new (0);
+ axb = mpi_new (0);
+ y = mpi_new (0);
+
+ mpi_powm (x_3, x, three, base->p);
+ mpi_mulm (axb, base->a, x, base->p);
+ mpi_addm (axb, axb, base->b, base->p);
+ mpi_addm (y, x_3, axb, base->p);
+
+ mpi_free (x_3);
+ mpi_free (axb);
+ mpi_free (three);
+ return y; /* The quadratic value of the coordinate if it exist. */
+}
+
+
+/* Generate a random secret scalar k with an order of p
+
+ At the beginning this was identical to the code is in elgamal.c.
+ Later imporved by mmr. Further simplified by wk. */
+static gcry_mpi_t
+gen_k (gcry_mpi_t p, int security_level)
+{
+ gcry_mpi_t k;
+ unsigned int nbits;
+
+ nbits = mpi_get_nbits (p);
+ k = mpi_snew (nbits);
+ if (DBG_CIPHER)
+ log_debug ("choosing a random k of %u bits at seclevel %d\n",
+ nbits, security_level);
+
+ gcry_mpi_randomize (k, nbits, security_level);
+
+ mpi_mod (k, k, p); /* k = k mod p */
+
+ return k;
+}
+
+
+/* Generate the crypto system setup. This function takes the NAME of
+ a curve or the desired number of bits and stores at R_CURVE the
+ parameters of the named curve or those of a suitable curve. The
+ chosen number of bits is stored on R_NBITS. */
+static gpg_err_code_t
+fill_in_curve (unsigned int nbits, const char *name,
+ elliptic_curve_t *curve, unsigned int *r_nbits)
+{
+ int idx, aliasno;
+ const char *resname = NULL; /* Set to a found curve name. */
+
+ if (name)
+ {
+ /* First check our native curves. */
+ for (idx = 0; domain_parms[idx].desc; idx++)
+ if (!strcmp (name, domain_parms[idx].desc))
+ {
+ resname = domain_parms[idx].desc;
+ break;
+ }
+ /* If not found consult the alias table. */
+ if (!domain_parms[idx].desc)
+ {
+ for (aliasno = 0; curve_aliases[aliasno].name; aliasno++)
+ if (!strcmp (name, curve_aliases[aliasno].other))
+ break;
+ if (curve_aliases[aliasno].name)
+ {
+ for (idx = 0; domain_parms[idx].desc; idx++)
+ if (!strcmp (curve_aliases[aliasno].name,
+ domain_parms[idx].desc))
+ {
+ resname = domain_parms[idx].desc;
+ break;
+ }
+ }
+ }
+ }
+ else
+ {
+ for (idx = 0; domain_parms[idx].desc; idx++)
+ if (nbits == domain_parms[idx].nbits)
+ break;
+ }
+ if (!domain_parms[idx].desc)
+ return GPG_ERR_INV_VALUE;
+
+ /* In fips mode we only support NIST curves. Note that it is
+ possible to bypass this check by specifying the curve parameters
+ directly. */
+ if (fips_mode () && !domain_parms[idx].fips )
+ return GPG_ERR_NOT_SUPPORTED;
+
+ *r_nbits = domain_parms[idx].nbits;
+ curve->p = scanval (domain_parms[idx].p);
+ curve->a = scanval (domain_parms[idx].a);
+ curve->b = scanval (domain_parms[idx].b);
+ curve->n = scanval (domain_parms[idx].n);
+ curve->G.x = scanval (domain_parms[idx].g_x);
+ curve->G.y = scanval (domain_parms[idx].g_y);
+ curve->G.z = mpi_alloc_set_ui (1);
+ curve->name = resname;
+
+ return 0;
+}
+
+
+/*
+ * First obtain the setup. Over the finite field randomize an scalar
+ * secret value, and calculate the public point.
+ */
+static gpg_err_code_t
+generate_key (ECC_secret_key *sk, unsigned int nbits, const char *name,
+ int transient_key,
+ gcry_mpi_t g_x, gcry_mpi_t g_y,
+ gcry_mpi_t q_x, gcry_mpi_t q_y,
+ const char **r_usedcurve)
+{
+ gpg_err_code_t err;
+ elliptic_curve_t E;
+ gcry_mpi_t d;
+ mpi_point_t Q;
+ mpi_ec_t ctx;
+ gcry_random_level_t random_level;
+
+ *r_usedcurve = NULL;
+
+ err = fill_in_curve (nbits, name, &E, &nbits);
+ if (err)
+ return err;
+
+ if (DBG_CIPHER)
+ {
+ log_mpidump ("ecgen curve p", E.p);
+ log_mpidump ("ecgen curve a", E.a);
+ log_mpidump ("ecgen curve b", E.b);
+ log_mpidump ("ecgen curve n", E.n);
+ log_mpidump ("ecgen curve Gx", E.G.x);
+ log_mpidump ("ecgen curve Gy", E.G.y);
+ log_mpidump ("ecgen curve Gz", E.G.z);
+ if (E.name)
+ log_debug ("ecgen curve used: %s\n", E.name);
+ }
+
+ random_level = transient_key ? GCRY_STRONG_RANDOM : GCRY_VERY_STRONG_RANDOM;
+ d = gen_k (E.n, random_level);
+
+ /* Compute Q. */
+ point_init (&Q);
+ ctx = _gcry_mpi_ec_init (E.p, E.a);
+ _gcry_mpi_ec_mul_point (&Q, d, &E.G, ctx);
+
+ /* Copy the stuff to the key structures. */
+ sk->E.p = mpi_copy (E.p);
+ sk->E.a = mpi_copy (E.a);
+ sk->E.b = mpi_copy (E.b);
+ point_init (&sk->E.G);
+ point_set (&sk->E.G, &E.G);
+ sk->E.n = mpi_copy (E.n);
+ point_init (&sk->Q);
+ point_set (&sk->Q, &Q);
+ sk->d = mpi_copy (d);
+ /* We also return copies of G and Q in affine coordinates if
+ requested. */
+ if (g_x && g_y)
+ {
+ if (_gcry_mpi_ec_get_affine (g_x, g_y, &sk->E.G, ctx))
+ log_fatal ("ecgen: Failed to get affine coordinates\n");
+ }
+ if (q_x && q_y)
+ {
+ if (_gcry_mpi_ec_get_affine (q_x, q_y, &sk->Q, ctx))
+ log_fatal ("ecgen: Failed to get affine coordinates\n");
+ }
+ _gcry_mpi_ec_free (ctx);
+
+ point_free (&Q);
+ mpi_free (d);
+
+ *r_usedcurve = E.name;
+ curve_free (&E);
+
+ /* Now we can test our keys (this should never fail!). */
+ test_keys (sk, nbits - 64);
+
+ return 0;
+}
+
+
+/*
+ * To verify correct skey it use a random information.
+ * First, encrypt and decrypt this dummy value,
+ * test if the information is recuperated.
+ * Second, test with the sign and verify functions.
+ */
+static void
+test_keys (ECC_secret_key *sk, unsigned int nbits)
+{
+ ECC_public_key pk;
+ gcry_mpi_t test = mpi_new (nbits);
+ mpi_point_t R_;
+ gcry_mpi_t c = mpi_new (nbits);
+ gcry_mpi_t out = mpi_new (nbits);
+ gcry_mpi_t r = mpi_new (nbits);
+ gcry_mpi_t s = mpi_new (nbits);
+
+ if (DBG_CIPHER)
+ log_debug ("Testing key.\n");
+
+ point_init (&R_);
+
+ pk.E = curve_copy (sk->E);
+ point_init (&pk.Q);
+ point_set (&pk.Q, &sk->Q);
+
+ gcry_mpi_randomize (test, nbits, GCRY_WEAK_RANDOM);
+
+ if (sign (test, sk, r, s) )
+ log_fatal ("ECDSA operation: sign failed\n");
+
+ if (verify (test, &pk, r, s))
+ {
+ log_fatal ("ECDSA operation: sign, verify failed\n");
+ }
+
+ if (DBG_CIPHER)
+ log_debug ("ECDSA operation: sign, verify ok.\n");
+
+ point_free (&pk.Q);
+ curve_free (&pk.E);
+
+ point_free (&R_);
+ mpi_free (s);
+ mpi_free (r);
+ mpi_free (out);
+ mpi_free (c);
+ mpi_free (test);
+}
+
+
+/*
+ * To check the validity of the value, recalculate the correspondence
+ * between the public value and the secret one.
+ */
+static int
+check_secret_key (ECC_secret_key * sk)
+{
+ int rc = 1;
+ mpi_point_t Q;
+ gcry_mpi_t y_2, y2;
+ mpi_ec_t ctx = NULL;
+
+ point_init (&Q);
+
+ /* ?primarity test of 'p' */
+ /* (...) //!! */
+ /* G in E(F_p) */
+ y_2 = gen_y_2 (sk->E.G.x, &sk->E); /* y^2=x^3+a*x+b */
+ y2 = mpi_alloc (0);
+ mpi_mulm (y2, sk->E.G.y, sk->E.G.y, sk->E.p); /* y^2=y*y */
+ if (mpi_cmp (y_2, y2))
+ {
+ if (DBG_CIPHER)
+ log_debug ("Bad check: Point 'G' does not belong to curve 'E'!\n");
+ goto leave;
+ }
+ /* G != PaI */
+ if (!mpi_cmp_ui (sk->E.G.z, 0))
+ {
+ if (DBG_CIPHER)
+ log_debug ("Bad check: 'G' cannot be Point at Infinity!\n");
+ goto leave;
+ }
+
+ ctx = _gcry_mpi_ec_init (sk->E.p, sk->E.a);
+
+ _gcry_mpi_ec_mul_point (&Q, sk->E.n, &sk->E.G, ctx);
+ if (mpi_cmp_ui (Q.z, 0))
+ {
+ if (DBG_CIPHER)
+ log_debug ("check_secret_key: E is not a curve of order n\n");
+ goto leave;
+ }
+ /* pubkey cannot be PaI */
+ if (!mpi_cmp_ui (sk->Q.z, 0))
+ {
+ if (DBG_CIPHER)
+ log_debug ("Bad check: Q can not be a Point at Infinity!\n");
+ goto leave;
+ }
+ /* pubkey = [d]G over E */
+ _gcry_mpi_ec_mul_point (&Q, sk->d, &sk->E.G, ctx);
+ if ((Q.x == sk->Q.x) && (Q.y == sk->Q.y) && (Q.z == sk->Q.z))
+ {
+ if (DBG_CIPHER)
+ log_debug
+ ("Bad check: There is NO correspondence between 'd' and 'Q'!\n");
+ goto leave;
+ }
+ rc = 0; /* Okay. */
+
+ leave:
+ _gcry_mpi_ec_free (ctx);
+ mpi_free (y2);
+ mpi_free (y_2);
+ point_free (&Q);
+ return rc;
+}
+
+
+/*
+ * Return the signature struct (r,s) from the message hash. The caller
+ * must have allocated R and S.
+ */
+static gpg_err_code_t
+sign (gcry_mpi_t input, ECC_secret_key *skey, gcry_mpi_t r, gcry_mpi_t s)
+{
+ gpg_err_code_t err = 0;
+ gcry_mpi_t k, dr, sum, k_1, x;
+ mpi_point_t I;
+ mpi_ec_t ctx;
+
+ if (DBG_CIPHER)
+ log_mpidump ("ecdsa sign hash ", input );
+
+ k = NULL;
+ dr = mpi_alloc (0);
+ sum = mpi_alloc (0);
+ k_1 = mpi_alloc (0);
+ x = mpi_alloc (0);
+ point_init (&I);
+
+ mpi_set_ui (s, 0);
+ mpi_set_ui (r, 0);
+
+ ctx = _gcry_mpi_ec_init (skey->E.p, skey->E.a);
+
+ while (!mpi_cmp_ui (s, 0)) /* s == 0 */
+ {
+ while (!mpi_cmp_ui (r, 0)) /* r == 0 */
+ {
+ /* Note, that we are guaranteed to enter this loop at least
+ once because r has been intialized to 0. We can't use a
+ do_while because we want to keep the value of R even if S
+ has to be recomputed. */
+ mpi_free (k);
+ k = gen_k (skey->E.n, GCRY_STRONG_RANDOM);
+ _gcry_mpi_ec_mul_point (&I, k, &skey->E.G, ctx);
+ if (_gcry_mpi_ec_get_affine (x, NULL, &I, ctx))
+ {
+ if (DBG_CIPHER)
+ log_debug ("ecc sign: Failed to get affine coordinates\n");
+ err = GPG_ERR_BAD_SIGNATURE;
+ goto leave;
+ }
+ mpi_mod (r, x, skey->E.n); /* r = x mod n */
+ }
+ mpi_mulm (dr, skey->d, r, skey->E.n); /* dr = d*r mod n */
+ mpi_addm (sum, input, dr, skey->E.n); /* sum = hash + (d*r) mod n */
+ mpi_invm (k_1, k, skey->E.n); /* k_1 = k^(-1) mod n */
+ mpi_mulm (s, k_1, sum, skey->E.n); /* s = k^(-1)*(hash+(d*r)) mod n */
+ }
+
+ if (DBG_CIPHER)
+ {
+ log_mpidump ("ecdsa sign result r ", r);
+ log_mpidump ("ecdsa sign result s ", s);
+ }
+
+ leave:
+ _gcry_mpi_ec_free (ctx);
+ point_free (&I);
+ mpi_free (x);
+ mpi_free (k_1);
+ mpi_free (sum);
+ mpi_free (dr);
+ mpi_free (k);
+
+ return err;
+}
+
+
+/*
+ * Check if R and S verifies INPUT.
+ */
+static gpg_err_code_t
+verify (gcry_mpi_t input, ECC_public_key *pkey, gcry_mpi_t r, gcry_mpi_t s)
+{
+ gpg_err_code_t err = 0;
+ gcry_mpi_t h, h1, h2, x, y;
+ mpi_point_t Q, Q1, Q2;
+ mpi_ec_t ctx;
+
+ if( !(mpi_cmp_ui (r, 0) > 0 && mpi_cmp (r, pkey->E.n) < 0) )
+ return GPG_ERR_BAD_SIGNATURE; /* Assertion 0 < r < n failed. */
+ if( !(mpi_cmp_ui (s, 0) > 0 && mpi_cmp (s, pkey->E.n) < 0) )
+ return GPG_ERR_BAD_SIGNATURE; /* Assertion 0 < s < n failed. */
+
+ h = mpi_alloc (0);
+ h1 = mpi_alloc (0);
+ h2 = mpi_alloc (0);
+ x = mpi_alloc (0);
+ y = mpi_alloc (0);
+ point_init (&Q);
+ point_init (&Q1);
+ point_init (&Q2);
+
+ ctx = _gcry_mpi_ec_init (pkey->E.p, pkey->E.a);
+
+ /* h = s^(-1) (mod n) */
+ mpi_invm (h, s, pkey->E.n);
+/* log_mpidump (" h", h); */
+ /* h1 = hash * s^(-1) (mod n) */
+ mpi_mulm (h1, input, h, pkey->E.n);
+/* log_mpidump (" h1", h1); */
+ /* Q1 = [ hash * s^(-1) ]G */
+ _gcry_mpi_ec_mul_point (&Q1, h1, &pkey->E.G, ctx);
+/* log_mpidump ("Q1.x", Q1.x); */
+/* log_mpidump ("Q1.y", Q1.y); */
+/* log_mpidump ("Q1.z", Q1.z); */
+ /* h2 = r * s^(-1) (mod n) */
+ mpi_mulm (h2, r, h, pkey->E.n);
+/* log_mpidump (" h2", h2); */
+ /* Q2 = [ r * s^(-1) ]Q */
+ _gcry_mpi_ec_mul_point (&Q2, h2, &pkey->Q, ctx);
+/* log_mpidump ("Q2.x", Q2.x); */
+/* log_mpidump ("Q2.y", Q2.y); */
+/* log_mpidump ("Q2.z", Q2.z); */
+ /* Q = ([hash * s^(-1)]G) + ([r * s^(-1)]Q) */
+ _gcry_mpi_ec_add_points (&Q, &Q1, &Q2, ctx);
+/* log_mpidump (" Q.x", Q.x); */
+/* log_mpidump (" Q.y", Q.y); */
+/* log_mpidump (" Q.z", Q.z); */
+
+ if (!mpi_cmp_ui (Q.z, 0))
+ {
+ if (DBG_CIPHER)
+ log_debug ("ecc verify: Rejected\n");
+ err = GPG_ERR_BAD_SIGNATURE;
+ goto leave;
+ }
+ if (_gcry_mpi_ec_get_affine (x, y, &Q, ctx))
+ {
+ if (DBG_CIPHER)
+ log_debug ("ecc verify: Failed to get affine coordinates\n");
+ err = GPG_ERR_BAD_SIGNATURE;
+ goto leave;
+ }
+ mpi_mod (x, x, pkey->E.n); /* x = x mod E_n */
+ if (mpi_cmp (x, r)) /* x != r */
+ {
+ if (DBG_CIPHER)
+ {
+ log_mpidump (" x", x);
+ log_mpidump (" y", y);
+ log_mpidump (" r", r);
+ log_mpidump (" s", s);
+ log_debug ("ecc verify: Not verified\n");
+ }
+ err = GPG_ERR_BAD_SIGNATURE;
+ goto leave;
+ }
+ if (DBG_CIPHER)
+ log_debug ("ecc verify: Accepted\n");
+
+ leave:
+ _gcry_mpi_ec_free (ctx);
+ point_free (&Q2);
+ point_free (&Q1);
+ point_free (&Q);
+ mpi_free (y);
+ mpi_free (x);
+ mpi_free (h2);
+ mpi_free (h1);
+ mpi_free (h);
+ return err;
+}
+
+
+
+/*********************************************
+ ************** interface ******************
+ *********************************************/
+static gcry_mpi_t
+ec2os (gcry_mpi_t x, gcry_mpi_t y, gcry_mpi_t p)
+{
+ gpg_error_t err;
+ int pbytes = (mpi_get_nbits (p)+7)/8;
+ size_t n;
+ unsigned char *buf, *ptr;
+ gcry_mpi_t result;
+
+ buf = gcry_xmalloc ( 1 + 2*pbytes );
+ *buf = 04; /* Uncompressed point. */
+ ptr = buf+1;
+ err = gcry_mpi_print (GCRYMPI_FMT_USG, ptr, pbytes, &n, x);
+ if (err)
+ log_fatal ("mpi_print failed: %s\n", gpg_strerror (err));
+ if (n < pbytes)
+ {
+ memmove (ptr+(pbytes-n), ptr, n);
+ memset (ptr, 0, (pbytes-n));
+ }
+ ptr += pbytes;
+ err = gcry_mpi_print (GCRYMPI_FMT_USG, ptr, pbytes, &n, y);
+ if (err)
+ log_fatal ("mpi_print failed: %s\n", gpg_strerror (err));
+ if (n < pbytes)
+ {
+ memmove (ptr+(pbytes-n), ptr, n);
+ memset (ptr, 0, (pbytes-n));
+ }
+
+ err = gcry_mpi_scan (&result, GCRYMPI_FMT_USG, buf, 1+2*pbytes, NULL);
+ if (err)
+ log_fatal ("mpi_scan failed: %s\n", gpg_strerror (err));
+ gcry_free (buf);
+
+ return result;
+}
+
+
+/* RESULT must have been initialized and is set on success to the
+ point given by VALUE. */
+static gcry_error_t
+os2ec (mpi_point_t *result, gcry_mpi_t value)
+{
+ gcry_error_t err;
+ size_t n;
+ unsigned char *buf;
+ gcry_mpi_t x, y;
+
+ n = (mpi_get_nbits (value)+7)/8;
+ buf = gcry_xmalloc (n);
+ err = gcry_mpi_print (GCRYMPI_FMT_USG, buf, n, &n, value);
+ if (err)
+ {
+ gcry_free (buf);
+ return err;
+ }
+ if (n < 1)
+ {
+ gcry_free (buf);
+ return GPG_ERR_INV_OBJ;
+ }
+ if (*buf != 4)
+ {
+ gcry_free (buf);
+ return GPG_ERR_NOT_IMPLEMENTED; /* No support for point compression. */
+ }
+ if ( ((n-1)%2) )
+ {
+ gcry_free (buf);
+ return GPG_ERR_INV_OBJ;
+ }
+ n = (n-1)/2;
+ err = gcry_mpi_scan (&x, GCRYMPI_FMT_USG, buf+1, n, NULL);
+ if (err)
+ {
+ gcry_free (buf);
+ return err;
+ }
+ err = gcry_mpi_scan (&y, GCRYMPI_FMT_USG, buf+1+n, n, NULL);
+ gcry_free (buf);
+ if (err)
+ {
+ mpi_free (x);
+ return err;
+ }
+
+ mpi_set (result->x, x);
+ mpi_set (result->y, y);
+ mpi_set_ui (result->z, 1);
+
+ mpi_free (x);
+ mpi_free (y);
+
+ return 0;
+}
+
+
+/* Extended version of ecc_generate. */
+static gcry_err_code_t
+ecc_generate_ext (int algo, unsigned int nbits, unsigned long evalue,
+ const gcry_sexp_t genparms,
+ gcry_mpi_t *skey, gcry_mpi_t **retfactors,
+ gcry_sexp_t *r_extrainfo)
+{
+ gpg_err_code_t ec;
+ ECC_secret_key sk;
+ gcry_mpi_t g_x, g_y, q_x, q_y;
+ char *curve_name = NULL;
+ gcry_sexp_t l1;
+ int transient_key = 0;
+ const char *usedcurve = NULL;
+
+ (void)algo;
+ (void)evalue;
+
+ if (genparms)
+ {
+ /* Parse the optional "curve" parameter. */
+ l1 = gcry_sexp_find_token (genparms, "curve", 0);
+ if (l1)
+ {
+ curve_name = _gcry_sexp_nth_string (l1, 1);
+ gcry_sexp_release (l1);
+ if (!curve_name)
+ return GPG_ERR_INV_OBJ; /* No curve name or value too large. */
+ }
+
+ /* Parse the optional transient-key flag. */
+ l1 = gcry_sexp_find_token (genparms, "transient-key", 0);
+ if (l1)
+ {
+ transient_key = 1;
+ gcry_sexp_release (l1);
+ }
+ }
+
+ /* NBITS is required if no curve name has been given. */
+ if (!nbits && !curve_name)
+ return GPG_ERR_NO_OBJ; /* No NBITS parameter. */
+
+ g_x = mpi_new (0);
+ g_y = mpi_new (0);
+ q_x = mpi_new (0);
+ q_y = mpi_new (0);
+ ec = generate_key (&sk, nbits, curve_name, transient_key, g_x, g_y, q_x, q_y,
+ &usedcurve);
+ gcry_free (curve_name);
+ if (ec)
+ return ec;
+ if (usedcurve) /* Fixme: No error return checking. */
+ gcry_sexp_build (r_extrainfo, NULL, "(curve %s)", usedcurve);
+
+ skey[0] = sk.E.p;
+ skey[1] = sk.E.a;
+ skey[2] = sk.E.b;
+ skey[3] = ec2os (g_x, g_y, sk.E.p);
+ skey[4] = sk.E.n;
+ skey[5] = ec2os (q_x, q_y, sk.E.p);
+ skey[6] = sk.d;
+
+ mpi_free (g_x);
+ mpi_free (g_y);
+ mpi_free (q_x);
+ mpi_free (q_y);
+
+ point_free (&sk.E.G);
+ point_free (&sk.Q);
+
+ /* Make an empty list of factors. */
+ *retfactors = gcry_calloc ( 1, sizeof **retfactors );
+ if (!*retfactors)
+ return gpg_err_code_from_syserror (); /* Fixme: relase mem? */
+
+ if (DBG_CIPHER)
+ {
+ log_mpidump ("ecgen result p", skey[0]);
+ log_mpidump ("ecgen result a", skey[1]);
+ log_mpidump ("ecgen result b", skey[2]);
+ log_mpidump ("ecgen result G", skey[3]);
+ log_mpidump ("ecgen result n", skey[4]);
+ log_mpidump ("ecgen result Q", skey[5]);
+ log_mpidump ("ecgen result d", skey[6]);
+ }
+
+ return 0;
+}
+
+
+static gcry_err_code_t
+ecc_generate (int algo, unsigned int nbits, unsigned long evalue,
+ gcry_mpi_t *skey, gcry_mpi_t **retfactors)
+{
+ (void)evalue;
+ return ecc_generate_ext (algo, nbits, 0, NULL, skey, retfactors, NULL);
+}
+
+
+/* Return the parameters of the curve NAME in an MPI array. */
+static gcry_err_code_t
+ecc_get_param (const char *name, gcry_mpi_t *pkey)
+{
+ gpg_err_code_t err;
+ unsigned int nbits;
+ elliptic_curve_t E;
+ mpi_ec_t ctx;
+ gcry_mpi_t g_x, g_y;
+
+ err = fill_in_curve (0, name, &E, &nbits);
+ if (err)
+ return err;
+
+ g_x = mpi_new (0);
+ g_y = mpi_new (0);
+ ctx = _gcry_mpi_ec_init (E.p, E.a);
+ if (_gcry_mpi_ec_get_affine (g_x, g_y, &E.G, ctx))
+ log_fatal ("ecc get param: Failed to get affine coordinates\n");
+ _gcry_mpi_ec_free (ctx);
+ point_free (&E.G);
+
+ pkey[0] = E.p;
+ pkey[1] = E.a;
+ pkey[2] = E.b;
+ pkey[3] = ec2os (g_x, g_y, E.p);
+ pkey[4] = E.n;
+ pkey[5] = NULL;
+
+ mpi_free (g_x);
+ mpi_free (g_y);
+
+ return 0;
+}
+
+
+/* Return the parameters of the curve NAME as an S-expression. */
+static gcry_sexp_t
+ecc_get_param_sexp (const char *name)
+{
+ gcry_mpi_t pkey[6];
+ gcry_sexp_t result;
+ int i;
+
+ if (ecc_get_param (name, pkey))
+ return NULL;
+
+ if (gcry_sexp_build (&result, NULL,
+ "(public-key(ecc(p%m)(a%m)(b%m)(g%m)(n%m)))",
+ pkey[0], pkey[1], pkey[2], pkey[3], pkey[4]))
+ result = NULL;
+
+ for (i=0; pkey[i]; i++)
+ gcry_mpi_release (pkey[i]);
+
+ return result;
+}
+
+
+/* Return the name matching the parameters in PKEY. */
+static const char *
+ecc_get_curve (gcry_mpi_t *pkey, int iterator, unsigned int *r_nbits)
+{
+ gpg_err_code_t err;
+ elliptic_curve_t E;
+ int idx;
+ gcry_mpi_t tmp;
+ const char *result = NULL;
+
+ if (r_nbits)
+ *r_nbits = 0;
+
+ if (!pkey)
+ {
+ idx = iterator;
+ if (idx >= 0 && idx < DIM (domain_parms))
+ {
+ result = domain_parms[idx].desc;
+ if (r_nbits)
+ *r_nbits = domain_parms[idx].nbits;
+ }
+ return result;
+ }
+
+ if (!pkey[0] || !pkey[1] || !pkey[2] || !pkey[3] || !pkey[4])
+ return NULL;
+
+ E.p = pkey[0];
+ E.a = pkey[1];
+ E.b = pkey[2];
+ point_init (&E.G);
+ err = os2ec (&E.G, pkey[3]);
+ if (err)
+ {
+ point_free (&E.G);
+ return NULL;
+ }
+ E.n = pkey[4];
+
+ for (idx = 0; domain_parms[idx].desc; idx++)
+ {
+ tmp = scanval (domain_parms[idx].p);
+ if (!mpi_cmp (tmp, E.p))
+ {
+ mpi_free (tmp);
+ tmp = scanval (domain_parms[idx].a);
+ if (!mpi_cmp (tmp, E.a))
+ {
+ mpi_free (tmp);
+ tmp = scanval (domain_parms[idx].b);
+ if (!mpi_cmp (tmp, E.b))
+ {
+ mpi_free (tmp);
+ tmp = scanval (domain_parms[idx].n);
+ if (!mpi_cmp (tmp, E.n))
+ {
+ mpi_free (tmp);
+ tmp = scanval (domain_parms[idx].g_x);
+ if (!mpi_cmp (tmp, E.G.x))
+ {
+ mpi_free (tmp);
+ tmp = scanval (domain_parms[idx].g_y);
+ if (!mpi_cmp (tmp, E.G.y))
+ {
+ result = domain_parms[idx].desc;
+ if (r_nbits)
+ *r_nbits = domain_parms[idx].nbits;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ mpi_free (tmp);
+ }
+
+ point_free (&E.G);
+
+ return result;
+}
+
+
+static gcry_err_code_t
+ecc_check_secret_key (int algo, gcry_mpi_t *skey)
+{
+ gpg_err_code_t err;
+ ECC_secret_key sk;
+
+ (void)algo;
+
+ /* FIXME: This check looks a bit fishy: Now long is the array? */
+ if (!skey[0] || !skey[1] || !skey[2] || !skey[3] || !skey[4] || !skey[5]
+ || !skey[6])
+ return GPG_ERR_BAD_MPI;
+
+ sk.E.p = skey[0];
+ sk.E.a = skey[1];
+ sk.E.b = skey[2];
+ point_init (&sk.E.G);
+ err = os2ec (&sk.E.G, skey[3]);
+ if (err)
+ {
+ point_free (&sk.E.G);
+ return err;
+ }
+ sk.E.n = skey[4];
+ point_init (&sk.Q);
+ err = os2ec (&sk.Q, skey[5]);
+ if (err)
+ {
+ point_free (&sk.E.G);
+ point_free (&sk.Q);
+ return err;
+ }
+
+ sk.d = skey[6];
+
+ if (check_secret_key (&sk))
+ {
+ point_free (&sk.E.G);
+ point_free (&sk.Q);
+ return GPG_ERR_BAD_SECKEY;
+ }
+ point_free (&sk.E.G);
+ point_free (&sk.Q);
+ return 0;
+}
+
+
+static gcry_err_code_t
+ecc_sign (int algo, gcry_mpi_t *resarr, gcry_mpi_t data, gcry_mpi_t *skey)
+{
+ gpg_err_code_t err;
+ ECC_secret_key sk;
+
+ (void)algo;
+
+ if (!data || !skey[0] || !skey[1] || !skey[2] || !skey[3] || !skey[4]
+ || !skey[5] || !skey[6] )
+ return GPG_ERR_BAD_MPI;
+
+ sk.E.p = skey[0];
+ sk.E.a = skey[1];
+ sk.E.b = skey[2];
+ point_init (&sk.E.G);
+ err = os2ec (&sk.E.G, skey[3]);
+ if (err)
+ {
+ point_free (&sk.E.G);
+ return err;
+ }
+ sk.E.n = skey[4];
+ point_init (&sk.Q);
+ err = os2ec (&sk.Q, skey[5]);
+ if (err)
+ {
+ point_free (&sk.E.G);
+ point_free (&sk.Q);
+ return err;
+ }
+ sk.d = skey[6];
+
+ resarr[0] = mpi_alloc (mpi_get_nlimbs (sk.E.p));
+ resarr[1] = mpi_alloc (mpi_get_nlimbs (sk.E.p));
+ err = sign (data, &sk, resarr[0], resarr[1]);
+ if (err)
+ {
+ mpi_free (resarr[0]);
+ mpi_free (resarr[1]);
+ resarr[0] = NULL; /* Mark array as released. */
+ }
+ point_free (&sk.E.G);
+ point_free (&sk.Q);
+ return err;
+}
+
+
+static gcry_err_code_t
+ecc_verify (int algo, gcry_mpi_t hash, gcry_mpi_t *data, gcry_mpi_t *pkey,
+ int (*cmp)(void *, gcry_mpi_t), void *opaquev)
+{
+ gpg_err_code_t err;
+ ECC_public_key pk;
+
+ (void)algo;
+ (void)cmp;
+ (void)opaquev;
+
+ if (!data[0] || !data[1] || !hash || !pkey[0] || !pkey[1] || !pkey[2]
+ || !pkey[3] || !pkey[4] || !pkey[5] )
+ return GPG_ERR_BAD_MPI;
+
+ pk.E.p = pkey[0];
+ pk.E.a = pkey[1];
+ pk.E.b = pkey[2];
+ point_init (&pk.E.G);
+ err = os2ec (&pk.E.G, pkey[3]);
+ if (err)
+ {
+ point_free (&pk.E.G);
+ return err;
+ }
+ pk.E.n = pkey[4];
+ point_init (&pk.Q);
+ err = os2ec (&pk.Q, pkey[5]);
+ if (err)
+ {
+ point_free (&pk.E.G);
+ point_free (&pk.Q);
+ return err;
+ }
+
+ err = verify (hash, &pk, data[0], data[1]);
+
+ point_free (&pk.E.G);
+ point_free (&pk.Q);
+ return err;
+}
+
+
+/* ecdh raw is classic 2-round DH protocol published in 1976.
+ *
+ * Overview of ecc_encrypt_raw and ecc_decrypt_raw.
+ *
+ * As with any PK operation, encrypt version uses a public key and
+ * decrypt -- private.
+ *
+ * Symbols used below:
+ * G - field generator point
+ * d - private long-term scalar
+ * dG - public long-term key
+ * k - ephemeral scalar
+ * kG - ephemeral public key
+ * dkG - shared secret
+ *
+ * ecc_encrypt_raw description:
+ * input:
+ * data[0] : private scalar (k)
+ * output:
+ * result[0] : shared point (kdG)
+ * result[1] : generated ephemeral public key (kG)
+ *
+ * ecc_decrypt_raw description:
+ * input:
+ * data[0] : a point kG (ephemeral public key)
+ * output:
+ * result[0] : shared point (kdG)
+ */
+static gcry_err_code_t
+ecc_encrypt_raw (int algo, gcry_mpi_t *resarr, gcry_mpi_t k,
+ gcry_mpi_t *pkey, int flags)
+{
+ ECC_public_key pk;
+ mpi_ec_t ctx;
+ gcry_mpi_t result[2];
+ int err;
+
+ (void)algo;
+ (void)flags;
+
+ if (!k
+ || !pkey[0] || !pkey[1] || !pkey[2] || !pkey[3] || !pkey[4] || !pkey[5])
+ return GPG_ERR_BAD_MPI;
+
+ pk.E.p = pkey[0];
+ pk.E.a = pkey[1];
+ pk.E.b = pkey[2];
+ point_init (&pk.E.G);
+ err = os2ec (&pk.E.G, pkey[3]);
+ if (err)
+ {
+ point_free (&pk.E.G);
+ return err;
+ }
+ pk.E.n = pkey[4];
+ point_init (&pk.Q);
+ err = os2ec (&pk.Q, pkey[5]);
+ if (err)
+ {
+ point_free (&pk.E.G);
+ point_free (&pk.Q);
+ return err;
+ }
+
+ ctx = _gcry_mpi_ec_init (pk.E.p, pk.E.a);
+
+ /* The following is false: assert( mpi_cmp_ui( R.x, 1 )==0 );, so */
+ {
+ mpi_point_t R; /* Result that we return. */
+ gcry_mpi_t x, y;
+
+ x = mpi_new (0);
+ y = mpi_new (0);
+
+ point_init (&R);
+
+ /* R = kQ <=> R = kdG */
+ _gcry_mpi_ec_mul_point (&R, k, &pk.Q, ctx);
+
+ if (_gcry_mpi_ec_get_affine (x, y, &R, ctx))
+ log_fatal ("ecdh: Failed to get affine coordinates for kdG\n");
+
+ result[0] = ec2os (x, y, pk.E.p);
+
+ /* R = kG */
+ _gcry_mpi_ec_mul_point (&R, k, &pk.E.G, ctx);
+
+ if (_gcry_mpi_ec_get_affine (x, y, &R, ctx))
+ log_fatal ("ecdh: Failed to get affine coordinates for kG\n");
+
+ result[1] = ec2os (x, y, pk.E.p);
+
+ mpi_free (x);
+ mpi_free (y);
+
+ point_free (&R);
+ }
+
+ _gcry_mpi_ec_free (ctx);
+ point_free (&pk.E.G);
+ point_free (&pk.Q);
+
+ if (!result[0] || !result[1])
+ {
+ mpi_free (result[0]);
+ mpi_free (result[1]);
+ return GPG_ERR_ENOMEM;
+ }
+
+ /* Success. */
+ resarr[0] = result[0];
+ resarr[1] = result[1];
+
+ return 0;
+}
+
+/* input:
+ * data[0] : a point kG (ephemeral public key)
+ * output:
+ * resaddr[0] : shared point kdG
+ *
+ * see ecc_encrypt_raw for details.
+ */
+static gcry_err_code_t
+ecc_decrypt_raw (int algo, gcry_mpi_t *result, gcry_mpi_t *data,
+ gcry_mpi_t *skey, int flags)
+{
+ ECC_secret_key sk;
+ mpi_point_t R; /* Result that we return. */
+ mpi_point_t kG;
+ mpi_ec_t ctx;
+ gcry_mpi_t r;
+ int err;
+
+ (void)algo;
+ (void)flags;
+
+ *result = NULL;
+
+ if (!data || !data[0]
+ || !skey[0] || !skey[1] || !skey[2] || !skey[3] || !skey[4]
+ || !skey[5] || !skey[6] )
+ return GPG_ERR_BAD_MPI;
+
+ point_init (&kG);
+ err = os2ec (&kG, data[0]);
+ if (err)
+ {
+ point_free (&kG);
+ return err;
+ }
+
+
+ sk.E.p = skey[0];
+ sk.E.a = skey[1];
+ sk.E.b = skey[2];
+ point_init (&sk.E.G);
+ err = os2ec (&sk.E.G, skey[3]);
+ if (err)
+ {
+ point_free (&kG);
+ point_free (&sk.E.G);
+ return err;
+ }
+ sk.E.n = skey[4];
+ point_init (&sk.Q);
+ err = os2ec (&sk.Q, skey[5]);
+ if (err)
+ {
+ point_free (&kG);
+ point_free (&sk.E.G);
+ point_free (&sk.Q);
+ return err;
+ }
+ sk.d = skey[6];
+
+ ctx = _gcry_mpi_ec_init (sk.E.p, sk.E.a);
+
+ /* R = dkG */
+ point_init (&R);
+ _gcry_mpi_ec_mul_point (&R, sk.d, &kG, ctx);
+
+ point_free (&kG);
+
+ /* The following is false: assert( mpi_cmp_ui( R.x, 1 )==0 );, so: */
+ {
+ gcry_mpi_t x, y;
+
+ x = mpi_new (0);
+ y = mpi_new (0);
+
+ if (_gcry_mpi_ec_get_affine (x, y, &R, ctx))
+ log_fatal ("ecdh: Failed to get affine coordinates\n");
+
+ r = ec2os (x, y, sk.E.p);
+ mpi_free (x);
+ mpi_free (y);
+ }
+
+ point_free (&R);
+ _gcry_mpi_ec_free (ctx);
+ point_free (&kG);
+ point_free (&sk.E.G);
+ point_free (&sk.Q);
+
+ if (!r)
+ return GPG_ERR_ENOMEM;
+
+ /* Success. */
+
+ *result = r;
+
+ return 0;
+}
+
+
+static unsigned int
+ecc_get_nbits (int algo, gcry_mpi_t *pkey)
+{
+ (void)algo;
+
+ return mpi_get_nbits (pkey[0]);
+}
+
+
+/* See rsa.c for a description of this function. */
+static gpg_err_code_t
+compute_keygrip (gcry_md_hd_t md, gcry_sexp_t keyparam)
+{
+#define N_COMPONENTS 6
+ static const char names[N_COMPONENTS+1] = "pabgnq";
+ gpg_err_code_t ec = 0;
+ gcry_sexp_t l1;
+ gcry_mpi_t values[N_COMPONENTS];
+ int idx;
+
+ /* Clear the values for easier error cleanup. */
+ for (idx=0; idx < N_COMPONENTS; idx++)
+ values[idx] = NULL;
+
+ /* Fill values with all provided parameters. */
+ for (idx=0; idx < N_COMPONENTS; idx++)
+ {
+ l1 = gcry_sexp_find_token (keyparam, names+idx, 1);
+ if (l1)
+ {
+ values[idx] = gcry_sexp_nth_mpi (l1, 1, GCRYMPI_FMT_USG);
+ gcry_sexp_release (l1);
+ if (!values[idx])
+ {
+ ec = GPG_ERR_INV_OBJ;
+ goto leave;
+ }
+ }
+ }
+
+ /* Check whether a curve parameter is available and use that to fill
+ in missing values. */
+ l1 = gcry_sexp_find_token (keyparam, "curve", 5);
+ if (l1)
+ {
+ char *curve;
+ gcry_mpi_t tmpvalues[N_COMPONENTS];
+
+ for (idx = 0; idx < N_COMPONENTS; idx++)
+ tmpvalues[idx] = NULL;
+
+ curve = _gcry_sexp_nth_string (l1, 1);
+ gcry_sexp_release (l1);
+ if (!curve)
+ {
+ ec = GPG_ERR_INV_OBJ; /* Name missing or out of core. */
+ goto leave;
+ }
+ ec = ecc_get_param (curve, tmpvalues);
+ gcry_free (curve);
+ if (ec)
+ goto leave;
+
+ for (idx = 0; idx < N_COMPONENTS; idx++)
+ {
+ if (!values[idx])
+ values[idx] = tmpvalues[idx];
+ else
+ mpi_free (tmpvalues[idx]);
+ }
+ }
+
+ /* Check that all parameters are known and normalize all MPIs (that
+ should not be required but we use an internal function later and
+ thus we better make 100% sure that they are normalized). */
+ for (idx = 0; idx < N_COMPONENTS; idx++)
+ if (!values[idx])
+ {
+ ec = GPG_ERR_NO_OBJ;
+ goto leave;
+ }
+ else
+ _gcry_mpi_normalize (values[idx]);
+
+ /* Hash them all. */
+ for (idx = 0; idx < N_COMPONENTS; idx++)
+ {
+ char buf[30];
+ unsigned char *rawmpi;
+ unsigned int rawmpilen;
+
+ rawmpi = _gcry_mpi_get_buffer (values[idx], &rawmpilen, NULL);
+ if (!rawmpi)
+ {
+ ec = gpg_err_code_from_syserror ();
+ goto leave;
+ }
+ snprintf (buf, sizeof buf, "(1:%c%u:", names[idx], rawmpilen);
+ gcry_md_write (md, buf, strlen (buf));
+ gcry_md_write (md, rawmpi, rawmpilen);
+ gcry_md_write (md, ")", 1);
+ gcry_free (rawmpi);
+ }
+
+ leave:
+ for (idx = 0; idx < N_COMPONENTS; idx++)
+ _gcry_mpi_release (values[idx]);
+
+ return ec;
+#undef N_COMPONENTS
+}
+
+
+
+
+
+/*
+ Self-test section.
+ */
+
+
+static gpg_err_code_t
+selftests_ecdsa (selftest_report_func_t report)
+{
+ const char *what;
+ const char *errtxt;
+
+ what = "low-level";
+ errtxt = NULL; /*selftest ();*/
+ if (errtxt)
+ goto failed;
+
+ /* FIXME: need more tests. */
+
+ return 0; /* Succeeded. */
+
+ failed:
+ if (report)
+ report ("pubkey", GCRY_PK_ECDSA, what, errtxt);
+ return GPG_ERR_SELFTEST_FAILED;
+}
+
+
+/* Run a full self-test for ALGO and return 0 on success. */
+static gpg_err_code_t
+run_selftests (int algo, int extended, selftest_report_func_t report)
+{
+ gpg_err_code_t ec;
+
+ (void)extended;
+
+ switch (algo)
+ {
+ case GCRY_PK_ECDSA:
+ ec = selftests_ecdsa (report);
+ break;
+ default:
+ ec = GPG_ERR_PUBKEY_ALGO;
+ break;
+
+ }
+ return ec;
+}
+
+
+
+
+static const char *ecdsa_names[] =
+ {
+ "ecdsa",
+ "ecc",
+ NULL,
+ };
+static const char *ecdh_names[] =
+ {
+ "ecdh",
+ "ecc",
+ NULL,
+ };
+
+gcry_pk_spec_t _gcry_pubkey_spec_ecdsa =
+ {
+ "ECDSA", ecdsa_names,
+ "pabgnq", "pabgnqd", "", "rs", "pabgnq",
+ GCRY_PK_USAGE_SIGN,
+ ecc_generate,
+ ecc_check_secret_key,
+ NULL,
+ NULL,
+ ecc_sign,
+ ecc_verify,
+ ecc_get_nbits
+ };
+
+gcry_pk_spec_t _gcry_pubkey_spec_ecdh =
+ {
+ "ECDH", ecdh_names,
+ "pabgnq", "pabgnqd", "se", "", "pabgnq",
+ GCRY_PK_USAGE_ENCR,
+ ecc_generate,
+ ecc_check_secret_key,
+ ecc_encrypt_raw,
+ ecc_decrypt_raw,
+ NULL,
+ NULL,
+ ecc_get_nbits
+ };
+
+
+pk_extra_spec_t _gcry_pubkey_extraspec_ecdsa =
+ {
+ run_selftests,
+ ecc_generate_ext,
+ compute_keygrip,
+ ecc_get_param,
+ ecc_get_curve,
+ ecc_get_param_sexp
+ };
diff --git a/libgcrypt-1.5.3-ecc-test-fix.patch b/libgcrypt-1.5.3-ecc-test-fix.patch
new file mode 100644
index 0000000..a5cca64
--- /dev/null
+++ b/libgcrypt-1.5.3-ecc-test-fix.patch
@@ -0,0 +1,12 @@
+diff -up libgcrypt-1.5.3/tests/benchmark.c.eccfix libgcrypt-1.5.3/tests/benchmark.c
+--- libgcrypt-1.5.3/tests/benchmark.c.eccfix 2013-10-20 23:45:32.157297611 +0100
++++ libgcrypt-1.5.3/tests/benchmark.c 2013-10-20 23:45:42.683275072 +0100
+@@ -962,7 +962,7 @@ ecc_bench (int iterations, int print_hea
+ {
+ #if USE_ECC
+ gpg_error_t err;
+- int p_sizes[] = { 192, 224, 256, 384, 521 };
++ int p_sizes[] = { 256, 384 };
+ int testno;
+
+ if (print_header)
diff --git a/libgcrypt.spec b/libgcrypt.spec
index 6aca195..833e92a 100644
--- a/libgcrypt.spec
+++ b/libgcrypt.spec
@@ -1,17 +1,19 @@
Name: libgcrypt
Version: 1.5.3
-Release: 1%{?dist}
+Release: 2%{?dist}
URL: http://www.gnupg.org/
Source0: libgcrypt-%{version}-hobbled.tar.xz
# The original libgcrypt sources now contain potentially patented ECC
# cipher support. We have to remove it in the tarball we ship with
-# the hobble-libgcrypt script.
+# the hobble-libgcrypt script.
+# (We replace it with RH approved ECC in Source4-5)
#Source0: ftp://ftp.gnupg.org/gcrypt/libgcrypt/libgcrypt-%{version}.tar.bz2
#Source1: ftp://ftp.gnupg.org/gcrypt/libgcrypt/libgcrypt-%{version}.tar.bz2.sig
Source2: wk at g10code.com
Source3: hobble-libgcrypt
-# do not run the ecc curves test
-Patch1: libgcrypt-1.5.0-noecc.patch
+# Approved ECC support (from 1.5.3)
+Source4: ecc.c
+Source5: curves.c
# make FIPS hmac compatible with fipscheck - non upstreamable
Patch2: libgcrypt-1.5.0-use-fipscheck.patch
# fix tests in the FIPS mode, fix the FIPS-186-3 DSA keygen
@@ -29,6 +31,8 @@ Patch11: libgcrypt-1.5.1-use-poll.patch
Patch12: libgcrypt-1.5.2-aliasing.patch
# slight optimalization of mpicoder.c to silence Valgrind (#968288)
Patch13: libgcrypt-1.5.2-mpicoder-gccopt.patch
+# fix tests to work with approved ECC
+Patch14: libgcrypt-1.5.3-ecc-test-fix.patch
%define gcrylibdir %{_libdir}
@@ -64,7 +68,6 @@ applications using libgcrypt.
%prep
%setup -q
%{SOURCE3}
-%patch1 -p1 -b .noecc
%patch2 -p1 -b .use-fipscheck
%patch5 -p1 -b .tests
%patch6 -p1 -b .cfgrandom
@@ -73,6 +76,10 @@ applications using libgcrypt.
%patch11 -p1 -b .use-poll
%patch12 -p1 -b .aliasing
%patch13 -p1 -b .gccopt
+%patch14 -p1 -b .eccfix
+cp %{SOURCE4} cipher/
+rm -rf tests/curves.c
+cp %{SOURCE5} tests/curves.c
%build
%configure --disable-static \
@@ -81,7 +88,7 @@ applications using libgcrypt.
%endif
--enable-noexecstack \
--enable-hmac-binary-check \
- --enable-pubkey-ciphers='dsa elgamal rsa' \
+ --enable-pubkey-ciphers='dsa elgamal rsa ecc' \
--disable-O-flag-munging
make %{?_smp_mflags}
@@ -174,6 +181,9 @@ exit 0
%doc COPYING
%changelog
+* Sun Oct 20 2013 Tom Callaway <spot at fedoraproject.org> - 1.5.3-2
+- add cleared ECC support
+
* Fri Jul 26 2013 Tomáš Mráz <tmraz at redhat.com> 1.5.3-1
- new upstream version fixing cache side-channel attack on RSA private keys
More information about the scm-commits
mailing list