diff options
40 files changed, 1715 insertions, 331 deletions
@@ -8,6 +8,11 @@ (NAME_OF_INSTALLED_GPG): New ac_define. * autogen.sh [--build-w32ce]: Use --enable-gpg2-is-gpg. +2011-01-21 Werner Koch <[email protected]> + + * configure.ac: Need Libgcrypt 1.4.6 due to AESWRAP. + (HAVE_GCRY_PK_ECDH): Add new test. + 2011-01-03 Werner Koch <[email protected]> * README.SVN: Rename to README.GIT. diff --git a/agent/ChangeLog b/agent/ChangeLog index 542695bea..7dace3aef 100644 --- a/agent/ChangeLog +++ b/agent/ChangeLog @@ -2,6 +2,12 @@ * trustlist.c (read_one_trustfile): Also chop an CR. +2011-01-21 Werner Koch <[email protected]> + + * pksign.c (do_encode_dsa): Compare MDLEN to bytes. + + * cvt-openpgp.c (GCRY_PK_ECDH) [!HAVE_GCRY_PK_ECDH]: New. + 2010-12-02 Werner Koch <[email protected]> * gpg-agent.c (CHECK_OWN_SOCKET_INTERVAL) [W32CE]: Set to 60 diff --git a/agent/agent.h b/agent/agent.h index 7716bb0c2..e31b6a78e 100644 --- a/agent/agent.h +++ b/agent/agent.h @@ -215,6 +215,7 @@ const char *get_agent_ssh_socket_name (void); void *get_agent_scd_notify_event (void); #endif void agent_sighup_action (void); +int map_pk_openpgp_to_gcry (int openpgp_algo); /*-- command.c --*/ gpg_error_t agent_inq_pinentry_launched (ctrl_t ctrl, unsigned long pid); diff --git a/agent/cvt-openpgp.c b/agent/cvt-openpgp.c index e6a14c436..02c2bc841 100644 --- a/agent/cvt-openpgp.c +++ b/agent/cvt-openpgp.c @@ -28,6 +28,13 @@ #include "i18n.h" #include "cvt-openpgp.h" +/* Macros for compatibility with older libgcrypt versions. */ +#ifndef HAVE_GCRY_PK_ECDSA +# define GCRY_PK_ECDH 302 +#endif + + + /* Helper to pass data via the callback to do_unprotect. */ struct try_do_unprotect_arg_s @@ -80,6 +87,12 @@ get_keygrip (int pubkey_algo, gcry_mpi_t *pkey, unsigned char *grip) "(public-key(rsa(n%m)(e%m)))", pkey[0], pkey[1]); break; + case GCRY_PK_ECDSA: + case GCRY_PK_ECDH: + err = gcry_sexp_build (&s_pkey, NULL, + "(public-key(ecc(c%m)(q%m)))", pkey[0], pkey[1]); + break; + default: err = gpg_error (GPG_ERR_PUBKEY_ALGO); break; @@ -94,7 +107,9 @@ get_keygrip (int pubkey_algo, gcry_mpi_t *pkey, unsigned char *grip) /* Convert a secret key given as algorithm id and an array of key - parameters into our s-expression based format. */ + parameters into our s-expression based format. Note that + PUBKEY_ALGO is a standard id and not an OpenPGP id. + */ static gpg_error_t convert_secret_key (gcry_sexp_t *r_key, int pubkey_algo, gcry_mpi_t *skey) { @@ -103,6 +118,9 @@ convert_secret_key (gcry_sexp_t *r_key, int pubkey_algo, gcry_mpi_t *skey) *r_key = NULL; + /* FIXME: This is not consistent with the above comment. */ + pubkey_algo = map_pk_openpgp_to_gcry (pubkey_algo); + switch (pubkey_algo) { case GCRY_PK_DSA: @@ -128,6 +146,18 @@ convert_secret_key (gcry_sexp_t *r_key, int pubkey_algo, gcry_mpi_t *skey) skey[5]); break; + case GCRY_PK_ECDSA: + err = gcry_sexp_build (&s_skey, NULL, + "(private-key(ecdsa(c%m)(q%m)(d%m)))", + skey[0], skey[1], skey[2]); + break; + + case GCRY_PK_ECDH: + err = gcry_sexp_build (&s_skey, NULL, + "(private-key(ecdh(c%m)(q%m)(p%m)(d%m)))", + skey[0], skey[1], skey[2], skey[3]); + break; + default: err = gpg_error (GPG_ERR_PUBKEY_ALGO); break; @@ -202,6 +232,10 @@ do_unprotect (const char *passphrase, *r_key = NULL; + /* Unfortunately, the OpenPGP PK algorithm numbers need to be + re-mapped for Libgcrypt. */ + pubkey_algo = map_pk_openpgp_to_gcry (pubkey_algo); + /* Count the actual number of MPIs is in the array and set the remainder to NULL for easier processing later on. */ for (skeylen = 0; skey[skeylen]; skeylen++) @@ -219,9 +253,6 @@ do_unprotect (const char *passphrase, if (gcry_pk_test_algo (pubkey_algo)) { - /* The algorithm numbers are Libgcrypt numbers but fortunately - the OpenPGP algorithm numbers map one-to-one to the Libgcrypt - numbers. */ log_info (_("public key algorithm %d (%s) is not supported\n"), pubkey_algo, gcry_pk_algo_name (pubkey_algo)); return gpg_error (GPG_ERR_PUBKEY_ALGO); @@ -1007,7 +1038,8 @@ convert_to_openpgp (ctrl_t ctrl, gcry_sexp_t s_key, const char *passphrase, case GCRY_PK_ELG: algoname = "elg"; npkey = 3; elems = "pgyx"; break; case GCRY_PK_ELG_E: algoname = "elg"; npkey = 3; elems = "pgyx"; break; case GCRY_PK_DSA: algoname = "dsa"; npkey = 4; elems = "pqgyx"; break; - case GCRY_PK_ECDSA: algoname = "ecdsa"; npkey = 6; elems = "pabgnqd"; break; + case GCRY_PK_ECDSA: algoname = "ecdsa"; npkey = 2; elems = "cqd"; break; + case GCRY_PK_ECDH: algoname = "ecdh"; npkey = 3; elems = "cqpd"; break; default: algoname = ""; npkey = 0; elems = NULL; break; } assert (!elems || strlen (elems) < DIM (array) ); @@ -1037,7 +1069,7 @@ convert_to_openpgp (ctrl_t ctrl, gcry_sexp_t s_key, const char *passphrase, int format_args_buf_int[1]; void *format_args[10+2]; size_t n; - gcry_sexp_t tmpkey, tmpsexp; + gcry_sexp_t tmpkey, tmpsexp = NULL; snprintf (countbuf, sizeof countbuf, "%lu", s2k_count); @@ -1085,7 +1117,6 @@ convert_to_openpgp (ctrl_t ctrl, gcry_sexp_t s_key, const char *passphrase, for (i=0; i < DIM (array); i++) gcry_mpi_release (array[i]); - + return err; } - diff --git a/agent/findkey.c b/agent/findkey.c index 91fb8c14c..02e938e6e 100644 --- a/agent/findkey.c +++ b/agent/findkey.c @@ -726,6 +726,16 @@ key_parms_from_sexp (gcry_sexp_t s_key, gcry_sexp_t *r_list, algoname = "dsa"; elems = "pqgy"; } + else if (n==5 && !memcmp (name, "ecdsa", 5)) + { + algoname = "ecdsa"; + elems = "cq"; + } + else if (n==4 && !memcmp (name, "ecdh", 4)) + { + algoname = "ecdh"; + elems = "cqp"; + } else if (n==3 && !memcmp (name, "elg", 3)) { algoname = "elg"; diff --git a/agent/gpg-agent.c b/agent/gpg-agent.c index ca150b471..db9039278 100644 --- a/agent/gpg-agent.c +++ b/agent/gpg-agent.c @@ -51,6 +51,7 @@ #include "gc-opt-flags.h" #include "exechelp.h" #include "asshelp.h" +#include "../include/cipher.h" /* for PUBKEY_ALGO_ECDSA, PUBKEY_ALGO_ECDH */ enum cmd_and_opt_values { aNull = 0, @@ -2301,3 +2302,12 @@ check_for_running_agent (int silent, int mode) assuan_release (ctx); return 0; } + +/* TODO: it is also in misc, which is not linked with the agent */ +/* FIXME: The agent should not know about openpgp internals - weel + except for some stuff in cvt-openpgp. */ +int +map_pk_openpgp_to_gcry (int algo) +{ + return (algo==PUBKEY_ALGO_ECDSA ? GCRY_PK_ECDSA : (algo==PUBKEY_ALGO_ECDH ? GCRY_PK_ECDH : algo)); +} diff --git a/agent/pksign.c b/agent/pksign.c index ac5f4e1a0..0414bc347 100644 --- a/agent/pksign.c +++ b/agent/pksign.c @@ -113,18 +113,21 @@ get_dsa_qbits (gcry_sexp_t key) /* Encode a message digest for use with an DSA algorithm. */ static gpg_error_t -do_encode_dsa (const byte * md, size_t mdlen, int dsaalgo, gcry_sexp_t pkey, +do_encode_dsa (const byte *md, size_t mdlen, int dsaalgo, gcry_sexp_t pkey, gcry_sexp_t *r_hash) { gpg_error_t err; gcry_sexp_t hash; unsigned int qbits; + int pkalgo; *r_hash = NULL; - if (dsaalgo == GCRY_PK_ECDSA) + pkalgo = map_pk_openpgp_to_gcry (dsaalgo); + + if (pkalgo == GCRY_PK_ECDSA) qbits = gcry_pk_get_nbits (pkey); - else if (dsaalgo == GCRY_PK_DSA) + else if (pkalgo == GCRY_PK_DSA) qbits = get_dsa_qbits (pkey); else return gpg_error (GPG_ERR_WRONG_PUBKEY_ALGO); @@ -143,20 +146,28 @@ do_encode_dsa (const byte * md, size_t mdlen, int dsaalgo, gcry_sexp_t pkey, if (qbits < 160) { log_error (_("%s key uses an unsafe (%u bit) hash\n"), - gcry_pk_algo_name (dsaalgo), qbits); + gcry_pk_algo_name (pkalgo), qbits); return gpg_error (GPG_ERR_INV_LENGTH); } /* Check if we're too short. Too long is safe as we'll - automatically left-truncate. */ - if (mdlen < qbits/8) + * automatically left-truncate. + * + * This check would require the use of SHA512 with ECDSA 512. I + * think this is overkill to fail in this case. Therefore, relax + * the check, but only for ECDSA keys. We may need to adjust it + * later for general case. (Note that the check is really a bug for + * ECDSA 521 as the only hash that matches it is SHA 512, but 512 < + * 521 ). + */ + if (mdlen < ((pkalgo==GCRY_PK_ECDSA && qbits > 521) ? 512 : qbits)/8) { log_error (_("a %zu bit hash is not valid for a %u bit %s key\n"), mdlen*8, gcry_pk_get_nbits (pkey), - gcry_pk_algo_name (dsaalgo)); + gcry_pk_algo_name (pkalgo)); /* FIXME: we need to check the requirements for ECDSA. */ - if (mdlen < 20 || dsaalgo == GCRY_PK_DSA) + if (mdlen < 20 || pkalgo == GCRY_PK_DSA) return gpg_error (GPG_ERR_INV_LENGTH); } diff --git a/agent/protect.c b/agent/protect.c index 795d06231..d0a5fe9e3 100644 --- a/agent/protect.c +++ b/agent/protect.c @@ -43,7 +43,7 @@ /* A table containing the information needed to create a protected - private key */ + private key. */ static struct { const char *algo; const char *parmlist; @@ -52,6 +52,8 @@ static struct { { "rsa", "nedpqu", 2, 5 }, { "dsa", "pqgyx", 4, 4 }, { "elg", "pgyx", 3, 3 }, + { "ecdsa","cqd", 2, 2 }, + { "ecdh", "cqpd", 3, 3 }, { NULL } }; @@ -488,7 +490,7 @@ agent_protect (const unsigned char *plainkey, const char *passphrase, depth--; hash_end = s; s++; - /* skip to the end of the S-exp */ + /* Skip to the end of the S-expression. */ assert (depth == 1); rc = sskip (&s, &depth); if (rc) diff --git a/common/ChangeLog b/common/ChangeLog index 0150658e0..61f6b292b 100644 --- a/common/ChangeLog +++ b/common/ChangeLog @@ -50,6 +50,11 @@ * session-env.c (update_var): Fix same value detection. Fixes bug#1311. +2011-01-10 Werner Koch <[email protected]> + + * session-env.c (update_var): Fix same value detection. Fixes + bug#1311. + 2010-12-17 Werner Koch <[email protected]> * asshelp.c (lock_spawning): Add arg VERBOSE. Improve timeout diff --git a/common/convert.c b/common/convert.c index aa3a3a809..5df6b335e 100644 --- a/common/convert.c +++ b/common/convert.c @@ -23,6 +23,7 @@ #include <ctype.h> #include "util.h" +#include "gcrypt.h" /* FIXME: really needed? */ #define tohex(n) ((n) < 10 ? ((n) + '0') : (((n) - 10) + 'A')) @@ -245,5 +246,36 @@ hex2str_alloc (const char *hexstring, size_t *r_count) return result; } +/* returns hex representation of the MPI; + * caller must free with xfree + * Returns NULL on error, never throws + */ +char * +mpi2hex( gcry_mpi_t m ) +{ +#warning we have code for this in libcrypt + size_t nbytes; + size_t nbytes2; + int rc; + byte *p; + + nbytes = (mpi_get_nbits ( m )+7)/8; + if( nbytes == 0 ) + return NULL; + p = xtrymalloc( nbytes*3+1 ); + if( p==NULL ) + return NULL; + rc = gcry_mpi_print (GCRYMPI_FMT_USG, p+2*nbytes+1, nbytes, &nbytes2, m); + if( rc ) { + xfree( p ); + return NULL; + } + bin2hex( p+2*nbytes+1, nbytes2, p ); + p[nbytes2*2] = '\0'; + /*printf("%s:%d>>>> Created the string %s from %d bytes %02x %02x + ..., MPI was %d bytes\n", __FILE__, __LINE__, p, nbytes2, + p[2*nbytes+1], p[2*nbytes+2], nbytes); */ + return p; +} diff --git a/common/util.h b/common/util.h index f06701fc0..99d58e172 100644 --- a/common/util.h +++ b/common/util.h @@ -196,6 +196,7 @@ gpg_error_t get_pk_algo_from_canon_sexp (const unsigned char *keydata, int hex2bin (const char *string, void *buffer, size_t length); int hexcolon2bin (const char *string, void *buffer, size_t length); char *bin2hex (const void *buffer, size_t length, char *stringbuf); +char *mpi2hex (gcry_mpi_t m); char *bin2hexcolon (const void *buffer, size_t length, char *stringbuf); const char *hex2str (const char *hexstring, char *buffer, size_t bufsize, size_t *buflen); diff --git a/configure.ac b/configure.ac index b108d19b4..a944e80ac 100644 --- a/configure.ac +++ b/configure.ac @@ -43,7 +43,7 @@ development_version=no NEED_GPG_ERROR_VERSION=1.8 NEED_LIBGCRYPT_API=1 -NEED_LIBGCRYPT_VERSION=1.4.0 +NEED_LIBGCRYPT_VERSION=1.4.6 NEED_LIBASSUAN_API=2 NEED_LIBASSUAN_VERSION=2.0.0 @@ -742,6 +742,20 @@ AM_PATH_GPG_ERROR("$NEED_GPG_ERROR_VERSION", AM_PATH_LIBGCRYPT("$NEED_LIBGCRYPT_API:$NEED_LIBGCRYPT_VERSION", have_libgcrypt=yes,have_libgcrypt=no) +AC_CACHE_CHECK([whether Libgcrypt support ECDH], gnupg_cv_gcry_pk_ecdh, + [ _gnupg_gcry_save_cflags=$CFLAGS + CFLAGS="$CFLAGS $LIBGCRYPT_CFLAGS" + AC_TRY_COMPILE( + [#include <gcrypt>], + [ return GCRY_PK_ECDH; ], + gnupg_cv_gcry_pk_ecdh=yes, + gnupg_cv_gcry_pk_ecdh=no) + CFLAGS=$_gnupg_gcry_save_cflags]) +if test "$gnupg_cv_gcry_pk_ecdh" = yes; then + AC_DEFINE([HAVE_GCRY_PK_ECDH], 1, + [Define if gcrypt.h has the enum value for ECDH.]) +fi + # # libassuan is used for IPC @@ -1484,7 +1498,7 @@ AC_ARG_ENABLE(optimization, AC_HELP_STRING([--disable-optimization], [disable compiler optimization]), [if test $enableval = no ; then - CFLAGS=`echo $CFLAGS | sed 's/-O[[0-9]]//'` + CFLAGS=`echo $CFLAGS | sed s/-O[[1-9]]\ /-O0\ /g` fi]) # diff --git a/dirmngr/Makefile.am b/dirmngr/Makefile.am index 8c41c53b2..79acae9f7 100644 --- a/dirmngr/Makefile.am +++ b/dirmngr/Makefile.am @@ -62,7 +62,7 @@ endif dirmngr_LDADD = $(libcommonpth) ../gl/libgnu.a $(DNSLIBS) $(LIBASSUAN_LIBS) \ $(LIBGCRYPT_LIBS) $(KSBA_LIBS) $(PTH_LIBS) $(LIBINTL) $(LIBICONV) if !USE_LDAPWRAPPER -dirmngr_LDADD += $(LDAPLIBS) +dirmngr_LDADD += $(LDAPLIBS) -llber #FIXME: Test for liblber first. endif dirmngr_LDFLAGS = $(extra_bin_ldflags) diff --git a/g10/ChangeLog b/g10/ChangeLog index a60d5d581..4c28363a2 100644 --- a/g10/ChangeLog +++ b/g10/ChangeLog @@ -20,6 +20,12 @@ out to ../common/keyserver.h. (keyserver_spec_t): New. +2011-01-21 Werner Koch <[email protected]> + + * seskey.c (encode_md_value): Truncate the DSA hash again. + + * misc.c (openpgp_pk_algo_name): Always use the gcrypt function. + 2010-12-09 Werner Koch <[email protected]> * tdbio.c (tdbio_set_dbname) [W32CE]: Take care of missing errno. diff --git a/g10/Makefile.am b/g10/Makefile.am index 475529c4e..5d2470207 100644 --- a/g10/Makefile.am +++ b/g10/Makefile.am @@ -72,7 +72,8 @@ common_source = \ plaintext.c \ sig-check.c \ keylist.c \ - pkglue.c pkglue.h + pkglue.c pkglue.h \ + ecdh.c gpg2_SOURCES = gpg.c \ server.c \ @@ -110,7 +111,8 @@ gpg2_SOURCES = gpg.c \ gpgv2_SOURCES = gpgv.c \ $(common_source) \ - verify.c + verify.c \ + verify-stubs.c #gpgd_SOURCES = gpgd.c \ # ks-proto.h \ diff --git a/g10/build-packet.c b/g10/build-packet.c index 83d6c7a73..d138e0614 100644 --- a/g10/build-packet.c +++ b/g10/build-packet.c @@ -178,11 +178,20 @@ mpi_write (iobuf_t out, gcry_mpi_t a) return rc; } +/* Write the name OID, encoded as an mpi, to OUT. The format of the + * content of the MPI is one byte LEN, following by LEN bytes that are + * DER representation of an ASN.1 OID. This is true for each of the 3 + * following functions. */ +#define iobuf_name_oid_write iobuf_write_size_body_mpi +/* Write the value of KEK fields for ECDH. */ +#define ecdh_kek_params_write iobuf_write_size_body_mpi -/**************** - * calculate the length of a packet described by PKT - */ +/* Write the value of encrypted filed for ECDH. */ +#define ecdh_esk_write iobuf_write_size_body_mpi + + +/* Calculate the length of a packet described by PKT. */ u32 calc_packet_length( PACKET *pkt ) { @@ -290,10 +299,35 @@ do_key (iobuf_t out, int ctb, PKT_public_key *pk) } assert (npkey < nskey); - /* Writing the public parameters is easy. */ - for (i=0; i < npkey; i++ ) - if ((err = mpi_write (a, pk->pkey[i]))) - goto leave; + /* Writing the public parameters is easy. Except if we do an + adjustment for ECC OID and possibly KEK params for ECDH. */ + if (pk->pubkey_algo == PUBKEY_ALGO_ECDSA + || pk->pubkey_algo == PUBKEY_ALGO_ECDH) + { + /* Write DER of OID with preceeding length byte. */ + err = iobuf_name_oid_write (a, pk->pkey[0]); + if (err) + goto leave; + /* Write point Q, the public key. */ + err = mpi_write (a, pk->pkey[1]); + if (err) + goto leave; + + /* Write one more public field for ECDH. */ + if (pk->pubkey_algo == PUBKEY_ALGO_ECDH) + { + err = ecdh_kek_params_write(a,pk->pkey[2]); + if (err) + goto leave; + } + } + else + { + for (i=0; i < npkey; i++ ) + if ((err = mpi_write (a, pk->pkey[i]))) + goto leave; + } + if (pk->seckey_info) { @@ -458,13 +492,26 @@ do_pubkey_enc( IOBUF out, int ctb, PKT_pubkey_enc *enc ) n = pubkey_get_nenc( enc->pubkey_algo ); if ( !n ) write_fake_data( a, enc->data[0] ); - for (i=0; i < n && !rc ; i++ ) - rc = mpi_write(a, enc->data[i] ); + + if (enc->pubkey_algo == PUBKEY_ALGO_ECDH ) + { + /* The second field persists as a LEN+field structure, even + * though it is stored for uniformity as an MPI internally. */ + assert (n == 2); + rc = mpi_write (a, enc->data[0]); + if (!rc) + rc = ecdh_esk_write (a, enc->data[1]); + } + else + { + for (i=0; i < n && !rc ; i++ ) + rc = mpi_write(a, enc->data[i] ); + } if (!rc) { - write_header(out, ctb, iobuf_get_temp_length(a) ); - rc = iobuf_write_temp( out, a ); + write_header (out, ctb, iobuf_get_temp_length(a) ); + rc = iobuf_write_temp (out, a); } iobuf_close(a); return rc; diff --git a/g10/call-agent.c b/g10/call-agent.c index 9528e1427..dc2ace0e5 100644 --- a/g10/call-agent.c +++ b/g10/call-agent.c @@ -1751,7 +1751,7 @@ agent_pkdecrypt (ctrl_t ctrl, const char *keygrip, const char *desc, membuf_t data; size_t n, len; char *p, *buf, *endp; - + if (!keygrip || strlen(keygrip) != 40 || !s_ciphertext || !r_buf || !r_buflen) return gpg_error (GPG_ERR_INV_VALUE); *r_buf = NULL; diff --git a/g10/ecdh.c b/g10/ecdh.c new file mode 100644 index 000000000..cb251fef2 --- /dev/null +++ b/g10/ecdh.c @@ -0,0 +1,534 @@ +/* ecdh.c - ECDH public key operations used in public key glue code + * Copyright (C) 2010 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * GnuPG 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <assert.h> + +#include "gpg.h" +#include "util.h" +#include "pkglue.h" +#include "main.h" +#include "options.h" + +gcry_mpi_t +pk_ecdh_default_params_to_mpi (int qbits) +{ + gpg_error_t err; + gcry_mpi_t result; + /* Defaults are the strongest possible choices. Performance is not + an issue here, only interoperability. */ + byte kek_params[4] = { + 3 /*size of following field*/, + 1 /*fixed version for KDF+AESWRAP*/, + DIGEST_ALGO_SHA512 /* KEK MD */, + CIPHER_ALGO_AES256 /*KEK AESWRAP alg*/ + }; + int i; + + static const struct { + int qbits; + int openpgp_hash_id; + int openpgp_cipher_id; + } kek_params_table[] = { + { 256, DIGEST_ALGO_SHA256, CIPHER_ALGO_AES }, + { 384, DIGEST_ALGO_SHA384, CIPHER_ALGO_AES256 }, + + /* Note: 528 is 521 rounded to the 8 bit boundary */ + { 528, DIGEST_ALGO_SHA512, CIPHER_ALGO_AES256 } + }; + + for (i=0; i<sizeof(kek_params_table)/sizeof(kek_params_table[0]); i++) + { + if (kek_params_table[i].qbits >= qbits) + { + kek_params[2] = kek_params_table[i].openpgp_hash_id; + kek_params[3] = kek_params_table[i].openpgp_cipher_id; + break; + } + } + if (DBG_CIPHER) + log_printhex ("ecdh kek params are", kek_params, sizeof(kek_params) ); + + err = gcry_mpi_scan (&result, GCRYMPI_FMT_USG, + kek_params, sizeof(kek_params), NULL); + if (err) + log_fatal ("mpi_scan failed: %s\n", gpg_strerror (err)); + + return result; +} + + +/* Returns allocated (binary) KEK parameters; the size is returned in + * sizeout. The caller must free the returned value with xfree. + * Returns NULL on error. + */ +byte * +pk_ecdh_default_params (int qbits, size_t *sizeout) +{ + /* Defaults are the strongest possible choices. Performance is not + an issue here, only interoperability. */ + byte kek_params[4] = { + 3 /*size of following field*/, + 1 /*fixed version for KDF+AESWRAP*/, + DIGEST_ALGO_SHA512 /* KEK MD */, + CIPHER_ALGO_AES256 /* KEK AESWRAP alg */ + }; + int i; + + static const struct { + int qbits; + int openpgp_hash_id; + int openpgp_cipher_id; + } kek_params_table[] = { + { 256, DIGEST_ALGO_SHA256, CIPHER_ALGO_AES }, + { 384, DIGEST_ALGO_SHA384, CIPHER_ALGO_AES256 }, + /* Note: 528 is 521 rounded to the 8 bit boundary */ + { 528, DIGEST_ALGO_SHA512, CIPHER_ALGO_AES256 } + }; + + byte *p; + + *sizeout = 0; + + for (i=0; i<sizeof(kek_params_table)/sizeof(kek_params_table[0]); i++) + { + if (kek_params_table[i].qbits >= qbits) + { + kek_params[2] = kek_params_table[i].openpgp_hash_id; + kek_params[3] = kek_params_table[i].openpgp_cipher_id; + break; + } + } + if (DBG_CIPHER ) + log_printhex ("ecdh kek params are", kek_params, sizeof(kek_params)); + + p = xtrymalloc (sizeof(kek_params)); + if (!p) + return NULL; + memcpy (p, kek_params, sizeof(kek_params)); + *sizeout = sizeof(kek_params); + return p; +} + + +/* Encrypts/decrypts 'data' with a key derived from shared_mpi ECC + * point using FIPS SP 800-56A compliant method, which is key + * derivation + key wrapping. The direction is determined by the first + * parameter (is_encrypt=1 --> this is encryption). The result is + * returned in out as a size+value MPI. + * + * TODO: memory leaks (x_secret). + */ +static int +pk_ecdh_encrypt_with_shared_point (int is_encrypt, gcry_mpi_t shared_mpi, + const byte pk_fp[MAX_FINGERPRINT_LEN], + gcry_mpi_t data, gcry_mpi_t *pkey, + gcry_mpi_t *out) +{ + byte *secret_x; + int secret_x_size; + byte kdf_params[256]; + int kdf_params_size=0; + int nbits; + int kdf_hash_algo; + int kdf_encr_algo; + int rc; + + *out = NULL; + + nbits = pubkey_nbits( PUBKEY_ALGO_ECDH, pkey ); + + { + size_t nbytes; + /* Extract x component of the shared point: this is the actual + shared secret */ + nbytes = (mpi_get_nbits (pkey[1] /* public point */)+7)/8; + secret_x = xmalloc_secure( nbytes ); + rc = gcry_mpi_print (GCRYMPI_FMT_USG, secret_x, nbytes, + &nbytes, shared_mpi); + if (rc) + { + xfree (secret_x); + log_error ("ec ephemeral export of shared point failed: %s\n", + gpg_strerror (rc)); + return rc; + } + secret_x_size = (nbits+7)/8; + assert (nbytes > secret_x_size); + memmove (secret_x, secret_x+1, secret_x_size); + memset (secret_x+secret_x_size, 0, nbytes-secret_x_size); + + if (DBG_CIPHER) + log_printhex ("ecdh shared secret X is:", secret_x, secret_x_size ); + } + + /*** We have now the shared secret bytes in secret_x. ***/ + + /* At this point we are done with PK encryption and the rest of the + * function uses symmetric key encryption techniques to protect the + * input 'data'. The following two sections will simply replace + * current secret_x with a value derived from it. This will become + * a KEK. + */ + { + IOBUF obuf = iobuf_temp(); + rc = iobuf_write_size_body_mpi ( obuf, pkey[2] ); /* KEK params */ + + kdf_params_size = iobuf_temp_to_buffer (obuf, + kdf_params, sizeof(kdf_params)); + + if (DBG_CIPHER) + log_printhex ("ecdh KDF public key params are:", + kdf_params, kdf_params_size ); + + /* Expect 4 bytes 03 01 hash_alg symm_alg. */ + if (kdf_params_size != 4 || kdf_params[0] != 3 || kdf_params[1] != 1) + return GPG_ERR_BAD_PUBKEY; + + kdf_hash_algo = kdf_params[2]; + kdf_encr_algo = kdf_params[3]; + + if (DBG_CIPHER) + log_debug ("ecdh KDF algorithms %s+%s with aeswrap\n", + gcry_md_algo_name (kdf_hash_algo), + openpgp_cipher_algo_name (kdf_encr_algo)); + + if (kdf_hash_algo != GCRY_MD_SHA256 + && kdf_hash_algo != GCRY_MD_SHA384 + && kdf_hash_algo != GCRY_MD_SHA512) + return GPG_ERR_BAD_PUBKEY; + if (kdf_encr_algo != GCRY_CIPHER_AES128 + && kdf_encr_algo != GCRY_CIPHER_AES192 + && kdf_encr_algo != GCRY_CIPHER_AES256) + return GPG_ERR_BAD_PUBKEY; + } + + /* Build kdf_params. */ + { + IOBUF obuf; + + obuf = iobuf_temp(); + /* variable-length field 1, curve name OID */ + rc = iobuf_write_size_body_mpi ( obuf, pkey[0] ); + /* fixed-length field 2 */ + iobuf_put (obuf, PUBKEY_ALGO_ECDH); + /* variable-length field 3, KDF params */ + rc = (rc ? rc : iobuf_write_size_body_mpi ( obuf, pkey[2] )); + /* fixed-length field 4 */ + iobuf_write (obuf, "Anonymous Sender ", 20); + /* fixed-length field 5, recipient fp */ + iobuf_write (obuf, pk_fp, 20); + + kdf_params_size = iobuf_temp_to_buffer (obuf, + kdf_params, sizeof(kdf_params)); + iobuf_close (obuf); + if (rc) + return rc; + + if(DBG_CIPHER) + log_printhex ("ecdh KDF message params are:", + kdf_params, kdf_params_size ); + } + + /* Derive a KEK (key wrapping key) using kdf_params and secret_x. */ + { + gcry_md_hd_t h; + int old_size; + + rc = gcry_md_open (&h, kdf_hash_algo, 0); + if(rc) + log_bug ("gcry_md_open failed for algo %d: %s", + kdf_hash_algo, gpg_strerror (gcry_error(rc))); + gcry_md_write(h, "\x00\x00\x00\x01", 4); /* counter = 1 */ + gcry_md_write(h, secret_x, secret_x_size); /* x of the point X */ + gcry_md_write(h, kdf_params, kdf_params_size); /* KDF parameters */ + + gcry_md_final (h); + + assert( gcry_md_get_algo_dlen (kdf_hash_algo) >= 32 ); + + memcpy (secret_x, gcry_md_read (h, kdf_hash_algo), + gcry_md_get_algo_dlen (kdf_hash_algo)); + gcry_md_close (h); + + old_size = secret_x_size; + assert( old_size >= gcry_cipher_get_algo_keylen( kdf_encr_algo ) ); + secret_x_size = gcry_cipher_get_algo_keylen( kdf_encr_algo ); + assert( secret_x_size <= gcry_md_get_algo_dlen (kdf_hash_algo) ); + + /* We could have allocated more, so clean the tail before returning. */ + memset( secret_x+secret_x_size, old_size-secret_x_size, 0 ); + if (DBG_CIPHER) + log_printhex ("ecdh KEK is:", secret_x, secret_x_size ); + } + + /* And, finally, aeswrap with key secret_x. */ + { + gcry_cipher_hd_t hd; + size_t nbytes; + + byte *data_buf; + int data_buf_size; + + gcry_mpi_t result; + + rc = gcry_cipher_open (&hd, kdf_encr_algo, GCRY_CIPHER_MODE_AESWRAP, 0); + if (rc) + { + log_error ("ecdh failed to initialize AESWRAP: %s\n", + gpg_strerror (rc)); + return rc; + } + + rc = gcry_cipher_setkey (hd, secret_x, secret_x_size); + xfree( secret_x ); + if (rc) + { + gcry_cipher_close (hd); + log_error ("ecdh failed in gcry_cipher_setkey: %s\n", + gpg_strerror (rc)); + return rc; + } + + data_buf_size = (gcry_mpi_get_nbits(data)+7)/8; + assert ((data_buf_size & 7) == (is_encrypt ? 0 : 1)); + + data_buf = xtrymalloc_secure( 1 + 2*data_buf_size + 8); + if (!data_buf) + { + gcry_cipher_close (hd); + return GPG_ERR_ENOMEM; + } + + if (is_encrypt) + { + byte *in = data_buf+1+data_buf_size+8; + + /* Write data MPI into the end of data_buf. data_buf is size + aeswrap data. */ + rc = gcry_mpi_print (GCRYMPI_FMT_USG, in, + data_buf_size, &nbytes, data/*in*/); + if (rc) + { + log_error ("ecdh failed to export DEK: %s\n", gpg_strerror (rc)); + gcry_cipher_close (hd); + xfree (data_buf); + return rc; + } + + if (DBG_CIPHER) + log_printhex ("ecdh encrypting :", in, data_buf_size ); + + rc = gcry_cipher_encrypt (hd, data_buf+1, data_buf_size+8, + in, data_buf_size); + memset (in, 0, data_buf_size); + gcry_cipher_close (hd); + if (rc) + { + log_error ("ecdh failed in gcry_cipher_encrypt: %s\n", + gpg_strerror (rc)); + xfree (data_buf); + return rc; + } + data_buf[0] = data_buf_size+8; + + if (DBG_CIPHER) + log_printhex ("ecdh encrypted to:", data_buf+1, data_buf[0] ); + + rc = gcry_mpi_scan (&result, GCRYMPI_FMT_USG, + data_buf, 1+data_buf[0], NULL); + /* (byte)size + aeswrap of DEK */ + xfree( data_buf ); + if (rc) + { + log_error ("ecdh failed to create an MPI: %s\n", gpg_strerror (rc)); + return rc; + } + + *out = result; + } + else + { + byte *in; + + rc = gcry_mpi_print (GCRYMPI_FMT_USG, data_buf, data_buf_size, + &nbytes, data/*in*/); + if (nbytes != data_buf_size || data_buf[0] != data_buf_size-1) + { + log_error ("ecdh inconsistent size\n"); + xfree (data_buf); + return GPG_ERR_BAD_MPI; + } + in = data_buf+data_buf_size; + data_buf_size = data_buf[0]; + + if (DBG_CIPHER) + log_printhex ("ecdh decrypting :", data_buf+1, data_buf_size); + + rc = gcry_cipher_decrypt (hd, in, data_buf_size, data_buf+1, + data_buf_size); + gcry_cipher_close (hd); + if (rc) + { + log_error ("ecdh failed in gcry_cipher_decrypt: %s\n", + gpg_strerror (rc)); + xfree (data_buf); + return rc; + } + + data_buf_size -= 8; + + if (DBG_CIPHER) + log_printhex ("ecdh decrypted to :", in, data_buf_size); + + /* Padding is removed later. */ + /* if (in[data_buf_size-1] > 8 ) */ + /* { */ + /* log_error("ecdh failed at decryption: invalid padding. %02x > 8\n", */ + /* in[data_buf_size-1] ); */ + /* return GPG_ERR_BAD_KEY; */ + /* } */ + + rc = gcry_mpi_scan ( &result, GCRYMPI_FMT_USG, in, data_buf_size, NULL); + xfree (data_buf); + if (rc) + { + log_error ("ecdh failed to create a plain text MPI: %s\n", + gpg_strerror (rc)); + return rc; + } + + *out = result; + } + } + + return rc; +} + + +static gcry_mpi_t +gen_k (unsigned nbits) +{ + gcry_mpi_t k; + + k = gcry_mpi_snew (nbits); + if (DBG_CIPHER) + log_debug ("choosing a random k of %u bits\n", nbits); + + gcry_mpi_randomize (k, nbits-1, GCRY_STRONG_RANDOM); + + if (DBG_CIPHER) + { + unsigned char *buffer; + if (gcry_mpi_aprint (GCRYMPI_FMT_HEX, &buffer, NULL, k)) + BUG (); + log_debug("ephemeral scalar MPI #0: %s\n", buffer); + gcry_free( buffer ); + } + + return k; +} + +/* Perform ECDH encryption, which involves ECDH key generation. */ +int +pk_ecdh_encrypt (gcry_mpi_t *resarr, const byte pk_fp[MAX_FINGERPRINT_LEN], + gcry_mpi_t data, gcry_mpi_t * pkey) +{ + gcry_sexp_t s_ciph, s_data, s_pkey; + + int nbits; + int rc; + gcry_mpi_t k; + + nbits = pubkey_nbits (PUBKEY_ALGO_ECDH, pkey); + + /*** Generate an ephemeral key, actually, a scalar. ***/ + + k = gen_k (nbits); + if( k == NULL ) + BUG (); + + /*** Done with ephemeral key generation. + * Now use ephemeral secret to get the shared secret. ***/ + + rc = gcry_sexp_build (&s_pkey, NULL, + "(public-key(ecdh(c%m)(q%m)(p%m)))", + pkey[0], pkey[1], pkey[2]); + if (rc) + BUG (); + + /* Put the data into a simple list. */ + /* Ephemeral scalar goes as data. */ + if (gcry_sexp_build (&s_data, NULL, "%m", k)) + BUG (); + + /* Pass it to libgcrypt. */ + rc = gcry_pk_encrypt (&s_ciph, s_data, s_pkey); + gcry_sexp_release (s_data); + gcry_sexp_release (s_pkey); + if (rc) + return rc; + + /* Finally, perform encryption. */ + + { + /* ... and get the shared point/ */ + gcry_mpi_t shared; + + shared = mpi_from_sexp (s_ciph, "a"); + gcry_sexp_release (s_ciph); + /* Ephemeral public key. */ + resarr[0] = mpi_from_sexp (s_ciph, "b"); + + if (DBG_CIPHER) + { + unsigned char *buffer; + + if (gcry_mpi_aprint (GCRYMPI_FMT_HEX, &buffer, NULL, resarr[0])) + BUG (); + log_debug("ephemeral key MPI: %s\n", buffer); + gcry_free( buffer ); + } + + rc = pk_ecdh_encrypt_with_shared_point (1 /*=encrypton*/, shared, + pk_fp, data, pkey, resarr+1); + mpi_release (shared); + } + + return rc; +} + + +/* Perform ECDH decryption. */ +int +pk_ecdh_decrypt (gcry_mpi_t * result, const byte sk_fp[MAX_FINGERPRINT_LEN], + gcry_mpi_t data, gcry_mpi_t shared, gcry_mpi_t * skey) +{ + if (!data) + return gpg_error (GPG_ERR_BAD_MPI); + return pk_ecdh_encrypt_with_shared_point (0 /*=decryption*/, shared, + sk_fp, data/*encr data as an MPI*/, + skey, result); +} + + diff --git a/g10/encrypt.c b/g10/encrypt.c index 55f9b27fb..f52921582 100644 --- a/g10/encrypt.c +++ b/g10/encrypt.c @@ -876,7 +876,9 @@ write_pubkey_enc_from_list (PK_LIST pk_list, DEK *dek, iobuf_t out) for ( ; pk_list; pk_list = pk_list->next ) { gcry_mpi_t frame; - + byte fp[MAX_FINGERPRINT_LEN]; + size_t fpn; + pk = pk_list->pk; print_pubkey_algo_note ( pk->pubkey_algo ); @@ -892,6 +894,9 @@ write_pubkey_enc_from_list (PK_LIST pk_list, DEK *dek, iobuf_t out) compliance_failure(); } + fingerprint_from_pk (pk, fp, &fpn); + assert (fpn == 20); + /* Okay, what's going on: We have the session key somewhere in * the structure DEK and want to encode this session key in an * integer value of n bits. pubkey_nbits gives us the number of @@ -904,9 +909,9 @@ write_pubkey_enc_from_list (PK_LIST pk_list, DEK *dek, iobuf_t out) * for Elgamal). We don't need frame anymore because we have * everything now in enc->data which is the passed to * build_packet(). */ - frame = encode_session_key (dek, + frame = encode_session_key (pk->pubkey_algo, dek, pubkey_nbits (pk->pubkey_algo, pk->pkey)); - rc = pk_encrypt (pk->pubkey_algo, enc->data, frame, pk->pkey); + rc = pk_encrypt (pk->pubkey_algo, enc->data, frame, fp, pk->pkey); gcry_mpi_release (frame); if (rc) log_error ("pubkey_encrypt failed: %s\n", gpg_strerror (rc) ); diff --git a/g10/export.c b/g10/export.c index 43856ffea..1eb0baa8b 100644 --- a/g10/export.c +++ b/g10/export.c @@ -1215,6 +1215,16 @@ build_sexp_seckey (iobuf_t out, PACKET *pkt, int *indent) /* iobuf_put (out,')'); iobuf_put (out,'\n'); */ /* (*indent)--; */ /* } */ + /* else if (sk->pubkey_algo == PUBKEY_ALGO_ECDSA && !sk->is_protected) */ + /* { */ + /* write_sexp_line (out, indent, "(ecdsa\n"); */ + /* (*indent)++; */ + /* write_sexp_keyparm (out, indent, "c", sk->skey[0]); iobuf_put (out,'\n'); */ + /* write_sexp_keyparm (out, indent, "q", sk->skey[6]); iobuf_put (out,'\n'); */ + /* write_sexp_keyparm (out, indent, "d", sk->skey[7]); */ + /* iobuf_put (out,')'); iobuf_put (out,'\n'); */ + /* (*indent)--; */ + /* } */ /* else if (is_ELGAMAL (sk->pubkey_algo) && !sk->is_protected) */ /* { */ /* write_sexp_line (out, indent, "(elg\n"); */ diff --git a/g10/getkey.c b/g10/getkey.c index f114920d2..65f5829dc 100644 --- a/g10/getkey.c +++ b/g10/getkey.c @@ -138,7 +138,10 @@ cache_public_key (PKT_public_key * pk) return; if (is_ELGAMAL (pk->pubkey_algo) - || pk->pubkey_algo == PUBKEY_ALGO_DSA || is_RSA (pk->pubkey_algo)) + || pk->pubkey_algo == PUBKEY_ALGO_DSA + || pk->pubkey_algo == PUBKEY_ALGO_ECDSA + || pk->pubkey_algo == PUBKEY_ALGO_ECDH + || is_RSA (pk->pubkey_algo)) { keyid_from_pk (pk, keyid); } @@ -858,7 +858,7 @@ my_strusage( int level ) case 34: if (!pubkeys) pubkeys = build_list (_("Pubkey: "), 0, - gcry_pk_algo_name, + openpgp_pk_algo_name, openpgp_pk_test_algo ); p = pubkeys; break; diff --git a/g10/keygen.c b/g10/keygen.c index ec7e7e79c..e75da792e 100644 --- a/g10/keygen.c +++ b/g10/keygen.c @@ -18,6 +18,7 @@ * along with this program; if not, see <http://www.gnu.org/licenses/>. */ +#warning wk: check these changes. #include <config.h> #include <stdio.h> #include <stdlib.h> @@ -42,6 +43,7 @@ #include "i18n.h" #include "keyserver-internal.h" #include "call-agent.h" +#include "pkglue.h" /* The default algorithms. If you change them remember to change them also in gpg.c:gpgconf_list. You should also check that the value @@ -49,10 +51,6 @@ #define DEFAULT_STD_ALGO GCRY_PK_RSA #define DEFAULT_STD_KEYSIZE 2048 -#define KEYGEN_FLAG_NO_PROTECTION 1 -#define KEYGEN_FLAG_TRANSIENT_KEY 2 - - #define MAX_PREFS 30 enum para_name { @@ -1130,17 +1128,15 @@ key_from_sexp (gcry_mpi_t *array, gcry_sexp_t sexp, } - -/* Common code for the key generation fucntion gen_xxx. */ static int -common_gen (const char *keyparms, int algo, const char *algoelem, - kbnode_t pub_root, u32 timestamp, u32 expireval, int is_subkey, - int keygen_flags, char **cache_nonce_addr) +common_key_gen (const char *keyparms, int algo, const char *algoelem, + int keygen_flags, char **cache_nonce_addr, PKT_public_key **pk_out) { int err; - PACKET *pkt; PKT_public_key *pk; gcry_sexp_t s_key; + + *pk_out = NULL; err = agent_genkey (NULL, cache_nonce_addr, keyparms, !!(keygen_flags & KEYGEN_FLAG_NO_PROTECTION), &s_key); @@ -1158,10 +1154,7 @@ common_gen (const char *keyparms, int algo, const char *algoelem, return err; } - pk->timestamp = timestamp; pk->version = 4; - if (expireval) - pk->expiredate = pk->timestamp + expireval; pk->pubkey_algo = algo; err = key_from_sexp (pk->pkey, s_key, "public-key", algoelem); @@ -1174,21 +1167,45 @@ common_gen (const char *keyparms, int algo, const char *algoelem, } gcry_sexp_release (s_key); - pkt = xtrycalloc (1, sizeof *pkt); - if (!pkt) - { - err = gpg_error_from_syserror (); - free_public_key (pk); - return err; - } - - pkt->pkttype = is_subkey ? PKT_PUBLIC_SUBKEY : PKT_PUBLIC_KEY; - pkt->pkt.public_key = pk; - add_kbnode (pub_root, new_kbnode (pkt)); + *pk_out = pk; return 0; } +/* Common code for the key generation fucntion gen_xxx. */ +static int +common_gen (const char *keyparms, int algo, const char *algoelem, + kbnode_t pub_root, u32 timestamp, u32 expireval, int is_subkey, + int keygen_flags, char **cache_nonce_addr) +{ + PKT_public_key *pk; + int err; + + err = common_key_gen( keyparms, algo, algoelem, keygen_flags, cache_nonce_addr, &pk ); + + if( !err ) { + PACKET *pkt; + + pk->timestamp = timestamp; + if (expireval) + pk->expiredate = pk->timestamp + expireval; + + pkt = xtrycalloc (1, sizeof *pkt); + if (!pkt) + { + err = gpg_error_from_syserror (); + free_public_key (pk); + return err; + } + + pkt->pkttype = is_subkey ? PKT_PUBLIC_SUBKEY : PKT_PUBLIC_KEY; + pkt->pkt.public_key = pk; + + add_kbnode (pub_root, new_kbnode (pkt)); + } + + return err; +} /* * Generate an Elgamal key. @@ -1326,6 +1343,186 @@ gen_dsa (unsigned int nbits, KBNODE pub_root, return err; } +/* Returns allocated ECC key generation S-explression + call gcry_sexp_release ( out ) to free it. + */ +static int +delme__pk_ecc_build_sexp( int qbits, int algo, int is_long_term, gcry_sexp_t *out ) { + gcry_mpi_t kek_params; + char *kek_params_s; + int rc; + + if( is_long_term && algo == PUBKEY_ALGO_ECDH ) + kek_params = pk_ecdh_default_params_to_mpi( qbits ); + else + kek_params = NULL; + + if( kek_params ) { + kek_params_s = mpi2hex( kek_params ); + mpi_release( kek_params ); + } + + rc = gcry_sexp_build (out, NULL, + algo == PUBKEY_ALGO_ECDSA ? + "(genkey(ecdsa(nbits %d)(qbits %d)))" : + "(genkey(ecdh(nbits %d)(qbits %d)(transient-key %d)(kek-params %s)))", + (int)qbits, (int)qbits, (int)(is_long_term==0), kek_params_s); + xfree( kek_params_s ); + if (rc) { + log_debug("ec gen gcry_sexp_build failed: %s\n", gpg_strerror (rc)); + return rc; + } + return 0; +} + +static char * +pk_ecc_build_key_params( int qbits, int algo, int transient ) { + byte *kek_params = NULL; + size_t kek_params_size; + char nbitsstr[35]; + char qbitsstr[35]; + char *keyparms; + int n; + + /* KEK parameters are only needed for long term key generation */ + if( !transient && algo == PUBKEY_ALGO_ECDH ) + kek_params = pk_ecdh_default_params( qbits, &kek_params_size ); + else + kek_params = NULL; + + snprintf (nbitsstr, sizeof nbitsstr, "%u", qbits); + snprintf (qbitsstr, sizeof qbitsstr, "%u", qbits); + if( algo == PUBKEY_ALGO_ECDSA || kek_params == NULL ) + keyparms = xtryasprintf ( + "(genkey(%s(nbits %zu:%s)(qbits %zu:%s)(transient-key 1:%d)))", + algo == PUBKEY_ALGO_ECDSA ? "ecdsa" : "ecdh", + strlen (nbitsstr), nbitsstr, + strlen (qbitsstr), qbitsstr, + transient ); + else { + assert( kek_params != NULL ); + keyparms = xtryasprintf ( + "(genkey(ecdh(nbits %zu:%s)(qbits %zu:%s)(transient-key 1:%d)(kek-params %u:", + strlen (nbitsstr), nbitsstr, + strlen (qbitsstr), qbitsstr, + transient, + (unsigned)kek_params_size ); + if( keyparms != NULL ) { + n = strlen(keyparms); + keyparms = xtryrealloc( keyparms, n + kek_params_size + 4 ); + } + if( keyparms == NULL ) { + xfree( kek_params ); + return NULL; + } + memcpy( keyparms+n, kek_params, kek_params_size ); + xfree( kek_params ); + memcpy( keyparms+n+kek_params_size, ")))", 4 ); + } + return keyparms; +} + +/* This common function is used in this file and also to generate ephemeral keys for ECDH. + * Caller must call free_public_key and free_secret_key */ +int +pk_ecc_keypair_gen( PKT_public_key **pk_out, int algo, int keygen_flags, char **cache_nonce_addr, unsigned nbits) { + int err; + unsigned int qbits; + char *keyparms; + // PUBKEY_ALGO_ECDH, PUBKEY_ALGO_ECDSA + static const char * const ec_pub_params[2] = { "cqp", "cq" }; + //static const char * const ec_priv_params[2] = { "cqpd", "cqd" }; + + assert( algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_ECDH ); + assert( PUBKEY_ALGO_ECDSA == PUBKEY_ALGO_ECDH + 1 ); + + *pk_out = NULL; + + if( pubkey_get_npkey (PUBKEY_ALGO_ECDSA) != 2 || pubkey_get_nskey (PUBKEY_ALGO_ECDSA) != 3 || + pubkey_get_npkey (PUBKEY_ALGO_ECDH) != 3 || pubkey_get_nskey (PUBKEY_ALGO_ECDH) != 4 ) + { + log_info(_("incompatible version of gcrypt library (expect named curve logic for ECC)\n") ); + return GPG_ERR_EPROGMISMATCH; + } + + if ( nbits != 256 && nbits != 384 && nbits != 521 ) + { + log_info(_("keysize invalid; using 256 bits instead of passed in %d\n"), nbits ); + } + + /* + Figure out a q size based on the key size. See gen_dsa for more details. + Due to 8-bit rounding we may get 528 here instead of 521 + */ + nbits = qbits = (nbits < 521 ? nbits : 521 ); + + keyparms = pk_ecc_build_key_params(qbits, algo, !!((keygen_flags & KEYGEN_FLAG_TRANSIENT_KEY) && (keygen_flags & KEYGEN_FLAG_NO_PROTECTION)) ); + if (!keyparms) { + err = gpg_error_from_syserror (); + log_error ("ec pk_ecc_build_key_params failed: %s\n", gpg_strerror (err) ); + } + else + { + err = common_key_gen (keyparms, algo, ec_pub_params[algo-PUBKEY_ALGO_ECDH], + keygen_flags, cache_nonce_addr, pk_out); + xfree (keyparms); + } + +#if 0 + /* always allocase seckey_info for EC keys. TODO: is this needed? */ + if( *pk_out ) { + struct seckey_info *ski; + + (*pk_out)->seckey_info = ski = xtrycalloc (1, sizeof *ski); + if (!(*pk_out)->seckey_info) { + free_public_key(*pk_out); + *pk_out = NULL; + return gpg_error_from_syserror (); + } + + ski->is_protected = 0; + ski->algo = 0; + } +#endif + + return err; +} + + +/**************** + * Generate an ECC OpenPGP key + */ +static gpg_error_t +gen_ecc (int algo, unsigned int nbits, KBNODE pub_root, + u32 timestamp, u32 expireval, int is_subkey, + int keygen_flags, char **cache_nonce_addr) +{ + int rc; + PACKET *pkt; + PKT_public_key *pk; + + rc = pk_ecc_keypair_gen( &pk, algo, keygen_flags, cache_nonce_addr, nbits ); + if( rc ) + return rc; + + /* the rest is very similar to common_gen */ + + pk->timestamp = timestamp; + if (expireval) + pk->expiredate = pk->timestamp + expireval; + + //assert( pk->seckey_info != NULL ); + /// TODO: the new agent-based model doesn't return private portion here (the pkey array is allocated, but private MPIs are NULL, so this will cause a crash... ) + ///pk->seckey_info->csum = checksum_mpi ( pk->pkey[algo==PUBKEY_ALGO_ECDSA ? 2 : 3] ); /* corresponds to 'd' in 'cqd' or 'cqpd' */ + + pkt = xmalloc_clear(sizeof *pkt); + pkt->pkttype = is_subkey ? PKT_PUBLIC_SUBKEY : PKT_PUBLIC_KEY; + pkt->pkt.public_key = pk; + add_kbnode(pub_root, new_kbnode( pkt )); + + return 0; +} + /* * Generate an RSA key. @@ -1557,6 +1754,8 @@ ask_algo (int addmode, int *r_subkey_algo, unsigned int *r_usage) tty_printf (_(" (%d) RSA (set your own capabilities)\n"), 8 ); } + tty_printf (_(" (%d) ECDSA and ECDH\n"), 9 ); + for(;;) { *r_usage = 0; @@ -1613,6 +1812,12 @@ ask_algo (int addmode, int *r_subkey_algo, unsigned int *r_usage) *r_usage = ask_key_flags (algo, addmode); break; } + else if (algo == 9) + { + algo = PUBKEY_ALGO_ECDSA; + *r_subkey_algo = PUBKEY_ALGO_ECDH; + break; + } else tty_printf (_("Invalid selection.\n")); } @@ -1657,13 +1862,20 @@ ask_keysize (int algo, unsigned int primary_keysize) max=3072; break; + case PUBKEY_ALGO_ECDSA: + case PUBKEY_ALGO_ECDH: + min=256; + def=256; + max=521; + break; + case PUBKEY_ALGO_RSA: min=1024; break; } tty_printf(_("%s keys may be between %u and %u bits long.\n"), - gcry_pk_algo_name (algo), min, max); + openpgp_pk_algo_name (algo), min, max); for(;;) { @@ -1682,7 +1894,7 @@ ask_keysize (int algo, unsigned int primary_keysize) if(nbits<min || nbits>max) tty_printf(_("%s keysizes must be in the range %u-%u\n"), - gcry_pk_algo_name (algo), min, max); + openpgp_pk_algo_name (algo), min, max); else break; } @@ -1692,10 +1904,18 @@ ask_keysize (int algo, unsigned int primary_keysize) leave: if( algo == PUBKEY_ALGO_DSA && (nbits % 64) ) { - nbits = ((nbits + 63) / 64) * 64; - if (!autocomp) - tty_printf(_("rounded up to %u bits\n"), nbits ); + if( !(algo == PUBKEY_ALGO_ECDSA && nbits==521) ) { + nbits = ((nbits + 63) / 64) * 64; + if (!autocomp) + tty_printf(_("rounded up to %u bits\n"), nbits ); + } } + else if( algo == PUBKEY_ALGO_ECDH || algo == PUBKEY_ALGO_ECDSA ) { + if( nbits != 256 && nbits != 384 && nbits != 521 ) { + nbits = min; + tty_printf(_("unsupported ECDH value, corrected to the minimum %u bits\n"), nbits ); + } + } else if( (nbits % 32) ) { nbits = ((nbits + 31) / 32) * 32; @@ -2185,6 +2405,9 @@ do_create (int algo, unsigned int nbits, KBNODE pub_root, else if (algo == PUBKEY_ALGO_DSA) err = gen_dsa (nbits, pub_root, timestamp, expiredate, is_subkey, keygen_flags, cache_nonce_addr); + else if( algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_ECDH ) + err = gen_ecc (algo, nbits, pub_root, timestamp, expiredate, is_subkey, + keygen_flags, cache_nonce_addr); else if (algo == PUBKEY_ALGO_RSA) err = gen_rsa (algo, nbits, pub_root, timestamp, expiredate, is_subkey, keygen_flags, cache_nonce_addr); diff --git a/g10/keyid.c b/g10/keyid.c index 62ce03685..0405b8b2f 100644 --- a/g10/keyid.c +++ b/g10/keyid.c @@ -54,9 +54,11 @@ pubkey_letter( int algo ) case PUBKEY_ALGO_RSA: return 'R' ; case PUBKEY_ALGO_RSA_E: return 'r' ; case PUBKEY_ALGO_RSA_S: return 's' ; - case PUBKEY_ALGO_ELGAMAL_E: return 'g'; + case PUBKEY_ALGO_ELGAMAL_E: return 'g' ; case PUBKEY_ALGO_ELGAMAL: return 'G' ; case PUBKEY_ALGO_DSA: return 'D' ; + case PUBKEY_ALGO_ECDSA: return 'E' ; /* ECC DSA (sign only) */ + case PUBKEY_ALGO_ECDH: return 'e' ; /* ECC DH (encrypt only) */ default: return '?'; } } @@ -88,19 +90,27 @@ hash_public_key (gcry_md_hd_t md, PKT_public_key *pk) } else { - for(i=0; i < npkey; i++ ) + for (i=0; i < npkey; i++ ) { - if (gcry_mpi_print (GCRYMPI_FMT_PGP, NULL, 0, &nbytes, pk->pkey[i])) + enum gcry_mpi_format fmt; + + if ((pk->pubkey_algo == PUBKEY_ALGO_ECDSA + || pk->pubkey_algo == PUBKEY_ALGO_ECDH) + && (i == 0 || i == 2)) + fmt = GCRYMPI_FMT_USG; /* Name of OID or KEK parms. */ + else + fmt = GCRYMPI_FMT_PGP; + + if (gcry_mpi_print (fmt, NULL, 0, &nbytes, pk->pkey[i])) BUG (); pp[i] = xmalloc (nbytes); - if (gcry_mpi_print (GCRYMPI_FMT_PGP, pp[i], nbytes, - &nbytes, pk->pkey[i])) + if (gcry_mpi_print (fmt, pp[i], nbytes, &nbytes, pk->pkey[i])) BUG (); nn[i] = nbytes; n += nn[i]; } } - + gcry_md_putc ( md, 0x99 ); /* ctb */ /* What does it mean if n is greater than than 0xFFFF ? */ gcry_md_putc ( md, n >> 8 ); /* 2 byte length header */ @@ -712,6 +722,19 @@ keygrip_from_pk (PKT_public_key *pk, unsigned char *array) pk->pkey[0], pk->pkey[1]); break; + case PUBKEY_ALGO_ECDSA: + case PUBKEY_ALGO_ECDH: + err = gcry_sexp_build (&s_pkey, NULL, + "(public-key(ecc(c%m)(q%m)))", + pk->pkey[0], pk->pkey[1]); + break; + + /* case PUBKEY_ALGO_ECDH: */ + /* err = gcry_sexp_build (&s_pkey, NULL, */ + /* "(public-key(ecdh(c%m)(q%m)(p%m)))", */ + /* pk->pkey[0], pk->pkey[1], pk->pkey[2]); */ + /* break; */ + default: err = gpg_error (GPG_ERR_PUBKEY_ALGO); break; diff --git a/g10/main.h b/g10/main.h index 427834023..1b6f30516 100644 --- a/g10/main.h +++ b/g10/main.h @@ -87,17 +87,21 @@ u16 checksum_mpi( gcry_mpi_t a ); u32 buffer_to_u32( const byte *buffer ); const byte *get_session_marker( size_t *rlen ); int map_cipher_openpgp_to_gcry (int algo); -#define openpgp_cipher_open(_a,_b,_c,_d) gcry_cipher_open((_a),map_cipher_openpgp_to_gcry((_b)),(_c),(_d)) -#define openpgp_cipher_get_algo_keylen(_a) gcry_cipher_get_algo_keylen(map_cipher_openpgp_to_gcry((_a))) -#define openpgp_cipher_get_algo_blklen(_a) gcry_cipher_get_algo_blklen(map_cipher_openpgp_to_gcry((_a))) +#define openpgp_cipher_open(_a,_b,_c,_d) \ + gcry_cipher_open((_a),map_cipher_openpgp_to_gcry((_b)),(_c),(_d)) +#define openpgp_cipher_get_algo_keylen(_a) \ + gcry_cipher_get_algo_keylen(map_cipher_openpgp_to_gcry((_a))) +#define openpgp_cipher_get_algo_blklen(_a) \ + gcry_cipher_get_algo_blklen(map_cipher_openpgp_to_gcry((_a))) int openpgp_cipher_blocklen (int algo); int openpgp_cipher_test_algo( int algo ); const char *openpgp_cipher_algo_name (int algo); +int map_pk_openpgp_to_gcry (int algo); int openpgp_pk_test_algo( int algo ); int openpgp_pk_test_algo2 ( int algo, unsigned int use ); int openpgp_pk_algo_usage ( int algo ); -const char *openpgp_pk_algo_name (int algo); int openpgp_md_test_algo( int algo ); +const char *openpgp_pk_algo_name (int algo); const char *openpgp_md_algo_name (int algo); #ifdef USE_IDEA @@ -157,6 +161,11 @@ int pubkey_get_nsig( int algo ); int pubkey_get_nenc( int algo ); unsigned int pubkey_nbits( int algo, gcry_mpi_t *pkey ); int mpi_print (estream_t stream, gcry_mpi_t a, int mode); +int iobuf_write_size_body_mpi (iobuf_t out, gcry_mpi_t a); +int iobuf_read_size_body (iobuf_t inp, byte *body, int body_max_size, + int pktlen, gcry_mpi_t *out); + +int ecdsa_qbits_from_Q( int qbits ); /*-- status.c --*/ void set_status_fd ( int fd ); @@ -251,6 +260,12 @@ gpg_error_t generate_card_subkeypair (kbnode_t pub_keyblock, int save_unprotected_key_to_card (PKT_public_key *sk, int keyno); #endif +#define KEYGEN_FLAG_NO_PROTECTION 1 +#define KEYGEN_FLAG_TRANSIENT_KEY 2 +int pk_ecc_keypair_gen (PKT_public_key **pk_out, int algo, + int keygen_flags, char **cache_nonce_addr, + unsigned nbits); + /*-- openfile.c --*/ int overwrite_filep( const char *fname ); char *make_outfile_name( const char *iname ); @@ -261,7 +276,7 @@ void try_make_homedir( const char *fname ); /*-- seskey.c --*/ void make_session_key( DEK *dek ); -gcry_mpi_t encode_session_key( DEK *dek, unsigned nbits ); +gcry_mpi_t encode_session_key( int openpgp_pk_algo, DEK *dek, unsigned nbits ); gcry_mpi_t encode_md_value (PKT_public_key *pk, gcry_md_hd_t md, int hash_algo ); diff --git a/g10/mainproc.c b/g10/mainproc.c index 72cefce43..dcbc4b45a 100644 --- a/g10/mainproc.c +++ b/g10/mainproc.c @@ -384,6 +384,8 @@ proc_pubkey_enc( CTX c, PACKET *pkt ) } else if( is_ELGAMAL(enc->pubkey_algo) || enc->pubkey_algo == PUBKEY_ALGO_DSA + || enc->pubkey_algo == PUBKEY_ALGO_ECDSA + || enc->pubkey_algo == PUBKEY_ALGO_ECDH || is_RSA(enc->pubkey_algo) || enc->pubkey_algo == PUBKEY_ALGO_ELGAMAL) { /* Note that we also allow type 20 Elgamal keys for decryption. @@ -450,7 +452,7 @@ print_pkenc_list( struct kidlist_item *list, int failed ) if ( !failed && list->reason ) continue; - algstr = gcry_pk_algo_name ( list->pubkey_algo ); + algstr = openpgp_pk_algo_name ( list->pubkey_algo ); pk = xmalloc_clear( sizeof *pk ); if( !algstr ) @@ -1616,7 +1618,7 @@ check_sig_and_print( CTX c, KBNODE node ) /* (Indendation below not yet changed to GNU style.) */ - astr = gcry_pk_algo_name ( sig->pubkey_algo ); + astr = openpgp_pk_algo_name ( sig->pubkey_algo ); if(keystrlen()>8) { log_info(_("Signature made %s\n"),asctimestamp(sig->timestamp)); diff --git a/g10/misc.c b/g10/misc.c index 1725258c5..bdd797c16 100644 --- a/g10/misc.c +++ b/g10/misc.c @@ -1,6 +1,6 @@ /* misc.c - miscellaneous functions * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, - * 2008, 2009 Free Software Foundation, Inc. + * 2008, 2009, 2010 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -64,6 +64,7 @@ #include "call-agent.h" #include "i18n.h" +#include <assert.h> static int string_count_chr (const char *string, int c) @@ -294,7 +295,7 @@ print_pubkey_algo_note( int algo ) { warn=1; log_info (_("WARNING: using experimental public key algorithm %s\n"), - gcry_pk_algo_name (algo)); + openpgp_cipher_algo_name (algo)); } } else if (algo == 20) @@ -365,6 +366,19 @@ map_cipher_gcry_to_openpgp (int algo) } } +/* Map OpenPGP public key algorithm numbers to those used by + Libgcrypt. */ +int +map_pk_openpgp_to_gcry (int algo) +{ + switch (algo) + { + case PUBKEY_ALGO_ECDSA: return GCRY_PK_ECDSA; + case PUBKEY_ALGO_ECDH: return GCRY_PK_ECDH; + default: return algo; + } +} + /* Return the block length of an OpenPGP cipher algorithm. */ int @@ -424,7 +438,8 @@ openpgp_pk_test_algo( int algo ) if (algo < 0 || algo > 110) return gpg_error (GPG_ERR_PUBKEY_ALGO); - return gcry_pk_test_algo (algo); + + return gcry_pk_test_algo (map_pk_openpgp_to_gcry (algo)); } int @@ -442,7 +457,8 @@ openpgp_pk_test_algo2( int algo, unsigned int use ) if (algo < 0 || algo > 110) return gpg_error (GPG_ERR_PUBKEY_ALGO); - return gcry_pk_algo_info (algo, GCRYCTL_TEST_ALGO, NULL, &use_buf); + return gcry_pk_algo_info (map_pk_openpgp_to_gcry (algo), + GCRYCTL_TEST_ALGO, NULL, &use_buf); } int @@ -457,6 +473,7 @@ openpgp_pk_algo_usage ( int algo ) | PUBKEY_USAGE_ENC | PUBKEY_USAGE_AUTH); break; case PUBKEY_ALGO_RSA_E: + case PUBKEY_ALGO_ECDH: use = PUBKEY_USAGE_ENC; break; case PUBKEY_ALGO_RSA_S: @@ -472,6 +489,8 @@ openpgp_pk_algo_usage ( int algo ) case PUBKEY_ALGO_DSA: use = PUBKEY_USAGE_CERT | PUBKEY_USAGE_SIG | PUBKEY_USAGE_AUTH; break; + case PUBKEY_ALGO_ECDSA: + use = PUBKEY_USAGE_CERT | PUBKEY_USAGE_SIG | PUBKEY_USAGE_AUTH; default: break; } @@ -484,19 +503,7 @@ openpgp_pk_algo_usage ( int algo ) const char * openpgp_pk_algo_name (int algo) { - switch (algo) - { - case PUBKEY_ALGO_RSA: - case PUBKEY_ALGO_RSA_E: - case PUBKEY_ALGO_RSA_S: return "rsa"; - - case PUBKEY_ALGO_ELGAMAL: - case PUBKEY_ALGO_ELGAMAL_E: return "elg"; - - case PUBKEY_ALGO_DSA: return "dsa"; - - default: return "?"; - } + return gcry_pk_algo_name (map_pk_openpgp_to_gcry (algo)); } @@ -1348,6 +1355,10 @@ pubkey_get_npkey( int algo ) if (algo == GCRY_PK_ELG_E) algo = GCRY_PK_ELG; + else if (algo == PUBKEY_ALGO_ECDSA) + algo = GCRY_PK_ECDSA; + else if (algo == PUBKEY_ALGO_ECDH) + algo = GCRY_PK_ECDH; if (gcry_pk_algo_info( algo, GCRYCTL_GET_ALGO_NPKEY, NULL, &n)) n = 0; return n; @@ -1361,6 +1372,10 @@ pubkey_get_nskey( int algo ) if (algo == GCRY_PK_ELG_E) algo = GCRY_PK_ELG; + else if (algo == PUBKEY_ALGO_ECDSA) + algo = GCRY_PK_ECDSA; + else if (algo == PUBKEY_ALGO_ECDH) + algo = GCRY_PK_ECDH; if (gcry_pk_algo_info( algo, GCRYCTL_GET_ALGO_NSKEY, NULL, &n )) n = 0; return n; @@ -1374,6 +1389,10 @@ pubkey_get_nsig( int algo ) if (algo == GCRY_PK_ELG_E) algo = GCRY_PK_ELG; + else if (algo == PUBKEY_ALGO_ECDSA) + algo = GCRY_PK_ECDSA; + else if (algo == PUBKEY_ALGO_ECDH) + algo = GCRY_PK_ECDH; if (gcry_pk_algo_info( algo, GCRYCTL_GET_ALGO_NSIGN, NULL, &n)) n = 0; return n; @@ -1387,6 +1406,10 @@ pubkey_get_nenc( int algo ) if (algo == GCRY_PK_ELG_E) algo = GCRY_PK_ELG; + else if (algo == PUBKEY_ALGO_ECDSA) + algo = GCRY_PK_ECDSA; + else if (algo == PUBKEY_ALGO_ECDH) + algo = GCRY_PK_ECDH; if (gcry_pk_algo_info( algo, GCRYCTL_GET_ALGO_NENCR, NULL, &n )) n = 0; return n; @@ -1400,6 +1423,9 @@ pubkey_nbits( int algo, gcry_mpi_t *key ) int rc, nbits; gcry_sexp_t sexp; +#warning Why this assert + assert( algo != GCRY_PK_ECDSA && algo != GCRY_PK_ECDH ); + if( algo == GCRY_PK_DSA ) { rc = gcry_sexp_build ( &sexp, NULL, "(public-key(dsa(p%m)(q%m)(g%m)(y%m)))", @@ -1415,6 +1441,11 @@ pubkey_nbits( int algo, gcry_mpi_t *key ) "(public-key(rsa(n%m)(e%m)))", key[0], key[1] ); } + else if( algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_ECDH ) { + rc = gcry_sexp_build ( &sexp, NULL, + "(public-key(ecc(c%m)(q%m)))", + key[0], key[1] /* not affecting the size calculation, so use 'ecc' == 'ecdsa' */ ); + } else return 0; @@ -1455,3 +1486,102 @@ mpi_print (estream_t fp, gcry_mpi_t a, int mode) return n; } + +/* + * Write a special size+body mpi A, to OUT. The format of the content + * of the MPI is one byte LEN, following by LEN bytes. + */ +/* FIXME: Rename this function: it is not in iobuf.c */ +int +iobuf_write_size_body_mpi (iobuf_t out, gcry_mpi_t a) +{ + byte buffer[256]; /* Fixed buffer for a public parameter, max possible */ + size_t nbytes = (mpi_get_nbits (a)+7)/8; + int rc; + + if( nbytes > sizeof(buffer) ) { + log_error("mpi with size+body is too large (%u bytes)\n", nbytes); + return gpg_error (GPG_ERR_TOO_LARGE); + } + + rc = gcry_mpi_print (GCRYMPI_FMT_USG, buffer, sizeof(buffer), &nbytes, a); + if( rc ) { + log_error("Failed to exported size+body mpi\n"); + return rc; + } + if( nbytes < 2 || buffer[0] != nbytes-1 ) { + if( nbytes > 2 ) + log_error("Internal size mismatch in mpi size+body: %02x != %02x (other bytes: %02x %02x ... %02x %02x)\n", + buffer[0], nbytes-1, buffer[1], buffer[2], buffer[nbytes-2], buffer[nbytes-1]); + else + log_error("Internal size mismatch in mpi size+body: only %d bytes\n", nbytes ); + return gpg_error (GPG_ERR_INV_DATA); + } + return iobuf_write( out, buffer, nbytes ); +} + + +/* + * Read a special size+body from inp into body[body_max_size] and + * return it in a buffer and as MPI. On success the number of + * consumed bytes will body[0]+1. The format of the content of the + * returned MPI is one byte LEN, following by LEN bytes. Caller is + * expected to pre-allocate fixed-size 255 byte buffer (or smaller + * when appropriate). + */ +/* FIXME: Rename this function: it is not in iobuf.c */ +int +iobuf_read_size_body (iobuf_t inp, byte *body, int body_max_size, + int pktlen, gcry_mpi_t *out ) +{ + unsigned n; + int rc; + gcry_mpi_t result; + + *out = NULL; + + if( (n = iobuf_readbyte(inp)) == -1 ) + { + return G10ERR_INVALID_PACKET; + } + if ( n >= body_max_size || n < 2) + { + log_error("invalid size+body field\n"); + return G10ERR_INVALID_PACKET; + } + body[0] = n; + if ((n = iobuf_read(inp, body+1, n)) == -1) + { + log_error("invalid size+body field\n"); + return G10ERR_INVALID_PACKET; + } + if (n+1 > pktlen) + { + log_error("size+body field is larger than the packet\n"); + return G10ERR_INVALID_PACKET; + } + rc = gcry_mpi_scan (&result, GCRYMPI_FMT_USG, body, n+1, NULL); + if (rc) + log_fatal ("mpi_scan failed: %s\n", gpg_strerror (rc)); + + *out = result; + + return rc; +} + + +/* pkey[1] or skey[1] is Q for ECDSA, which is an uncompressed point, + i.e. 04 <x> <y> */ +int +ecdsa_qbits_from_Q (int qbits ) +{ + if ((qbits%8) > 3) + { + log_error(_("ECDSA public key is expected to be in SEC encoding " + "multiple of 8 bits\n")); + return 0; + } + qbits -= qbits%8; + qbits /= 2; + return qbits; +} diff --git a/g10/parse-packet.c b/g10/parse-packet.c index 3714739d4..334a9a82b 100644 --- a/g10/parse-packet.c +++ b/g10/parse-packet.c @@ -939,20 +939,47 @@ parse_pubkeyenc (IOBUF inp, int pkttype, unsigned long pktlen, } else { - for (i = 0; i < ndata; i++) - { - n = pktlen; - k->data[i] = mpi_read (inp, &n, 0); - pktlen -= n; - if (list_mode) - { - es_fprintf (listfp, "\tdata: "); - mpi_print (listfp, k->data[i], mpi_print_mode); - es_putc ('\n', listfp); - } - if (!k->data[i]) - rc = gpg_error (GPG_ERR_INV_PACKET); - } + if (k->pubkey_algo == PUBKEY_ALGO_ECDH) + { + byte encr_buf[255]; + + assert (ndata == 2); + n = pktlen; + k->data[0] = mpi_read (inp, &n, 0); + pktlen -= n; + rc = iobuf_read_size_body (inp, encr_buf, sizeof(encr_buf), + pktlen, k->data+1); + if (rc) + goto leave; + + if (list_mode) + { + es_fprintf (listfp, "\tdata: "); + mpi_print (listfp, k->data[0], mpi_print_mode ); + es_putc ('\n', listfp); + es_fprintf (listfp, "\tdata: [% 3d bytes] ", encr_buf[0]+1); + mpi_print (listfp, k->data[1], mpi_print_mode ); + es_putc ('\n', listfp); + } + pktlen -= (encr_buf[0]+1); + } + else + { + for (i = 0; i < ndata; i++) + { + n = pktlen; + k->data[i] = mpi_read (inp, &n, 0); + pktlen -= n; + if (list_mode) + { + es_fprintf (listfp, "\tdata: "); + mpi_print (listfp, k->data[i], mpi_print_mode); + es_putc ('\n', listfp); + } + if (!k->data[i]) + rc = gpg_error (GPG_ERR_INV_PACKET); + } + } } leave: @@ -1926,20 +1953,74 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen, else { /* Fill in public key parameters. */ - for (i = 0; i < npkey; i++) - { - n = pktlen; - pk->pkey[i] = mpi_read (inp, &n, 0); - pktlen -= n; - if (list_mode) - { - es_fprintf (listfp, "\tpkey[%d]: ", i); - mpi_print (listfp, pk->pkey[i], mpi_print_mode); - es_putc ('\n', listfp); + if (algorithm == PUBKEY_ALGO_ECDSA || algorithm == PUBKEY_ALGO_ECDH) + { + /* FIXME: The code in this function ignores the errors. */ + byte name_oid[256]; + + err = iobuf_read_size_body (inp, name_oid, sizeof(name_oid), + pktlen, pk->pkey+0); + if (err) + goto leave; + n = name_oid[0]; + if (list_mode) + es_fprintf (listfp, "\tpkey[0]: curve OID [%d] ...%02x %02x\n", + n, name_oid[1+n-2], name_oid[1+n-1]); + pktlen -= (n+1); + /* Set item [1], which corresponds to the public key; these + two fields are all we need to uniquely define the key/ */ + n = pktlen; + pk->pkey[1] = mpi_read( inp, &n, 0 ); + pktlen -=n; + if (!pk->pkey[1]) + err = gpg_error (GPG_ERR_INV_PACKET); + else if (list_mode) + { + es_fprintf (listfp, "\tpkey[1]: "); + mpi_print (listfp, pk->pkey[1], mpi_print_mode); + es_putc ('\n', listfp); } - if (!pk->pkey[i]) - err = gpg_error (GPG_ERR_INV_PACKET); - } + /* One more field for ECDH. */ + if (algorithm == PUBKEY_ALGO_ECDH) + { + /* (NAMEOID holds the KEK params.) */ + err = iobuf_read_size_body (inp, name_oid, sizeof(name_oid), + pktlen, pk->pkey+2); + if (err) + goto leave; + n = name_oid[0]; + if (name_oid[1] != 1) + { + log_error ("invalid ecdh KEK parameters field type in " + "private key: understand type 1, " + "but found 0x%02x\n", name_oid[1]); + err = gpg_error (GPG_ERR_INV_PACKET); + goto leave; + } + if (list_mode) + es_fprintf (listfp, "\tpkey[2]: KEK params type=01 " + "hash:%d sym-algo:%d\n", + name_oid[1+n-2], name_oid[1+n-1]); + pktlen -= (n+1); + } + } + else + { + for (i = 0; i < npkey; i++) + { + n = pktlen; + pk->pkey[i] = mpi_read (inp, &n, 0); + pktlen -= n; + if (list_mode) + { + es_fprintf (listfp, "\tpkey[%d]: ", i); + mpi_print (listfp, pk->pkey[i], mpi_print_mode); + es_putc ('\n', listfp); + } + if (!pk->pkey[i]) + err = gpg_error (GPG_ERR_INV_PACKET); + } + } if (err) goto leave; } diff --git a/g10/passphrase.c b/g10/passphrase.c index 9f1218b6b..f29fca72f 100644 --- a/g10/passphrase.c +++ b/g10/passphrase.c @@ -323,7 +323,7 @@ passphrase_get ( u32 *keyid, int mode, const char *cacheid, int repeat, { char *uid; size_t uidlen; - const char *algo_name = gcry_pk_algo_name ( pk->pubkey_algo ); + const char *algo_name = openpgp_pk_algo_name ( pk->pubkey_algo ); const char *timestr; char *maink; @@ -585,7 +585,7 @@ passphrase_to_dek_ext (u32 *keyid, int pubkey_algo, if ( !get_pubkey( pk, keyid ) ) { - const char *s = gcry_pk_algo_name ( pk->pubkey_algo ); + const char *s = openpgp_pk_algo_name ( pk->pubkey_algo ); tty_printf (_("%u-bit %s key, ID %s, created %s"), nbits_from_pk( pk ), s?s:"?", keystr(keyid), @@ -690,7 +690,7 @@ gpg_format_keydesc (PKT_public_key *pk, int mode, int escaped) char *desc; const char *prompt; - algo_name = gcry_pk_algo_name (pk->pubkey_algo); + algo_name = openpgp_pk_algo_name (pk->pubkey_algo); timestr = strtimestamp (pk->timestamp); uid = get_user_id (pk->keyid, &uidlen); diff --git a/g10/pkglue.c b/g10/pkglue.c index 14a27535f..f5c85976f 100644 --- a/g10/pkglue.c +++ b/g10/pkglue.c @@ -1,5 +1,5 @@ /* pkglue.c - public key operations glue code - * Copyright (C) 2000, 2003 Free Software Foundation, Inc. + * Copyright (C) 2000, 2003, 2010 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -27,9 +27,11 @@ #include "gpg.h" #include "util.h" #include "pkglue.h" +#include "main.h" - -static gcry_mpi_t +/* FIXME: Better chnage the fucntion name because mpi_ is used by + gcrypt macros. */ +gcry_mpi_t mpi_from_sexp (gcry_sexp_t sexp, const char * item) { gcry_sexp_t list; @@ -50,42 +52,48 @@ mpi_from_sexp (gcry_sexp_t sexp, const char * item) * change the internal design to directly fit to libgcrypt. */ int -pk_verify (int algo, gcry_mpi_t hash, gcry_mpi_t * data, gcry_mpi_t * pkey) +pk_verify (int algo, gcry_mpi_t hash, gcry_mpi_t *data, gcry_mpi_t *pkey) { gcry_sexp_t s_sig, s_hash, s_pkey; int rc; + const int pkalgo = map_pk_openpgp_to_gcry (algo); - /* make a sexp from pkey */ - if (algo == GCRY_PK_DSA) + /* Make a sexp from pkey. */ + if (pkalgo == GCRY_PK_DSA) { rc = gcry_sexp_build (&s_pkey, NULL, "(public-key(dsa(p%m)(q%m)(g%m)(y%m)))", pkey[0], pkey[1], pkey[2], pkey[3]); } - else if (algo == GCRY_PK_ELG || algo == GCRY_PK_ELG_E) + else if (pkalgo == GCRY_PK_ELG || pkalgo == GCRY_PK_ELG_E) { rc = gcry_sexp_build (&s_pkey, NULL, "(public-key(elg(p%m)(g%m)(y%m)))", pkey[0], pkey[1], pkey[2]); } - else if (algo == GCRY_PK_RSA || algo == GCRY_PK_RSA_S) + else if (pkalgo == GCRY_PK_RSA || pkalgo == GCRY_PK_RSA_S) { rc = gcry_sexp_build (&s_pkey, NULL, "(public-key(rsa(n%m)(e%m)))", pkey[0], pkey[1]); } + else if (pkalgo == GCRY_PK_ECDSA) /* Same as GCRY_PK_ECDH */ + { + rc = gcry_sexp_build (&s_pkey, NULL, + "(public-key(ecdsa(c%m)(q%m)))", pkey[0], pkey[1]); + } else return GPG_ERR_PUBKEY_ALGO; if (rc) BUG (); /* gcry_sexp_build should never fail. */ - /* put hash into a S-Exp s_hash */ + /* Put hash into a S-Exp s_hash. */ if (gcry_sexp_build (&s_hash, NULL, "%m", hash)) BUG (); /* gcry_sexp_build should never fail. */ /* Put data into a S-Exp s_sig. */ s_sig = NULL; - if (algo == GCRY_PK_DSA) + if (pkalgo == GCRY_PK_DSA) { if (!data[0] || !data[1]) rc = gpg_error (GPG_ERR_BAD_MPI); @@ -93,7 +101,15 @@ pk_verify (int algo, gcry_mpi_t hash, gcry_mpi_t * data, gcry_mpi_t * pkey) rc = gcry_sexp_build (&s_sig, NULL, "(sig-val(dsa(r%m)(s%m)))", data[0], data[1]); } - else if (algo == GCRY_PK_ELG || algo == GCRY_PK_ELG_E) + else if (pkalgo == GCRY_PK_ECDSA) + { + if (!data[0] || !data[1]) + rc = gpg_error (GPG_ERR_BAD_MPI); + else + rc = gcry_sexp_build (&s_sig, NULL, + "(sig-val(ecdsa(r%m)(s%m)))", data[0], data[1]); + } + else if (pkalgo == GCRY_PK_ELG || pkalgo == GCRY_PK_ELG_E) { if (!data[0] || !data[1]) rc = gpg_error (GPG_ERR_BAD_MPI); @@ -101,7 +117,7 @@ pk_verify (int algo, gcry_mpi_t hash, gcry_mpi_t * data, gcry_mpi_t * pkey) rc = gcry_sexp_build (&s_sig, NULL, "(sig-val(elg(r%m)(s%m)))", data[0], data[1]); } - else if (algo == GCRY_PK_RSA || algo == GCRY_PK_RSA_S) + else if (pkalgo == GCRY_PK_RSA || pkalgo == GCRY_PK_RSA_S) { if (!data[0]) rc = gpg_error (GPG_ERR_BAD_MPI); @@ -128,12 +144,13 @@ pk_verify (int algo, gcry_mpi_t hash, gcry_mpi_t * data, gcry_mpi_t * pkey) * change the internal design to directly fit to libgcrypt. */ int -pk_encrypt (int algo, gcry_mpi_t * resarr, gcry_mpi_t data, gcry_mpi_t * pkey) +pk_encrypt (int algo, gcry_mpi_t *resarr, gcry_mpi_t data, + const byte pk_fp[MAX_FINGERPRINT_LEN], gcry_mpi_t *pkey) { gcry_sexp_t s_ciph, s_data, s_pkey; int rc; - /* make a sexp from pkey */ + /* Make a sexp from pkey. */ if (algo == GCRY_PK_ELG || algo == GCRY_PK_ELG_E) { rc = gcry_sexp_build (&s_pkey, NULL, @@ -146,17 +163,21 @@ pk_encrypt (int algo, gcry_mpi_t * resarr, gcry_mpi_t data, gcry_mpi_t * pkey) "(public-key(rsa(n%m)(e%m)))", pkey[0], pkey[1]); } + else if (algo == PUBKEY_ALGO_ECDH) + { + return pk_ecdh_encrypt (resarr, pk_fp, data, pkey); + } else return GPG_ERR_PUBKEY_ALGO; if (rc) BUG (); - /* put the data into a simple list */ + /* Put the data into a simple list. */ if (gcry_sexp_build (&s_data, NULL, "%m", data)) BUG (); - /* pass it to libgcrypt */ + /* Pass it to libgcrypt. */ rc = gcry_pk_encrypt (&s_ciph, s_data, s_pkey); gcry_sexp_release (s_data); gcry_sexp_release (s_pkey); @@ -164,9 +185,11 @@ pk_encrypt (int algo, gcry_mpi_t * resarr, gcry_mpi_t data, gcry_mpi_t * pkey) if (rc) ; else - { /* add better error handling or make gnupg use S-Exp directly */ + { /* Add better error handling or make gnupg use S-Exp directly. */ resarr[0] = mpi_from_sexp (s_ciph, "a"); - if (algo != GCRY_PK_RSA && algo != GCRY_PK_RSA_E) + if (algo != GCRY_PK_RSA + && algo != GCRY_PK_RSA_E + && algo != PUBKEY_ALGO_ECDH) resarr[1] = mpi_from_sexp (s_ciph, "b"); } @@ -175,72 +198,47 @@ pk_encrypt (int algo, gcry_mpi_t * resarr, gcry_mpi_t data, gcry_mpi_t * pkey) } - -/**************** - * Emulate our old PK interface here - sometime in the future we might - * change the internal design to directly fit to libgcrypt. - */ +/* Check whether SKEY is a suitable secret key. */ int -pk_decrypt (int algo, gcry_mpi_t * result, gcry_mpi_t * data, - gcry_mpi_t * skey) +pk_check_secret_key (int algo, gcry_mpi_t *skey) { - gcry_sexp_t s_skey, s_data, s_plain; + gcry_sexp_t s_skey; int rc; + const int gcry_pkalgo = map_pk_openpgp_to_gcry( algo ); - *result = NULL; - /* make a sexp from skey */ - if (algo == GCRY_PK_ELG || algo == GCRY_PK_ELG_E) + if (gcry_pkalgo == GCRY_PK_DSA) + { + rc = gcry_sexp_build (&s_skey, NULL, + "(private-key(dsa(p%m)(q%m)(g%m)(y%m)(x%m)))", + skey[0], skey[1], skey[2], skey[3], skey[4]); + } + else if (gcry_pkalgo == GCRY_PK_ELG || gcry_pkalgo == GCRY_PK_ELG_E) { rc = gcry_sexp_build (&s_skey, NULL, "(private-key(elg(p%m)(g%m)(y%m)(x%m)))", skey[0], skey[1], skey[2], skey[3]); } - else if (algo == GCRY_PK_RSA || algo == GCRY_PK_RSA_E) + else if (gcry_pkalgo == GCRY_PK_RSA + || gcry_pkalgo == GCRY_PK_RSA_S || gcry_pkalgo == GCRY_PK_RSA_E) { rc = gcry_sexp_build (&s_skey, NULL, "(private-key(rsa(n%m)(e%m)(d%m)(p%m)(q%m)(u%m)))", skey[0], skey[1], skey[2], skey[3], skey[4], skey[5]); } + else if (gcry_pkalgo == GCRY_PK_ECDSA || gcry_pkalgo == GCRY_PK_ECDH) + { + rc = gcry_sexp_build (&s_skey, NULL, + "(private-key(ecdsa(c%m)(q%m)(d%m)))", + skey[0], skey[1], skey[2] ); + } else return GPG_ERR_PUBKEY_ALGO; - if (rc) - BUG (); - - /* put data into a S-Exp s_data */ - if (algo == GCRY_PK_ELG || algo == GCRY_PK_ELG_E) - { - if (!data[0] || !data[1]) - rc = gpg_error (GPG_ERR_BAD_MPI); - else - rc = gcry_sexp_build (&s_data, NULL, - "(enc-val(elg(a%m)(b%m)))", data[0], data[1]); - } - else if (algo == GCRY_PK_RSA || algo == GCRY_PK_RSA_E) + if (!rc) { - if (!data[0]) - rc = gpg_error (GPG_ERR_BAD_MPI); - else - rc = gcry_sexp_build (&s_data, NULL, "(enc-val(rsa(a%m)))", data[0]); + rc = gcry_pk_testkey (s_skey); + gcry_sexp_release (s_skey); } - else - BUG (); - - if (rc) - BUG (); - - rc = gcry_pk_decrypt (&s_plain, s_data, s_skey); - gcry_sexp_release (s_skey); - gcry_sexp_release (s_data); - if (rc) - return rc; - - *result = gcry_sexp_nth_mpi (s_plain, 0, 0); - gcry_sexp_release (s_plain); - if (!*result) - return -1; /* oops */ - - return 0; + return rc; } - diff --git a/g10/pkglue.h b/g10/pkglue.h index f97def153..0ceb43f55 100644 --- a/g10/pkglue.h +++ b/g10/pkglue.h @@ -1,5 +1,5 @@ /* pkglue.h - public key operations definitions - * Copyright (C) 2003 Free Software Foundation, Inc. + * Copyright (C) 2003, 2010 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -20,13 +20,21 @@ #ifndef GNUPG_G10_PKGLUE_H #define GNUPG_G10_PKGLUE_H +gcry_mpi_t mpi_from_sexp (gcry_sexp_t sexp, const char * item); + int pk_verify (int algo, gcry_mpi_t hash, gcry_mpi_t *data, gcry_mpi_t *pkey); int pk_encrypt (int algo, gcry_mpi_t *resarr, gcry_mpi_t data, + const byte fp[MAX_FINGERPRINT_LEN], gcry_mpi_t *pkey); -int pk_decrypt (int algo, gcry_mpi_t *result, gcry_mpi_t *data, - gcry_mpi_t *skey); int pk_check_secret_key (int algo, gcry_mpi_t *skey); +int pk_ecdh_encrypt (gcry_mpi_t *resarr, const byte pk_fp[MAX_FINGERPRINT_LEN], + gcry_mpi_t data, gcry_mpi_t * pkey); +int pk_ecdh_decrypt (gcry_mpi_t *result, const byte sk_fp[MAX_FINGERPRINT_LEN], + gcry_mpi_t data, gcry_mpi_t shared, gcry_mpi_t * skey); + +gcry_mpi_t pk_ecdh_default_params_to_mpi (int qbits); +byte *pk_ecdh_default_params (int qbits, size_t *sizeout); #endif /*GNUPG_G10_PKGLUE_H*/ diff --git a/g10/pubkey-enc.c b/g10/pubkey-enc.c index 312b591e9..ddca41ec4 100644 --- a/g10/pubkey-enc.c +++ b/g10/pubkey-enc.c @@ -145,14 +145,18 @@ get_it (PKT_pubkey_enc *enc, DEK *dek, PKT_public_key *sk, u32 *keyid) gcry_sexp_t s_data; char *desc; char *keygrip; + byte fp[MAX_FINGERPRINT_LEN]; + size_t fpn; + const int gcry_pkalgo = map_pk_openpgp_to_gcry( sk->pubkey_algo ); /* Get the keygrip. */ err = hexkeygrip_from_pk (sk, &keygrip); if (err) goto leave; + /* Convert the data to an S-expression. */ - if (sk->pubkey_algo == GCRY_PK_ELG || sk->pubkey_algo == GCRY_PK_ELG_E) + if (gcry_pkalgo == GCRY_PK_ELG ||gcry_pkalgo == GCRY_PK_ELG_E) { if (!enc->data[0] || !enc->data[1]) err = gpg_error (GPG_ERR_BAD_MPI); @@ -160,7 +164,7 @@ get_it (PKT_pubkey_enc *enc, DEK *dek, PKT_public_key *sk, u32 *keyid) err = gcry_sexp_build (&s_data, NULL, "(enc-val(elg(a%m)(b%m)))", enc->data[0], enc->data[1]); } - else if (sk->pubkey_algo == GCRY_PK_RSA || sk->pubkey_algo == GCRY_PK_RSA_E) + else if (gcry_pkalgo == GCRY_PK_RSA || gcry_pkalgo == GCRY_PK_RSA_E) { if (!enc->data[0]) err = gpg_error (GPG_ERR_BAD_MPI); @@ -168,12 +172,23 @@ get_it (PKT_pubkey_enc *enc, DEK *dek, PKT_public_key *sk, u32 *keyid) err = gcry_sexp_build (&s_data, NULL, "(enc-val(rsa(a%m)))", enc->data[0]); } + else if (gcry_pkalgo == GCRY_PK_ECDH ) + { + if (!enc->data[0] || !enc->data[1]) + err = gpg_error (GPG_ERR_BAD_MPI); + else + err = gcry_sexp_build (&s_data, NULL, "(enc-val(ecdh(a%m)(b%m)))", + enc->data[0], enc->data[1]); + } else err = gpg_error (GPG_ERR_BUG); if (err) goto leave; + fingerprint_from_pk( sk, fp, &fpn ); + assert( fpn == 20 ); + /* Decrypt. */ desc = gpg_format_keydesc (sk, 0, 1); err = agent_pkdecrypt (NULL, keygrip, desc, s_data, &frame, &nframe); @@ -202,32 +217,73 @@ get_it (PKT_pubkey_enc *enc, DEK *dek, PKT_public_key *sk, u32 *keyid) if (DBG_CIPHER) log_printhex ("DEK frame:", frame, nframe); n = 0; - if (!card) + + if (sk->pubkey_algo == PUBKEY_ALGO_ECDH) { - if (n + 7 > nframe) + gcry_mpi_t shared_mpi; + gcry_mpi_t decoded; + + /* At the beginning the frame are the bytes of shared point MPI. */ + err = gcry_mpi_scan (&shared_mpi, GCRYMPI_FMT_USG, frame, nframe, NULL); + if (err) { - err = gpg_error (G10ERR_WRONG_SECKEY); + log_fatal ("mpi_scan failed: %s\n", gpg_strerror (err)); goto leave; } - if (frame[n] == 1 && frame[nframe - 1] == 2) + + err = pk_ecdh_decrypt (&decoded, fp, enc->data[1]/*encr data as an MPI*/, + shared_mpi, sk->pkey); + mpi_release (shared_mpi); + if(err) + goto leave; + + /* Reuse NFRAME, which size is sufficient to include the session key. */ + err = gcry_mpi_print (GCRYMPI_FMT_USG, frame, nframe, &nframe, decoded); + mpi_release (decoded); + if (err) + goto leave; + + /* Now the frame are the bytes decrypted but padded session key. */ + + /* Allow double padding for the benefit of DEK size concealment. + Higher than this is wasteful. */ + if (frame[nframe-1] > 8*2 || nframe <= 8) { - log_info (_("old encoding of the DEK is not supported\n")); - err = gpg_error (G10ERR_CIPHER_ALGO); - goto leave; + err = gpg_error (GPG_ERR_WRONG_SECKEY); + goto leave; } - if (frame[n] != 2) /* Something went wrong. */ + nframe -= frame[nframe-1]; /* Remove padding. */ + assert (n); /* (used just below) */ + } + else + { + if (!card) { - err = gpg_error (G10ERR_WRONG_SECKEY); - goto leave; + if (n + 7 > nframe) + { + err = gpg_error (GPG_ERR_WRONG_SECKEY); + goto leave; + } + if (frame[n] == 1 && frame[nframe - 1] == 2) + { + log_info (_("old encoding of the DEK is not supported\n")); + err = gpg_error (GPG_ERR_CIPHER_ALGO); + goto leave; + } + if (frame[n] != 2) /* Something went wrong. */ + { + err = gpg_error (GPG_ERR_WRONG_SECKEY); + goto leave; + } + for (n++; n < nframe && frame[n]; n++) /* Skip the random bytes. */ + ; + n++; /* Skip the zero byte. */ } - for (n++; n < nframe && frame[n]; n++) /* Skip the random bytes. */ - ; - n++; /* Skip the zero byte. */ } if (n + 4 > nframe) { - err = gpg_error (G10ERR_WRONG_SECKEY); + err = gpg_error (GPG_ERR_WRONG_SECKEY); goto leave; } diff --git a/g10/seskey.c b/g10/seskey.c index ee5584c66..2d7918d39 100644 --- a/g10/seskey.c +++ b/g10/seskey.c @@ -1,6 +1,6 @@ /* seskey.c - make sesssion keys etc. * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, - * 2006, 2009 Free Software Foundation, Inc. + * 2006, 2009, 2010 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -27,6 +27,7 @@ #include "gpg.h" #include "util.h" #include "cipher.h" +#include "options.h" #include "main.h" #include "i18n.h" @@ -73,81 +74,127 @@ make_session_key( DEK *dek ) * returns: A mpi with the session key (caller must free) */ gcry_mpi_t -encode_session_key (DEK *dek, unsigned int nbits) +encode_session_key (int openpgp_pk_algo, DEK *dek, unsigned int nbits) { - size_t nframe = (nbits+7) / 8; - byte *p; - byte *frame; - int i,n; - u16 csum; - gcry_mpi_t a; - - /* The current limitation is that we can only use a session key - * whose length is a multiple of BITS_PER_MPI_LIMB - * I think we can live with that. - */ - if( dek->keylen + 7 > nframe || !nframe ) - log_bug("can't encode a %d bit key in a %d bits frame\n", - dek->keylen*8, nbits ); - - /* We encode the session key in this way: - * - * 0 2 RND(n bytes) 0 A DEK(k bytes) CSUM(2 bytes) - * - * (But how can we store the leading 0 - the external representaion - * of MPIs doesn't allow leading zeroes =:-) - * - * RND are non-zero random bytes. - * A is the cipher algorithm - * DEK is the encryption key (session key) length k depends on the - * cipher algorithm (20 is used with blowfish160). - * CSUM is the 16 bit checksum over the DEK - */ - csum = 0; - for( p = dek->key, i=0; i < dek->keylen; i++ ) - csum += *p++; - - frame = xmalloc_secure( nframe ); - n = 0; - frame[n++] = 0; - frame[n++] = 2; - i = nframe - 6 - dek->keylen; - assert( i > 0 ); - p = gcry_random_bytes_secure (i, GCRY_STRONG_RANDOM); - /* Replace zero bytes by new values. */ - for(;;) { - int j, k; - byte *pp; - - /* count the zero bytes */ - for(j=k=0; j < i; j++ ) - if( !p[j] ) - k++; - if( !k ) - break; /* okay: no zero bytes */ - k += k/128 + 3; /* better get some more */ - pp = gcry_random_bytes_secure (k, GCRY_STRONG_RANDOM); - for(j=0; j < i && k ;) { - if( !p[j] ) - p[j] = pp[--k]; - if (p[j]) - j++; + size_t nframe = (nbits+7) / 8; + byte *p; + byte *frame; + int i,n; + u16 csum; + gcry_mpi_t a; + + if (DBG_CIPHER) + log_debug ("encode_session_key: encoding %d byte DEK", dek->keylen); + + csum = 0; + for (p = dek->key, i=0; i < dek->keylen; i++) + csum += *p++; + + /* Shortcut for ECDH. It's padding is minimal to simply make the + output be a multiple of 8 bytes. */ + if (openpgp_pk_algo == PUBKEY_ALGO_ECDH) + { + /* Pad to 8 byte granulatiry; the padding byte is the number of + * padded bytes. + * + * A DEK(k bytes) CSUM(2 bytes) 0x 0x 0x 0x ... 0x + * +---- x times ---+ + */ + nframe = (( 1 + dek->keylen + 2 /* The value so far is always odd. */ + + 7 ) & (~7)); + + /* alg+key+csum fit and the size is congruent to 8. */ + assert (!(nframe%8) && nframe > 1 + dek->keylen + 2 ); + + frame = xmalloc_secure (nframe); + n = 0; + frame[n++] = dek->algo; + memcpy (frame+n, dek->key, dek->keylen); + n += dek->keylen; + frame[n++] = csum >> 8; + frame[n++] = csum; + i = nframe - n; /* Number of padded bytes. */ + memset (frame+n, i, i); /* Use it as the value of each padded byte. */ + assert (n+i == nframe); + + if (DBG_CIPHER) + log_debug ("encode_session_key: " + "[%d] %02x %02x %02x ... %02x %02x %02x\n", + nframe, frame[0], frame[1], frame[2], + frame[nframe-3], frame[nframe-2], frame[nframe-1]); + + if (gcry_mpi_scan (&a, GCRYMPI_FMT_USG, frame, nframe, &nframe)) + BUG(); + xfree(frame); + return a; + } + + /* The current limitation is that we can only use a session key + * whose length is a multiple of BITS_PER_MPI_LIMB + * I think we can live with that. + */ + if (dek->keylen + 7 > nframe || !nframe) + log_bug ("can't encode a %d bit key in a %d bits frame\n", + dek->keylen*8, nbits ); + + /* We encode the session key in this way: + * + * 0 2 RND(n bytes) 0 A DEK(k bytes) CSUM(2 bytes) + * + * (But how can we store the leading 0 - the external representaion + * of MPIs doesn't allow leading zeroes =:-) + * + * RND are non-zero random bytes. + * A is the cipher algorithm + * DEK is the encryption key (session key) length k depends on the + * cipher algorithm (20 is used with blowfish160). + * CSUM is the 16 bit checksum over the DEK + */ + + frame = xmalloc_secure( nframe ); + n = 0; + frame[n++] = 0; + frame[n++] = 2; + i = nframe - 6 - dek->keylen; + assert( i > 0 ); + p = gcry_random_bytes_secure (i, GCRY_STRONG_RANDOM); + /* Replace zero bytes by new values. */ + for (;;) + { + int j, k; + byte *pp; + + /* Count the zero bytes. */ + for (j=k=0; j < i; j++ ) + if (!p[j]) + k++; + if (!k) + break; /* Okay: no zero bytes. */ + k += k/128 + 3; /* Better get some more. */ + pp = gcry_random_bytes_secure (k, GCRY_STRONG_RANDOM); + for (j=0; j < i && k ;) + { + if (!p[j]) + p[j] = pp[--k]; + if (p[j]) + j++; } - xfree(pp); + xfree (pp); } - memcpy( frame+n, p, i ); - xfree(p); - n += i; - frame[n++] = 0; - frame[n++] = dek->algo; - memcpy( frame+n, dek->key, dek->keylen ); n += dek->keylen; - frame[n++] = csum >>8; - frame[n++] = csum; - assert( n == nframe ); - if (gcry_mpi_scan( &a, GCRYMPI_FMT_USG, frame, n, &nframe)) - BUG(); - xfree(frame); - return a; + memcpy (frame+n, p, i); + xfree (p); + n += i; + frame[n++] = 0; + frame[n++] = dek->algo; + memcpy (frame+n, dek->key, dek->keylen ); + n += dek->keylen; + frame[n++] = csum >>8; + frame[n++] = csum; + assert (n == nframe); + if (gcry_mpi_scan( &a, GCRYMPI_FMT_USG, frame, n, &nframe)) + BUG(); + xfree (frame); + return a; } @@ -161,8 +208,8 @@ do_encode_md( gcry_md_hd_t md, int algo, size_t len, unsigned nbits, gcry_mpi_t a; if( len + asnlen + 4 > nframe ) - log_bug("can't encode a %d bit MD into a %d bits frame\n", - (int)(len*8), (int)nbits); + log_bug ("can't encode a %d bit MD into a %d bits frame, algo=%d\n", + (int)(len*8), (int)nbits, algo); /* We encode the MD in this way: * @@ -209,19 +256,27 @@ gcry_mpi_t encode_md_value (PKT_public_key *pk, gcry_md_hd_t md, int hash_algo) { gcry_mpi_t frame; + int pkalgo; assert (hash_algo); assert (pk); - if (pk->pubkey_algo == GCRY_PK_DSA) + pkalgo = map_pk_openpgp_to_gcry (pk->pubkey_algo); + + if (pkalgo == GCRY_PK_DSA || pkalgo == GCRY_PK_ECDSA) { - /* It's a DSA signature, so find out the size of q. */ + /* It's a DSA signature, so find out the size of q. */ size_t qbytes = gcry_mpi_get_nbits (pk->pkey[1]); + /* pkey[1] is Q for ECDSA, which is an uncompressed point, + i.e. 04 <x> <y> */ + if (pkalgo == GCRY_PK_ECDSA) + qbytes = ecdsa_qbits_from_Q (qbytes); + /* Make sure it is a multiple of 8 bits. */ - - if(qbytes%8) + + if (qbytes%8) { log_error(_("DSA requires the hash length to be a" " multiple of 8 bits\n")); @@ -236,22 +291,39 @@ encode_md_value (PKT_public_key *pk, gcry_md_hd_t md, int hash_algo) DSA. ;) */ if (qbytes < 160) { - log_error (_("DSA key %s uses an unsafe (%zu bit) hash\n"), - keystr_from_pk (pk), qbytes); + log_error (_("%s key %s uses an unsafe (%zu bit) hash\n"), + gcry_pk_algo_name (pkalgo), keystr_from_pk (pk), qbytes); return NULL; } - + qbytes /= 8; /* Check if we're too short. Too long is safe as we'll - automatically left-truncate. */ - if (gcry_md_get_algo_dlen (hash_algo) < qbytes) + automatically left-truncate. + + FIXME: Check against FIPS. + This checks would require the use of SHA512 with ECDSA 512. I + think this is overkill to fail in this case. Therefore, + relax the check, but only for ECDSA keys. We may need to + adjust it later for general case. (Note that the check will + never pass for ECDSA 521 anyway as the only hash that + intended to match it is SHA 512, but 512 < 521). */ + if (gcry_md_get_algo_dlen (hash_algo) + < ((pkalgo == GCRY_PK_ECDSA && qbytes > (521)/8) ? 512/8 : qbytes)) { - log_error (_("DSA key %s requires a %zu bit or larger hash\n"), - keystr_from_pk(pk), qbytes*8); + log_error (_("%s key %s requires a %zu bit or larger hash " + "(hash is %s\n"), + gcry_pk_algo_name (pkalgo), + keystr_from_pk(pk), qbytes*8, + gcry_md_algo_name (hash_algo)); return NULL; } + /* By passing QBYTES as length to mpi_scan, we do the truncation + of the hash. + + Note that in case of ECDSA 521 the hash is always smaller + than the key size. */ if (gcry_mpi_scan (&frame, GCRYMPI_FMT_USG, gcry_md_read (md, hash_algo), qbytes, &qbytes)) BUG(); diff --git a/g10/sign.c b/g10/sign.c index 5c00424a6..30dc66d5f 100644 --- a/g10/sign.c +++ b/g10/sign.c @@ -227,21 +227,6 @@ hash_sigversion_to_magic (gcry_md_hd_t md, const PKT_signature *sig) } } - -static gcry_mpi_t -mpi_from_sexp (gcry_sexp_t sexp, const char * item) -{ - gcry_sexp_t list; - gcry_mpi_t data; - - list = gcry_sexp_find_token (sexp, item, 0); - assert (list); - data = gcry_sexp_nth_mpi (list, 1, 0); - assert (data); - gcry_sexp_release (list); - return data; -} - /* Perform the sign operation. If CACHE_NONCE is given the agent is advised to use that cached passphrase fro the key. */ static int @@ -418,7 +403,7 @@ match_dsa_hash (unsigned int qbytes) if (qbytes <= 48) return DIGEST_ALGO_SHA384; - if (qbytes <= 64) + if (qbytes <= 66 ) /* 66 corresponds to 521 (64 to 512) */ return DIGEST_ALGO_SHA512; return DEFAULT_DIGEST_ALGO; @@ -451,10 +436,15 @@ hash_for (PKT_public_key *pk) { return recipient_digest_algo; } - else if (pk->pubkey_algo == PUBKEY_ALGO_DSA) + else if (pk->pubkey_algo == PUBKEY_ALGO_DSA + || pk->pubkey_algo == PUBKEY_ALGO_ECDSA) { - unsigned int qbytes = gcry_mpi_get_nbits (pk->pkey[1]) / 8; + unsigned int qbytes = gcry_mpi_get_nbits (pk->pkey[1]); + if (pk->pubkey_algo == PUBKEY_ALGO_ECDSA) + qbytes = ecdsa_qbits_from_Q (qbytes); + qbytes = qbytes/8; + /* It's a DSA key, so find a hash that is the same size as q or larger. If q is 160, assume it is an old DSA key and use a 160-bit hash unless --enable-dsa2 is set, in which case act @@ -935,10 +925,15 @@ sign_file (ctrl_t ctrl, strlist_t filenames, int detached, strlist_t locusr, for (sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next ) { - if (sk_rover->pk->pubkey_algo == PUBKEY_ALGO_DSA) + if (sk_rover->pk->pubkey_algo == PUBKEY_ALGO_DSA + || sk_rover->pk->pubkey_algo == PUBKEY_ALGO_ECDSA) { - int temp_hashlen = gcry_mpi_get_nbits - (sk_rover->pk->pkey[1])+7/8; + int temp_hashlen = (gcry_mpi_get_nbits + (sk_rover->pk->pkey[1])); + + if (sk_rover->pk->pubkey_algo == PUBKEY_ALGO_ECDSA) + temp_hashlen = ecdsa_qbits_from_Q (temp_hashlen); + temp_hashlen = (temp_hashlen+7)/8; /* Pick a hash that is large enough for our largest q */ @@ -1490,11 +1485,14 @@ make_keysig_packet( PKT_signature **ret_sig, PKT_public_key *pk, if(opt.cert_digest_algo) digest_algo=opt.cert_digest_algo; - else if(pksk->pubkey_algo==PUBKEY_ALGO_RSA + else if(pksk->pubkey_algo == PUBKEY_ALGO_RSA && pk->version<4 && sigversion<4) digest_algo = DIGEST_ALGO_MD5; - else if(pksk->pubkey_algo==PUBKEY_ALGO_DSA) + else if(pksk->pubkey_algo == PUBKEY_ALGO_DSA) digest_algo = match_dsa_hash (gcry_mpi_get_nbits (pksk->pkey[1])/8); + else if(pksk->pubkey_algo == PUBKEY_ALGO_ECDSA ) + digest_algo = match_dsa_hash (ecdsa_qbits_from_Q + (gcry_mpi_get_nbits (pksk->pkey[1]))/8); else digest_algo = DIGEST_ALGO_SHA1; } diff --git a/g10/verify-stubs.c b/g10/verify-stubs.c new file mode 100644 index 000000000..c4c657b9f --- /dev/null +++ b/g10/verify-stubs.c @@ -0,0 +1,31 @@ +/* To satisfy the linker for the gpgv target + * Copyright (C) 2010 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * GnuPG 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <config.h> + +#include <stdio.h> +#include "gpg.h" +#include "main.h" + +int +pk_ecc_keypair_gen (PKT_public_key **pk_out, int algo, int keygen_flags, + char **cache_nonce_addr, unsigned nbits) +{ + return GPG_ERR_NOT_IMPLEMENTED; +} diff --git a/g13/utils.c b/g13/utils.c index ef0c572a6..4b374df10 100644 --- a/g13/utils.c +++ b/g13/utils.c @@ -176,5 +176,5 @@ next_tuple (tupledesc_t tupledesc, unsigned int *r_tag, size_t *r_length) } return NULL; -} +} diff --git a/g13/utils.h b/g13/utils.h index ef718d60d..528ce16ce 100644 --- a/g13/utils.h +++ b/g13/utils.h @@ -38,7 +38,7 @@ const void *find_tuple (tupledesc_t tupledesc, unsigned int tag, size_t *r_length); const void *next_tuple (tupledesc_t tupledesc, unsigned int *r_tag, size_t *r_length); - +char *mpi2hex( gcry_mpi_t m ); #endif /*G13_UTILS_H*/ diff --git a/include/ChangeLog b/include/ChangeLog index 339800f37..8dd88ffbb 100644 --- a/include/ChangeLog +++ b/include/ChangeLog @@ -1,3 +1,9 @@ +2011-01-21 Werner Koch <[email protected]> + + * cipher.h (GCRY_PK_USAGE_CERT): Remove compatibility macros + because we now require libgcrypt 1.4.6. + (GCRY_PK_ECDH): Add replacement. + 2009-08-20 Daiki Ueno <[email protected]> (wk) * cipher.h (struct DEK): Add field S2K_CACHEID. diff --git a/include/cipher.h b/include/cipher.h index 8e198283d..03d38da5b 100644 --- a/include/cipher.h +++ b/include/cipher.h @@ -1,6 +1,6 @@ /* cipher.h - Definitions for OpenPGP * Copyright (C) 1998, 1999, 2000, 2001, 2006, - * 2007 Free Software Foundation, Inc. + * 2007, 2010 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -23,10 +23,8 @@ #include <gcrypt.h> /* Macros for compatibility with older libgcrypt versions. */ -#ifndef GCRY_PK_USAGE_CERT -# define GCRY_PK_USAGE_CERT 4 -# define GCRY_PK_USAGE_AUTH 8 -# define GCRY_PK_USAGE_UNKN 128 +#ifndef HAVE_GCRY_PK_ECDSA +# define GCRY_PK_ECDH 302 #endif @@ -56,6 +54,8 @@ #define PUBKEY_ALGO_RSA_S /* 3 */ GCRY_PK_RSA_S /* RSA sign only. */ #define PUBKEY_ALGO_ELGAMAL_E /* 16 */ GCRY_PK_ELG_E /* Elgamal encr only */ #define PUBKEY_ALGO_DSA /* 17 */ GCRY_PK_DSA +#define PUBKEY_ALGO_ECDH 18 +#define PUBKEY_ALGO_ECDSA 19 #define PUBKEY_ALGO_ELGAMAL /* 20 */ GCRY_PK_ELG /* Elgamal encr+sign */ #define PUBKEY_USAGE_SIG GCRY_PK_USAGE_SIGN /* Good for signatures. */ diff --git a/kbx/keybox-openpgp.c b/kbx/keybox-openpgp.c index 0968cf8b3..f1de685cf 100644 --- a/kbx/keybox-openpgp.c +++ b/kbx/keybox-openpgp.c @@ -186,7 +186,7 @@ next_packet (unsigned char const **bufptr, size_t *buflen, } -/* Parse a key packet and store the ionformation in KI. */ +/* Parse a key packet and store the information in KI. */ static gpg_error_t parse_key (const unsigned char *data, size_t datalen, struct _keybox_openpgp_key_info *ki) @@ -243,6 +243,12 @@ parse_key (const unsigned char *data, size_t datalen, case 17: /* DSA */ npkey = 4; break; + case 18: /* ECDH */ + npkey = 3; + break; + case 19: /* ECDSA */ + npkey = 2; + break; default: /* Unknown algorithm. */ return gpg_error (GPG_ERR_UNKNOWN_ALGORITHM); } |