summaryrefslogtreecommitdiffstats
path: root/comm/third_party/libgcrypt/cipher/ecc.c
diff options
context:
space:
mode:
Diffstat (limited to 'comm/third_party/libgcrypt/cipher/ecc.c')
-rw-r--r--comm/third_party/libgcrypt/cipher/ecc.c1779
1 files changed, 1779 insertions, 0 deletions
diff --git a/comm/third_party/libgcrypt/cipher/ecc.c b/comm/third_party/libgcrypt/cipher/ecc.c
new file mode 100644
index 0000000000..5d8c7607ab
--- /dev/null
+++ b/comm/third_party/libgcrypt/cipher/ecc.c
@@ -0,0 +1,1779 @@
+/* ecc.c - Elliptic Curve Cryptography
+ * Copyright (C) 2007, 2008, 2010, 2011 Free Software Foundation, Inc.
+ * Copyright (C) 2013, 2015 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 <http://www.gnu.org/licenses/>.
+ */
+
+/* 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 originally 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:
+
+ - 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.
+
+*/
+
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "g10lib.h"
+#include "mpi.h"
+#include "cipher.h"
+#include "context.h"
+#include "ec-context.h"
+#include "pubkey-internal.h"
+#include "ecc-common.h"
+
+
+static const char *ecc_names[] =
+ {
+ "ecc",
+ "ecdsa",
+ "ecdh",
+ "eddsa",
+ "gost",
+ "sm2",
+ NULL,
+ };
+
+
+/* Sample NIST P-256 key from RFC 6979 A.2.5 */
+static const char sample_public_key_secp256[] =
+ "(public-key"
+ " (ecc"
+ " (curve secp256r1)"
+ " (q #04"
+ /**/ "60FED4BA255A9D31C961EB74C6356D68C049B8923B61FA6CE669622E60F29FB6"
+ /**/ "7903FE1008B8BC99A41AE9E95628BC64F2F1B20C2D7E9F5177A3C294D4462299#)))";
+
+static const char sample_secret_key_secp256[] =
+ "(private-key"
+ " (ecc"
+ " (curve secp256r1)"
+ " (d #C9AFA9D845BA75166B5C215767B1D6934E50C3DB36E89B127B8A622B120F6721#)"
+ " (q #04"
+ /**/ "60FED4BA255A9D31C961EB74C6356D68C049B8923B61FA6CE669622E60F29FB6"
+ /**/ "7903FE1008B8BC99A41AE9E95628BC64F2F1B20C2D7E9F5177A3C294D4462299#)))";
+
+
+/* Registered progress function and its callback value. */
+static void (*progress_cb) (void *, const char*, int, int, int);
+static void *progress_cb_data;
+
+
+
+/* Local prototypes. */
+static void test_keys (mpi_ec_t ec, unsigned int nbits);
+static void test_ecdh_only_keys (mpi_ec_t ec, unsigned int nbits, int flags);
+static unsigned int ecc_get_nbits (gcry_sexp_t parms);
+
+
+
+
+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); */
+/* } */
+
+
+
+/**
+ * nist_generate_key - Standard version of the ECC key generation.
+ * @ec: Elliptic curve computation context.
+ * @flags: Flags controlling aspects of the creation.
+ * @r_x: On success this receives an allocated MPI with the affine
+ * x-coordinate of the poblic key. On error NULL is stored.
+ * @r_y: Ditto for the y-coordinate.
+ *
+ * Return: An error code.
+ *
+ * The @flags bits used by this function are %PUBKEY_FLAG_TRANSIENT to
+ * use a faster RNG, and %PUBKEY_FLAG_NO_KEYTEST to skip the assertion
+ * that the key works as expected.
+ *
+ * FIXME: Check whether N is needed.
+ */
+static gpg_err_code_t
+nist_generate_key (mpi_ec_t ec, int flags,
+ gcry_mpi_t *r_x, gcry_mpi_t *r_y)
+{
+ mpi_point_struct Q;
+ gcry_random_level_t random_level;
+ gcry_mpi_t x, y;
+ const unsigned int pbits = ec->nbits;
+
+ point_init (&Q);
+
+ if ((flags & PUBKEY_FLAG_TRANSIENT_KEY))
+ random_level = GCRY_STRONG_RANDOM;
+ else
+ random_level = GCRY_VERY_STRONG_RANDOM;
+
+ /* Generate a secret. */
+ if (ec->dialect == ECC_DIALECT_ED25519
+ || ec->dialect == ECC_DIALECT_SAFECURVE
+ || (flags & PUBKEY_FLAG_DJB_TWEAK))
+ {
+ char *rndbuf;
+ int len = (pbits+7)/8;
+
+ rndbuf = _gcry_random_bytes_secure (len, random_level);
+ if (ec->dialect == ECC_DIALECT_SAFECURVE)
+ ec->d = mpi_set_opaque (NULL, rndbuf, len*8);
+ else
+ {
+ ec->d = mpi_snew (pbits);
+ if ((pbits % 8))
+ rndbuf[0] &= (1 << (pbits % 8)) - 1;
+ rndbuf[0] |= (1 << ((pbits + 7) % 8));
+ rndbuf[len-1] &= (256 - ec->h);
+ _gcry_mpi_set_buffer (ec->d, rndbuf, len, 0);
+ xfree (rndbuf);
+ }
+ }
+ else
+ ec->d = _gcry_dsa_gen_k (ec->n, random_level);
+
+ /* Compute Q. */
+ _gcry_mpi_ec_mul_point (&Q, ec->d, ec->G, ec);
+
+ x = mpi_new (pbits);
+ if (r_y == NULL)
+ y = NULL;
+ else
+ y = mpi_new (pbits);
+ if (_gcry_mpi_ec_get_affine (x, y, &Q, ec))
+ log_fatal ("ecgen: Failed to get affine coordinates for %s\n", "Q");
+
+ /* We want the Q=(x,y) be a "compliant key" in terms of the
+ * http://tools.ietf.org/html/draft-jivsov-ecc-compact, which simply
+ * means that we choose either Q=(x,y) or -Q=(x,p-y) such that we
+ * end up with the min(y,p-y) as the y coordinate. Such a public
+ * key allows the most efficient compression: y can simply be
+ * dropped because we know that it's a minimum of the two
+ * possibilities without any loss of security. Note that we don't
+ * do that for Ed25519 so that we do not violate the special
+ * construction of the secret key. */
+ if (r_y == NULL || ec->dialect == ECC_DIALECT_ED25519)
+ ec->Q = mpi_point_set (NULL, Q.x, Q.y, Q.z);
+ else
+ {
+ gcry_mpi_t negative;
+
+ negative = mpi_new (pbits);
+
+ if (ec->model == MPI_EC_WEIERSTRASS)
+ mpi_sub (negative, ec->p, y); /* negative = p - y */
+ else
+ mpi_sub (negative, ec->p, x); /* negative = p - x */
+
+ if (mpi_cmp (negative, y) < 0) /* p - y < p */
+ {
+ /* We need to end up with -Q; this assures that new Q's y is
+ the smallest one */
+ if (ec->model == MPI_EC_WEIERSTRASS)
+ {
+ mpi_free (y);
+ y = negative;
+ }
+ else
+ {
+ mpi_free (x);
+ x = negative;
+ }
+ mpi_sub (ec->d, ec->n, ec->d); /* d = order - d */
+ ec->Q = mpi_point_set (NULL, x, y, mpi_const (MPI_C_ONE));
+
+ if (DBG_CIPHER)
+ log_debug ("ecgen converted Q to a compliant point\n");
+ }
+ else /* p - y >= p */
+ {
+ /* No change is needed exactly 50% of the time: just copy. */
+ mpi_free (negative);
+ ec->Q = mpi_point_set (NULL, Q.x, Q.y, Q.z);
+ if (DBG_CIPHER)
+ log_debug ("ecgen didn't need to convert Q to a compliant point\n");
+ }
+ }
+
+ *r_x = x;
+ if (r_y)
+ *r_y = y;
+
+ point_free (&Q);
+ /* Now we can test our keys (this should never fail!). */
+ if ((flags & PUBKEY_FLAG_NO_KEYTEST))
+ ; /* User requested to skip the test. */
+ else if (ec->model == MPI_EC_MONTGOMERY)
+ test_ecdh_only_keys (ec, ec->nbits - 63, flags);
+ else
+ test_keys (ec, ec->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 (mpi_ec_t ec, unsigned int nbits)
+{
+ gcry_mpi_t test = mpi_new (nbits);
+ mpi_point_struct 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_);
+
+ _gcry_mpi_randomize (test, nbits, GCRY_WEAK_RANDOM);
+
+ if (_gcry_ecc_ecdsa_sign (test, ec, r, s, 0, 0) )
+ log_fatal ("ECDSA operation: sign failed\n");
+
+ if (_gcry_ecc_ecdsa_verify (test, ec, r, s))
+ {
+ log_fatal ("ECDSA operation: sign, verify failed\n");
+ }
+
+ if (DBG_CIPHER)
+ log_debug ("ECDSA operation: sign, verify ok.\n");
+
+ point_free (&R_);
+ mpi_free (s);
+ mpi_free (r);
+ mpi_free (out);
+ mpi_free (c);
+ mpi_free (test);
+}
+
+
+static void
+test_ecdh_only_keys (mpi_ec_t ec, unsigned int nbits, int flags)
+{
+ gcry_mpi_t test;
+ mpi_point_struct R_;
+ gcry_mpi_t x0, x1;
+
+ if (DBG_CIPHER)
+ log_debug ("Testing ECDH only key.\n");
+
+ point_init (&R_);
+
+ if (ec->dialect == ECC_DIALECT_SAFECURVE || (flags & PUBKEY_FLAG_DJB_TWEAK))
+ {
+ char *rndbuf;
+ const unsigned int pbits = ec->nbits;
+ int len = (pbits+7)/8;
+
+ rndbuf = _gcry_random_bytes (len, GCRY_WEAK_RANDOM);
+ if (ec->dialect == ECC_DIALECT_SAFECURVE)
+ test = mpi_set_opaque (NULL, rndbuf, len*8);
+ else
+ {
+ test = mpi_new (pbits);
+ if ((pbits % 8))
+ rndbuf[0] &= (1 << (pbits % 8)) - 1;
+ rndbuf[0] |= (1 << ((pbits + 7) % 8));
+ rndbuf[len-1] &= (256 - ec->h);
+ _gcry_mpi_set_buffer (test, rndbuf, len, 0);
+ xfree (rndbuf);
+ }
+ }
+ else
+ {
+ test = mpi_new (nbits);
+ _gcry_mpi_randomize (test, nbits, GCRY_WEAK_RANDOM);
+ }
+
+ x0 = mpi_new (0);
+ x1 = mpi_new (0);
+
+ /* R_ = hkQ <=> R_ = hkdG */
+ _gcry_mpi_ec_mul_point (&R_, test, ec->Q, ec);
+ if (ec->dialect == ECC_DIALECT_STANDARD && !(flags & PUBKEY_FLAG_DJB_TWEAK))
+ _gcry_mpi_ec_mul_point (&R_, _gcry_mpi_get_const (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");
+
+ _gcry_mpi_ec_mul_point (&R_, test, ec->G, ec);
+ _gcry_mpi_ec_mul_point (&R_, ec->d, &R_, ec);
+ /* R_ = hdkG */
+ if (ec->dialect == ECC_DIALECT_STANDARD && !(flags & PUBKEY_FLAG_DJB_TWEAK))
+ _gcry_mpi_ec_mul_point (&R_, _gcry_mpi_get_const (ec->h), &R_, ec);
+
+ if (_gcry_mpi_ec_get_affine (x1, NULL, &R_, ec))
+ log_fatal ("ecdh: Failed to get affine coordinates for hdkG\n");
+
+ if (mpi_cmp (x0, x1))
+ {
+ log_fatal ("ECDH test failed.\n");
+ }
+
+ mpi_free (x0);
+ mpi_free (x1);
+
+ point_free (&R_);
+ 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 (mpi_ec_t ec, int flags)
+{
+ int rc = 1;
+ mpi_point_struct Q;
+ gcry_mpi_t x1, y1;
+ gcry_mpi_t x2 = NULL;
+ gcry_mpi_t y2 = NULL;
+
+ point_init (&Q);
+ x1 = mpi_new (0);
+ if (ec->model == MPI_EC_MONTGOMERY)
+ y1 = NULL;
+ else
+ y1 = mpi_new (0);
+
+ /* G in E(F_p) */
+ if (!_gcry_mpi_ec_curve_point (ec->G, ec))
+ {
+ if (DBG_CIPHER)
+ log_debug ("Bad check: Point 'G' does not belong to curve 'E'!\n");
+ goto leave;
+ }
+
+ /* G != PaI */
+ if (!mpi_cmp_ui (ec->G->z, 0))
+ {
+ if (DBG_CIPHER)
+ log_debug ("Bad check: 'G' cannot be Point at Infinity!\n");
+ goto leave;
+ }
+
+ /* Check order of curve. */
+ if (ec->dialect == ECC_DIALECT_STANDARD && !(flags & PUBKEY_FLAG_DJB_TWEAK))
+ {
+ _gcry_mpi_ec_mul_point (&Q, ec->n, ec->G, ec);
+ 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 (ec->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 */
+ if (!_gcry_ecc_compute_public (&Q, ec))
+ {
+ if (DBG_CIPHER)
+ log_debug ("Bad check: computation of dG failed\n");
+ goto leave;
+ }
+ if (_gcry_mpi_ec_get_affine (x1, y1, &Q, ec))
+ {
+ if (DBG_CIPHER)
+ log_debug ("Bad check: Q can not be a Point at Infinity!\n");
+ goto leave;
+ }
+
+ if ((flags & PUBKEY_FLAG_EDDSA)
+ || (ec->model == MPI_EC_EDWARDS && ec->dialect == ECC_DIALECT_SAFECURVE))
+ ; /* Fixme: EdDSA is special. */
+ else if (!mpi_cmp_ui (ec->Q->z, 1))
+ {
+ /* Fast path if Q is already in affine coordinates. */
+ if (mpi_cmp (x1, ec->Q->x) || (y1 && mpi_cmp (y1, ec->Q->y)))
+ {
+ if (DBG_CIPHER)
+ log_debug
+ ("Bad check: There is NO correspondence between 'd' and 'Q'!\n");
+ goto leave;
+ }
+ }
+ else
+ {
+ x2 = mpi_new (0);
+ y2 = mpi_new (0);
+ if (_gcry_mpi_ec_get_affine (x2, y2, ec->Q, ec))
+ {
+ if (DBG_CIPHER)
+ log_debug ("Bad check: Q can not be a Point at Infinity!\n");
+ goto leave;
+ }
+
+ if (mpi_cmp (x1, x2) || mpi_cmp (y1, y2))
+ {
+ if (DBG_CIPHER)
+ log_debug
+ ("Bad check: There is NO correspondence between 'd' and 'Q'!\n");
+ goto leave;
+ }
+ }
+ rc = 0; /* Okay. */
+
+ leave:
+ mpi_free (x2);
+ mpi_free (x1);
+ mpi_free (y1);
+ mpi_free (y2);
+ point_free (&Q);
+ return rc;
+}
+
+
+
+/*********************************************
+ ************** interface ******************
+ *********************************************/
+
+static gcry_err_code_t
+ecc_generate (const gcry_sexp_t genparms, gcry_sexp_t *r_skey)
+{
+ gpg_err_code_t rc;
+ gcry_mpi_t Gx = NULL;
+ gcry_mpi_t Gy = NULL;
+ gcry_mpi_t Qx = NULL;
+ gcry_mpi_t Qy = NULL;
+ mpi_ec_t ec = NULL;
+ gcry_sexp_t curve_info = NULL;
+ gcry_sexp_t curve_flags = NULL;
+ gcry_mpi_t base = NULL;
+ gcry_mpi_t public = NULL;
+ int flags = 0;
+
+ rc = _gcry_mpi_ec_internal_new (&ec, &flags, "ecgen curve", genparms, NULL);
+ if (rc)
+ goto leave;
+
+ if ((flags & PUBKEY_FLAG_EDDSA)
+ || (ec->model == MPI_EC_EDWARDS && ec->dialect == ECC_DIALECT_SAFECURVE))
+ rc = _gcry_ecc_eddsa_genkey (ec, flags);
+ else if (ec->model == MPI_EC_MONTGOMERY)
+ rc = nist_generate_key (ec, flags, &Qx, NULL);
+ else
+ rc = nist_generate_key (ec, flags, &Qx, &Qy);
+ if (rc)
+ goto leave;
+
+ /* Copy data to the result. */
+ Gx = mpi_new (0);
+ Gy = mpi_new (0);
+ if (ec->model != MPI_EC_MONTGOMERY)
+ {
+ if (_gcry_mpi_ec_get_affine (Gx, Gy, ec->G, ec))
+ log_fatal ("ecgen: Failed to get affine coordinates for %s\n", "G");
+ base = _gcry_ecc_ec2os (Gx, Gy, ec->p);
+ }
+ if (((ec->dialect == ECC_DIALECT_SAFECURVE && ec->model == MPI_EC_EDWARDS)
+ || ec->dialect == ECC_DIALECT_ED25519 || ec->model == MPI_EC_MONTGOMERY)
+ && !(flags & PUBKEY_FLAG_NOCOMP))
+ {
+ unsigned char *encpk;
+ unsigned int encpklen;
+
+ if (ec->model == MPI_EC_MONTGOMERY)
+ rc = _gcry_ecc_mont_encodepoint (Qx, ec->nbits,
+ ec->dialect != ECC_DIALECT_SAFECURVE,
+ &encpk, &encpklen);
+ else
+ /* (Gx and Gy are used as scratch variables) */
+ rc = _gcry_ecc_eddsa_encodepoint (ec->Q, ec, Gx, Gy,
+ (ec->dialect != ECC_DIALECT_SAFECURVE
+ && !!(flags & PUBKEY_FLAG_COMP)),
+ &encpk, &encpklen);
+ if (rc)
+ goto leave;
+ public = mpi_new (0);
+ mpi_set_opaque (public, encpk, encpklen*8);
+ }
+ else
+ {
+ if (!Qx)
+ {
+ /* This is the case for a key from _gcry_ecc_eddsa_generate
+ with no compression. */
+ Qx = mpi_new (0);
+ Qy = mpi_new (0);
+ if (_gcry_mpi_ec_get_affine (Qx, Qy, ec->Q, ec))
+ log_fatal ("ecgen: Failed to get affine coordinates for %s\n", "Q");
+ }
+ public = _gcry_ecc_ec2os (Qx, Qy, ec->p);
+ }
+ if (ec->name)
+ {
+ rc = sexp_build (&curve_info, NULL, "(curve %s)", ec->name);
+ if (rc)
+ goto leave;
+ }
+
+ if ((flags & PUBKEY_FLAG_PARAM) || (flags & PUBKEY_FLAG_EDDSA)
+ || (flags & PUBKEY_FLAG_DJB_TWEAK))
+ {
+ rc = sexp_build
+ (&curve_flags, NULL,
+ ((flags & PUBKEY_FLAG_PARAM) && (flags & PUBKEY_FLAG_EDDSA))?
+ "(flags param eddsa)" :
+ ((flags & PUBKEY_FLAG_PARAM) && (flags & PUBKEY_FLAG_DJB_TWEAK))?
+ "(flags param djb-tweak)" :
+ ((flags & PUBKEY_FLAG_PARAM))?
+ "(flags param)" : ((flags & PUBKEY_FLAG_EDDSA))?
+ "(flags eddsa)" : "(flags djb-tweak)" );
+ if (rc)
+ goto leave;
+ }
+
+ if ((flags & PUBKEY_FLAG_PARAM) && ec->name)
+ rc = sexp_build (r_skey, NULL,
+ "(key-data"
+ " (public-key"
+ " (ecc%S%S(p%m)(a%m)(b%m)(g%m)(n%m)(h%u)(q%m)))"
+ " (private-key"
+ " (ecc%S%S(p%m)(a%m)(b%m)(g%m)(n%m)(h%u)(q%m)(d%m)))"
+ " )",
+ curve_info, curve_flags,
+ ec->p, ec->a, ec->b, base, ec->n, ec->h, public,
+ curve_info, curve_flags,
+ ec->p, ec->a, ec->b, base, ec->n, ec->h, public,
+ ec->d);
+ else
+ rc = sexp_build (r_skey, NULL,
+ "(key-data"
+ " (public-key"
+ " (ecc%S%S(q%m)))"
+ " (private-key"
+ " (ecc%S%S(q%m)(d%m)))"
+ " )",
+ curve_info, curve_flags,
+ public,
+ curve_info, curve_flags,
+ public, ec->d);
+ if (rc)
+ goto leave;
+
+ if (DBG_CIPHER)
+ {
+ log_printmpi ("ecgen result p", ec->p);
+ log_printmpi ("ecgen result a", ec->a);
+ log_printmpi ("ecgen result b", ec->b);
+ log_printmpi ("ecgen result G", base);
+ log_printmpi ("ecgen result n", ec->n);
+ log_debug ("ecgen result h:+%02x\n", ec->h);
+ log_printmpi ("ecgen result Q", public);
+ log_printmpi ("ecgen result d", ec->d);
+ if ((flags & PUBKEY_FLAG_EDDSA))
+ log_debug ("ecgen result using Ed25519+EdDSA\n");
+ }
+
+ leave:
+ mpi_free (public);
+ mpi_free (base);
+ mpi_free (Gx);
+ mpi_free (Gy);
+ mpi_free (Qx);
+ mpi_free (Qy);
+ _gcry_mpi_ec_free (ec);
+ sexp_release (curve_flags);
+ sexp_release (curve_info);
+ return rc;
+}
+
+
+static gcry_err_code_t
+ecc_check_secret_key (gcry_sexp_t keyparms)
+{
+ gcry_err_code_t rc;
+ int flags = 0;
+ mpi_ec_t ec = NULL;
+
+ /*
+ * Extract the key.
+ */
+ rc = _gcry_mpi_ec_internal_new (&ec, &flags, "ecc_testkey", keyparms, NULL);
+ if (rc)
+ goto leave;
+ if (!ec->p || !ec->a || !ec->b || !ec->G || !ec->n || !ec->Q || !ec->d)
+ {
+ rc = GPG_ERR_NO_OBJ;
+ goto leave;
+ }
+
+ if (check_secret_key (ec, flags))
+ rc = GPG_ERR_BAD_SECKEY;
+
+ leave:
+ _gcry_mpi_ec_free (ec);
+ if (DBG_CIPHER)
+ log_debug ("ecc_testkey => %s\n", gpg_strerror (rc));
+ return rc;
+}
+
+
+static gcry_err_code_t
+ecc_sign (gcry_sexp_t *r_sig, gcry_sexp_t s_data, gcry_sexp_t keyparms)
+{
+ gcry_err_code_t rc;
+ struct pk_encoding_ctx ctx;
+ gcry_mpi_t data = NULL;
+ gcry_mpi_t sig_r = NULL;
+ gcry_mpi_t sig_s = NULL;
+ mpi_ec_t ec = NULL;
+ int flags = 0;
+
+ _gcry_pk_util_init_encoding_ctx (&ctx, PUBKEY_OP_SIGN, 0);
+
+ /*
+ * Extract the key.
+ */
+ rc = _gcry_mpi_ec_internal_new (&ec, &flags, "ecc_sign", keyparms, NULL);
+ if (rc)
+ goto leave;
+ if (!ec->p || !ec->a || !ec->b || !ec->G || !ec->n || !ec->d)
+ {
+ rc = GPG_ERR_NO_OBJ;
+ goto leave;
+ }
+
+ ctx.flags |= flags;
+ if (ec->model == MPI_EC_EDWARDS && ec->dialect == ECC_DIALECT_SAFECURVE)
+ ctx.flags |= PUBKEY_FLAG_EDDSA;
+ /* Clear hash algo for EdDSA. */
+ if ((ctx.flags & PUBKEY_FLAG_EDDSA))
+ ctx.hash_algo = GCRY_MD_NONE;
+
+ /* Extract the data. */
+ rc = _gcry_pk_util_data_to_mpi (s_data, &data, &ctx);
+ if (rc)
+ goto leave;
+ if (DBG_CIPHER)
+ log_mpidump ("ecc_sign data", data);
+
+ /* Hash algo is determined by curve in EdDSA. Fill it if not specified. */
+ if ((ctx.flags & PUBKEY_FLAG_EDDSA) && !ctx.hash_algo)
+ {
+ if (ec->dialect == ECC_DIALECT_ED25519)
+ ctx.hash_algo = GCRY_MD_SHA512;
+ else if (ec->dialect == ECC_DIALECT_SAFECURVE)
+ ctx.hash_algo = GCRY_MD_SHAKE256;
+ }
+
+ sig_r = mpi_new (0);
+ sig_s = mpi_new (0);
+ if ((ctx.flags & PUBKEY_FLAG_EDDSA))
+ {
+ /* EdDSA requires the public key. */
+ rc = _gcry_ecc_eddsa_sign (data, ec, sig_r, sig_s, &ctx);
+ if (!rc)
+ rc = sexp_build (r_sig, NULL,
+ "(sig-val(eddsa(r%M)(s%M)))", sig_r, sig_s);
+ }
+ else if ((ctx.flags & PUBKEY_FLAG_GOST))
+ {
+ rc = _gcry_ecc_gost_sign (data, ec, sig_r, sig_s);
+ if (!rc)
+ rc = sexp_build (r_sig, NULL,
+ "(sig-val(gost(r%M)(s%M)))", sig_r, sig_s);
+ }
+ else if ((ctx.flags & PUBKEY_FLAG_SM2))
+ {
+ rc = _gcry_ecc_sm2_sign (data, ec, sig_r, sig_s,
+ ctx.flags, ctx.hash_algo);
+ if (!rc)
+ rc = sexp_build (r_sig, NULL,
+ "(sig-val(sm2(r%M)(s%M)))", sig_r, sig_s);
+ }
+ else
+ {
+ rc = _gcry_ecc_ecdsa_sign (data, ec, sig_r, sig_s,
+ ctx.flags, ctx.hash_algo);
+ if (!rc)
+ rc = sexp_build (r_sig, NULL,
+ "(sig-val(ecdsa(r%M)(s%M)))", sig_r, sig_s);
+ }
+
+ leave:
+ _gcry_mpi_release (sig_r);
+ _gcry_mpi_release (sig_s);
+ _gcry_mpi_release (data);
+ _gcry_mpi_ec_free (ec);
+ _gcry_pk_util_free_encoding_ctx (&ctx);
+ if (DBG_CIPHER)
+ log_debug ("ecc_sign => %s\n", gpg_strerror (rc));
+ return rc;
+}
+
+
+static gcry_err_code_t
+ecc_verify (gcry_sexp_t s_sig, gcry_sexp_t s_data, gcry_sexp_t s_keyparms)
+{
+ gcry_err_code_t rc;
+ struct pk_encoding_ctx ctx;
+ gcry_sexp_t l1 = NULL;
+ gcry_mpi_t sig_r = NULL;
+ gcry_mpi_t sig_s = NULL;
+ gcry_mpi_t data = NULL;
+ int sigflags;
+ mpi_ec_t ec = NULL;
+ int flags = 0;
+
+ _gcry_pk_util_init_encoding_ctx (&ctx, PUBKEY_OP_VERIFY,
+ ecc_get_nbits (s_keyparms));
+
+ /*
+ * Extract the key.
+ */
+ rc = _gcry_mpi_ec_internal_new (&ec, &flags, "ecc_verify",
+ s_keyparms, NULL);
+ if (rc)
+ goto leave;
+ if (!ec->p || !ec->a || !ec->b || !ec->G || !ec->n || !ec->Q)
+ {
+ rc = GPG_ERR_NO_OBJ;
+ goto leave;
+ }
+
+ if (ec->model == MPI_EC_MONTGOMERY)
+ {
+ if (DBG_CIPHER)
+ log_debug ("ecc_verify: Can't use a Montgomery curve\n");
+ rc = GPG_ERR_INTERNAL;
+ goto leave;
+ }
+
+ ctx.flags |= flags;
+ if (ec->model == MPI_EC_EDWARDS && ec->dialect == ECC_DIALECT_SAFECURVE)
+ ctx.flags |= PUBKEY_FLAG_EDDSA;
+ /* Clear hash algo for EdDSA. */
+ if ((ctx.flags & PUBKEY_FLAG_EDDSA))
+ ctx.hash_algo = GCRY_MD_NONE;
+
+ /* Extract the data. */
+ rc = _gcry_pk_util_data_to_mpi (s_data, &data, &ctx);
+ if (rc)
+ goto leave;
+ if (DBG_CIPHER)
+ log_mpidump ("ecc_verify data", data);
+
+ /* Hash algo is determined by curve in EdDSA. Fill it if not specified. */
+ if ((ctx.flags & PUBKEY_FLAG_EDDSA) && !ctx.hash_algo)
+ {
+ if (ec->dialect == ECC_DIALECT_ED25519)
+ ctx.hash_algo = GCRY_MD_SHA512;
+ else if (ec->dialect == ECC_DIALECT_SAFECURVE)
+ ctx.hash_algo = GCRY_MD_SHAKE256;
+ }
+
+ /*
+ * Extract the signature value.
+ */
+ rc = _gcry_pk_util_preparse_sigval (s_sig, ecc_names, &l1, &sigflags);
+ if (rc)
+ goto leave;
+ rc = sexp_extract_param (l1, NULL, (sigflags & PUBKEY_FLAG_EDDSA)? "/rs":"rs",
+ &sig_r, &sig_s, NULL);
+ if (rc)
+ goto leave;
+ if (DBG_CIPHER)
+ {
+ log_mpidump ("ecc_verify s_r", sig_r);
+ log_mpidump ("ecc_verify s_s", sig_s);
+ }
+ if ((ctx.flags & PUBKEY_FLAG_EDDSA) ^ (sigflags & PUBKEY_FLAG_EDDSA))
+ {
+ rc = GPG_ERR_CONFLICT; /* Inconsistent use of flag/algoname. */
+ goto leave;
+ }
+
+ /*
+ * Verify the signature.
+ */
+ if ((sigflags & PUBKEY_FLAG_EDDSA))
+ {
+ rc = _gcry_ecc_eddsa_verify (data, ec, sig_r, sig_s, &ctx);
+ }
+ else if ((sigflags & PUBKEY_FLAG_GOST))
+ {
+ rc = _gcry_ecc_gost_verify (data, ec, sig_r, sig_s);
+ }
+ else if ((sigflags & PUBKEY_FLAG_SM2))
+ {
+ rc = _gcry_ecc_sm2_verify (data, ec, sig_r, sig_s);
+ }
+ else
+ {
+ rc = _gcry_ecc_ecdsa_verify (data, ec, sig_r, sig_s);
+ }
+
+ leave:
+ _gcry_mpi_release (data);
+ _gcry_mpi_release (sig_r);
+ _gcry_mpi_release (sig_s);
+ _gcry_mpi_ec_free (ec);
+ sexp_release (l1);
+ _gcry_pk_util_free_encoding_ctx (&ctx);
+ if (DBG_CIPHER)
+ log_debug ("ecc_verify => %s\n", rc?gpg_strerror (rc):"Good");
+ return rc;
+}
+
+
+/* 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: A new S-expression with the parameters:
+ * s : shared point (kdG)
+ * e : 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 (gcry_sexp_t *r_ciph, gcry_sexp_t s_data, gcry_sexp_t keyparms)
+{
+ unsigned int nbits;
+ gcry_err_code_t rc;
+ struct pk_encoding_ctx ctx;
+ gcry_mpi_t mpi_s = NULL;
+ gcry_mpi_t mpi_e = NULL;
+ gcry_mpi_t data = NULL;
+ mpi_ec_t ec = NULL;
+ int flags = 0;
+ int no_error_on_infinity;
+
+ _gcry_pk_util_init_encoding_ctx (&ctx, PUBKEY_OP_ENCRYPT,
+ (nbits = ecc_get_nbits (keyparms)));
+
+ /*
+ * Extract the key.
+ */
+ rc = _gcry_mpi_ec_internal_new (&ec, &flags, "ecc_encrypt", keyparms, NULL);
+ if (rc)
+ goto leave;
+
+ if (ec->dialect == ECC_DIALECT_SAFECURVE)
+ {
+ ctx.flags |= PUBKEY_FLAG_RAW_FLAG;
+ no_error_on_infinity = 1;
+ }
+ else if ((flags & PUBKEY_FLAG_DJB_TWEAK))
+ no_error_on_infinity = 1;
+ else
+ no_error_on_infinity = 0;
+
+ /*
+ * Extract the data.
+ */
+ rc = _gcry_pk_util_data_to_mpi (s_data, &data, &ctx);
+ if (rc)
+ goto leave;
+
+ /*
+ * Tweak the scalar bits by cofactor and number of bits of the field.
+ * It assumes the cofactor is a power of 2.
+ */
+ if ((flags & PUBKEY_FLAG_DJB_TWEAK))
+ {
+ int i;
+
+ for (i = 0; (ec->h & (1 << i)) == 0; i++)
+ mpi_clear_bit (data, i);
+ mpi_set_highbit (data, ec->nbits - 1);
+ }
+ if (DBG_CIPHER)
+ log_mpidump ("ecc_encrypt data", data);
+
+ if (!ec->p || !ec->a || !ec->b || !ec->G || !ec->n || !ec->Q)
+ {
+ rc = GPG_ERR_NO_OBJ;
+ goto leave;
+ }
+
+ if ((ctx.flags & PUBKEY_FLAG_SM2))
+ {
+ /* All encryption will be done, return it. */
+ rc = _gcry_ecc_sm2_encrypt (r_ciph, data, ec);
+ goto leave;
+ }
+
+ /* The following is false: assert( mpi_cmp_ui( R.x, 1 )==0 );, so */
+ {
+ mpi_point_struct R; /* Result that we return. */
+ gcry_mpi_t x, y;
+ unsigned char *rawmpi;
+ unsigned int rawmpilen;
+
+ rc = 0;
+ x = mpi_new (0);
+ if (ec->model == MPI_EC_MONTGOMERY)
+ y = NULL;
+ else
+ y = mpi_new (0);
+
+ point_init (&R);
+
+ /* R = kQ <=> R = kdG */
+ _gcry_mpi_ec_mul_point (&R, data, ec->Q, ec);
+
+ if (_gcry_mpi_ec_get_affine (x, y, &R, ec))
+ {
+ /*
+ * Here, X is 0. In the X25519 computation on Curve25519, X0
+ * function maps infinity to zero. So, when PUBKEY_FLAG_DJB_TWEAK
+ * is enabled, return the result of 0 not raising an error.
+ *
+ * This is a corner case. It never occurs with properly
+ * generated public keys, but it might happen with blindly
+ * imported public key which might not follow the key
+ * generation procedure.
+ */
+ if (!no_error_on_infinity)
+ { /* It's not for X25519, then, the input data was simply wrong. */
+ rc = GPG_ERR_INV_DATA;
+ goto leave_main;
+ }
+ }
+ if (y)
+ mpi_s = _gcry_ecc_ec2os (x, y, ec->p);
+ else
+ {
+ rc = _gcry_ecc_mont_encodepoint (x, nbits,
+ ec->dialect != ECC_DIALECT_SAFECURVE,
+ &rawmpi, &rawmpilen);
+ if (rc)
+ goto leave_main;
+ mpi_s = mpi_new (0);
+ mpi_set_opaque (mpi_s, rawmpi, rawmpilen*8);
+ }
+
+ /* R = kG */
+ _gcry_mpi_ec_mul_point (&R, data, ec->G, ec);
+
+ if (_gcry_mpi_ec_get_affine (x, y, &R, ec))
+ {
+ rc = GPG_ERR_INV_DATA;
+ goto leave_main;
+ }
+ if (y)
+ mpi_e = _gcry_ecc_ec2os (x, y, ec->p);
+ else
+ {
+ rc = _gcry_ecc_mont_encodepoint (x, nbits,
+ ec->dialect != ECC_DIALECT_SAFECURVE,
+ &rawmpi, &rawmpilen);
+ if (!rc)
+ {
+ mpi_e = mpi_new (0);
+ mpi_set_opaque (mpi_e, rawmpi, rawmpilen*8);
+ }
+ }
+
+ leave_main:
+ mpi_free (x);
+ mpi_free (y);
+ point_free (&R);
+ if (rc)
+ goto leave;
+ }
+
+ if (!rc)
+ rc = sexp_build (r_ciph, NULL, "(enc-val(ecdh(s%m)(e%m)))", mpi_s, mpi_e);
+
+ leave:
+ _gcry_mpi_release (data);
+ _gcry_mpi_release (mpi_s);
+ _gcry_mpi_release (mpi_e);
+ _gcry_mpi_ec_free (ec);
+ _gcry_pk_util_free_encoding_ctx (&ctx);
+ if (DBG_CIPHER)
+ log_debug ("ecc_encrypt => %s\n", gpg_strerror (rc));
+ return rc;
+}
+
+
+/* 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 (gcry_sexp_t *r_plain, gcry_sexp_t s_data, gcry_sexp_t keyparms)
+{
+ unsigned int nbits;
+ gpg_err_code_t rc;
+ struct pk_encoding_ctx ctx;
+ gcry_sexp_t l1 = NULL;
+ gcry_mpi_t data_e = NULL;
+ mpi_ec_t ec = NULL;
+ mpi_point_struct kG;
+ mpi_point_struct R;
+ gcry_mpi_t r = NULL;
+ int flags = 0;
+ int enable_specific_point_validation;
+
+ point_init (&kG);
+ point_init (&R);
+
+ _gcry_pk_util_init_encoding_ctx (&ctx, PUBKEY_OP_DECRYPT,
+ (nbits = ecc_get_nbits (keyparms)));
+
+ /*
+ * Extract the key.
+ */
+ rc = _gcry_mpi_ec_internal_new (&ec, &flags, "ecc_decrypt", keyparms, NULL);
+ if (rc)
+ goto leave;
+
+ if (!ec->p || !ec->a || !ec->b || !ec->G || !ec->n || !ec->d)
+ {
+ rc = GPG_ERR_NO_OBJ;
+ goto leave;
+ }
+
+ /*
+ * Extract the data.
+ */
+ rc = _gcry_pk_util_preparse_encval (s_data, ecc_names, &l1, &ctx);
+ if (rc)
+ goto leave;
+ if ((ctx.flags & PUBKEY_FLAG_SM2))
+ {
+ /* All decryption will be done, return it. */
+ rc = _gcry_ecc_sm2_decrypt (r_plain, l1, ec);
+ goto leave;
+ }
+ else
+ {
+ 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 (ec->dialect == ECC_DIALECT_SAFECURVE || (flags & PUBKEY_FLAG_DJB_TWEAK))
+ enable_specific_point_validation = 1;
+ else
+ enable_specific_point_validation = 0;
+
+ /*
+ * Compute the plaintext.
+ */
+ if (ec->model == MPI_EC_MONTGOMERY)
+ rc = _gcry_ecc_mont_decodepoint (data_e, ec, &kG);
+ else
+ rc = _gcry_ecc_sec_decodepoint (data_e, ec, &kG);
+ if (rc)
+ goto leave;
+
+ if (DBG_CIPHER)
+ log_printpnt ("ecc_decrypt kG", &kG, NULL);
+
+ if (enable_specific_point_validation)
+ {
+ /* For X25519, by its definition, validation should not be done. */
+ /* (Instead, we do output check.)
+ *
+ * However, to mitigate secret key leak from our implementation,
+ * we also do input validation here. For constant-time
+ * implementation, we can remove this input validation.
+ */
+ if (_gcry_mpi_ec_bad_point (&kG, ec))
+ {
+ rc = GPG_ERR_INV_DATA;
+ goto leave;
+ }
+ }
+ else if (!_gcry_mpi_ec_curve_point (&kG, ec))
+ {
+ rc = GPG_ERR_INV_DATA;
+ goto leave;
+ }
+
+ /* R = dkG */
+ _gcry_mpi_ec_mul_point (&R, ec->d, &kG, ec);
+
+ /* The following is false: assert( mpi_cmp_ui( R.x, 1 )==0 );, so: */
+ {
+ gcry_mpi_t x, y;
+
+ x = mpi_new (0);
+ if (ec->model == MPI_EC_MONTGOMERY)
+ y = NULL;
+ else
+ y = mpi_new (0);
+
+ if (_gcry_mpi_ec_get_affine (x, y, &R, ec))
+ {
+ rc = GPG_ERR_INV_DATA;
+ goto leave;
+ /*
+ * Note for X25519.
+ *
+ * By the definition of X25519, this is the case where X25519
+ * returns 0, mapping infinity to zero. However, we
+ * deliberately let it return an error.
+ *
+ * For X25519 ECDH, comming here means that it might be
+ * decrypted by anyone with the shared secret of 0 (the result
+ * of this function could be always 0 by other scalar values,
+ * other than the private key of D).
+ *
+ * So, it looks like an encrypted message but it can be
+ * decrypted by anyone, or at least something wrong
+ * happens. Recipient should not proceed as if it were
+ * properly encrypted message.
+ *
+ * This handling is needed for our major usage of GnuPG,
+ * where it does the One-Pass Diffie-Hellman method,
+ * C(1, 1, ECC CDH), with an ephemeral key.
+ */
+ }
+
+ if (y)
+ r = _gcry_ecc_ec2os (x, y, ec->p);
+ else
+ {
+
+ unsigned char *rawmpi;
+ unsigned int rawmpilen;
+
+ rc = _gcry_ecc_mont_encodepoint (x, nbits,
+ ec->dialect != ECC_DIALECT_SAFECURVE,
+ &rawmpi, &rawmpilen);
+ if (rc)
+ goto leave;
+
+ r = mpi_new (0);
+ mpi_set_opaque (r, rawmpi, rawmpilen*8);
+ }
+ if (!r)
+ rc = gpg_err_code_from_syserror ();
+ else
+ rc = 0;
+ mpi_free (x);
+ mpi_free (y);
+ }
+ if (DBG_CIPHER)
+ log_printmpi ("ecc_decrypt res", r);
+
+ if (!rc)
+ rc = sexp_build (r_plain, NULL, "(value %m)", r);
+
+ leave:
+ point_free (&R);
+ point_free (&kG);
+ _gcry_mpi_release (r);
+ _gcry_mpi_release (data_e);
+ sexp_release (l1);
+ _gcry_mpi_ec_free (ec);
+ _gcry_pk_util_free_encoding_ctx (&ctx);
+ if (DBG_CIPHER)
+ log_debug ("ecc_decrypt => %s\n", gpg_strerror (rc));
+ return rc;
+}
+
+
+/* Return the number of bits for the key described by PARMS. On error
+ * 0 is returned. The format of PARMS starts with the algorithm name;
+ * for example:
+ *
+ * (ecc
+ * (curve <name>)
+ * (p <mpi>)
+ * (a <mpi>)
+ * (b <mpi>)
+ * (g <mpi>)
+ * (n <mpi>)
+ * (q <mpi>))
+ *
+ * More parameters may be given. Either P or CURVE is needed.
+ */
+static unsigned int
+ecc_get_nbits (gcry_sexp_t parms)
+{
+ gcry_sexp_t l1;
+ gcry_mpi_t p;
+ unsigned int nbits = 0;
+ char *curve;
+
+ l1 = sexp_find_token (parms, "p", 1);
+ if (!l1)
+ { /* Parameter P not found - check whether we have "curve". */
+ l1 = sexp_find_token (parms, "curve", 5);
+ if (!l1)
+ return 0; /* Neither P nor CURVE found. */
+
+ curve = sexp_nth_string (l1, 1);
+ sexp_release (l1);
+ if (!curve)
+ return 0; /* No curve name given (or out of core). */
+
+ if (_gcry_ecc_fill_in_curve (0, curve, NULL, &nbits))
+ nbits = 0;
+ xfree (curve);
+ }
+ else
+ {
+ p = sexp_nth_mpi (l1, 1, GCRYMPI_FMT_USG);
+ sexp_release (l1);
+ if (p)
+ {
+ nbits = mpi_get_nbits (p);
+ _gcry_mpi_release (p);
+ }
+ }
+ return nbits;
+}
+
+
+/* See rsa.c for a description of this function. */
+static gpg_err_code_t
+compute_keygrip (gcry_md_hd_t md, gcry_sexp_t keyparms)
+{
+#define N_COMPONENTS 6
+ static const char names[N_COMPONENTS] = "pabgnq";
+ gpg_err_code_t rc;
+ gcry_sexp_t l1;
+ gcry_mpi_t values[N_COMPONENTS];
+ int idx;
+ char *curvename = NULL;
+ int flags = 0;
+ enum gcry_mpi_ec_models model = 0;
+ enum ecc_dialects dialect = 0;
+ const unsigned char *raw;
+ unsigned int n;
+
+ /* Clear the values first. */
+ for (idx=0; idx < N_COMPONENTS; idx++)
+ values[idx] = NULL;
+
+
+ /* Look for flags. */
+ l1 = sexp_find_token (keyparms, "flags", 0);
+ if (l1)
+ {
+ rc = _gcry_pk_util_parse_flaglist (l1, &flags, NULL);
+ if (rc)
+ goto leave;
+ }
+
+ /* Extract the parameters. */
+ if ((flags & PUBKEY_FLAG_PARAM))
+ rc = sexp_extract_param (keyparms, NULL, "p?a?b?g?n?/q",
+ &values[0], &values[1], &values[2],
+ &values[3], &values[4], &values[5],
+ NULL);
+ else
+ rc = sexp_extract_param (keyparms, NULL, "/q", &values[5], NULL);
+ if (rc)
+ goto leave;
+
+ /* Check whether a curve parameter is available and use that to fill
+ in missing values. */
+ sexp_release (l1);
+ l1 = sexp_find_token (keyparms, "curve", 5);
+ if (l1)
+ {
+ curvename = sexp_nth_string (l1, 1);
+ if (curvename)
+ {
+ rc = _gcry_ecc_update_curve_param (curvename,
+ &model, &dialect,
+ &values[0], &values[1], &values[2],
+ &values[3], &values[4]);
+ if (rc)
+ goto leave;
+ }
+ }
+
+ /* Guess required fields if a curve parameter has not been given.
+ FIXME: This is a crude hacks. We need to fix that. */
+ if (!curvename)
+ {
+ model = ((flags & PUBKEY_FLAG_EDDSA)
+ ? MPI_EC_EDWARDS
+ : MPI_EC_WEIERSTRASS);
+ dialect = ((flags & PUBKEY_FLAG_EDDSA)
+ ? ECC_DIALECT_ED25519
+ : ECC_DIALECT_STANDARD);
+ }
+
+ /* 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])
+ {
+ rc = GPG_ERR_NO_OBJ;
+ goto leave;
+ }
+ else
+ _gcry_mpi_normalize (values[idx]);
+
+ /* Uncompress the public key with the exception of EdDSA where
+ compression is the default and we thus compute the keygrip using
+ the compressed version. Because we don't support any non-eddsa
+ compression, the only thing we need to do is to compress
+ EdDSA. */
+ if ((flags & PUBKEY_FLAG_EDDSA) && dialect == ECC_DIALECT_ED25519)
+ {
+ const unsigned int pbits = mpi_get_nbits (values[0]);
+
+ rc = _gcry_ecc_eddsa_ensure_compact (values[5], pbits);
+ if (rc)
+ goto leave;
+ }
+ else if ((flags & PUBKEY_FLAG_DJB_TWEAK))
+ {
+ /* Remove the prefix 0x40 for keygrip computation. */
+ raw = mpi_get_opaque (values[5], &n);
+ if (raw)
+ {
+ n = (n + 7)/8;
+
+ if (n > 1 && (n%2) && raw[0] == 0x40)
+ if (!_gcry_mpi_set_opaque_copy (values[5], raw + 1, (n - 1)*8))
+ rc = gpg_err_code_from_syserror ();
+ }
+ else
+ {
+ rc = GPG_ERR_INV_OBJ;
+ goto leave;
+ }
+ }
+
+ /* Hash them all. */
+ for (idx = 0; idx < N_COMPONENTS; idx++)
+ {
+ char buf[30];
+
+ if (mpi_is_opaque (values[idx]))
+ {
+ raw = mpi_get_opaque (values[idx], &n);
+ n = (n + 7)/8;
+ snprintf (buf, sizeof buf, "(1:%c%u:", names[idx], n);
+ _gcry_md_write (md, buf, strlen (buf));
+ _gcry_md_write (md, raw, n);
+ _gcry_md_write (md, ")", 1);
+ }
+ else
+ {
+ unsigned char *rawmpi;
+ unsigned int rawmpilen;
+
+ rawmpi = _gcry_mpi_get_buffer (values[idx], 0, &rawmpilen, NULL);
+ if (!rawmpi)
+ {
+ rc = 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);
+ xfree (rawmpi);
+ }
+ }
+
+ leave:
+ xfree (curvename);
+ sexp_release (l1);
+ for (idx = 0; idx < N_COMPONENTS; idx++)
+ _gcry_mpi_release (values[idx]);
+
+ return rc;
+#undef N_COMPONENTS
+}
+
+
+
+/*
+ Low-level API helper functions.
+ */
+
+/* This is the worker function for gcry_pubkey_get_sexp for ECC
+ algorithms. Note that the caller has already stored NULL at
+ R_SEXP. */
+gpg_err_code_t
+_gcry_pk_ecc_get_sexp (gcry_sexp_t *r_sexp, int mode, mpi_ec_t ec)
+{
+ gpg_err_code_t rc;
+ gcry_mpi_t mpi_G = NULL;
+ gcry_mpi_t mpi_Q = NULL;
+
+ if (!ec->p || !ec->a || !ec->b || !ec->G || !ec->n)
+ return GPG_ERR_BAD_CRYPT_CTX;
+
+ if (mode == GCRY_PK_GET_SECKEY && !ec->d)
+ return GPG_ERR_NO_SECKEY;
+
+ /* Compute the public point if it is missing. */
+ if (!ec->Q && ec->d)
+ ec->Q = _gcry_ecc_compute_public (NULL, ec);
+
+ /* Encode G and Q. */
+ mpi_G = _gcry_mpi_ec_ec2os (ec->G, ec);
+ if (!mpi_G)
+ {
+ rc = GPG_ERR_BROKEN_PUBKEY;
+ goto leave;
+ }
+ if (!ec->Q)
+ {
+ rc = GPG_ERR_BAD_CRYPT_CTX;
+ goto leave;
+ }
+
+ if (ec->dialect == ECC_DIALECT_ED25519)
+ {
+ unsigned char *encpk;
+ unsigned int encpklen;
+
+ rc = _gcry_ecc_eddsa_encodepoint (ec->Q, ec, NULL, NULL, 0,
+ &encpk, &encpklen);
+ if (rc)
+ goto leave;
+ mpi_Q = mpi_set_opaque (NULL, encpk, encpklen*8);
+ encpk = NULL;
+ }
+ else if (ec->model == MPI_EC_MONTGOMERY)
+ {
+ unsigned char *encpk;
+ unsigned int encpklen;
+
+ rc = _gcry_ecc_mont_encodepoint (ec->Q->x, ec->nbits,
+ ec->dialect != ECC_DIALECT_SAFECURVE,
+ &encpk, &encpklen);
+ if (rc)
+ goto leave;
+ mpi_Q = mpi_set_opaque (NULL, encpk, encpklen*8);
+ }
+ else
+ {
+ mpi_Q = _gcry_mpi_ec_ec2os (ec->Q, ec);
+ }
+ if (!mpi_Q)
+ {
+ rc = GPG_ERR_BROKEN_PUBKEY;
+ goto leave;
+ }
+
+ /* Fixme: We should return a curve name instead of the parameters if
+ if know that they match a curve. */
+
+ if (ec->d && (!mode || mode == GCRY_PK_GET_SECKEY))
+ {
+ /* Let's return a private key. */
+ rc = sexp_build (r_sexp, NULL,
+ "(private-key(ecc(p%m)(a%m)(b%m)(g%m)(n%m)(h%u)(q%m)(d%m)))",
+ ec->p, ec->a, ec->b, mpi_G, ec->n, ec->h, mpi_Q, ec->d);
+ }
+ else if (ec->Q)
+ {
+ /* Let's return a public key. */
+ rc = sexp_build (r_sexp, NULL,
+ "(public-key(ecc(p%m)(a%m)(b%m)(g%m)(n%m)(h%u)(q%m)))",
+ ec->p, ec->a, ec->b, mpi_G, ec->n, ec->h, mpi_Q);
+ }
+ else
+ rc = GPG_ERR_BAD_CRYPT_CTX;
+
+ leave:
+ mpi_free (mpi_Q);
+ mpi_free (mpi_G);
+ return rc;
+}
+
+
+
+/*
+ Self-test section.
+ */
+
+static const char *
+selftest_sign (gcry_sexp_t pkey, gcry_sexp_t skey)
+{
+ /* Sample data from RFC 6979 section A.2.5, hash is of message "sample" */
+ static const char sample_data[] =
+ "(data (flags rfc6979)"
+ " (hash sha256 #af2bdbe1aa9b6ec1e2ade1d694f41fc71a831d0268e98915"
+ /**/ "62113d8a62add1bf#))";
+ static const char sample_data_bad[] =
+ "(data (flags rfc6979)"
+ " (hash sha256 #bf2bdbe1aa9b6ec1e2ade1d694f41fc71a831d0268e98915"
+ /**/ "62113d8a62add1bf#))";
+ static const char signature_r[] =
+ "efd48b2aacb6a8fd1140dd9cd45e81d69d2c877b56aaf991c34d0ea84eaf3716";
+ static const char signature_s[] =
+ "f7cb1c942d657c41d436c7a1b6e29f65f3e900dbb9aff4064dc4ab2f843acda8";
+
+ const char *errtxt = NULL;
+ gcry_error_t err;
+ gcry_sexp_t data = NULL;
+ gcry_sexp_t data_bad = NULL;
+ gcry_sexp_t sig = NULL;
+ gcry_sexp_t l1 = NULL;
+ gcry_sexp_t l2 = NULL;
+ gcry_mpi_t r = NULL;
+ gcry_mpi_t s = NULL;
+ gcry_mpi_t calculated_r = NULL;
+ gcry_mpi_t calculated_s = NULL;
+ int cmp;
+
+ err = sexp_sscan (&data, NULL, sample_data, strlen (sample_data));
+ if (!err)
+ err = sexp_sscan (&data_bad, NULL,
+ sample_data_bad, strlen (sample_data_bad));
+ if (!err)
+ err = _gcry_mpi_scan (&r, GCRYMPI_FMT_HEX, signature_r, 0, NULL);
+ if (!err)
+ err = _gcry_mpi_scan (&s, GCRYMPI_FMT_HEX, signature_s, 0, NULL);
+
+ if (err)
+ {
+ errtxt = "converting data failed";
+ goto leave;
+ }
+
+ err = _gcry_pk_sign (&sig, data, skey);
+ if (err)
+ {
+ errtxt = "signing failed";
+ goto leave;
+ }
+
+ /* check against known signature */
+ errtxt = "signature validity failed";
+ l1 = _gcry_sexp_find_token (sig, "sig-val", 0);
+ if (!l1)
+ goto leave;
+ l2 = _gcry_sexp_find_token (l1, "ecdsa", 0);
+ if (!l2)
+ goto leave;
+
+ sexp_release (l1);
+ l1 = l2;
+
+ l2 = _gcry_sexp_find_token (l1, "r", 0);
+ if (!l2)
+ goto leave;
+ calculated_r = _gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG);
+ if (!calculated_r)
+ goto leave;
+
+ sexp_release (l2);
+ l2 = _gcry_sexp_find_token (l1, "s", 0);
+ if (!l2)
+ goto leave;
+ calculated_s = _gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG);
+ if (!calculated_s)
+ goto leave;
+
+ errtxt = "known sig check failed";
+
+ cmp = _gcry_mpi_cmp (r, calculated_r);
+ if (cmp)
+ goto leave;
+ cmp = _gcry_mpi_cmp (s, calculated_s);
+ if (cmp)
+ goto leave;
+
+ errtxt = NULL;
+
+ /* verify generated signature */
+ err = _gcry_pk_verify (sig, data, pkey);
+ if (err)
+ {
+ errtxt = "verify failed";
+ goto leave;
+ }
+ err = _gcry_pk_verify (sig, data_bad, pkey);
+ if (gcry_err_code (err) != GPG_ERR_BAD_SIGNATURE)
+ {
+ errtxt = "bad signature not detected";
+ goto leave;
+ }
+
+
+ leave:
+ sexp_release (sig);
+ sexp_release (data_bad);
+ sexp_release (data);
+ sexp_release (l1);
+ sexp_release (l2);
+ mpi_release (r);
+ mpi_release (s);
+ mpi_release (calculated_r);
+ mpi_release (calculated_s);
+ return errtxt;
+}
+
+
+static gpg_err_code_t
+selftests_ecdsa (selftest_report_func_t report)
+{
+ const char *what;
+ const char *errtxt;
+ gcry_error_t err;
+ gcry_sexp_t skey = NULL;
+ gcry_sexp_t pkey = NULL;
+
+ what = "convert";
+ err = sexp_sscan (&skey, NULL, sample_secret_key_secp256,
+ strlen (sample_secret_key_secp256));
+ if (!err)
+ err = sexp_sscan (&pkey, NULL, sample_public_key_secp256,
+ strlen (sample_public_key_secp256));
+ if (err)
+ {
+ errtxt = _gcry_strerror (err);
+ goto failed;
+ }
+
+ what = "key consistency";
+ err = ecc_check_secret_key(skey);
+ if (err)
+ {
+ errtxt = _gcry_strerror (err);
+ goto failed;
+ }
+
+ what = "sign";
+ errtxt = selftest_sign (pkey, skey);
+ if (errtxt)
+ goto failed;
+
+ sexp_release(pkey);
+ sexp_release(skey);
+ return 0; /* Succeeded. */
+
+ failed:
+ sexp_release(pkey);
+ sexp_release(skey);
+ if (report)
+ report ("pubkey", GCRY_PK_ECC, 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)
+{
+ (void)extended;
+
+ if (algo != GCRY_PK_ECC)
+ return GPG_ERR_PUBKEY_ALGO;
+
+ return selftests_ecdsa (report);
+}
+
+
+
+
+gcry_pk_spec_t _gcry_pubkey_spec_ecc =
+ {
+ GCRY_PK_ECC, { 0, 1 },
+ (GCRY_PK_USAGE_SIGN | GCRY_PK_USAGE_ENCR),
+ "ECC", ecc_names,
+ "pabgnhq", "pabgnhqd", "se", "rs", "pabgnhq",
+ ecc_generate,
+ ecc_check_secret_key,
+ ecc_encrypt_raw,
+ ecc_decrypt_raw,
+ ecc_sign,
+ ecc_verify,
+ ecc_get_nbits,
+ run_selftests,
+ compute_keygrip,
+ _gcry_ecc_get_curve,
+ _gcry_ecc_get_param_sexp
+ };