summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNIIBE Yutaka <gniibe@fsij.org>2021-09-10 21:15:04 +0900
committerNIIBE Yutaka <gniibe@fsij.org>2021-09-10 21:16:24 +0900
commitb82e66cf067b58b992e43b72938ac88d52d06385 (patch)
tree221b3fbc22938ebc1a65caae8f6c7a24f864f701
parent023b689d8f3aa98a71a7c943fe113d7a13ebe466 (diff)
downloadlibgcrypt-b82e66cf067b58b992e43b72938ac88d52d06385.tar.gz
experiment: Implement ECDSA with NIST curves by PKEY API.
-- Need to fix _gcry_ecc_ecdsa_sign to support supplying K externally. Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
-rw-r--r--cipher/Makefile.am2
-rw-r--r--cipher/pkey-ecc-nist.c220
-rw-r--r--cipher/pkey-internal.h13
-rw-r--r--cipher/pkey.c95
-rw-r--r--tests/t-ecdsa.c2
5 files changed, 302 insertions, 30 deletions
diff --git a/cipher/Makefile.am b/cipher/Makefile.am
index 8b647d94..29ee30c1 100644
--- a/cipher/Makefile.am
+++ b/cipher/Makefile.am
@@ -58,7 +58,7 @@ libcipher_la_SOURCES = \
cipher-selftest.c cipher-selftest.h \
pubkey.c pubkey-internal.h pubkey-util.c \
pkey.c pkey-internal.h \
- pkey-ed25519.c pkey-ed448.c pkey-rsa.c pkey-dsa.c \
+ pkey-ed25519.c pkey-ed448.c pkey-rsa.c pkey-dsa.c pkey-ecc-nist.c \
md.c \
mac.c mac-internal.h \
mac-hmac.c mac-cmac.c mac-gmac.c mac-poly1305.c \
diff --git a/cipher/pkey-ecc-nist.c b/cipher/pkey-ecc-nist.c
new file mode 100644
index 00000000..9011a15e
--- /dev/null
+++ b/cipher/pkey-ecc-nist.c
@@ -0,0 +1,220 @@
+/* pkey-ecc-nist.c - PKEY API implementation for NIST Curves
+ * Copyright (C) 2021 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+
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <errno.h>
+
+#include "g10lib.h"
+#include "gcrypt-int.h"
+#include "pkey-internal.h"
+
+static const char *
+get_curve_name (int curve)
+{
+ switch (curve)
+ {
+ case GCRY_PKEY_CURVE_NIST_P224:
+ return "nistp224";
+ case GCRY_PKEY_CURVE_NIST_P256:
+ return "nistp256";
+ case GCRY_PKEY_CURVE_NIST_P384:
+ return "nistp384";
+ case GCRY_PKEY_CURVE_NIST_P521:
+ return "nistp521";
+ default:
+ return "unknown";
+ }
+}
+
+gcry_error_t
+_gcry_pkey_nist_sign (gcry_pkey_hd_t h,
+ int num_in, const unsigned char *const in[],
+ const size_t in_len[],
+ int num_out, unsigned char *out[], size_t out_len[])
+{
+ gcry_error_t err = 0;
+ gcry_sexp_t s_sk = NULL;
+ gcry_sexp_t s_msg= NULL;
+ gcry_sexp_t s_sig= NULL;
+ gcry_sexp_t s_tmp, s_tmp2;
+ const char *curve;
+ const char *md_name;
+
+ if (num_in != 2) /* For now, k should be specified by the caller. */
+ return gpg_error (GPG_ERR_INV_ARG);
+
+ if (num_out != 2)
+ return gpg_error (GPG_ERR_INV_ARG);
+
+ curve = get_curve_name (h->ecc.curve);
+ if (h->ecc.pk)
+ err = sexp_build (&s_sk, NULL,
+ "(private-key"
+ " (ecc"
+ " (curve %s)"
+ " (q %b)"
+ " (d %b)))", curve,
+ (int)h->ecc.pk_len, h->ecc.pk,
+ (int)h->ecc.sk_len, h->ecc.sk);
+ else
+ err = sexp_build (&s_sk, NULL,
+ "(private-key"
+ " (ecc"
+ " (curve %s)"
+ " (d %b)))", curve,
+ (int)h->ecc.sk_len, h->ecc.sk);
+ if (err)
+ return err;
+
+ md_name = _gcry_md_algo_name (h->ecc.md_algo);
+
+ if ((h->flags & GCRY_PKEY_FLAG_PREHASH))
+ err = sexp_build (&s_msg, NULL,
+ "(data"
+ " (flags prehash)"
+ " (label %b)"
+ " (hash-algo %s)"
+ " (value %b))",
+ (int)in_len[1], in[1],
+ md_name, (int)in_len[0], in[0]);
+ else
+ err = sexp_build (&s_msg, NULL,
+ "(data "
+ " (label %b)"
+ " (value %b))", (int)in_len[1], in[1],
+ (int)in_len[0], in[0]);
+ if (err)
+ {
+ sexp_release (s_sk);
+ return err;
+ }
+
+ err = _gcry_pk_sign (&s_sig, s_msg, s_sk);
+ sexp_release (s_sk);
+ sexp_release (s_msg);
+ if (err)
+ return err;
+
+ out[0] = out[1] = NULL;
+ s_tmp2 = NULL;
+ s_tmp = sexp_find_token (s_sig, "sig-val", 0);
+ if (s_tmp)
+ {
+ s_tmp2 = s_tmp;
+ s_tmp = sexp_find_token (s_tmp2, "ecdsa", 0);
+ if (s_tmp)
+ {
+ sexp_release (s_tmp2);
+ s_tmp2 = s_tmp;
+ s_tmp = sexp_find_token (s_tmp2, "r", 0);
+ if (s_tmp)
+ {
+ out[0] = sexp_nth_buffer (s_tmp, 1, &out_len[0]);
+ sexp_release (s_tmp);
+ }
+ s_tmp = sexp_find_token (s_tmp2, "s", 0);
+ if (s_tmp)
+ {
+ out[1] = sexp_nth_buffer (s_tmp, 1, &out_len[1]);
+ sexp_release (s_tmp);
+ }
+ }
+ }
+ sexp_release (s_tmp2);
+
+ if (out[0] == NULL || out[1] == NULL)
+ err = gpg_error (GPG_ERR_BAD_SIGNATURE);
+
+ sexp_release (s_sig);
+
+ return err;
+}
+
+gcry_error_t
+_gcry_pkey_nist_verify (gcry_pkey_hd_t h,
+ int num_in, const unsigned char *const in[],
+ const size_t in_len[])
+{
+ gcry_error_t err = 0;
+ gcry_sexp_t s_pk = NULL;
+ gcry_sexp_t s_msg= NULL;
+ gcry_sexp_t s_sig= NULL;
+ const char *curve;
+ const char *md_name;
+
+ if (num_in != 4) /* For now, k should be specified by the caller. */
+ return gpg_error (GPG_ERR_INV_ARG);
+
+ curve = get_curve_name (h->ecc.curve);
+ err = sexp_build (&s_pk, NULL,
+ "(public-key"
+ " (ecc"
+ " (curve %s)"
+ " (q %b)))", curve,
+ (int)h->ecc.pk_len, h->ecc.pk);
+ if (err)
+ return err;
+
+ md_name = _gcry_md_algo_name (h->ecc.md_algo);
+
+ if ((h->flags & GCRY_PKEY_FLAG_PREHASH))
+ err = sexp_build (&s_msg, NULL,
+ "(data"
+ " (flags prehash)"
+ " (label %b)"
+ " (hash-algo %s)"
+ " (value %b))",
+ (int)in_len[1], in[1],
+ md_name, (int)in_len[0], in[0]);
+ else
+ err = sexp_build (&s_msg, NULL,
+ "(data "
+ " (label %b)"
+ " (value %b))", (int)in_len[1], in[1],
+ (int)in_len[0], in[0]);
+ if (err)
+ {
+ sexp_release (s_pk);
+ return err;
+ }
+
+ err = sexp_build (&s_sig, NULL,
+ "(sig-val(ecdsa(r %b)(s %b)))",
+ (int)in_len[2], in[2],
+ (int)in_len[3], in[3]);
+ if (err)
+ {
+ sexp_release (s_msg);
+ sexp_release (s_pk);
+ return err;
+ }
+
+ err = _gcry_pk_verify (s_sig, s_msg, s_pk);
+
+ sexp_release (s_sig);
+ sexp_release (s_msg);
+ sexp_release (s_pk);
+
+ return err;
+}
diff --git a/cipher/pkey-internal.h b/cipher/pkey-internal.h
index 2f67ff67..d81fb72c 100644
--- a/cipher/pkey-internal.h
+++ b/cipher/pkey-internal.h
@@ -78,8 +78,20 @@ gcry_error_t _gcry_pkey_dsa_verify (gcry_pkey_hd_t h,
int num_in, const unsigned char *const in[],
const size_t in_len[]);
+gcry_error_t _gcry_pkey_nist_sign (gcry_pkey_hd_t h,
+ int num_in, const unsigned char *const in[],
+ const size_t in_len[],
+ int num_out, unsigned char *out[],
+ size_t out_len[]);
+
+gcry_error_t _gcry_pkey_nist_verify (gcry_pkey_hd_t h, int num_in,
+ const unsigned char *const in[],
+ const size_t in_len[]);
+
+
struct pkey_ecc {
int curve;
+ int md_algo; /* Only used for NIST cuves. */
unsigned char *pk;
size_t pk_len;
@@ -90,7 +102,6 @@ struct pkey_ecc {
struct pkey_rsa {
int scheme;
-
int md_algo;
unsigned char *n;
diff --git a/cipher/pkey.c b/cipher/pkey.c
index 93e56e67..3fc69b2e 100644
--- a/cipher/pkey.c
+++ b/cipher/pkey.c
@@ -58,10 +58,13 @@ _gcry_pkey_vopen (gcry_pkey_hd_t *h_p, int algo, unsigned int flags,
unsigned char *sk;
curve = va_arg (arg_ptr, int);
- if (curve == GCRY_PKEY_CURVE_ED25519)
- ;
- else if (curve == GCRY_PKEY_CURVE_ED448)
- ;
+ if (curve == GCRY_PKEY_CURVE_ED25519 || curve == GCRY_PKEY_CURVE_ED448)
+ h->ecc.md_algo = 0;
+ else if (curve == GCRY_PKEY_CURVE_NIST_P224
+ || curve == GCRY_PKEY_CURVE_NIST_P256
+ || curve == GCRY_PKEY_CURVE_NIST_P384
+ || curve == GCRY_PKEY_CURVE_NIST_P521)
+ h->ecc.md_algo = va_arg (arg_ptr, int);
else
err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
if (err)
@@ -72,8 +75,54 @@ _gcry_pkey_vopen (gcry_pkey_hd_t *h_p, int algo, unsigned int flags,
h->ecc.curve = curve;
- pk = va_arg (arg_ptr, unsigned char *);
- h->ecc.pk_len = va_arg (arg_ptr, size_t);
+ if (h->ecc.md_algo)
+ { /* NIST curves */
+ unsigned char *x, *y;
+ size_t x_len, y_len;
+
+ x = va_arg (arg_ptr, unsigned char *);
+ x_len = va_arg (arg_ptr, size_t);
+ y = va_arg (arg_ptr, unsigned char *);
+ y_len = va_arg (arg_ptr, size_t);
+
+ if (x && y)
+ {
+ h->ecc.pk_len = 1 + x_len + y_len;
+ h->ecc.pk = xtrymalloc (h->ecc.pk_len);
+ if (!h->ecc.pk)
+ {
+ err = gpg_error_from_syserror ();
+ xfree (h);
+ return err;
+ }
+
+ h->ecc.pk[0] = 0x04;
+ memcpy (h->ecc.pk+1, x, x_len);
+ memcpy (h->ecc.pk+1+x_len, y, y_len);
+ }
+ else
+ h->ecc.pk = NULL;
+ }
+ else
+ { /* Modern curves */
+ pk = va_arg (arg_ptr, unsigned char *);
+ h->ecc.pk_len = va_arg (arg_ptr, size_t);
+
+ if (pk)
+ {
+ h->ecc.pk = xtrymalloc (h->ecc.pk_len);
+ if (!h->ecc.pk)
+ {
+ err = gpg_error_from_syserror ();
+ xfree (h);
+ return err;
+ }
+ memcpy (h->ecc.pk, pk, h->ecc.pk_len);
+ }
+ else
+ h->ecc.pk = NULL;
+ }
+
if (!(flags & GCRY_PKEY_FLAG_SECRET))
{
h->ecc.sk = sk = NULL;
@@ -85,26 +134,6 @@ _gcry_pkey_vopen (gcry_pkey_hd_t *h_p, int algo, unsigned int flags,
h->ecc.sk_len = va_arg (arg_ptr, size_t);
}
- if (err)
- {
- xfree (h);
- return err;
- }
-
- if (pk)
- {
- h->ecc.pk = xtrymalloc (h->ecc.pk_len);
- if (!h->ecc.pk)
- {
- err = gpg_error_from_syserror ();
- xfree (h);
- return err;
- }
- memcpy (h->ecc.pk, pk, h->ecc.pk_len);
- }
- else
- h->ecc.pk = NULL;
-
if (sk)
{
h->ecc.sk = xtrymalloc_secure (h->ecc.sk_len);
@@ -362,7 +391,6 @@ _gcry_pkey_op (gcry_pkey_hd_t h, int cmd,
if (h->algo == GCRY_PKEY_ECC)
{
- /* Just for Ed25519 and Ed448 for now. Will support more... */
if (h->ecc.curve == GCRY_PKEY_CURVE_ED25519)
{
if (cmd == GCRY_PKEY_OP_SIGN)
@@ -383,6 +411,19 @@ _gcry_pkey_op (gcry_pkey_hd_t h, int cmd,
else
err = gpg_error (GPG_ERR_INV_OP);
}
+ else if (h->ecc.curve == GCRY_PKEY_CURVE_NIST_P224
+ || h->ecc.curve == GCRY_PKEY_CURVE_NIST_P256
+ || h->ecc.curve == GCRY_PKEY_CURVE_NIST_P384
+ || h->ecc.curve == GCRY_PKEY_CURVE_NIST_P521)
+ {
+ if (cmd == GCRY_PKEY_OP_SIGN)
+ err = _gcry_pkey_nist_sign (h, num_in, in, in_len,
+ num_out, out, out_len);
+ else if (cmd == GCRY_PKEY_OP_VERIFY)
+ err = _gcry_pkey_nist_verify (h, num_in, in, in_len);
+ else
+ err = gpg_error (GPG_ERR_INV_OP);
+ }
else
err = gpg_error (GPG_ERR_INV_OP);
}
diff --git a/tests/t-ecdsa.c b/tests/t-ecdsa.c
index fb7f2741..18876273 100644
--- a/tests/t-ecdsa.c
+++ b/tests/t-ecdsa.c
@@ -210,7 +210,7 @@ one_test (const char *curvename, const char *sha_alg,
size_t in_len[4];
unsigned char *out[2] = { NULL, NULL };
size_t out_len[2] = { 0, 0 };
- unsigned int flags = 0;
+ unsigned int flags = GCRY_PKEY_FLAG_PREHASH;
int curve;
int md_algo;