diff options
author | NIIBE Yutaka <gniibe@fsij.org> | 2021-09-10 21:15:04 +0900 |
---|---|---|
committer | NIIBE Yutaka <gniibe@fsij.org> | 2021-09-10 21:16:24 +0900 |
commit | b82e66cf067b58b992e43b72938ac88d52d06385 (patch) | |
tree | 221b3fbc22938ebc1a65caae8f6c7a24f864f701 | |
parent | 023b689d8f3aa98a71a7c943fe113d7a13ebe466 (diff) | |
download | libgcrypt-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.am | 2 | ||||
-rw-r--r-- | cipher/pkey-ecc-nist.c | 220 | ||||
-rw-r--r-- | cipher/pkey-internal.h | 13 | ||||
-rw-r--r-- | cipher/pkey.c | 95 | ||||
-rw-r--r-- | tests/t-ecdsa.c | 2 |
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; |