From e0972d3d962548972872d889b362560e499340d1 Mon Sep 17 00:00:00 2001 From: Andrey Jivsov Date: Wed, 5 Jan 2011 17:33:17 -0800 Subject: Integrating http://code.google.com/p/gnupg-ecc/source/detail?r=15 . The following works: gpg2 --gen-key (ECC) gpg2 --list-keys gpg2 --list-packets ~/.gnupg/pubring.gpg gpg2 --list-packets ECDH doesn't work yet as the code must be re-written to adjust for gpg-agent refactoring. --- g10/Makefile.am | 6 +- g10/armor.c | 2 +- g10/build-packet.c | 46 +++++- g10/call-agent.c | 3 + g10/call-agent.h | 1 + g10/ecdh.c | 477 +++++++++++++++++++++++++++++++++++++++++++++++++++++ g10/encrypt.c | 11 +- g10/export.c | 12 ++ g10/getkey.c | 5 +- g10/gpg.c | 4 +- g10/keygen.c | 281 +++++++++++++++++++++++++++---- g10/keyid.c | 26 ++- g10/main.h | 13 +- g10/mainproc.c | 6 +- g10/misc.c | 147 ++++++++++++++++- g10/parse-packet.c | 115 ++++++++++--- g10/passphrase.c | 6 +- g10/pkglue.c | 151 +++++++++++++++-- g10/pkglue.h | 12 +- g10/pubkey-enc.c | 62 ++++--- g10/seskey.c | 69 ++++++-- g10/sign.c | 38 ++--- g10/verify-stubs.c | 30 ++++ 23 files changed, 1370 insertions(+), 153 deletions(-) create mode 100644 g10/ecdh.c create mode 100644 g10/verify-stubs.c (limited to 'g10') diff --git a/g10/Makefile.am b/g10/Makefile.am index c8fc4821e..b82fe07f3 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 \ @@ -109,7 +110,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/armor.c b/g10/armor.c index a6195fc3d..8cfd35c1f 100644 --- a/g10/armor.c +++ b/g10/armor.c @@ -1079,7 +1079,7 @@ armor_filter( void *opaque, int control, iobuf_writestr(a,afx->eol); if( !opt.no_version ) { - iobuf_writestr(a, "Version: GnuPG v" VERSION " (" + iobuf_writestr(a, "Version: GnuPG v" VERSION "-ecc (" PRINTABLE_OS_NAME ")" ); iobuf_writestr(a,afx->eol); } diff --git a/g10/build-packet.c b/g10/build-packet.c index 83d6c7a73..3a2c206c8 100644 --- a/g10/build-packet.c +++ b/g10/build-packet.c @@ -178,6 +178,16 @@ 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 +/* Write the value of encrypted filed for ECDH. */ +#define ecdh_esk_write iobuf_write_size_body_mpi /**************** @@ -290,10 +300,24 @@ 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; + if( pk->pubkey_algo != PUBKEY_ALGO_ECDSA && pk->pubkey_algo != PUBKEY_ALGO_ECDH ) { + /* Writing the public parameters is easy, */ + for (i=0; i < npkey; i++ ) + if ((err = mpi_write (a, pk->pkey[i]))) + goto leave; + } + else { + /* ... except we do an adjustment for ECC OID and possibly KEK params for ECDH */ + if( (err=iobuf_name_oid_write(a, pk->pkey[0])) || /* DER of OID with preceeding length byte */ + (err = mpi_write (a, pk->pkey[1])) ) /* point Q, the public key */ + { + goto leave; + } + if( pk->pubkey_algo == PUBKEY_ALGO_ECDH && (err=ecdh_kek_params_write(a,pk->pkey[2]))) { /* one more public field for ECDH */ + goto leave; + } + /* followed by possibly protected private scalar */ + } if (pk->seckey_info) { @@ -458,8 +482,18 @@ 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 ) { + for (i=0; i < n && !rc ; i++ ) + rc = mpi_write(a, enc->data[i] ); + } + else { + /* 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] ); + } if (!rc) { diff --git a/g10/call-agent.c b/g10/call-agent.c index 9528e1427..25f9a537e 100644 --- a/g10/call-agent.c +++ b/g10/call-agent.c @@ -1744,6 +1744,7 @@ inq_ciphertext_cb (void *opaque, const char *line) gpg_error_t agent_pkdecrypt (ctrl_t ctrl, const char *keygrip, const char *desc, gcry_sexp_t s_ciphertext, + const byte sk_fp[MAX_FINGERPRINT_LEN], unsigned char **r_buf, size_t *r_buflen) { gpg_error_t err; @@ -1751,6 +1752,8 @@ agent_pkdecrypt (ctrl_t ctrl, const char *keygrip, const char *desc, membuf_t data; size_t n, len; char *p, *buf, *endp; + + /*TODO: use sk_fp */ if (!keygrip || strlen(keygrip) != 40 || !s_ciphertext || !r_buf || !r_buflen) return gpg_error (GPG_ERR_INV_VALUE); diff --git a/g10/call-agent.h b/g10/call-agent.h index e09c30990..45e593bb8 100644 --- a/g10/call-agent.h +++ b/g10/call-agent.h @@ -168,6 +168,7 @@ gpg_error_t agent_pksign (ctrl_t ctrl, const char *cache_nonce, /* Decrypt a ciphertext. */ gpg_error_t agent_pkdecrypt (ctrl_t ctrl, const char *keygrip, const char *desc, gcry_sexp_t s_ciphertext, + const byte sk_fp[MAX_FINGERPRINT_LEN], unsigned char **r_buf, size_t *r_buflen); /* Retrieve a key encryption key. */ diff --git a/g10/ecdh.c b/g10/ecdh.c new file mode 100644 index 000000000..6615b75a4 --- /dev/null +++ b/g10/ecdh.c @@ -0,0 +1,477 @@ +/* ecdh.c - ECDH public key operations used in public key glue code + * Copyright (C) 2000, 2003 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 . + */ + +#include +#include +#include +#include +#include +#include + +#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 }, + { 528, DIGEST_ALGO_SHA512, CIPHER_ALGO_AES256 } // 528 is 521 rounded to the 8 bit boundary + }; + + for( i=0; i= 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 returned value with xfree. + * Returns NULL on error + */ +byte * +pk_ecdh_default_params( int qbits, size_t *sizeout ) { + 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 }, + { 528, DIGEST_ALGO_SHA512, CIPHER_ALGO_AES256 } // 528 is 521 rounded to the 8 bit boundary + }; + + byte *p; + + *sizeout = 0; + + for( i=0; i= 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 == NULL ) + 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 ); + + if( kdf_params_size != 4 || kdf_params[0] != 3 || kdf_params[1] != 1 ) /* expect 4 bytes 03 01 hash_alg symm_alg */ + 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) ); + + memset( secret_x+secret_x_size, old_size-secret_x_size, 0 ); /* we could have allocated more, so clean the tail before returning */ + 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 = xmalloc_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; +} + +/* 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; + + PKT_public_key *pk_eph; + int nbits; + int rc; + + nbits = pubkey_nbits( PUBKEY_ALGO_ECDH, pkey ); + + /*** Generate an ephemeral key ***/ + + rc = pk_ecc_keypair_gen( &pk_eph, PUBKEY_ALGO_ECDH, KEYGEN_FLAG_TRANSIENT_KEY | KEYGEN_FLAG_NO_PROTECTION /*this is ephemeral*/, "", nbits ); + if( rc ) + return rc; + if( DBG_CIPHER ) { + unsigned char *buffer; + if (gcry_mpi_aprint (GCRYMPI_FMT_HEX, &buffer, NULL, pk_eph->pkey[1])) + BUG (); + log_debug("ephemeral key MPI #0: %s\n", buffer); + gcry_free( buffer ); + } + free_public_key (pk_eph); + + /*** 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 */ + if (gcry_sexp_build (&s_data, NULL, "%m", pk_eph->pkey[3])) /* ephemeral scalar goes as data */ + 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 */ + + { + gcry_mpi_t shared = mpi_from_sexp (s_ciph, "a"); /* ... and get the shared point */ + gcry_sexp_release (s_ciph); + resarr[0] = pk_eph->pkey[1]; /* ephemeral public key */ + + 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 * skey) { + gcry_sexp_t s_skey, s_data, s_ciph; + int rc; + + if (!data[0] || !data[1]) + return gpg_error (GPG_ERR_BAD_MPI); + + rc = gcry_sexp_build (&s_skey, NULL, + "(public-key(ecdh(c%m)(q%m)(p%m)))", + skey[0]/*curve*/, data[0]/*ephemeral key*/, skey[2]/*KDF params*/); + if (rc) + BUG (); + + /* put the data into a simple list */ + if (gcry_sexp_build (&s_data, NULL, "%m", skey[3])) /* static private key (scalar) goes as data */ + BUG (); + + rc = gcry_pk_encrypt (&s_ciph, s_data, s_skey); /* encrypting ephemeral key with our private scalar yields the shared point */ + gcry_sexp_release (s_skey); + gcry_sexp_release (s_data); + if (rc) + return rc; + + { + gcry_mpi_t shared = mpi_from_sexp (s_ciph, "a"); /* get the shared point */ + gcry_sexp_release (s_ciph); + rc = pk_ecdh_encrypt_with_shared_point ( 0 /*=decryption*/, shared, sk_fp, data[1]/*encr data as an MPI*/, skey, result ); + mpi_release( shared ); + } + + return rc; +} + + diff --git a/g10/encrypt.c b/g10/encrypt.c index 55f9b27fb..3c16309d0 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 91c6a73d7..82d97511f 100644 --- a/g10/export.c +++ b/g10/export.c @@ -1161,6 +1161,18 @@ 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); } diff --git a/g10/gpg.c b/g10/gpg.c index 4a17b2905..23b193402 100644 --- a/g10/gpg.c +++ b/g10/gpg.c @@ -813,7 +813,7 @@ my_strusage( int level ) const char *p; switch( level ) { - case 11: p = "gpg (GnuPG)"; + case 11: p = "gpg (GnuPG) ecc"; break; case 13: p = VERSION; break; case 17: p = PRINTABLE_OS_NAME; break; @@ -857,7 +857,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..f7f152659 100644 --- a/g10/keygen.c +++ b/g10/keygen.c @@ -42,6 +42,8 @@ #include "i18n.h" #include "keyserver-internal.h" #include "call-agent.h" +#include "pkglue.h" +#include "gcrypt.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(nbitsmax) 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..2a9bd1988 100644 --- a/g10/keyid.c +++ b/g10/keyid.c @@ -57,6 +57,8 @@ pubkey_letter( int algo ) 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 '?'; } } @@ -74,6 +76,8 @@ hash_public_key (gcry_md_hd_t md, PKT_public_key *pk) unsigned int nbits; size_t nbytes; int npkey = pubkey_get_npkey (pk->pubkey_algo); + /* name OID, MPI of public point, [for ECDH only: KEK params] */ + enum gcry_mpi_format ecc_pub_format[3] = {GCRYMPI_FMT_USG, GCRYMPI_FMT_PGP, GCRYMPI_FMT_USG}; /* Two extra bytes for the expiration date in v3 */ if(pk->version<4) @@ -90,11 +94,13 @@ hash_public_key (gcry_md_hd_t md, PKT_public_key *pk) { for(i=0; i < npkey; i++ ) { - if (gcry_mpi_print (GCRYMPI_FMT_PGP, NULL, 0, &nbytes, pk->pkey[i])) + const enum gcry_mpi_format fmt = + ((pk->pubkey_algo==PUBKEY_ALGO_ECDSA || pk->pubkey_algo==PUBKEY_ALGO_ECDH) ? ecc_pub_format[i] : 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]; @@ -712,6 +718,20 @@ 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 b673cf559..e336e5ce6 100644 --- a/g10/main.h +++ b/g10/main.h @@ -93,11 +93,12 @@ int map_cipher_openpgp_to_gcry (int algo); 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 +158,10 @@ 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 +256,10 @@ 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 +270,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..a09636b60 100644 --- a/g10/misc.c +++ b/g10/misc.c @@ -64,6 +64,7 @@ #include "call-agent.h" #include "i18n.h" +#include 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,12 @@ map_cipher_gcry_to_openpgp (int algo) } } +int +map_pk_openpgp_to_gcry (int algo) +{ + return (algo==PUBKEY_ALGO_ECDSA ? GCRY_PK_ECDSA : (algo==PUBKEY_ALGO_ECDH ? GCRY_PK_ECDH : algo)); +} + /* Return the block length of an OpenPGP cipher algorithm. */ int @@ -409,7 +416,13 @@ openpgp_cipher_test_algo( int algo ) const char * openpgp_cipher_algo_name (int algo) { - return gnupg_cipher_algo_name (map_cipher_openpgp_to_gcry (algo)); + return gcry_cipher_algo_name (map_cipher_openpgp_to_gcry (algo)); +} + +const char * +openpgp_pk_algo_name (int algo) +{ + return gcry_pk_algo_name ( algo == PUBKEY_ALGO_ECDSA ? GCRY_PK_ECDSA : ( algo == PUBKEY_ALGO_ECDH ? GCRY_PK_ECDH : algo ) ); } int @@ -424,7 +437,13 @@ openpgp_pk_test_algo( int algo ) if (algo < 0 || algo > 110) return gpg_error (GPG_ERR_PUBKEY_ALGO); - return gcry_pk_test_algo (algo); + + if( algo == PUBKEY_ALGO_ECDSA ) + algo = GCRY_PK_ECDSA; + else if( algo == PUBKEY_ALGO_ECDH ) + algo = GCRY_PK_ECDH; + + return gcry_pk_test_algo ( algo ); } int @@ -442,7 +461,12 @@ 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); + if( algo == PUBKEY_ALGO_ECDSA ) + algo = GCRY_PK_ECDSA; + else if( algo == PUBKEY_ALGO_ECDH ) + algo = GCRY_PK_ECDH; + + return gcry_pk_algo_info ( algo, GCRYCTL_TEST_ALGO, NULL, &use_buf); } int @@ -457,6 +481,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 +497,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; } @@ -480,7 +507,7 @@ openpgp_pk_algo_usage ( int algo ) /* Map the OpenPGP pubkey algorithm whose ID is contained in ALGO to a string representation of the algorithm name. For unknown algorithm - IDs this function returns "?". */ + IDs this function returns "?". const char * openpgp_pk_algo_name (int algo) { @@ -498,6 +525,7 @@ openpgp_pk_algo_name (int algo) default: return "?"; } } +*/ int @@ -1348,6 +1376,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 +1393,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 +1410,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 +1427,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 +1444,8 @@ pubkey_nbits( int algo, gcry_mpi_t *key ) int rc, nbits; gcry_sexp_t sexp; + 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 +1461,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 +1506,89 @@ 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 + */ +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). + */ +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 */ +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..42d680ac5 100644 --- a/g10/parse-packet.c +++ b/g10/parse-packet.c @@ -939,20 +939,40 @@ 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 ) { + 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); + } + } + else + { + 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]); + mpi_print(listfp, k->data[1], mpi_print_mode ); + es_putc ('\n', listfp); + } + pktlen -= (encr_buf[0]+1); + } } leave: @@ -1926,20 +1946,61 @@ 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 ) { + 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); + } + } + else { + /* note that 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 */ + // log_debug("Parsing ecc public key in the public packet, pktlen=%lu\n", pktlen); + n = pktlen; pk->pkey[1] = mpi_read( inp, &n, 0 ); pktlen -=n; + if( pk->pkey[1]==NULL ) + err = gpg_error(G10ERR_INVALID_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 ) { +#define kek_params name_oid + err = iobuf_read_size_body( inp, kek_params, sizeof(kek_params), pktlen, pk->pkey+2 ); + if( err ) + goto leave; + n = kek_params[0]; + if( kek_params[1] != 1 ) { + log_error("invalid ecdh KEK parameters field type in private key: understand type 1, but found 0x%02x\n", kek_params[1]); + err = gpg_error(G10ERR_INVALID_PACKET); + goto leave; + } + if( list_mode ) + es_fprintf (listfp, "\tpkey[2]: KEK params type=01 hash:%d sym-algo:%d\n", kek_params[1+n-2], kek_params[1+n-1] ); + pktlen -= (n+1); +#undef kek_params + } + } 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..f78591940 100644 --- a/g10/pkglue.c +++ b/g10/pkglue.c @@ -27,9 +27,10 @@ #include "gpg.h" #include "util.h" #include "pkglue.h" +#include "main.h" -static gcry_mpi_t +gcry_mpi_t mpi_from_sexp (gcry_sexp_t sexp, const char * item) { gcry_sexp_t list; @@ -44,6 +45,70 @@ mpi_from_sexp (gcry_sexp_t sexp, const char * item) } +/**************** + * Emulate our old PK interface here - sometime in the future we might + * change the internal design to directly fit to libgcrypt. + */ +int +pk_sign (int algo, gcry_mpi_t * data, gcry_mpi_t hash, gcry_mpi_t * skey) +{ + gcry_sexp_t s_sig, s_hash, s_skey; + int rc; + int gcry_pkalgo = map_pk_openpgp_to_gcry( algo ); + + /* make a sexp from skey */ + 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_RSA || gcry_pkalgo == GCRY_PK_RSA_S) + { + 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_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 (gcry_pkalgo == GCRY_PK_ECDSA) + { + 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 hash into a S-Exp s_hash */ + if (gcry_sexp_build (&s_hash, NULL, "%m", hash)) + BUG (); + + rc = gcry_pk_sign (&s_sig, s_hash, s_skey); + gcry_sexp_release (s_hash); + gcry_sexp_release (s_skey); + + if (rc) + ; + else if (algo == GCRY_PK_RSA || algo == GCRY_PK_RSA_S) + data[0] = mpi_from_sexp (s_sig, "s"); + else + { + data[0] = mpi_from_sexp (s_sig, "r"); + data[1] = mpi_from_sexp (s_sig, "s"); + } + + gcry_sexp_release (s_sig); + return rc; +} /**************** * Emulate our old PK interface here - sometime in the future we might @@ -54,25 +119,31 @@ 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 gcry_pkalgo = map_pk_openpgp_to_gcry( algo ); /* make a sexp from pkey */ - if (algo == GCRY_PK_DSA) + if (gcry_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 (gcry_pkalgo == GCRY_PK_ELG || gcry_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 (gcry_pkalgo == GCRY_PK_RSA || gcry_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 (gcry_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; @@ -85,7 +156,7 @@ pk_verify (int algo, gcry_mpi_t hash, gcry_mpi_t * data, gcry_mpi_t * pkey) /* Put data into a S-Exp s_sig. */ s_sig = NULL; - if (algo == GCRY_PK_DSA) + if (gcry_pkalgo == GCRY_PK_DSA) { if (!data[0] || !data[1]) rc = gpg_error (GPG_ERR_BAD_MPI); @@ -93,7 +164,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 (gcry_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 (gcry_pkalgo == GCRY_PK_ELG || gcry_pkalgo == GCRY_PK_ELG_E) { if (!data[0] || !data[1]) rc = gpg_error (GPG_ERR_BAD_MPI); @@ -101,7 +180,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 (gcry_pkalgo == GCRY_PK_RSA || gcry_pkalgo == GCRY_PK_RSA_S) { if (!data[0]) rc = gpg_error (GPG_ERR_BAD_MPI); @@ -128,7 +207,7 @@ 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; @@ -146,6 +225,10 @@ 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; @@ -166,7 +249,7 @@ pk_encrypt (int algo, gcry_mpi_t * resarr, gcry_mpi_t data, gcry_mpi_t * pkey) else { /* 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"); } @@ -181,7 +264,7 @@ pk_encrypt (int algo, gcry_mpi_t * resarr, gcry_mpi_t data, gcry_mpi_t * pkey) * change the internal design to directly fit to libgcrypt. */ int -pk_decrypt (int algo, gcry_mpi_t * result, gcry_mpi_t * data, +pk_decrypt (int algo, gcry_mpi_t * result, const byte sk_fp[MAX_FINGERPRINT_LEN], gcry_mpi_t * data, gcry_mpi_t * skey) { gcry_sexp_t s_skey, s_data, s_plain; @@ -202,6 +285,9 @@ pk_decrypt (int algo, gcry_mpi_t * result, gcry_mpi_t * data, skey[0], skey[1], skey[2], skey[3], skey[4], skey[5]); } + else if( algo == PUBKEY_ALGO_ECDH ) { + return pk_ecdh_decrypt( result, sk_fp, data, skey ); + } else return GPG_ERR_PUBKEY_ALGO; @@ -244,3 +330,48 @@ pk_decrypt (int algo, gcry_mpi_t * result, gcry_mpi_t * data, return 0; } + +/* Check whether SKEY is a suitable secret key. */ +int +pk_check_secret_key (int algo, gcry_mpi_t *skey) +{ + gcry_sexp_t s_skey; + int rc; + const int gcry_pkalgo = map_pk_openpgp_to_gcry( algo ); + + 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 (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) + { + rc = gcry_pk_testkey (s_skey); + gcry_sexp_release (s_skey); + } + return rc; +} diff --git a/g10/pkglue.h b/g10/pkglue.h index f97def153..0d5194818 100644 --- a/g10/pkglue.h +++ b/g10/pkglue.h @@ -20,13 +20,23 @@ #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_sign (int algo, gcry_mpi_t *data, gcry_mpi_t hash, + gcry_mpi_t *skey); 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, +int pk_decrypt (int algo, gcry_mpi_t *result, const byte fp[MAX_FINGERPRINT_LEN], 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 * 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..a5224e20a 100644 --- a/g10/pubkey-enc.c +++ b/g10/pubkey-enc.c @@ -145,6 +145,8 @@ 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; /* Get the keygrip. */ err = hexkeygrip_from_pk (sk, &keygrip); @@ -174,9 +176,12 @@ get_it (PKT_pubkey_enc *enc, DEK *dek, PKT_public_key *sk, u32 *keyid) 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); + err = agent_pkdecrypt (NULL, keygrip, desc, s_data, fp, &frame, &nframe); xfree (desc); gcry_sexp_release (s_data); if (err) @@ -202,28 +207,41 @@ 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 (n + 7 > nframe) - { - err = gpg_error (G10ERR_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 (G10ERR_CIPHER_ALGO); - goto leave; - } - if (frame[n] != 2) /* Something went wrong. */ - { - err = gpg_error (G10ERR_WRONG_SECKEY); - goto leave; - } - for (n++; n < nframe && frame[n]; n++) /* Skip the random bytes. */ - ; - n++; /* Skip the zero byte. */ + + if( sk->pubkey_algo != PUBKEY_ALGO_ECDH ) { + if (!card) + { + if (n + 7 > nframe) + { + err = gpg_error (G10ERR_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 (G10ERR_CIPHER_ALGO); + goto leave; + } + if (frame[n] != 2) /* Something went wrong. */ + { + err = gpg_error (G10ERR_WRONG_SECKEY); + goto leave; + } + for (n++; n < nframe && frame[n]; n++) /* Skip the random bytes. */ + ; + n++; /* Skip the zero byte. */ + } + } + else { + /* Allow double padding for the benefit of DEK size concealment. + * Higher than this is wasteful. + */ + if( frame[nframe-1] > 8*2 || nframe <= 8 ) { + err = G10ERR_WRONG_SECKEY; goto leave; } + nframe -= frame[nframe-1]; /* remove padding */ + assert( n==0 ); /* used just bellow */ + } if (n + 4 > nframe) { diff --git a/g10/seskey.c b/g10/seskey.c index ee5584c66..4cc9158c9 100644 --- a/g10/seskey.c +++ b/g10/seskey.c @@ -27,6 +27,7 @@ #include "gpg.h" #include "util.h" #include "cipher.h" +#include "options.h" #include "main.h" #include "i18n.h" @@ -73,15 +74,48 @@ 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; + u16 csum = 0; gcry_mpi_t a; + if( DBG_CIPHER ) + log_debug("encode_session_key: encoding %d byte DEK", dek->keylen); + + 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); + assert( !(nframe%8) && nframe > 1 + dek->keylen + 2 ); /* alg+key+csum fit and the size is congruent to 8 */ + 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 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", 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. @@ -103,9 +137,6 @@ encode_session_key (DEK *dek, unsigned int nbits) * 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; @@ -161,8 +192,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,16 +240,23 @@ gcry_mpi_t encode_md_value (PKT_public_key *pk, gcry_md_hd_t md, int hash_algo) { gcry_mpi_t frame; + int gcry_pkalgo; assert (hash_algo); assert (pk); - if (pk->pubkey_algo == GCRY_PK_DSA) + gcry_pkalgo = map_pk_openpgp_to_gcry( pk->pubkey_algo ); + + if (gcry_pkalgo == GCRY_PK_DSA || gcry_pkalgo == GCRY_PK_ECDSA ) { /* 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 */ + if( gcry_pkalgo==GCRY_PK_ECDSA ) + qbytes = ecdsa_qbits_from_Q( qbytes ); + /* Make sure it is a multiple of 8 bits. */ if(qbytes%8) @@ -236,7 +274,8 @@ 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"), + log_error (_("%s key %s uses an unsafe (%zu bit) hash\n"), + gcry_pk_algo_name( gcry_pkalgo ), keystr_from_pk (pk), qbytes); return NULL; } @@ -245,10 +284,16 @@ encode_md_value (PKT_public_key *pk, gcry_md_hd_t md, int hash_algo) /* 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) + /* 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) < qbytes ) + if (gcry_md_get_algo_dlen (hash_algo) < ((gcry_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, used hash-algo=%d\n"), + gcry_pk_algo_name( gcry_pkalgo ), + keystr_from_pk(pk), qbytes*8, hash_algo); return NULL; } diff --git a/g10/sign.c b/g10/sign.c index 5c00424a6..ccf796446 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,9 +436,13 @@ 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 @@ -935,10 +924,13 @@ 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 */ @@ -1494,7 +1486,9 @@ make_keysig_packet( PKT_signature **ret_sig, PKT_public_key *pk, && pk->version<4 && sigversion<4) digest_algo = DIGEST_ALGO_MD5; else if(pksk->pubkey_algo==PUBKEY_ALGO_DSA) - digest_algo = match_dsa_hash (gcry_mpi_get_nbits (pksk->pkey[1])/8); + 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..d1f0aa105 --- /dev/null +++ b/g10/verify-stubs.c @@ -0,0 +1,30 @@ +/* To satisfy the linker for the gpgv target + * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2005, 2006, + * 2007 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 . + */ + +#include + +#include +#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; +} -- cgit v1.2.3 From ded546b4b5cc2caafa654d94ac8f69a23960427e Mon Sep 17 00:00:00 2001 From: Andrey Jivsov Date: Thu, 6 Jan 2011 15:44:01 -0800 Subject: Milestone: Data signing/verification and key signing/verification work with ECDSA. --- g10/parse-packet.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'g10') diff --git a/g10/parse-packet.c b/g10/parse-packet.c index 42d680ac5..d43ab2cba 100644 --- a/g10/parse-packet.c +++ b/g10/parse-packet.c @@ -967,7 +967,7 @@ parse_pubkeyenc (IOBUF inp, int pkttype, unsigned long pktlen, 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]); + es_fprintf (listfp, "\tdata: [% 3d bytes] ", encr_buf[0]+1); mpi_print(listfp, k->data[1], mpi_print_mode ); es_putc ('\n', listfp); } -- cgit v1.2.3 From 5761a9ba74e41f52660e20a1de700fe784c97832 Mon Sep 17 00:00:00 2001 From: Andrey Jivsov Date: Mon, 10 Jan 2011 20:24:14 -0800 Subject: 'g10/gpg2 --encrypt --debug 15 -r ecdsa -a -o _e.asc _' and 'g10/gpg2 --debug 15 _e.asc', as well as decoding of an old message posted on https://sites.google.com/site/brainhub/pgpecckeys work. This is the milestone 2 that brings in ECDH support from http://code.google.com/p/gnupg-ecc/source/detail?r=15 . This corresponds to the commit 899386826c85f1e757e75bcc5d5b2159d05676a0 in libgcrypt --- g10/call-agent.c | 3 --- g10/call-agent.h | 1 - g10/ecdh.c | 82 +++++++++++++++++++++++--------------------------------- g10/pkglue.c | 74 -------------------------------------------------- g10/pkglue.h | 4 +-- g10/pubkey-enc.c | 40 ++++++++++++++++++++++++--- 6 files changed, 73 insertions(+), 131 deletions(-) (limited to 'g10') diff --git a/g10/call-agent.c b/g10/call-agent.c index 25f9a537e..dc2ace0e5 100644 --- a/g10/call-agent.c +++ b/g10/call-agent.c @@ -1744,7 +1744,6 @@ inq_ciphertext_cb (void *opaque, const char *line) gpg_error_t agent_pkdecrypt (ctrl_t ctrl, const char *keygrip, const char *desc, gcry_sexp_t s_ciphertext, - const byte sk_fp[MAX_FINGERPRINT_LEN], unsigned char **r_buf, size_t *r_buflen) { gpg_error_t err; @@ -1753,8 +1752,6 @@ agent_pkdecrypt (ctrl_t ctrl, const char *keygrip, const char *desc, size_t n, len; char *p, *buf, *endp; - /*TODO: use sk_fp */ - 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/call-agent.h b/g10/call-agent.h index 45e593bb8..e09c30990 100644 --- a/g10/call-agent.h +++ b/g10/call-agent.h @@ -168,7 +168,6 @@ gpg_error_t agent_pksign (ctrl_t ctrl, const char *cache_nonce, /* Decrypt a ciphertext. */ gpg_error_t agent_pkdecrypt (ctrl_t ctrl, const char *keygrip, const char *desc, gcry_sexp_t s_ciphertext, - const byte sk_fp[MAX_FINGERPRINT_LEN], unsigned char **r_buf, size_t *r_buflen); /* Retrieve a key encryption key. */ diff --git a/g10/ecdh.c b/g10/ecdh.c index 6615b75a4..091a28cde 100644 --- a/g10/ecdh.c +++ b/g10/ecdh.c @@ -76,8 +76,6 @@ pk_ecdh_default_params_to_mpi( int qbits ) { */ byte * pk_ecdh_default_params( int qbits, size_t *sizeout ) { - 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*/, @@ -370,6 +368,29 @@ pk_ecdh_encrypt_with_shared_point ( int is_encrypt, gcry_mpi_t shared_mpi, 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 @@ -377,25 +398,17 @@ pk_ecdh_encrypt (gcry_mpi_t * resarr, const byte pk_fp[MAX_FINGERPRINT_LEN], gcr { gcry_sexp_t s_ciph, s_data, s_pkey; - PKT_public_key *pk_eph; int nbits; int rc; + gcry_mpi_t k; nbits = pubkey_nbits( PUBKEY_ALGO_ECDH, pkey ); - - /*** Generate an ephemeral key ***/ - rc = pk_ecc_keypair_gen( &pk_eph, PUBKEY_ALGO_ECDH, KEYGEN_FLAG_TRANSIENT_KEY | KEYGEN_FLAG_NO_PROTECTION /*this is ephemeral*/, "", nbits ); - if( rc ) - return rc; - if( DBG_CIPHER ) { - unsigned char *buffer; - if (gcry_mpi_aprint (GCRYMPI_FMT_HEX, &buffer, NULL, pk_eph->pkey[1])) - BUG (); - log_debug("ephemeral key MPI #0: %s\n", buffer); - gcry_free( buffer ); - } - free_public_key (pk_eph); + /*** 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. ***/ @@ -406,7 +419,7 @@ pk_ecdh_encrypt (gcry_mpi_t * resarr, const byte pk_fp[MAX_FINGERPRINT_LEN], gcr BUG (); /* put the data into a simple list */ - if (gcry_sexp_build (&s_data, NULL, "%m", pk_eph->pkey[3])) /* ephemeral scalar goes as data */ + if (gcry_sexp_build (&s_data, NULL, "%m", k)) /* ephemeral scalar goes as data */ BUG (); /* pass it to libgcrypt */ @@ -421,7 +434,7 @@ pk_ecdh_encrypt (gcry_mpi_t * resarr, const byte pk_fp[MAX_FINGERPRINT_LEN], gcr { gcry_mpi_t shared = mpi_from_sexp (s_ciph, "a"); /* ... and get the shared point */ gcry_sexp_release (s_ciph); - resarr[0] = pk_eph->pkey[1]; /* ephemeral public key */ + resarr[0] = mpi_from_sexp (s_ciph, "b"); /* ephemeral public key */ if( DBG_CIPHER ) { unsigned char *buffer; @@ -441,37 +454,10 @@ pk_ecdh_encrypt (gcry_mpi_t * resarr, const byte pk_fp[MAX_FINGERPRINT_LEN], gcr /* 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 * skey) { - gcry_sexp_t s_skey, s_data, s_ciph; - int rc; - - if (!data[0] || !data[1]) +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); - - rc = gcry_sexp_build (&s_skey, NULL, - "(public-key(ecdh(c%m)(q%m)(p%m)))", - skey[0]/*curve*/, data[0]/*ephemeral key*/, skey[2]/*KDF params*/); - if (rc) - BUG (); - - /* put the data into a simple list */ - if (gcry_sexp_build (&s_data, NULL, "%m", skey[3])) /* static private key (scalar) goes as data */ - BUG (); - - rc = gcry_pk_encrypt (&s_ciph, s_data, s_skey); /* encrypting ephemeral key with our private scalar yields the shared point */ - gcry_sexp_release (s_skey); - gcry_sexp_release (s_data); - if (rc) - return rc; - - { - gcry_mpi_t shared = mpi_from_sexp (s_ciph, "a"); /* get the shared point */ - gcry_sexp_release (s_ciph); - rc = pk_ecdh_encrypt_with_shared_point ( 0 /*=decryption*/, shared, sk_fp, data[1]/*encr data as an MPI*/, skey, result ); - mpi_release( shared ); - } - - return rc; + return pk_ecdh_encrypt_with_shared_point ( 0 /*=decryption*/, shared, sk_fp, data/*encr data as an MPI*/, skey, result ); } diff --git a/g10/pkglue.c b/g10/pkglue.c index f78591940..9050cc241 100644 --- a/g10/pkglue.c +++ b/g10/pkglue.c @@ -257,80 +257,6 @@ pk_encrypt (int algo, gcry_mpi_t * resarr, gcry_mpi_t data, const byte pk_fp[MAX return rc; } - - -/**************** - * Emulate our old PK interface here - sometime in the future we might - * change the internal design to directly fit to libgcrypt. - */ -int -pk_decrypt (int algo, gcry_mpi_t * result, const byte sk_fp[MAX_FINGERPRINT_LEN], gcry_mpi_t * data, - gcry_mpi_t * skey) -{ - gcry_sexp_t s_skey, s_data, s_plain; - int rc; - - *result = NULL; - /* make a sexp from skey */ - if (algo == GCRY_PK_ELG || algo == 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) - { - 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( algo == PUBKEY_ALGO_ECDH ) { - return pk_ecdh_decrypt( result, sk_fp, data, skey ); - } - 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 (!data[0]) - rc = gpg_error (GPG_ERR_BAD_MPI); - else - rc = gcry_sexp_build (&s_data, NULL, "(enc-val(rsa(a%m)))", data[0]); - } - 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; -} - - /* Check whether SKEY is a suitable secret key. */ int pk_check_secret_key (int algo, gcry_mpi_t *skey) diff --git a/g10/pkglue.h b/g10/pkglue.h index 0d5194818..a1c821dcd 100644 --- a/g10/pkglue.h +++ b/g10/pkglue.h @@ -33,8 +33,8 @@ int pk_decrypt (int algo, gcry_mpi_t *result, const byte fp[MAX_FINGERPRINT_LEN] 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 * 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 ); diff --git a/g10/pubkey-enc.c b/g10/pubkey-enc.c index a5224e20a..24411e8a1 100644 --- a/g10/pubkey-enc.c +++ b/g10/pubkey-enc.c @@ -147,14 +147,16 @@ get_it (PKT_pubkey_enc *enc, DEK *dek, PKT_public_key *sk, u32 *keyid) 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); @@ -162,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); @@ -170,6 +172,14 @@ 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); @@ -181,7 +191,7 @@ get_it (PKT_pubkey_enc *enc, DEK *dek, PKT_public_key *sk, u32 *keyid) /* Decrypt. */ desc = gpg_format_keydesc (sk, 0, 1); - err = agent_pkdecrypt (NULL, keygrip, desc, s_data, fp, &frame, &nframe); + err = agent_pkdecrypt (NULL, keygrip, desc, s_data, &frame, &nframe); xfree (desc); gcry_sexp_release (s_data); if (err) @@ -233,6 +243,30 @@ get_it (PKT_pubkey_enc *enc, DEK *dek, PKT_public_key *sk, u32 *keyid) } } else { + gcry_mpi_t shared_mpi; + gcry_mpi_t decoded; + + /* at the beginning the frame is the bytes of shared point MPI */ + + err = gcry_mpi_scan (&shared_mpi, GCRYMPI_FMT_USG, frame, nframe, NULL); + if (err) { + log_fatal ("mpi_scan failed: %s\n", gpg_strerror (err)); + goto leave; + } + + 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 is the bytes decrypted but padded session key */ + /* Allow double padding for the benefit of DEK size concealment. * Higher than this is wasteful. */ -- cgit v1.2.3 From b73d8ed06ffef8d2fd70ab2e48da9ef515472fe9 Mon Sep 17 00:00:00 2001 From: Andrey Jivsov Date: Wed, 12 Jan 2011 21:14:45 -0800 Subject: Fixed key generation with P-521. Confirmed that signature generation and verification work. --- g10/seskey.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'g10') diff --git a/g10/seskey.c b/g10/seskey.c index 4cc9158c9..e50cf5c02 100644 --- a/g10/seskey.c +++ b/g10/seskey.c @@ -288,7 +288,6 @@ encode_md_value (PKT_public_key *pk, gcry_md_hd_t md, int hash_algo) * 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) < qbytes ) if (gcry_md_get_algo_dlen (hash_algo) < ((gcry_pkalgo==GCRY_PK_ECDSA && qbytes>(521)/8) ? 512/8 : qbytes) ) { log_error (_("%s key %s requires a %zu bit or larger hash, used hash-algo=%d\n"), @@ -297,8 +296,9 @@ encode_md_value (PKT_public_key *pk, gcry_md_hd_t md, int hash_algo) return NULL; } + /* Note that in case of ECDSA 521 hash is always smaller than the key size */ if (gcry_mpi_scan (&frame, GCRYMPI_FMT_USG, - gcry_md_read (md, hash_algo), qbytes, &qbytes)) + gcry_md_read (md, hash_algo), gcry_md_get_algo_dlen (hash_algo), &qbytes)) BUG(); } else -- cgit v1.2.3 From 90b0ff23b7e51332592668e4034967c1aac1c593 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Fri, 21 Jan 2011 12:00:57 +0100 Subject: Editorial changes and allow building with old libgcrypts. Changed order of some conditional to make to put the special case into the true branch. Indentation changes. Minor other changes to make the ECC code more similar to the rest of our code. It builds but many sefltests still fail. Need to fix that before using it with an ECDH enabled libgcrypt. [/] 2011-01-21 Werner Koch * configure.ac: Need Libgcrypt 1.4.6 due to AESWRAP. (HAVE_GCRY_PK_ECDH): Add new test. [agent/] 2011-01-21 Werner Koch * cvt-openpgp.c (GCRY_PK_ECDH) [!HAVE_GCRY_PK_ECDH]: New. [include/] 2011-01-21 Werner Koch * cipher.h (GCRY_PK_USAGE_CERT): Remove compatibility macros because we now require libgcrypt 1.4.6. (GCRY_PK_ECDH): Add replacement. --- g10/armor.c | 2 +- g10/build-packet.c | 87 ++++++----- g10/ecdh.c | 451 +++++++++++++++++++++++++++++++---------------------- g10/encrypt.c | 4 +- g10/export.c | 22 ++- g10/gpg.c | 2 +- g10/keygen.c | 2 +- g10/keyid.c | 37 +++-- g10/main.h | 16 +- g10/misc.c | 133 ++++++++-------- g10/parse-packet.c | 194 ++++++++++++----------- g10/pkglue.c | 111 +++---------- g10/pkglue.h | 16 +- g10/pubkey-enc.c | 112 ++++++------- g10/seskey.c | 279 ++++++++++++++++++--------------- g10/sign.c | 30 ++-- g10/verify-stubs.c | 9 +- 17 files changed, 801 insertions(+), 706 deletions(-) (limited to 'g10') diff --git a/g10/armor.c b/g10/armor.c index 8cfd35c1f..a6195fc3d 100644 --- a/g10/armor.c +++ b/g10/armor.c @@ -1079,7 +1079,7 @@ armor_filter( void *opaque, int control, iobuf_writestr(a,afx->eol); if( !opt.no_version ) { - iobuf_writestr(a, "Version: GnuPG v" VERSION "-ecc (" + iobuf_writestr(a, "Version: GnuPG v" VERSION " (" PRINTABLE_OS_NAME ")" ); iobuf_writestr(a,afx->eol); } diff --git a/g10/build-packet.c b/g10/build-packet.c index 3a2c206c8..d138e0614 100644 --- a/g10/build-packet.c +++ b/g10/build-packet.c @@ -178,21 +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. - */ +/* 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 -/* Write the value of encrypted filed for ECDH. */ + +/* 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 - */ +/* Calculate the length of a packet described by PKT. */ u32 calc_packet_length( PACKET *pkt ) { @@ -300,24 +299,35 @@ do_key (iobuf_t out, int ctb, PKT_public_key *pk) } assert (npkey < nskey); - if( pk->pubkey_algo != PUBKEY_ALGO_ECDSA && pk->pubkey_algo != PUBKEY_ALGO_ECDH ) { - /* Writing the public parameters is easy, */ - for (i=0; i < npkey; i++ ) - if ((err = mpi_write (a, pk->pkey[i]))) - goto leave; - } - else { - /* ... except we do an adjustment for ECC OID and possibly KEK params for ECDH */ - if( (err=iobuf_name_oid_write(a, pk->pkey[0])) || /* DER of OID with preceeding length byte */ - (err = mpi_write (a, pk->pkey[1])) ) /* point Q, the public key */ + /* 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) { - goto leave; + /* 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; + } } - if( pk->pubkey_algo == PUBKEY_ALGO_ECDH && (err=ecdh_kek_params_write(a,pk->pkey[2]))) { /* one more public field for ECDH */ - goto leave; + else + { + for (i=0; i < npkey; i++ ) + if ((err = mpi_write (a, pk->pkey[i]))) + goto leave; } - /* followed by possibly protected private scalar */ - } + if (pk->seckey_info) { @@ -483,22 +493,25 @@ do_pubkey_enc( IOBUF out, int ctb, PKT_pubkey_enc *enc ) if ( !n ) write_fake_data( a, enc->data[0] ); - if( enc->pubkey_algo != PUBKEY_ALGO_ECDH ) { - for (i=0; i < n && !rc ; i++ ) - rc = mpi_write(a, enc->data[i] ); - } - else { - /* 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] ); - } + 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/ecdh.c b/g10/ecdh.c index 091a28cde..cb251fef2 100644 --- a/g10/ecdh.c +++ b/g10/ecdh.c @@ -1,5 +1,5 @@ /* ecdh.c - ECDH public key operations used in public key glue code - * Copyright (C) 2000, 2003 Free Software Foundation, Inc. + * Copyright (C) 2010 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -31,10 +31,12 @@ #include "options.h" gcry_mpi_t -pk_ecdh_default_params_to_mpi( int qbits ) { +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. */ + /* 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*/, @@ -50,41 +52,49 @@ pk_ecdh_default_params_to_mpi( int qbits ) { } kek_params_table[] = { { 256, DIGEST_ALGO_SHA256, CIPHER_ALGO_AES }, { 384, DIGEST_ALGO_SHA384, CIPHER_ALGO_AES256 }, - { 528, DIGEST_ALGO_SHA512, CIPHER_ALGO_AES256 } // 528 is 521 rounded to the 8 bit boundary + + /* Note: 528 is 521 rounded to the 8 bit boundary */ + { 528, DIGEST_ALGO_SHA512, CIPHER_ALGO_AES256 } }; - for( i=0; i= qbits ) { - kek_params[2] = kek_params_table[i].openpgp_hash_id; - kek_params[3] = kek_params_table[i].openpgp_cipher_id; - break; + for (i=0; i= 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) ); + 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); + 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 returned value with xfree. - * Returns NULL on error + +/* 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. */ +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*/ + CIPHER_ALGO_AES256 /* KEK AESWRAP alg */ }; int i; - + static const struct { int qbits; int openpgp_hash_id; @@ -92,39 +102,48 @@ pk_ecdh_default_params( int qbits, size_t *sizeout ) { } kek_params_table[] = { { 256, DIGEST_ALGO_SHA256, CIPHER_ALGO_AES }, { 384, DIGEST_ALGO_SHA384, CIPHER_ALGO_AES256 }, - { 528, DIGEST_ALGO_SHA512, CIPHER_ALGO_AES256 } // 528 is 521 rounded to the 8 bit boundary + /* 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= qbits ) { - kek_params[2] = kek_params_table[i].openpgp_hash_id; - kek_params[3] = kek_params_table[i].openpgp_cipher_id; - break; + + for (i=0; i= 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) ); + if (DBG_CIPHER ) + log_printhex ("ecdh kek params are", kek_params, sizeof(kek_params)); - p = xtrymalloc( sizeof(kek_params) ); - if( p == NULL ) + p = xtrymalloc (sizeof(kek_params)); + if (!p) return NULL; - memcpy( p, kek_params, sizeof(kek_params) ); + 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. + +/* 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) +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; @@ -141,55 +160,70 @@ pk_ecdh_encrypt_with_shared_point ( int is_encrypt, gcry_mpi_t shared_mpi, { size_t nbytes; - /* extract x component of the shared point: this is the actual shared secret */ + /* 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; - } + 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 ); + 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 ); + 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 ***/ + /*** 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. + /* 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)); - 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 ); + if (DBG_CIPHER) + log_printhex ("ecdh KDF public key params are:", + kdf_params, kdf_params_size ); - if( kdf_params_size != 4 || kdf_params[0] != 3 || kdf_params[1] != 1 ) /* expect 4 bytes 03 01 hash_alg symm_alg */ + /* 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 (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 ) + 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 ) + 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 */ + /* Build kdf_params. */ { IOBUF obuf; @@ -205,13 +239,15 @@ pk_ecdh_encrypt_with_shared_point ( int is_encrypt, gcry_mpi_t shared_mpi, /* 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 ) { + 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 ); + + 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. */ @@ -231,7 +267,8 @@ pk_ecdh_encrypt_with_shared_point ( int is_encrypt, gcry_mpi_t shared_mpi, 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)); + 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; @@ -239,12 +276,13 @@ pk_ecdh_encrypt_with_shared_point ( int is_encrypt, gcry_mpi_t shared_mpi, secret_x_size = gcry_cipher_get_algo_keylen( kdf_encr_algo ); assert( secret_x_size <= gcry_md_get_algo_dlen (kdf_hash_algo) ); - memset( secret_x+secret_x_size, old_size-secret_x_size, 0 ); /* we could have allocated more, so clean the tail before returning */ - if( DBG_CIPHER ) + /* 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 */ + } + + /* And, finally, aeswrap with key secret_x. */ { gcry_cipher_hd_t hd; size_t nbytes; @@ -256,115 +294,134 @@ pk_ecdh_encrypt_with_shared_point ( int is_encrypt, gcry_mpi_t shared_mpi, 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; - } + { + 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 = xmalloc_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 ); + log_error ("ecdh failed in gcry_cipher_setkey: %s\n", + gpg_strerror (rc)); return rc; } - if( DBG_CIPHER ) - log_printhex ("ecdh encrypting :", in, data_buf_size ); + data_buf_size = (gcry_mpi_get_nbits(data)+7)/8; + assert ((data_buf_size & 7) == (is_encrypt ? 0 : 1)); - 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) + data_buf = xtrymalloc_secure( 1 + 2*data_buf_size + 8); + if (!data_buf) { - log_error("ecdh failed in gcry_cipher_encrypt: %s\n", gpg_strerror (rc)); - xfree( data_buf ); - return rc; + gcry_cipher_close (hd); + return GPG_ERR_ENOMEM; } - 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) + if (is_encrypt) { - log_error("ecdh failed to create an MPI: %s\n", gpg_strerror (rc)); - return rc; - } - - *out = result; - } - else { - byte *in; + 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_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"); + rc = gcry_mpi_scan (&result, GCRYMPI_FMT_USG, + data_buf, 1+data_buf[0], NULL); + /* (byte)size + aeswrap of DEK */ xfree( data_buf ); - return GPG_ERR_BAD_MPI; + 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 ); + 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; - //} + 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; - } - + 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; } @@ -380,21 +437,22 @@ gen_k (unsigned 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 ); - } + 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. - */ +/* 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) +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; @@ -402,9 +460,9 @@ pk_ecdh_encrypt (gcry_mpi_t * resarr, const byte pk_fp[MAX_FINGERPRINT_LEN], gcr int rc; gcry_mpi_t k; - nbits = pubkey_nbits( PUBKEY_ALGO_ECDH, pkey ); + nbits = pubkey_nbits (PUBKEY_ALGO_ECDH, pkey); - /*** Generate an ephemeral key, actually, a scalar ***/ + /*** Generate an ephemeral key, actually, a scalar. ***/ k = gen_k (nbits); if( k == NULL ) @@ -414,50 +472,63 @@ pk_ecdh_encrypt (gcry_mpi_t * resarr, const byte pk_fp[MAX_FINGERPRINT_LEN], gcr * 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]); + "(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 */ - if (gcry_sexp_build (&s_data, NULL, "%m", k)) /* ephemeral scalar goes as data */ + /* 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 */ + /* 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 */ + /* Finally, perform encryption. */ { - gcry_mpi_t shared = mpi_from_sexp (s_ciph, "a"); /* ... and get the shared point */ + /* ... and get the shared point/ */ + gcry_mpi_t shared; + + shared = mpi_from_sexp (s_ciph, "a"); gcry_sexp_release (s_ciph); - resarr[0] = mpi_from_sexp (s_ciph, "b"); /* ephemeral public key */ + /* Ephemeral public key. */ + resarr[0] = mpi_from_sexp (s_ciph, "b"); - if( DBG_CIPHER ) { + 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 ); + } + + rc = pk_ecdh_encrypt_with_shared_point (1 /*=encrypton*/, shared, + pk_fp, data, pkey, resarr+1); + mpi_release (shared); } - + return rc; } -/* Perform ECDH decryption. - */ + +/* 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) { +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 ); + 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 3c16309d0..f52921582 100644 --- a/g10/encrypt.c +++ b/g10/encrypt.c @@ -894,8 +894,8 @@ 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 ); + 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 diff --git a/g10/export.c b/g10/export.c index 82d97511f..74a7b0c51 100644 --- a/g10/export.c +++ b/g10/export.c @@ -1161,18 +1161,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 (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/gpg.c b/g10/gpg.c index 23b193402..3794aa2b7 100644 --- a/g10/gpg.c +++ b/g10/gpg.c @@ -813,7 +813,7 @@ my_strusage( int level ) const char *p; switch( level ) { - case 11: p = "gpg (GnuPG) ecc"; + case 11: p = "gpg (GnuPG)"; break; case 13: p = VERSION; break; case 17: p = PRINTABLE_OS_NAME; break; diff --git a/g10/keygen.c b/g10/keygen.c index f7f152659..e75da792e 100644 --- a/g10/keygen.c +++ b/g10/keygen.c @@ -18,6 +18,7 @@ * along with this program; if not, see . */ +#warning wk: check these changes. #include #include #include @@ -43,7 +44,6 @@ #include "keyserver-internal.h" #include "call-agent.h" #include "pkglue.h" -#include "gcrypt.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 diff --git a/g10/keyid.c b/g10/keyid.c index 2a9bd1988..0405b8b2f 100644 --- a/g10/keyid.c +++ b/g10/keyid.c @@ -54,11 +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) + case PUBKEY_ALGO_ECDSA: return 'E' ; /* ECC DSA (sign only) */ + case PUBKEY_ALGO_ECDH: return 'e' ; /* ECC DH (encrypt only) */ default: return '?'; } } @@ -76,8 +76,6 @@ hash_public_key (gcry_md_hd_t md, PKT_public_key *pk) unsigned int nbits; size_t nbytes; int npkey = pubkey_get_npkey (pk->pubkey_algo); - /* name OID, MPI of public point, [for ECDH only: KEK params] */ - enum gcry_mpi_format ecc_pub_format[3] = {GCRYMPI_FMT_USG, GCRYMPI_FMT_PGP, GCRYMPI_FMT_USG}; /* Two extra bytes for the expiration date in v3 */ if(pk->version<4) @@ -92,11 +90,17 @@ 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++ ) { - const enum gcry_mpi_format fmt = - ((pk->pubkey_algo==PUBKEY_ALGO_ECDSA || pk->pubkey_algo==PUBKEY_ALGO_ECDH) ? ecc_pub_format[i] : GCRYMPI_FMT_PGP); - + 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); @@ -106,7 +110,7 @@ hash_public_key (gcry_md_hd_t md, PKT_public_key *pk) 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 */ @@ -724,13 +728,12 @@ keygrip_from_pk (PKT_public_key *pk, unsigned char *array) "(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; -*/ + + /* 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); diff --git a/g10/main.h b/g10/main.h index e336e5ce6..c7980ac9a 100644 --- a/g10/main.h +++ b/g10/main.h @@ -87,9 +87,12 @@ 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); @@ -159,7 +162,8 @@ 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 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 ); @@ -258,7 +262,9 @@ int save_unprotected_key_to_card (PKT_public_key *sk, int keyno); #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); +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 ); diff --git a/g10/misc.c b/g10/misc.c index a09636b60..6f77119fe 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. * @@ -366,10 +366,17 @@ 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) { - return (algo==PUBKEY_ALGO_ECDSA ? GCRY_PK_ECDSA : (algo==PUBKEY_ALGO_ECDH ? GCRY_PK_ECDH : algo)); + switch (algo) + { + case PUBKEY_ALGO_ECDSA: return GCRY_PK_ECDSA; + case PUBKEY_ALGO_ECDH: return GCRY_PK_ECDH; + default: return algo; + } } @@ -416,13 +423,7 @@ openpgp_cipher_test_algo( int algo ) const char * openpgp_cipher_algo_name (int algo) { - return gcry_cipher_algo_name (map_cipher_openpgp_to_gcry (algo)); -} - -const char * -openpgp_pk_algo_name (int algo) -{ - return gcry_pk_algo_name ( algo == PUBKEY_ALGO_ECDSA ? GCRY_PK_ECDSA : ( algo == PUBKEY_ALGO_ECDH ? GCRY_PK_ECDH : algo ) ); + return gnupg_cipher_algo_name (map_cipher_openpgp_to_gcry (algo)); } int @@ -438,12 +439,7 @@ openpgp_pk_test_algo( int algo ) if (algo < 0 || algo > 110) return gpg_error (GPG_ERR_PUBKEY_ALGO); - if( algo == PUBKEY_ALGO_ECDSA ) - algo = GCRY_PK_ECDSA; - else if( algo == PUBKEY_ALGO_ECDH ) - algo = GCRY_PK_ECDH; - - return gcry_pk_test_algo ( algo ); + return gcry_pk_test_algo (map_pk_openpgp_to_gcry (algo)); } int @@ -461,12 +457,8 @@ openpgp_pk_test_algo2( int algo, unsigned int use ) if (algo < 0 || algo > 110) return gpg_error (GPG_ERR_PUBKEY_ALGO); - if( algo == PUBKEY_ALGO_ECDSA ) - algo = GCRY_PK_ECDSA; - else if( algo == PUBKEY_ALGO_ECDH ) - algo = GCRY_PK_ECDH; - - 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 @@ -507,10 +499,12 @@ openpgp_pk_algo_usage ( int algo ) /* Map the OpenPGP pubkey algorithm whose ID is contained in ALGO to a string representation of the algorithm name. For unknown algorithm - IDs this function returns "?". + IDs this function returns "?". */ const char * openpgp_pk_algo_name (int algo) { + /* We use fixed strings to have pretty names instead of those from + libgcrypt. */ switch (algo) { case PUBKEY_ALGO_RSA: @@ -522,10 +516,13 @@ openpgp_pk_algo_name (int algo) case PUBKEY_ALGO_DSA: return "dsa"; - default: return "?"; + case PUBKEY_ALGO_ECDSA:return "ecdsa"; + + case PUBKEY_ALGO_ECDH: return "ecdh"; + + default: gcry_pk_algo_name (map_pk_openpgp_to_gcry (algo)); } } -*/ int @@ -1444,6 +1441,7 @@ 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 ) { @@ -1506,10 +1504,12 @@ 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 + * 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) { @@ -1538,57 +1538,68 @@ iobuf_write_size_body_mpi (iobuf_t out, gcry_mpi_t a) 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). + * 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 ) { +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; - } + 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; - } + 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 */ -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; +/* pkey[1] or skey[1] is Q for ECDSA, which is an uncompressed point, + i.e. 04 */ +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 d43ab2cba..5df336e55 100644 --- a/g10/parse-packet.c +++ b/g10/parse-packet.c @@ -939,40 +939,47 @@ parse_pubkeyenc (IOBUF inp, int pkttype, unsigned long pktlen, } else { - if( k->pubkey_algo != PUBKEY_ALGO_ECDH ) { - 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); - } - } - else + 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); - } + 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: @@ -1946,61 +1953,74 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen, else { /* Fill in public key parameters. */ - if( algorithm != PUBKEY_ALGO_ECDSA && algorithm != PUBKEY_ALGO_ECDH ) { - 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); - } - } - else { - /* note that 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 */ - // log_debug("Parsing ecc public key in the public packet, pktlen=%lu\n", pktlen); - n = pktlen; pk->pkey[1] = mpi_read( inp, &n, 0 ); pktlen -=n; - if( pk->pkey[1]==NULL ) - err = gpg_error(G10ERR_INVALID_PACKET); - else if( list_mode ) { - es_fprintf (listfp, "\tpkey[1]: "); - mpi_print(listfp, pk->pkey[1], 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); } - /* One more field for ECDH */ - if( algorithm == PUBKEY_ALGO_ECDH ) { -#define kek_params name_oid - err = iobuf_read_size_body( inp, kek_params, sizeof(kek_params), pktlen, pk->pkey+2 ); - if( err ) - goto leave; - n = kek_params[0]; - if( kek_params[1] != 1 ) { - log_error("invalid ecdh KEK parameters field type in private key: understand type 1, but found 0x%02x\n", kek_params[1]); - err = gpg_error(G10ERR_INVALID_PACKET); - goto leave; - } - if( list_mode ) - es_fprintf (listfp, "\tpkey[2]: KEK params type=01 hash:%d sym-algo:%d\n", kek_params[1+n-2], kek_params[1+n-1] ); - pktlen -= (n+1); -#undef kek_params - } - } + /* 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/pkglue.c b/g10/pkglue.c index 9050cc241..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. * @@ -29,7 +29,8 @@ #include "pkglue.h" #include "main.h" - +/* 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) { @@ -45,101 +46,37 @@ mpi_from_sexp (gcry_sexp_t sexp, const char * item) } -/**************** - * Emulate our old PK interface here - sometime in the future we might - * change the internal design to directly fit to libgcrypt. - */ -int -pk_sign (int algo, gcry_mpi_t * data, gcry_mpi_t hash, gcry_mpi_t * skey) -{ - gcry_sexp_t s_sig, s_hash, s_skey; - int rc; - int gcry_pkalgo = map_pk_openpgp_to_gcry( algo ); - - /* make a sexp from skey */ - 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_RSA || gcry_pkalgo == GCRY_PK_RSA_S) - { - 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_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 (gcry_pkalgo == GCRY_PK_ECDSA) - { - 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 hash into a S-Exp s_hash */ - if (gcry_sexp_build (&s_hash, NULL, "%m", hash)) - BUG (); - - rc = gcry_pk_sign (&s_sig, s_hash, s_skey); - gcry_sexp_release (s_hash); - gcry_sexp_release (s_skey); - - if (rc) - ; - else if (algo == GCRY_PK_RSA || algo == GCRY_PK_RSA_S) - data[0] = mpi_from_sexp (s_sig, "s"); - else - { - data[0] = mpi_from_sexp (s_sig, "r"); - data[1] = mpi_from_sexp (s_sig, "s"); - } - - gcry_sexp_release (s_sig); - return rc; -} /**************** * Emulate our old PK interface here - sometime in the future we might * 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 gcry_pkalgo = map_pk_openpgp_to_gcry( algo ); + const int pkalgo = map_pk_openpgp_to_gcry (algo); - /* make a sexp from pkey */ - if (gcry_pkalgo == 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 (gcry_pkalgo == GCRY_PK_ELG || gcry_pkalgo == 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 (gcry_pkalgo == GCRY_PK_RSA || gcry_pkalgo == 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 (gcry_pkalgo == GCRY_PK_ECDSA) /* same as GCRY_PK_ECDH */ + 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]); @@ -150,13 +87,13 @@ pk_verify (int algo, gcry_mpi_t hash, gcry_mpi_t * data, gcry_mpi_t * pkey) 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 (gcry_pkalgo == GCRY_PK_DSA) + if (pkalgo == GCRY_PK_DSA) { if (!data[0] || !data[1]) rc = gpg_error (GPG_ERR_BAD_MPI); @@ -164,7 +101,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(dsa(r%m)(s%m)))", data[0], data[1]); } - else if (gcry_pkalgo == GCRY_PK_ECDSA) + else if (pkalgo == GCRY_PK_ECDSA) { if (!data[0] || !data[1]) rc = gpg_error (GPG_ERR_BAD_MPI); @@ -172,7 +109,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(ecdsa(r%m)(s%m)))", data[0], data[1]); } - else if (gcry_pkalgo == GCRY_PK_ELG || gcry_pkalgo == GCRY_PK_ELG_E) + else if (pkalgo == GCRY_PK_ELG || pkalgo == GCRY_PK_ELG_E) { if (!data[0] || !data[1]) rc = gpg_error (GPG_ERR_BAD_MPI); @@ -180,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 (gcry_pkalgo == GCRY_PK_RSA || gcry_pkalgo == 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); @@ -207,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, const byte pk_fp[MAX_FINGERPRINT_LEN], 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, @@ -227,7 +165,7 @@ pk_encrypt (int algo, gcry_mpi_t * resarr, gcry_mpi_t data, const byte pk_fp[MAX } else if (algo == PUBKEY_ALGO_ECDH) { - return pk_ecdh_encrypt( resarr, pk_fp, data, pkey ); + return pk_ecdh_encrypt (resarr, pk_fp, data, pkey); } else return GPG_ERR_PUBKEY_ALGO; @@ -235,11 +173,11 @@ pk_encrypt (int algo, gcry_mpi_t * resarr, gcry_mpi_t data, const byte pk_fp[MAX 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); @@ -247,9 +185,11 @@ pk_encrypt (int algo, gcry_mpi_t * resarr, gcry_mpi_t data, const byte pk_fp[MAX 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 && algo != PUBKEY_ALGO_ECDH) + if (algo != GCRY_PK_RSA + && algo != GCRY_PK_RSA_E + && algo != PUBKEY_ALGO_ECDH) resarr[1] = mpi_from_sexp (s_ciph, "b"); } @@ -257,6 +197,7 @@ pk_encrypt (int algo, gcry_mpi_t * resarr, gcry_mpi_t data, const byte pk_fp[MAX return rc; } + /* Check whether SKEY is a suitable secret key. */ int pk_check_secret_key (int algo, gcry_mpi_t *skey) diff --git a/g10/pkglue.h b/g10/pkglue.h index a1c821dcd..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. * @@ -22,21 +22,19 @@ gcry_mpi_t mpi_from_sexp (gcry_sexp_t sexp, const char * item); -int pk_sign (int algo, gcry_mpi_t *data, gcry_mpi_t hash, - gcry_mpi_t *skey); 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, const byte fp[MAX_FINGERPRINT_LEN], 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); +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 ); +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 24411e8a1..ddca41ec4 100644 --- a/g10/pubkey-enc.c +++ b/g10/pubkey-enc.c @@ -218,68 +218,72 @@ get_it (PKT_pubkey_enc *enc, DEK *dek, PKT_public_key *sk, u32 *keyid) log_printhex ("DEK frame:", frame, nframe); n = 0; - if( sk->pubkey_algo != PUBKEY_ALGO_ECDH ) { - if (!card) - { - if (n + 7 > nframe) - { - err = gpg_error (G10ERR_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 (G10ERR_CIPHER_ALGO); - goto leave; - } - if (frame[n] != 2) /* Something went wrong. */ - { - err = gpg_error (G10ERR_WRONG_SECKEY); - goto leave; - } - for (n++; n < nframe && frame[n]; n++) /* Skip the random bytes. */ - ; - n++; /* Skip the zero byte. */ - } - } - else { - gcry_mpi_t shared_mpi; - gcry_mpi_t decoded; - - /* at the beginning the frame is the bytes of shared point MPI */ - - err = gcry_mpi_scan (&shared_mpi, GCRYMPI_FMT_USG, frame, nframe, NULL); - if (err) { - log_fatal ("mpi_scan failed: %s\n", gpg_strerror (err)); - goto leave; - } + if (sk->pubkey_algo == PUBKEY_ALGO_ECDH) + { + 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) + { + log_fatal ("mpi_scan failed: %s\n", gpg_strerror (err)); + goto leave; + } - 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; + 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; + /* 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 is the bytes decrypted but padded session key */ + /* 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 ) { - err = G10ERR_WRONG_SECKEY; goto leave; + /* Allow double padding for the benefit of DEK size concealment. + Higher than this is wasteful. */ + if (frame[nframe-1] > 8*2 || nframe <= 8) + { + err = gpg_error (GPG_ERR_WRONG_SECKEY); + goto leave; + } + nframe -= frame[nframe-1]; /* Remove padding. */ + assert (n); /* (used just below) */ + } + else + { + if (!card) + { + 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. */ + } } - nframe -= frame[nframe-1]; /* remove padding */ - assert( n==0 ); /* used just bellow */ - } 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 e50cf5c02..fa6765dc6 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. * @@ -76,109 +76,125 @@ make_session_key( DEK *dek ) gcry_mpi_t 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 = 0; - gcry_mpi_t a; - - if( DBG_CIPHER ) - log_debug("encode_session_key: encoding %d byte DEK", dek->keylen); - - 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); - assert( !(nframe%8) && nframe > 1 + dek->keylen + 2 ); /* alg+key+csum fit and the size is congruent to 8 */ - 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 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", 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; + 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++; + + /* 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; } @@ -192,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, algo=%d\n", - (int)(len*8), (int)nbits, algo); + 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: * @@ -240,26 +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 gcry_pkalgo; + int pkalgo; assert (hash_algo); assert (pk); - gcry_pkalgo = map_pk_openpgp_to_gcry( pk->pubkey_algo ); + pkalgo = map_pk_openpgp_to_gcry (pk->pubkey_algo); - if (gcry_pkalgo == GCRY_PK_DSA || gcry_pkalgo == GCRY_PK_ECDSA ) + 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 */ - if( gcry_pkalgo==GCRY_PK_ECDSA ) - qbytes = ecdsa_qbits_from_Q( qbytes ); - + /* pkey[1] is Q for ECDSA, which is an uncompressed point, + i.e. 04 */ + 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")); @@ -275,30 +292,38 @@ encode_md_value (PKT_public_key *pk, gcry_md_hd_t md, int hash_algo) if (qbytes < 160) { log_error (_("%s key %s uses an unsafe (%zu bit) hash\n"), - gcry_pk_algo_name( gcry_pkalgo ), - keystr_from_pk (pk), qbytes); + 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. */ - /* 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) < ((gcry_pkalgo==GCRY_PK_ECDSA && qbytes>(521)/8) ? 512/8 : 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 (_("%s key %s requires a %zu bit or larger hash, used hash-algo=%d\n"), - gcry_pk_algo_name( gcry_pkalgo ), - keystr_from_pk(pk), qbytes*8, hash_algo); + 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; } - /* Note that in case of ECDSA 521 hash is always smaller than the key size */ + /* Note that in case of ECDSA 521 hash is always smaller than + the key size. */ if (gcry_mpi_scan (&frame, GCRYMPI_FMT_USG, - gcry_md_read (md, hash_algo), gcry_md_get_algo_dlen (hash_algo), &qbytes)) + gcry_md_read (md, hash_algo), + gcry_md_get_algo_dlen (hash_algo), &qbytes)) BUG(); } else diff --git a/g10/sign.c b/g10/sign.c index ccf796446..30dc66d5f 100644 --- a/g10/sign.c +++ b/g10/sign.c @@ -436,14 +436,15 @@ hash_for (PKT_public_key *pk) { return recipient_digest_algo; } - else if(pk->pubkey_algo==PUBKEY_ALGO_DSA || pk->pubkey_algo==PUBKEY_ALGO_ECDSA ) + else if (pk->pubkey_algo == PUBKEY_ALGO_DSA + || pk->pubkey_algo == PUBKEY_ALGO_ECDSA) { unsigned int qbytes = gcry_mpi_get_nbits (pk->pkey[1]); - if( pk->pubkey_algo==PUBKEY_ALGO_ECDSA ) - qbytes = ecdsa_qbits_from_Q(qbytes); + 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 @@ -924,12 +925,14 @@ 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 || sk_rover->pk->pubkey_algo == PUBKEY_ALGO_ECDSA ) + 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]); + 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 ); + 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 @@ -1482,13 +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) - 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 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 index d1f0aa105..c4c657b9f 100644 --- a/g10/verify-stubs.c +++ b/g10/verify-stubs.c @@ -1,6 +1,5 @@ /* To satisfy the linker for the gpgv target - * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2005, 2006, - * 2007 Free Software Foundation, Inc. + * Copyright (C) 2010 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -25,6 +24,8 @@ #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; +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; } -- cgit v1.2.3 From 27929981fc23fabecf6af9fa1361361b821bb2fd Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Fri, 21 Jan 2011 15:22:41 +0100 Subject: Make most of the selftests work. Note that there is still a problem with tests/openpgp/sigs.test while using the option --digest-algo SHA256. --- g10/ChangeLog | 4 ++++ g10/misc.c | 20 +------------------- g10/sign.c | 3 +++ 3 files changed, 8 insertions(+), 19 deletions(-) (limited to 'g10') diff --git a/g10/ChangeLog b/g10/ChangeLog index 0c8cbd418..75415f466 100644 --- a/g10/ChangeLog +++ b/g10/ChangeLog @@ -1,3 +1,7 @@ +2011-01-21 Werner Koch + + * misc.c (openpgp_pk_algo_name): Always use the gcrypt function. + 2010-12-09 Werner Koch * tdbio.c (tdbio_set_dbname) [W32CE]: Take care of missing errno. diff --git a/g10/misc.c b/g10/misc.c index 6f77119fe..bdd797c16 100644 --- a/g10/misc.c +++ b/g10/misc.c @@ -503,25 +503,7 @@ openpgp_pk_algo_usage ( int algo ) const char * openpgp_pk_algo_name (int algo) { - /* We use fixed strings to have pretty names instead of those from - libgcrypt. */ - 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"; - - case PUBKEY_ALGO_ECDSA:return "ecdsa"; - - case PUBKEY_ALGO_ECDH: return "ecdh"; - - default: gcry_pk_algo_name (map_pk_openpgp_to_gcry (algo)); - } + return gcry_pk_algo_name (map_pk_openpgp_to_gcry (algo)); } diff --git a/g10/sign.c b/g10/sign.c index 30dc66d5f..cbb3c62e8 100644 --- a/g10/sign.c +++ b/g10/sign.c @@ -298,6 +298,9 @@ do_sign (PKT_public_key *pksk, PKT_signature *sig, { PKT_public_key *pk = xmalloc_clear (sizeof *pk); + log_debug ("checking created signature algo=%d\n", mdalgo); + log_printhex ("md:", dp, gcry_md_get_algo_dlen (mdalgo)); + if (get_pubkey (pk, sig->keyid )) err = gpg_error (GPG_ERR_NO_PUBKEY); else -- cgit v1.2.3 From c3db7705c049e31e678ff87e230b8160aa0027f1 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Fri, 21 Jan 2011 15:58:07 +0100 Subject: Truncate the DSA hash; fixes regression. Removed left over debug code. --- g10/ChangeLog | 2 ++ g10/seskey.c | 10 ++++++---- g10/sign.c | 3 --- 3 files changed, 8 insertions(+), 7 deletions(-) (limited to 'g10') diff --git a/g10/ChangeLog b/g10/ChangeLog index 75415f466..8e79587d8 100644 --- a/g10/ChangeLog +++ b/g10/ChangeLog @@ -1,5 +1,7 @@ 2011-01-21 Werner Koch + * 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 diff --git a/g10/seskey.c b/g10/seskey.c index fa6765dc6..2d7918d39 100644 --- a/g10/seskey.c +++ b/g10/seskey.c @@ -319,11 +319,13 @@ encode_md_value (PKT_public_key *pk, gcry_md_hd_t md, int hash_algo) return NULL; } - /* Note that in case of ECDSA 521 hash is always smaller than - the key size. */ + /* 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), - gcry_md_get_algo_dlen (hash_algo), &qbytes)) + gcry_md_read (md, hash_algo), qbytes, &qbytes)) BUG(); } else diff --git a/g10/sign.c b/g10/sign.c index cbb3c62e8..30dc66d5f 100644 --- a/g10/sign.c +++ b/g10/sign.c @@ -298,9 +298,6 @@ do_sign (PKT_public_key *pksk, PKT_signature *sig, { PKT_public_key *pk = xmalloc_clear (sizeof *pk); - log_debug ("checking created signature algo=%d\n", mdalgo); - log_printhex ("md:", dp, gcry_md_get_algo_dlen (mdalgo)); - if (get_pubkey (pk, sig->keyid )) err = gpg_error (GPG_ERR_NO_PUBKEY); else -- cgit v1.2.3 From b3adbb576e88a12ee9e7ea790a72ad5f1bff4c78 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Mon, 24 Jan 2011 12:02:35 +0100 Subject: Fix regression introduced by "editing only change". Signing and verification using a new key works again. --- g10/parse-packet.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'g10') diff --git a/g10/parse-packet.c b/g10/parse-packet.c index 5df336e55..334a9a82b 100644 --- a/g10/parse-packet.c +++ b/g10/parse-packet.c @@ -1953,7 +1953,7 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen, else { /* Fill in public key parameters. */ - if (algorithm == PUBKEY_ALGO_ECDSA && algorithm == PUBKEY_ALGO_ECDH) + if (algorithm == PUBKEY_ALGO_ECDSA || algorithm == PUBKEY_ALGO_ECDH) { /* FIXME: The code in this function ignores the errors. */ byte name_oid[256]; -- cgit v1.2.3 From 638dca5dbc7e119ff5a05dbdb109fbc171624605 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Tue, 25 Jan 2011 16:54:18 +0100 Subject: Editorial cleanups of keygen.c Also fixed a regression introduced by me in pubkey_enc.c. Added extra checks. Removed unused code. --- g10/ChangeLog | 18 +++ g10/Makefile.am | 3 +- g10/keygen.c | 331 +++++++++++++++++++++-------------------------------- g10/main.h | 5 - g10/pubkey-enc.c | 29 ++--- g10/verify-stubs.c | 31 ----- 6 files changed, 163 insertions(+), 254 deletions(-) delete mode 100644 g10/verify-stubs.c (limited to 'g10') diff --git a/g10/ChangeLog b/g10/ChangeLog index 4c28363a2..801146459 100644 --- a/g10/ChangeLog +++ b/g10/ChangeLog @@ -1,3 +1,21 @@ +2011-01-25 Werner Koch + + * pubkey-enc.c (get_it): Fix assertion. + Use GPG_ERR_WRONG_SECKEY instead of log_fatal. Add safety checks + for NFRAME. + + * main.h (KEYGEN_FLAG_NO_PROTECTION, KEYGEN_FLAG_TRANSIENT_KEY): + Move back to .. + * keygen.c: .. here. + (pk_ecc_keypair_gen): Make static. + (common_key_gen): Fold back into .. + (common_gen): .. this. + (delme__pk_ecc_build_sexp): Remove unused function. + (pk_ecc_keypair_gen): Fold it into .. + (gen_ecc): .. this. + (ask_keysize): Use proper rounding for ECC. + * verify-stubs.c: Remove. + 2011-01-20 Werner Koch * keyserver.c: Rewrite most stuff for use with dirmngr. Get rid diff --git a/g10/Makefile.am b/g10/Makefile.am index 5d2470207..a28b2ab50 100644 --- a/g10/Makefile.am +++ b/g10/Makefile.am @@ -111,8 +111,7 @@ gpg2_SOURCES = gpg.c \ gpgv2_SOURCES = gpgv.c \ $(common_source) \ - verify.c \ - verify-stubs.c + verify.c #gpgd_SOURCES = gpgd.c \ # ks-proto.h \ diff --git a/g10/keygen.c b/g10/keygen.c index e75da792e..366bedf0e 100644 --- a/g10/keygen.c +++ b/g10/keygen.c @@ -1,6 +1,6 @@ /* keygen.c - generate a key pair - * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, - * 2006, 2007, 2009, 2010 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 + * 2007, 2009, 2010, 2011 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -18,7 +18,6 @@ * along with this program; if not, see . */ -#warning wk: check these changes. #include #include #include @@ -51,6 +50,11 @@ #define DEFAULT_STD_ALGO GCRY_PK_RSA #define DEFAULT_STD_KEYSIZE 2048 +/* Flag bits used during key generation. */ +#define KEYGEN_FLAG_NO_PROTECTION 1 +#define KEYGEN_FLAG_TRANSIENT_KEY 2 + +/* Maximum number of supported algorithm preferences. */ #define MAX_PREFS 30 enum para_name { @@ -1128,16 +1132,17 @@ key_from_sexp (gcry_mpi_t *array, gcry_sexp_t sexp, } +/* Common code for the key generation fucntion gen_xxx. */ static int -common_key_gen (const char *keyparms, int algo, const char *algoelem, - int keygen_flags, char **cache_nonce_addr, PKT_public_key **pk_out) +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) { 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); if (err) @@ -1154,7 +1159,10 @@ common_key_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); @@ -1167,45 +1175,21 @@ common_key_gen (const char *keyparms, int algo, const char *algoelem, } gcry_sexp_release (s_key); - *pk_out = pk; + 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 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. @@ -1343,40 +1327,14 @@ 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 ) { +/* Create an S-expression string out QBITS, ALGO and the TRANSIENT + flag. On success a malloced string is returned, on failure NULL + and ERRNO is set. */ +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]; @@ -1384,142 +1342,105 @@ pk_ecc_build_key_params( int qbits, int algo, int transient ) { 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 ); + /* 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 (algo == PUBKEY_ALGO_ECDSA || !kek_params) + { + 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); } - if( keyparms == NULL ) { - xfree( kek_params ); - return NULL; + else + { + assert (kek_params); + + keyparms = xtryasprintf ("(genkey(ecdh(nbits %zu:%s)" + /**/ "(qbits %zu:%s)" + /**/ "(transient-key 1:%d)" + /**/ "(kek-params %zu:", + strlen (nbitsstr), nbitsstr, + strlen (qbitsstr), qbitsstr, + transient, + kek_params_size); + if (keyparms) + { + n = strlen (keyparms); + keyparms = xtryrealloc (keyparms, n + kek_params_size + 4); + } + if (!keyparms) + { + xfree (kek_params); + return NULL; + } + memcpy (keyparms+n, kek_params, kek_params_size); + xfree (kek_params); + memcpy (keyparms+n+kek_params_size, ")))", 4); } - 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) { + +/* + * Generate an ECC 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 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 ); + assert (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_ECDH); - *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 (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_error ("broken version of Libgcrypt\n"); + return gpg_error (GPG_ERR_INTERNAL); /* ABI silently changed. */ + } - if ( nbits != 256 && nbits != 384 && nbits != 521 ) + if (nbits != 256 && nbits != 384 && nbits != 521) { - log_info(_("keysize invalid; using 256 bits instead of passed in %d\n"), nbits ); + log_info (_("keysize invalid; using %u bits\n"), 256); + /* FIXME: Where do we set it to 256? */ } - /* - 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 - */ + /* 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) ); - } + 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 ("ecc 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); + err = common_gen (keyparms, algo, + algo == PUBKEY_ALGO_ECDSA? "cq" : "cqp", + pub_root, timestamp, expireval, is_subkey, + keygen_flags, cache_nonce_addr); 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; } @@ -1755,7 +1676,7 @@ ask_algo (int addmode, int *r_subkey_algo, unsigned int *r_usage) } tty_printf (_(" (%d) ECDSA and ECDH\n"), 9 ); - + for(;;) { *r_usage = 0; @@ -1877,7 +1798,7 @@ ask_keysize (int algo, unsigned int primary_keysize) tty_printf(_("%s keys may be between %u and %u bits long.\n"), openpgp_pk_algo_name (algo), min, max); - for(;;) + for (;;) { char *prompt, *answer; @@ -1899,28 +1820,34 @@ ask_keysize (int algo, unsigned int primary_keysize) break; } - tty_printf(_("Requested keysize is %u bits\n"), nbits ); + tty_printf (_("Requested keysize is %u bits\n"), nbits); leave: - if( algo == PUBKEY_ALGO_DSA && (nbits % 64) ) + if (algo == PUBKEY_ALGO_DSA && (nbits % 64)) { - if( !(algo == PUBKEY_ALGO_ECDSA && nbits==521) ) { - nbits = ((nbits + 63) / 64) * 64; - if (!autocomp) - tty_printf(_("rounded up to %u bits\n"), nbits ); - } + 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) ) + else if (algo == PUBKEY_ALGO_ECDH || algo == PUBKEY_ALGO_ECDSA) + { + if (nbits != 256 && nbits != 384 && nbits != 521) + { + if (nbits < 256) + nbits = 256; + else if (nbits < 384) + nbits = 384; + else + nbits = 521; + if (!autocomp) + tty_printf (_("rounded to %u bits\n"), nbits); + } + } + else if ((nbits % 32)) { nbits = ((nbits + 31) / 32) * 32; if (!autocomp) - tty_printf(_("rounded up to %u bits\n"), nbits ); + tty_printf (_("rounded up to %u bits\n"), nbits ); } return nbits; @@ -2405,7 +2332,7 @@ 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 ) + 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) diff --git a/g10/main.h b/g10/main.h index 1b6f30516..ea1ac2e1f 100644 --- a/g10/main.h +++ b/g10/main.h @@ -260,11 +260,6 @@ 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 ); diff --git a/g10/pubkey-enc.c b/g10/pubkey-enc.c index ddca41ec4..43f5419db 100644 --- a/g10/pubkey-enc.c +++ b/g10/pubkey-enc.c @@ -145,18 +145,17 @@ 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]; + byte fp[MAX_FINGERPRINT_LEN]; size_t fpn; - const int gcry_pkalgo = map_pk_openpgp_to_gcry( sk->pubkey_algo ); + const int 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 (gcry_pkalgo == GCRY_PK_ELG ||gcry_pkalgo == GCRY_PK_ELG_E) + if (pkalgo == GCRY_PK_ELG || pkalgo == GCRY_PK_ELG_E) { if (!enc->data[0] || !enc->data[1]) err = gpg_error (GPG_ERR_BAD_MPI); @@ -164,7 +163,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 (gcry_pkalgo == GCRY_PK_RSA || gcry_pkalgo == GCRY_PK_RSA_E) + else if (pkalgo == GCRY_PK_RSA || pkalgo == GCRY_PK_RSA_E) { if (!enc->data[0]) err = gpg_error (GPG_ERR_BAD_MPI); @@ -172,12 +171,12 @@ 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 ) + else if (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)))", + err = gcry_sexp_build (&s_data, NULL, "(enc-val(ecdh(a%m)(b%m)))", enc->data[0], enc->data[1]); } else @@ -186,8 +185,9 @@ get_it (PKT_pubkey_enc *enc, DEK *dek, PKT_public_key *sk, u32 *keyid) if (err) goto leave; - fingerprint_from_pk( sk, fp, &fpn ); - assert( fpn == 20 ); + /* fixme: only needed for ECDH. Don't compute always. */ + fingerprint_from_pk (sk, fp, &fpn); + assert (fpn == 20); /* Decrypt. */ desc = gpg_format_keydesc (sk, 0, 1); @@ -222,12 +222,12 @@ get_it (PKT_pubkey_enc *enc, DEK *dek, PKT_public_key *sk, u32 *keyid) { 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) { - log_fatal ("mpi_scan failed: %s\n", gpg_strerror (err)); + err = gpg_error (GPG_ERR_WRONG_SECKEY); goto leave; } @@ -247,13 +247,14 @@ get_it (PKT_pubkey_enc *enc, DEK *dek, PKT_public_key *sk, u32 *keyid) /* Allow double padding for the benefit of DEK size concealment. Higher than this is wasteful. */ - if (frame[nframe-1] > 8*2 || nframe <= 8) + if (!nframe || frame[nframe-1] > 8*2 || nframe <= 8 + || frame[nframe-1] > nframe) { err = gpg_error (GPG_ERR_WRONG_SECKEY); - goto leave; + goto leave; } nframe -= frame[nframe-1]; /* Remove padding. */ - assert (n); /* (used just below) */ + assert (!n); /* (used just below) */ } else { diff --git a/g10/verify-stubs.c b/g10/verify-stubs.c deleted file mode 100644 index c4c657b9f..000000000 --- a/g10/verify-stubs.c +++ /dev/null @@ -1,31 +0,0 @@ -/* 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 . - */ - -#include - -#include -#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; -} -- cgit v1.2.3 From 302c5a826c0fd0b2aab85ad3c287b65429db2066 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Tue, 25 Jan 2011 17:48:51 +0100 Subject: More ECDH code cleanups --- g10/ChangeLog | 6 ++++ g10/ecdh.c | 104 +++++++++++++++++----------------------------------------- g10/keygen.c | 57 ++++++++++++++++++-------------- g10/pkglue.h | 6 ++-- 4 files changed, 72 insertions(+), 101 deletions(-) (limited to 'g10') diff --git a/g10/ChangeLog b/g10/ChangeLog index 801146459..3ce4d1fac 100644 --- a/g10/ChangeLog +++ b/g10/ChangeLog @@ -1,5 +1,9 @@ 2011-01-25 Werner Koch + * ecdh.c (pk_ecdh_default_params_to_mpi): Remove. + (pk_ecdh_default_params): Rewrite. Factor KEK table out to .. + (kek_params_table): .. here. + * pubkey-enc.c (get_it): Fix assertion. Use GPG_ERR_WRONG_SECKEY instead of log_fatal. Add safety checks for NFRAME. @@ -14,6 +18,8 @@ (pk_ecc_keypair_gen): Fold it into .. (gen_ecc): .. this. (ask_keysize): Use proper rounding for ECC. + (pk_ecc_build_key_params): Remove NBITSSTR. + * verify-stubs.c: Remove. 2011-01-20 Werner Koch diff --git a/g10/ecdh.c b/g10/ecdh.c index cb251fef2..1fa36aa21 100644 --- a/g10/ecdh.c +++ b/g10/ecdh.c @@ -30,26 +30,15 @@ #include "main.h" #include "options.h" -gcry_mpi_t -pk_ecdh_default_params_to_mpi (int qbits) +/* A table with the default KEK parameters used by GnuPG. */ +static const struct { - 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[] = { + unsigned int qbits; + int openpgp_hash_id; /* KEK digest algorithm. */ + int openpgp_cipher_id; /* KEK cipher algorithm. */ +} kek_params_table[] = + /* Note: Must be sorted by ascending values for QBITS. */ + { { 256, DIGEST_ALGO_SHA256, CIPHER_ALGO_AES }, { 384, DIGEST_ALGO_SHA384, CIPHER_ALGO_AES256 }, @@ -57,77 +46,44 @@ pk_ecdh_default_params_to_mpi (int qbits) { 528, DIGEST_ALGO_SHA512, CIPHER_ALGO_AES256 } }; - for (i=0; i= 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. - */ + sizeout. The caller must free the returned value. Returns NULL + and sets ERRNO on error. */ byte * -pk_ecdh_default_params (int qbits, size_t *sizeout) +pk_ecdh_default_params (unsigned 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 */ - }; + byte kek_params[4]; 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; + byte *buffer; - *sizeout = 0; + kek_params[0] = 3; /* Number of bytes to follow. */ + kek_params[1] = 1; /* Version for KDF+AESWRAP. */ - for (i=0; i= qbits) + if (kek_params_table[i].qbits >= qbits + || i+1 == DIM (kek_params_table)) { 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) + assert (i < DIM (kek_params_table)); + if (DBG_CIPHER) + log_printhex ("ECDH KEK params are", kek_params, sizeof(kek_params) ); + + buffer = xtrymalloc (sizeof(kek_params)); + if (!buffer) return NULL; - memcpy (p, kek_params, sizeof(kek_params)); - *sizeout = sizeof(kek_params); - return p; + memcpy (buffer, kek_params, sizeof (kek_params)); + *sizeout = sizeof (kek_params); + return buffer; } diff --git a/g10/keygen.c b/g10/keygen.c index 366bedf0e..b42121b28 100644 --- a/g10/keygen.c +++ b/g10/keygen.c @@ -1329,7 +1329,7 @@ gen_dsa (unsigned int nbits, KBNODE pub_root, -/* Create an S-expression string out QBITS, ALGO and the TRANSIENT +/* Create an S-expression string out of QBITS, ALGO and the TRANSIENT flag. On success a malloced string is returned, on failure NULL and ERRNO is set. */ static char * @@ -1337,56 +1337,63 @@ 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; + char *result; + size_t 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); + { + kek_params = pk_ecdh_default_params (qbits, &kek_params_size); + if (!kek_params) + return NULL; + } else kek_params = NULL; - snprintf (nbitsstr, sizeof nbitsstr, "%u", qbits); snprintf (qbitsstr, sizeof qbitsstr, "%u", qbits); if (algo == PUBKEY_ALGO_ECDSA || !kek_params) { - keyparms = xtryasprintf ("(genkey(%s(nbits %zu:%s)" + result = 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, strlen (qbitsstr), qbitsstr, transient); } else { - assert (kek_params); + char *tmpstr; - keyparms = xtryasprintf ("(genkey(ecdh(nbits %zu:%s)" - /**/ "(qbits %zu:%s)" - /**/ "(transient-key 1:%d)" - /**/ "(kek-params %zu:", - strlen (nbitsstr), nbitsstr, - strlen (qbitsstr), qbitsstr, - transient, - kek_params_size); - if (keyparms) + assert (kek_params); + tmpstr = xtryasprintf ("(genkey(ecdh(nbits %zu:%s)" + /**/ "(qbits %zu:%s)" + /**/ "(transient-key 1:%d)" + /**/ "(kek-params %zu:", + strlen (qbitsstr), qbitsstr, + strlen (qbitsstr), qbitsstr, + transient, + kek_params_size); + if (!tmpstr) { - n = strlen (keyparms); - keyparms = xtryrealloc (keyparms, n + kek_params_size + 4); + xfree (kek_params); + return NULL; } - if (!keyparms) + /* Append the binary KEK parmas. */ + n = strlen (tmpstr); + result = xtryrealloc (tmpstr, n + kek_params_size + 4); + if (!result) { + xfree (tmpstr); xfree (kek_params); return NULL; } - memcpy (keyparms+n, kek_params, kek_params_size); - xfree (kek_params); - memcpy (keyparms+n+kek_params_size, ")))", 4); + memcpy (result + n, kek_params, kek_params_size); + strcpy (result + n + kek_params_size, ")))"); } - return keyparms; + xfree (kek_params); + return result; } diff --git a/g10/pkglue.h b/g10/pkglue.h index 0ceb43f55..b1cfbe507 100644 --- a/g10/pkglue.h +++ b/g10/pkglue.h @@ -20,6 +20,7 @@ #ifndef GNUPG_G10_PKGLUE_H #define GNUPG_G10_PKGLUE_H +/*-- pkglue.c --*/ 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, @@ -29,12 +30,13 @@ int pk_encrypt (int algo, gcry_mpi_t *resarr, gcry_mpi_t data, gcry_mpi_t *pkey); int pk_check_secret_key (int algo, gcry_mpi_t *skey); + +/*-- ecdh.c --*/ +byte *pk_ecdh_default_params (unsigned int qbits, size_t *sizeout); 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*/ -- cgit v1.2.3 From d879c287ac1da7990c97b911018d63410c60433c Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Tue, 25 Jan 2011 20:28:25 +0100 Subject: Started with some code cleanups in ECDH. The goal is to have the ECDH code more uniform with the other algorithms. Also make error messages and variable names more similar to other places. --- g10/ChangeLog | 3 + g10/ecdh.c | 212 +++++++++++++++++++++++++--------------------------------- g10/misc.c | 2 +- g10/pkglue.c | 70 +++++++++++++++---- g10/pkglue.h | 7 ++ 5 files changed, 158 insertions(+), 136 deletions(-) (limited to 'g10') diff --git a/g10/ChangeLog b/g10/ChangeLog index 3ce4d1fac..9e1aa01da 100644 --- a/g10/ChangeLog +++ b/g10/ChangeLog @@ -3,6 +3,9 @@ * ecdh.c (pk_ecdh_default_params_to_mpi): Remove. (pk_ecdh_default_params): Rewrite. Factor KEK table out to .. (kek_params_table): .. here. + (pk_ecdh_generate_ephemeral_key): New. + (pk_ecdh_encrypt): Remove. + (pk_ecdh_encrypt_with_shared_point): Make public. * pubkey-enc.c (get_it): Fix assertion. Use GPG_ERR_WRONG_SECKEY instead of log_fatal. Add safety checks diff --git a/g10/ecdh.c b/g10/ecdh.c index 1fa36aa21..71c32fd5d 100644 --- a/g10/ecdh.c +++ b/g10/ecdh.c @@ -87,20 +87,26 @@ pk_ecdh_default_params (unsigned int qbits, size_t *sizeout) } -/* 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). +/* Encrypts/decrypts DATA using a key derived from the ECC shared + point SHARED_MPI using the FIPS SP 800-56A compliant method + key_derivation+key_wrapping. If IS_ENCRYPT is true the function + encrypts; if false, it decrypts. On success the result is stored + at R_RESULT; on failure NULL is stored at R_RESULT and an error + code returned. + + FIXME: explain PKEY and PK_FP. */ -static int + +/* + TODO: memory leaks (x_secret). +*/ +gpg_error_t 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) + gcry_mpi_t *r_result) { + gpg_error_t err; byte *secret_x; int secret_x_size; byte kdf_params[256]; @@ -108,47 +114,54 @@ pk_ecdh_encrypt_with_shared_point (int is_encrypt, gcry_mpi_t shared_mpi, int nbits; int kdf_hash_algo; int kdf_encr_algo; - int rc; - *out = NULL; + *r_result = NULL; - nbits = pubkey_nbits( PUBKEY_ALGO_ECDH, pkey ); + nbits = pubkey_nbits (PUBKEY_ALGO_ECDH, pkey); + if (!nbits) + return gpg_error (GPG_ERR_TOO_SHORT); { size_t nbytes; + /* Extract x component of the shared point: this is the actual - shared secret */ + 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) + secret_x = xtrymalloc_secure (nbytes); + if (!secret_x) + return gpg_error_from_syserror (); + + err = gcry_mpi_print (GCRYMPI_FMT_USG, secret_x, nbytes, + &nbytes, shared_mpi); + if (err) { xfree (secret_x); - log_error ("ec ephemeral export of shared point failed: %s\n", - gpg_strerror (rc)); - return rc; + log_error ("ECDH ephemeral export of shared point failed: %s\n", + gpg_strerror (err)); + return err; } + + /* fixme: explain what we are doing. */ 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 ); + 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 + * 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 */ + err = iobuf_write_size_body_mpi ( obuf, pkey[2] ); /* KEK params */ kdf_params_size = iobuf_temp_to_buffer (obuf, kdf_params, sizeof(kdf_params)); @@ -185,11 +198,11 @@ pk_ecdh_encrypt_with_shared_point (int is_encrypt, gcry_mpi_t shared_mpi, obuf = iobuf_temp(); /* variable-length field 1, curve name OID */ - rc = iobuf_write_size_body_mpi ( obuf, pkey[0] ); + err = 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] )); + err = (err ? err : iobuf_write_size_body_mpi ( obuf, pkey[2] )); /* fixed-length field 4 */ iobuf_write (obuf, "Anonymous Sender ", 20); /* fixed-length field 5, recipient fp */ @@ -198,8 +211,8 @@ pk_ecdh_encrypt_with_shared_point (int is_encrypt, gcry_mpi_t shared_mpi, kdf_params_size = iobuf_temp_to_buffer (obuf, kdf_params, sizeof(kdf_params)); iobuf_close (obuf); - if (rc) - return rc; + if (err) + return err; if(DBG_CIPHER) log_printhex ("ecdh KDF message params are:", @@ -211,10 +224,10 @@ pk_ecdh_encrypt_with_shared_point (int is_encrypt, gcry_mpi_t shared_mpi, gcry_md_hd_t h; int old_size; - rc = gcry_md_open (&h, kdf_hash_algo, 0); - if(rc) + err = gcry_md_open (&h, kdf_hash_algo, 0); + if(err) log_bug ("gcry_md_open failed for algo %d: %s", - kdf_hash_algo, gpg_strerror (gcry_error(rc))); + kdf_hash_algo, gpg_strerror (gcry_error(err))); 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 */ @@ -248,22 +261,22 @@ pk_ecdh_encrypt_with_shared_point (int is_encrypt, gcry_mpi_t shared_mpi, gcry_mpi_t result; - rc = gcry_cipher_open (&hd, kdf_encr_algo, GCRY_CIPHER_MODE_AESWRAP, 0); - if (rc) + err = gcry_cipher_open (&hd, kdf_encr_algo, GCRY_CIPHER_MODE_AESWRAP, 0); + if (err) { log_error ("ecdh failed to initialize AESWRAP: %s\n", - gpg_strerror (rc)); - return rc; + gpg_strerror (err)); + return err; } - rc = gcry_cipher_setkey (hd, secret_x, secret_x_size); + err = gcry_cipher_setkey (hd, secret_x, secret_x_size); xfree( secret_x ); - if (rc) + if (err) { gcry_cipher_close (hd); log_error ("ecdh failed in gcry_cipher_setkey: %s\n", - gpg_strerror (rc)); - return rc; + gpg_strerror (err)); + return err; } data_buf_size = (gcry_mpi_get_nbits(data)+7)/8; @@ -282,52 +295,52 @@ pk_ecdh_encrypt_with_shared_point (int is_encrypt, gcry_mpi_t shared_mpi, /* Write data MPI into the end of data_buf. data_buf is size aeswrap data. */ - rc = gcry_mpi_print (GCRYMPI_FMT_USG, in, + err = gcry_mpi_print (GCRYMPI_FMT_USG, in, data_buf_size, &nbytes, data/*in*/); - if (rc) + if (err) { - log_error ("ecdh failed to export DEK: %s\n", gpg_strerror (rc)); + log_error ("ecdh failed to export DEK: %s\n", gpg_strerror (err)); gcry_cipher_close (hd); xfree (data_buf); - return rc; + return err; } if (DBG_CIPHER) log_printhex ("ecdh encrypting :", in, data_buf_size ); - rc = gcry_cipher_encrypt (hd, data_buf+1, data_buf_size+8, + err = 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) + if (err) { log_error ("ecdh failed in gcry_cipher_encrypt: %s\n", - gpg_strerror (rc)); + gpg_strerror (err)); xfree (data_buf); - return rc; + return err; } 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, + err = gcry_mpi_scan (&result, GCRYMPI_FMT_USG, data_buf, 1+data_buf[0], NULL); /* (byte)size + aeswrap of DEK */ xfree( data_buf ); - if (rc) + if (err) { - log_error ("ecdh failed to create an MPI: %s\n", gpg_strerror (rc)); - return rc; + log_error ("ecdh failed to create an MPI: %s\n", gpg_strerror (err)); + return err; } - *out = result; + *r_result = result; } else { byte *in; - rc = gcry_mpi_print (GCRYMPI_FMT_USG, data_buf, data_buf_size, + err = 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) { @@ -341,15 +354,15 @@ pk_ecdh_encrypt_with_shared_point (int is_encrypt, gcry_mpi_t shared_mpi, 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, + err = gcry_cipher_decrypt (hd, in, data_buf_size, data_buf+1, data_buf_size); gcry_cipher_close (hd); - if (rc) + if (err) { log_error ("ecdh failed in gcry_cipher_decrypt: %s\n", - gpg_strerror (rc)); + gpg_strerror (err)); xfree (data_buf); - return rc; + return err; } data_buf_size -= 8; @@ -365,20 +378,20 @@ pk_ecdh_encrypt_with_shared_point (int is_encrypt, gcry_mpi_t shared_mpi, /* return GPG_ERR_BAD_KEY; */ /* } */ - rc = gcry_mpi_scan ( &result, GCRYMPI_FMT_USG, in, data_buf_size, NULL); + err = gcry_mpi_scan ( &result, GCRYMPI_FMT_USG, in, data_buf_size, NULL); xfree (data_buf); - if (rc) + if (err) { log_error ("ecdh failed to create a plain text MPI: %s\n", - gpg_strerror (rc)); - return rc; + gpg_strerror (err)); + return err; } - *out = result; + *r_result = result; } } - return rc; + return err; } @@ -405,76 +418,31 @@ gen_k (unsigned nbits) 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; +/* Generate an ephemeral key for the public ECDH key in PKEY. On + success the generated key is stored at R_K; on failure NULL is + stored at R_K and an error code returned. */ +gpg_error_t +pk_ecdh_generate_ephemeral_key (gcry_mpi_t *pkey, gcry_mpi_t *r_k) +{ + unsigned int nbits; gcry_mpi_t k; - nbits = pubkey_nbits (PUBKEY_ALGO_ECDH, pkey); - - /*** Generate an ephemeral key, actually, a scalar. ***/ + *r_k = NULL; + nbits = pubkey_nbits (PUBKEY_ALGO_ECDH, pkey); + if (!nbits) + return gpg_error (GPG_ERR_TOO_SHORT); k = gen_k (nbits); - if( k == NULL ) + if (!k) 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; + *r_k = k; + return 0; } + /* Perform ECDH decryption. */ int pk_ecdh_decrypt (gcry_mpi_t * result, const byte sk_fp[MAX_FINGERPRINT_LEN], diff --git a/g10/misc.c b/g10/misc.c index bdd797c16..fd00ec6d1 100644 --- a/g10/misc.c +++ b/g10/misc.c @@ -1423,7 +1423,7 @@ pubkey_nbits( int algo, gcry_mpi_t *key ) int rc, nbits; gcry_sexp_t sexp; -#warning Why this assert +#warning FIXME: We are mixing OpenPGP And CGrypt Ids assert( algo != GCRY_PK_ECDSA && algo != GCRY_PK_ECDH ); if( algo == GCRY_PK_DSA ) { diff --git a/g10/pkglue.c b/g10/pkglue.c index f5c85976f..3aba4e4c1 100644 --- a/g10/pkglue.c +++ b/g10/pkglue.c @@ -28,6 +28,7 @@ #include "util.h" #include "pkglue.h" #include "main.h" +#include "options.h" /* FIXME: Better chnage the fucntion name because mpi_ is used by gcrypt macros. */ @@ -156,26 +157,39 @@ pk_encrypt (int algo, gcry_mpi_t *resarr, gcry_mpi_t data, rc = gcry_sexp_build (&s_pkey, NULL, "(public-key(elg(p%m)(g%m)(y%m)))", pkey[0], pkey[1], pkey[2]); + /* Put DATA into a simplified S-expression. */ + if (rc || gcry_sexp_build (&s_data, NULL, "%m", data)) + BUG (); + } else if (algo == GCRY_PK_RSA || algo == GCRY_PK_RSA_E) { rc = gcry_sexp_build (&s_pkey, NULL, "(public-key(rsa(n%m)(e%m)))", pkey[0], pkey[1]); + /* Put DATA into a simplified S-expression. */ + if (rc || gcry_sexp_build (&s_data, NULL, "%m", data)) + BUG (); } else if (algo == PUBKEY_ALGO_ECDH) { - return pk_ecdh_encrypt (resarr, pk_fp, data, pkey); + gcry_mpi_t k; + + rc = pk_ecdh_generate_ephemeral_key (pkey, &k); + if (rc) + return rc; + + /* Now use the ephemeral secret to compute the shared point. */ + rc = gcry_sexp_build (&s_pkey, NULL, + "(public-key(ecdh(c%m)(q%m)(p%m)))", + pkey[0], pkey[1], pkey[2]); + /* Put K into a simplified S-expression. */ + if (rc || gcry_sexp_build (&s_data, NULL, "%m", k)) + BUG (); } else - return GPG_ERR_PUBKEY_ALGO; - - if (rc) - BUG (); + return gpg_error (GPG_ERR_PUBKEY_ALGO); - /* Put the data into a simple list. */ - if (gcry_sexp_build (&s_data, NULL, "%m", data)) - BUG (); /* Pass it to libgcrypt. */ rc = gcry_pk_encrypt (&s_ciph, s_data, s_pkey); @@ -184,12 +198,42 @@ pk_encrypt (int algo, gcry_mpi_t *resarr, gcry_mpi_t data, if (rc) ; - else - { /* Add better error handling or make gnupg use S-Exp directly. */ + else if (algo == PUBKEY_ALGO_ECDH) + { + gcry_mpi_t shared, public, result; + + /* Get the shared point and the ephemeral public key. */ + shared = mpi_from_sexp (s_ciph, "a"); + public = mpi_from_sexp (s_ciph, "b"); + gcry_sexp_release (s_ciph); + s_ciph = NULL; + if (DBG_CIPHER) + { + log_debug ("ECDH ephemeral key:"); + gcry_mpi_dump (public); + log_printf ("\n"); + } + + result = NULL; + rc = pk_ecdh_encrypt_with_shared_point (1 /*=encrypton*/, shared, + pk_fp, data, pkey, &result); + gcry_mpi_release (shared); + if (!rc) + { + resarr[0] = public; + resarr[1] = result; + } + else + { + gcry_mpi_release (public); + gcry_mpi_release (result); + } + } + else /* Elgamal or RSA case. */ + { /* Fixme: Add better error handling or make gnupg use + S-expressions directly. */ resarr[0] = mpi_from_sexp (s_ciph, "a"); - if (algo != GCRY_PK_RSA - && algo != GCRY_PK_RSA_E - && algo != PUBKEY_ALGO_ECDH) + if (algo != GCRY_PK_RSA && algo != GCRY_PK_RSA_E) resarr[1] = mpi_from_sexp (s_ciph, "b"); } diff --git a/g10/pkglue.h b/g10/pkglue.h index b1cfbe507..98d8c1440 100644 --- a/g10/pkglue.h +++ b/g10/pkglue.h @@ -33,6 +33,13 @@ int pk_check_secret_key (int algo, gcry_mpi_t *skey); /*-- ecdh.c --*/ byte *pk_ecdh_default_params (unsigned int qbits, size_t *sizeout); +gpg_error_t pk_ecdh_generate_ephemeral_key (gcry_mpi_t *pkey, gcry_mpi_t *r_k); +gpg_error_t 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); + 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], -- cgit v1.2.3 From 358afc0dc8980d5ae0cb700efbb61499625a4625 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Wed, 26 Jan 2011 17:17:43 +0100 Subject: Function name cleanups Also nuked some trailing spaces. --- g10/ChangeLog | 12 ++++++ g10/build-packet.c | 105 +++++++++++++++++++++++++++++++++-------------------- g10/ecdh.c | 6 +-- g10/main.h | 6 +-- g10/misc.c | 92 ++-------------------------------------------- g10/packet.h | 1 + g10/parse-packet.c | 61 ++++++++++++++++++++++++++++--- 7 files changed, 142 insertions(+), 141 deletions(-) (limited to 'g10') diff --git a/g10/ChangeLog b/g10/ChangeLog index 9e1aa01da..b27601563 100644 --- a/g10/ChangeLog +++ b/g10/ChangeLog @@ -1,3 +1,15 @@ +2011-01-26 Werner Koch + + * misc.c (ecdsa_qbits_from_Q): Use unsigned int. + + * misc.c (iobuf_read_size_body): Move and rename to .. + * parse-packet.c (read_size_body): .. here. Make static. + * misc.c (iobuf_write_size_body_mpi): Move and rename to .. + * build-packet.c (write_size_body_mpi): .. here. + (iobuf_name_oid_write, ecdh_kek_params_write, ecdh_esk_write): + Remove macros. Replace users by direct calls to + write_size_body_mpi. + 2011-01-25 Werner Koch * ecdh.c (pk_ecdh_default_params_to_mpi): Remove. diff --git a/g10/build-packet.c b/g10/build-packet.c index d138e0614..e2bbdb511 100644 --- a/g10/build-packet.c +++ b/g10/build-packet.c @@ -178,17 +178,44 @@ 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 +/* + * 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. + */ +gpg_error_t +write_size_body_mpi (iobuf_t out, gcry_mpi_t a) +{ + gpg_error_t err; + byte buffer[256]; /* Fixed buffer for a public parameter, max possible */ + size_t nbytes = (mpi_get_nbits (a)+7)/8; + + if (nbytes > sizeof(buffer)) + { + log_error("mpi with size+body is too large (%u bytes)\n", nbytes); + return gpg_error (GPG_ERR_TOO_LARGE); + } -/* Write the value of encrypted filed for ECDH. */ -#define ecdh_esk_write iobuf_write_size_body_mpi + err = gcry_mpi_print (GCRYMPI_FMT_USG, buffer, sizeof(buffer), &nbytes, a); + if (err) + { + log_error ("failed to exported size+body mpi\n"); + return err; + } + 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); +} /* Calculate the length of a packet described by PKT. */ @@ -228,11 +255,11 @@ calc_packet_length( PACKET *pkt ) static void write_fake_data (IOBUF out, gcry_mpi_t a) { - if (a) + if (a) { unsigned int n; void *p; - + p = gcry_mpi_get_opaque ( a, &n ); iobuf_write (out, p, (n+7)/8 ); } @@ -281,18 +308,18 @@ do_key (iobuf_t out, int ctb, PKT_public_key *pk) ndays = 0; write_16(a, ndays); } - + iobuf_put (a, pk->pubkey_algo ); - + /* Get number of secret and public parameters. They are held in one array first the public ones, then the secret ones. */ nskey = pubkey_get_nskey (pk->pubkey_algo); npkey = pubkey_get_npkey (pk->pubkey_algo); - + /* If we don't have any public parameters - which is the case if we don't know the algorithm used - the parameters are stored as one blob in a faked (opaque) MPI. */ - if (!npkey) + if (!npkey) { write_fake_data (a, pk->pkey[0]); goto leave; @@ -305,7 +332,7 @@ do_key (iobuf_t out, int ctb, PKT_public_key *pk) || pk->pubkey_algo == PUBKEY_ALGO_ECDH) { /* Write DER of OID with preceeding length byte. */ - err = iobuf_name_oid_write (a, pk->pkey[0]); + err = write_size_body_mpi (a, pk->pkey[0]); if (err) goto leave; /* Write point Q, the public key. */ @@ -316,7 +343,7 @@ do_key (iobuf_t out, int ctb, PKT_public_key *pk) /* Write one more public field for ECDH. */ if (pk->pubkey_algo == PUBKEY_ALGO_ECDH) { - err = ecdh_kek_params_write(a,pk->pkey[2]); + err = write_size_body_mpi (a, pk->pkey[2]); if (err) goto leave; } @@ -328,14 +355,14 @@ do_key (iobuf_t out, int ctb, PKT_public_key *pk) goto leave; } - + if (pk->seckey_info) { /* This is a secret key packet. */ struct seckey_info *ski = pk->seckey_info; /* Build the header for protected (encrypted) secret parameters. */ - if (ski->is_protected) + if (ski->is_protected) { if ( is_RSA (pk->pubkey_algo) && pk->version < 4 && !ski->s2k.mode ) { @@ -355,12 +382,12 @@ do_key (iobuf_t out, int ctb, PKT_public_key *pk) viewed as a private/experimental extension (this is not specified in rfc2440 but the same scheme is used for all other algorithm identifiers). */ - iobuf_put (a, 101); + iobuf_put (a, 101); iobuf_put (a, ski->s2k.hash_algo); iobuf_write (a, "GNU", 3 ); iobuf_put (a, ski->s2k.mode - 1000); } - else + else { iobuf_put (a, ski->s2k.mode); iobuf_put (a, ski->s2k.hash_algo); @@ -370,7 +397,7 @@ do_key (iobuf_t out, int ctb, PKT_public_key *pk) iobuf_write (a, ski->s2k.salt, 8); if (ski->s2k.mode == 3) - iobuf_put (a, ski->s2k.count); + iobuf_put (a, ski->s2k.count); /* For our special modes 1001, 1002 we do not need an IV. */ if (ski->s2k.mode != 1001 && ski->s2k.mode != 1002) @@ -381,10 +408,10 @@ do_key (iobuf_t out, int ctb, PKT_public_key *pk) iobuf_put (a, 0 ); if (ski->s2k.mode == 1001) - ; /* GnuPG extension - don't write a secret key at all. */ + ; /* GnuPG extension - don't write a secret key at all. */ else if (ski->s2k.mode == 1002) - { - /* GnuPG extension - divert to OpenPGP smartcard. */ + { + /* GnuPG extension - divert to OpenPGP smartcard. */ /* Length of the serial number or 0 for no serial number. */ iobuf_put (a, ski->ivlen ); /* The serial number gets stored in the IV field. */ @@ -395,19 +422,19 @@ do_key (iobuf_t out, int ctb, PKT_public_key *pk) /* The secret key is protected - write it out as it is. */ byte *p; unsigned int ndatabits; - + assert (gcry_mpi_get_flag (pk->pkey[npkey], GCRYMPI_FLAG_OPAQUE)); p = gcry_mpi_get_opaque (pk->pkey[npkey], &ndatabits); iobuf_write (a, p, (ndatabits+7)/8 ); } - else if (ski->is_protected) + else if (ski->is_protected) { /* The secret key is protected the old v4 way. */ - for ( ; i < nskey; i++ ) + for ( ; i < nskey; i++ ) { byte *p; unsigned int ndatabits; - + assert (gcry_mpi_get_flag (pk->pkey[i], GCRYMPI_FLAG_OPAQUE)); p = gcry_mpi_get_opaque (pk->pkey[i], &ndatabits); iobuf_write (a, p, (ndatabits+7)/8); @@ -476,9 +503,9 @@ do_pubkey_enc( IOBUF out, int ctb, PKT_pubkey_enc *enc ) int rc = 0; int n, i; IOBUF a = iobuf_temp(); - + write_version( a, ctb ); - if ( enc->throw_keyid ) + if ( enc->throw_keyid ) { write_32(a, 0 ); /* Don't tell Eve who can decrypt the message. */ write_32(a, 0 ); @@ -500,7 +527,7 @@ do_pubkey_enc( IOBUF out, int ctb, PKT_pubkey_enc *enc ) assert (n == 2); rc = mpi_write (a, enc->data[0]); if (!rc) - rc = ecdh_esk_write (a, enc->data[1]); + rc = write_size_body_mpi (a, enc->data[1]); } else { @@ -545,7 +572,7 @@ do_plaintext( IOBUF out, int ctb, PKT_plaintext *pt ) for(i=0; i < pt->namelen; i++ ) iobuf_put(out, pt->name[i] ); rc = write_32(out, pt->timestamp ); - if (rc) + if (rc) return rc; n = 0; @@ -661,7 +688,7 @@ delete_sig_subpkt (subpktarea_t *area, sigsubpkttype_t reqtype ) } if( buflen < n ) break; - + type = *buffer & 0x7f; if( type == reqtype ) { buffer++; @@ -695,7 +722,7 @@ delete_sig_subpkt (subpktarea_t *area, sigsubpkttype_t reqtype ) * Note: All pointers into sig->[un]hashed (e.g. returned by * parse_sig_subpkt) are not valid after a call to this function. The * data to put into the subpaket should be in a buffer with a length - * of buflen. + * of buflen. */ void build_sig_subpkt (PKT_signature *sig, sigsubpkttype_t type, @@ -798,7 +825,7 @@ build_sig_subpkt (PKT_signature *sig, sigsubpkttype_t type, case SIGSUBPKT_SIGNATURE: hashed = 0; break; - default: + default: hashed = 1; break; } @@ -849,7 +876,7 @@ build_sig_subpkt (PKT_signature *sig, sigsubpkttype_t type, memcpy (p, buffer, buflen); } - if (hashed) + if (hashed) sig->hashed = newarea; else sig->unhashed = newarea; @@ -1135,7 +1162,7 @@ do_signature( IOBUF out, int ctb, PKT_signature *sig ) if ( sig->version < 4 ) iobuf_put (a, 5 ); /* Constant */ iobuf_put (a, sig->sig_class ); - if ( sig->version < 4 ) + if ( sig->version < 4 ) { write_32(a, sig->timestamp ); write_32(a, sig->keyid[0] ); @@ -1143,7 +1170,7 @@ do_signature( IOBUF out, int ctb, PKT_signature *sig ) } iobuf_put(a, sig->pubkey_algo ); iobuf_put(a, sig->digest_algo ); - if ( sig->version >= 4 ) + if ( sig->version >= 4 ) { size_t nn; /* Timestamp and keyid must have been packed into the subpackets diff --git a/g10/ecdh.c b/g10/ecdh.c index 71c32fd5d..95bd8668f 100644 --- a/g10/ecdh.c +++ b/g10/ecdh.c @@ -161,7 +161,7 @@ pk_ecdh_encrypt_with_shared_point (int is_encrypt, gcry_mpi_t shared_mpi, */ { IOBUF obuf = iobuf_temp(); - err = iobuf_write_size_body_mpi ( obuf, pkey[2] ); /* KEK params */ + err = write_size_body_mpi (obuf, pkey[2]); /* KEK params */ kdf_params_size = iobuf_temp_to_buffer (obuf, kdf_params, sizeof(kdf_params)); @@ -198,11 +198,11 @@ pk_ecdh_encrypt_with_shared_point (int is_encrypt, gcry_mpi_t shared_mpi, obuf = iobuf_temp(); /* variable-length field 1, curve name OID */ - err = iobuf_write_size_body_mpi ( obuf, pkey[0] ); + err = write_size_body_mpi (obuf, pkey[0]); /* fixed-length field 2 */ iobuf_put (obuf, PUBKEY_ALGO_ECDH); /* variable-length field 3, KDF params */ - err = (err ? err : iobuf_write_size_body_mpi ( obuf, pkey[2] )); + err = (err ? err : write_size_body_mpi ( obuf, pkey[2] )); /* fixed-length field 4 */ iobuf_write (obuf, "Anonymous Sender ", 20); /* fixed-length field 5, recipient fp */ diff --git a/g10/main.h b/g10/main.h index ea1ac2e1f..4cec61f9d 100644 --- a/g10/main.h +++ b/g10/main.h @@ -161,11 +161,7 @@ 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 ); +unsigned int ecdsa_qbits_from_Q (unsigned int qbits); /*-- status.c --*/ void set_status_fd ( int fd ); diff --git a/g10/misc.c b/g10/misc.c index fd00ec6d1..dc2f73be4 100644 --- a/g10/misc.c +++ b/g10/misc.c @@ -1459,7 +1459,6 @@ pubkey_nbits( int algo, gcry_mpi_t *key ) -/* FIXME: Use gcry_mpi_print directly. */ int mpi_print (estream_t fp, gcry_mpi_t a, int mode) { @@ -1487,98 +1486,15 @@ mpi_print (estream_t fp, gcry_mpi_t a, int mode) } -/* - * 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 */ -int -ecdsa_qbits_from_Q (int qbits ) +unsigned int +ecdsa_qbits_from_Q (unsigned int qbits) { if ((qbits%8) > 3) { - log_error(_("ECDSA public key is expected to be in SEC encoding " - "multiple of 8 bits\n")); + log_error (_("ECDSA public key is expected to be in SEC encoding " + "multiple of 8 bits\n")); return 0; } qbits -= qbits%8; diff --git a/g10/packet.h b/g10/packet.h index 541462af6..d06c4c7e7 100644 --- a/g10/packet.h +++ b/g10/packet.h @@ -444,6 +444,7 @@ PACKET *create_gpg_control ( ctrlpkttype_t type, /*-- build-packet.c --*/ int build_packet( iobuf_t inp, PACKET *pkt ); +gpg_error_t write_size_body_mpi (iobuf_t out, gcry_mpi_t a); u32 calc_packet_length( PACKET *pkt ); void build_sig_subpkt( PKT_signature *sig, sigsubpkttype_t type, const byte *buffer, size_t buflen ); diff --git a/g10/parse-packet.c b/g10/parse-packet.c index 334a9a82b..a0844c7ac 100644 --- a/g10/parse-packet.c +++ b/g10/parse-packet.c @@ -741,6 +741,55 @@ read_rest (IOBUF inp, size_t pktlen, int partial) } +/* + * 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). + */ +static int +read_size_body (iobuf_t inp, byte *body, int body_max_size, + int pktlen, gcry_mpi_t *out ) +{ + unsigned int 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; +} + + +/* Parse a marker packet. */ static int parse_marker (IOBUF inp, int pkttype, unsigned long pktlen) { @@ -947,8 +996,8 @@ parse_pubkeyenc (IOBUF inp, int pkttype, unsigned long pktlen, 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); + rc = read_size_body (inp, encr_buf, sizeof(encr_buf), + pktlen, k->data+1); if (rc) goto leave; @@ -1958,8 +2007,8 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen, /* 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); + err = read_size_body (inp, name_oid, sizeof(name_oid), + pktlen, pk->pkey+0); if (err) goto leave; n = name_oid[0]; @@ -1984,8 +2033,8 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen, 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); + err = read_size_body (inp, name_oid, sizeof(name_oid), + pktlen, pk->pkey+2); if (err) goto leave; n = name_oid[0]; -- cgit v1.2.3 From 0fb0bb8d9a960a2473ab70a021d20639a43227e0 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Mon, 31 Jan 2011 09:27:06 +0100 Subject: Reworked the ECC changes to better fit into the Libgcrypt API. See ChangeLog for details. Key generation, signing and verification works. Encryption does not yet work. Requires latest Libgcrypt changes. --- g10/ChangeLog | 34 ++++++ g10/build-packet.c | 93 +++++++--------- g10/ecdh.c | 26 ++--- g10/keygen.c | 229 ++++++++++++++++++++++------------------ g10/keyid.c | 21 ++-- g10/main.h | 11 +- g10/misc.c | 304 ++++++++++++++++++++++++++++++++++++++++++++++++----- g10/parse-packet.c | 201 +++++++++++++---------------------- g10/pkglue.c | 48 ++++++--- g10/pkglue.h | 2 +- 10 files changed, 613 insertions(+), 356 deletions(-) (limited to 'g10') diff --git a/g10/ChangeLog b/g10/ChangeLog index b27601563..f6c144d7c 100644 --- a/g10/ChangeLog +++ b/g10/ChangeLog @@ -1,3 +1,37 @@ +2011-01-30 Werner Koch + + + * keyid.c (keygrip_from_pk): Adjust ECC cases. + * pkglue.c (pk_verify): Ditto. + + * parse-packet.c (read_size_body): Rewrite. + (parse_key): Simply ECC case. + (parse_pubkeyenc): Ditto. + + * misc.c (pubkey_get_npkey): Special case ECC. + (pubkey_get_nskey): Ditto. + (mpi_print): Support printfing of opaque values. + (openpgp_oid_to_str): New. + (pubkey_nbits): For ECC pass curve parameter. + + * ecdh.c (pk_ecdh_default_params): Change to return an opaque MPI. + + * build-packet.c (do_key): Automatically handle real and opaque + key parameters. + (write_fake_data): Return an error code. + (mpi_write): Support writing opaque MPIs. + (do_pubkey_enc): Simplify ECC handling. + +2011-01-28 Werner Koch + + * keygen.c (gen_ecc): Rewrite. Select a named curve and create a + keyspec based on that. + (pk_ecc_build_key_params): Remove. + (get_parameter_algo): Map algo number. + (ecckey_from_sexp): New. + * misc.c (map_pk_gcry_to_openpgp): New. + (openpgp_oid_from_str): New. Based on libksba code. + 2011-01-26 Werner Koch * misc.c (ecdsa_qbits_from_Q): Use unsigned int. diff --git a/g10/build-packet.c b/g10/build-packet.c index e2bbdb511..122ef15e7 100644 --- a/g10/build-packet.c +++ b/g10/build-packet.c @@ -1,6 +1,6 @@ /* build-packet.c - assemble packets and write them * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, - * 2006, 2010 Free Software Foundation, Inc. + * 2006, 2010, 2011 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -160,19 +160,31 @@ build_packet( IOBUF out, PACKET *pkt ) static int mpi_write (iobuf_t out, gcry_mpi_t a) { - char buffer[(MAX_EXTERN_MPI_BITS+7)/8+2]; /* 2 is for the mpi length. */ - size_t nbytes; int rc; - nbytes = DIM(buffer); - rc = gcry_mpi_print (GCRYMPI_FMT_PGP, buffer, nbytes, &nbytes, a ); - if( !rc ) - rc = iobuf_write( out, buffer, nbytes ); - else if (gpg_err_code(rc) == GPG_ERR_TOO_SHORT ) + if (gcry_mpi_get_flag (a, GCRYMPI_FLAG_OPAQUE)) { - log_info ("mpi too large (%u bits)\n", gcry_mpi_get_nbits (a)); - /* The buffer was too small. We better tell the user about the MPI. */ - rc = gpg_error (GPG_ERR_TOO_LARGE); + size_t nbits; + const void *p; + + p = gcry_mpi_get_opaque (a, &nbits); + rc = iobuf_write (out, p, (nbits+7)/8); + } + else + { + char buffer[(MAX_EXTERN_MPI_BITS+7)/8+2]; /* 2 is for the mpi length. */ + size_t nbytes; + + nbytes = DIM(buffer); + rc = gcry_mpi_print (GCRYMPI_FMT_PGP, buffer, nbytes, &nbytes, a ); + if( !rc ) + rc = iobuf_write( out, buffer, nbytes ); + else if (gpg_err_code(rc) == GPG_ERR_TOO_SHORT ) + { + log_info ("mpi too large (%u bits)\n", gcry_mpi_get_nbits (a)); + /* The buffer was too small. We better tell the user about the MPI. */ + rc = gpg_error (GPG_ERR_TOO_LARGE); + } } return rc; @@ -252,19 +264,20 @@ calc_packet_length( PACKET *pkt ) return n; } -static void + +static gpg_error_t write_fake_data (IOBUF out, gcry_mpi_t a) { - if (a) - { - unsigned int n; - void *p; + unsigned int n; + void *p; - p = gcry_mpi_get_opaque ( a, &n ); - iobuf_write (out, p, (n+7)/8 ); - } + if (!a) + return 0; + p = gcry_mpi_get_opaque ( a, &n); + return iobuf_write (out, p, (n+7)/8 ); } + static int do_user_id( IOBUF out, int ctb, PKT_user_id *uid ) { @@ -326,33 +339,11 @@ do_key (iobuf_t out, int ctb, PKT_public_key *pk) } assert (npkey < nskey); - /* 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) + for (i=0; i < npkey; i++ ) { - /* Write DER of OID with preceeding length byte. */ - err = write_size_body_mpi (a, pk->pkey[0]); - if (err) - goto leave; - /* Write point Q, the public key. */ - err = mpi_write (a, pk->pkey[1]); + err = mpi_write (a, pk->pkey[i]); if (err) goto leave; - - /* Write one more public field for ECDH. */ - if (pk->pubkey_algo == PUBKEY_ALGO_ECDH) - { - err = write_size_body_mpi (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; } @@ -520,20 +511,8 @@ do_pubkey_enc( IOBUF out, int ctb, PKT_pubkey_enc *enc ) if ( !n ) write_fake_data( a, enc->data[0] ); - 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 = write_size_body_mpi (a, enc->data[1]); - } - else - { - for (i=0; i < n && !rc ; i++ ) - rc = mpi_write(a, enc->data[i] ); - } + for (i=0; i < n && !rc ; i++ ) + rc = mpi_write (a, enc->data[i]); if (!rc) { diff --git a/g10/ecdh.c b/g10/ecdh.c index 95bd8668f..cf002b957 100644 --- a/g10/ecdh.c +++ b/g10/ecdh.c @@ -48,16 +48,17 @@ static const struct -/* Returns allocated (binary) KEK parameters; the size is returned in - sizeout. The caller must free the returned value. Returns NULL - and sets ERRNO on error. */ -byte * -pk_ecdh_default_params (unsigned int qbits, size_t *sizeout) +/* Return KEK parameters as an opaque MPI The caller must free the + returned value. Returns NULL and sets ERRNO on error. */ +gcry_mpi_t +pk_ecdh_default_params (unsigned int qbits) { - byte kek_params[4]; + byte *kek_params; int i; - byte *buffer; + kek_params = xtrymalloc (4); + if (!kek_params) + return NULL; kek_params[0] = 3; /* Number of bytes to follow. */ kek_params[1] = 1; /* Version for KDF+AESWRAP. */ @@ -78,12 +79,7 @@ pk_ecdh_default_params (unsigned int qbits, size_t *sizeout) if (DBG_CIPHER) log_printhex ("ECDH KEK params are", kek_params, sizeof(kek_params) ); - buffer = xtrymalloc (sizeof(kek_params)); - if (!buffer) - return NULL; - memcpy (buffer, kek_params, sizeof (kek_params)); - *sizeout = sizeof (kek_params); - return buffer; + return gcry_mpi_set_opaque (NULL, kek_params, 4 * 8); } @@ -411,8 +407,8 @@ gen_k (unsigned nbits) 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 ); + log_debug ("ephemeral scalar MPI #0: %s\n", buffer); + gcry_free (buffer); } return k; diff --git a/g10/keygen.c b/g10/keygen.c index b42121b28..4d911f0b9 100644 --- a/g10/keygen.c +++ b/g10/keygen.c @@ -1081,7 +1081,107 @@ write_keybinding (KBNODE root, PKT_public_key *pri_psk, PKT_public_key *sub_psk, } +static gpg_error_t +ecckey_from_sexp (gcry_mpi_t *array, gcry_sexp_t sexp, int algo) +{ + gpg_error_t err; + gcry_sexp_t list, l2; + char *curve; + int i; + const char *oidstr; + unsigned int nbits; + + array[0] = NULL; + array[1] = NULL; + array[2] = NULL; + + list = gcry_sexp_find_token (sexp, "public-key", 0); + if (!list) + return gpg_error (GPG_ERR_INV_OBJ); + l2 = gcry_sexp_cadr (list); + gcry_sexp_release (list); + list = l2; + if (!list) + return gpg_error (GPG_ERR_NO_OBJ); + + l2 = gcry_sexp_find_token (list, "curve", 0); + if (!l2) + { + err = gpg_error (GPG_ERR_NO_OBJ); + goto leave; + } + curve = gcry_sexp_nth_string (l2, 1); + if (!curve) + { + err = gpg_error (GPG_ERR_NO_OBJ); + goto leave; + } + gcry_sexp_release (l2); + if (!strcmp (curve, "NIST P-256")) + { + oidstr = "1.2.840.10045.3.1.7"; + nbits = 256; + } + else if (!strcmp (curve, "NIST P-384")) + { + oidstr = "1.3.132.0.34"; + nbits = 384; + } + else if (!strcmp (curve, "NIST P-521")) + { + oidstr = "1.3.132.0.35"; + nbits = 521; + } + else + { + err = gpg_error (GPG_ERR_INV_OBJ); + goto leave; + } + err = openpgp_oid_from_str (oidstr, &array[0]); + if (err) + goto leave; + + l2 = gcry_sexp_find_token (list, "q", 0); + if (!l2) + { + err = gpg_error (GPG_ERR_NO_OBJ); + goto leave; + } + array[1] = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG); + gcry_sexp_release (l2); + if (!array[1]) + { + err = gpg_error (GPG_ERR_INV_OBJ); + goto leave; + } + gcry_sexp_release (list); + if (algo == PUBKEY_ALGO_ECDH) + { + array[2] = pk_ecdh_default_params (nbits); + if (!array[2]) + { + err = gpg_error_from_syserror (); + goto leave; + } + } + + leave: + if (err) + { + for (i=0; i < 3; i++) + { + gcry_mpi_release (array[i]); + array[i] = NULL; + } + } + return 0; +} + + +/* Extract key parameters from SEXP and store them in ARRAY. ELEMS is + a string where each character denotes a parameter name. TOPNAME is + the name of the top element above the elements. */ static int key_from_sexp (gcry_mpi_t *array, gcry_sexp_t sexp, const char *topname, const char *elems) @@ -1165,7 +1265,10 @@ common_gen (const char *keyparms, int algo, const char *algoelem, pk->expiredate = pk->timestamp + expireval; pk->pubkey_algo = algo; - err = key_from_sexp (pk->pkey, s_key, "public-key", algoelem); + if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_ECDH) + err = ecckey_from_sexp (pk->pkey, s_key, algo); + else + err = key_from_sexp (pk->pkey, s_key, "public-key", algoelem); if (err) { log_error ("key_from_sexp failed: %s\n", gpg_strerror (err) ); @@ -1173,7 +1276,6 @@ common_gen (const char *keyparms, int algo, const char *algoelem, free_public_key (pk); return err; } - gcry_sexp_release (s_key); pkt = xtrycalloc (1, sizeof *pkt); if (!pkt) @@ -1329,126 +1431,45 @@ gen_dsa (unsigned int nbits, KBNODE pub_root, -/* Create an S-expression string out of QBITS, ALGO and the TRANSIENT - flag. On success a malloced string is returned, on failure NULL - and ERRNO is set. */ -static char * -pk_ecc_build_key_params (int qbits, int algo, int transient) -{ - byte *kek_params = NULL; - size_t kek_params_size; - char qbitsstr[35]; - char *result; - size_t 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); - if (!kek_params) - return NULL; - } - else - kek_params = NULL; - - snprintf (qbitsstr, sizeof qbitsstr, "%u", qbits); - if (algo == PUBKEY_ALGO_ECDSA || !kek_params) - { - result = xtryasprintf ("(genkey(%s(nbits %zu:%s)" - /**/ "(qbits %zu:%s)" - /**/ "(transient-key 1:%d)))", - algo == PUBKEY_ALGO_ECDSA ? "ecdsa" : "ecdh", - strlen (qbitsstr), qbitsstr, - strlen (qbitsstr), qbitsstr, - transient); - } - else - { - char *tmpstr; - - assert (kek_params); - tmpstr = xtryasprintf ("(genkey(ecdh(nbits %zu:%s)" - /**/ "(qbits %zu:%s)" - /**/ "(transient-key 1:%d)" - /**/ "(kek-params %zu:", - strlen (qbitsstr), qbitsstr, - strlen (qbitsstr), qbitsstr, - transient, - kek_params_size); - if (!tmpstr) - { - xfree (kek_params); - return NULL; - } - /* Append the binary KEK parmas. */ - n = strlen (tmpstr); - result = xtryrealloc (tmpstr, n + kek_params_size + 4); - if (!result) - { - xfree (tmpstr); - xfree (kek_params); - return NULL; - } - memcpy (result + n, kek_params, kek_params_size); - strcpy (result + n + kek_params_size, ")))"); - } - xfree (kek_params); - return result; -} - - /* * Generate an ECC key */ static gpg_error_t -gen_ecc (int algo, unsigned int nbits, KBNODE pub_root, +gen_ecc (int algo, unsigned int nbits, kbnode_t pub_root, u32 timestamp, u32 expireval, int is_subkey, int keygen_flags, char **cache_nonce_addr) { - int err; - unsigned int qbits; + gpg_error_t err; + const char *curve; char *keyparms; assert (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_ECDH); - 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_error ("broken version of Libgcrypt\n"); - return gpg_error (GPG_ERR_INTERNAL); /* ABI silently changed. */ - } - - if (nbits != 256 && nbits != 384 && nbits != 521) - { - log_info (_("keysize invalid; using %u bits\n"), 256); - /* FIXME: Where do we set it to 256? */ - } - - /* 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)) ); + /* For now we may only use one of the 3 NISY curves. */ + if (nbits <= 256) + curve = "NIST P-256"; + else if (nbits <= 384) + curve = "NIST P-384"; + else + curve = "NIST P-521"; + + keyparms = xtryasprintf ("(genkey(%s(curve %zu:%s)%s))", + algo == PUBKEY_ALGO_ECDSA ? "ecdsa" : "ecdh", + strlen (curve), curve, + ((keygen_flags & KEYGEN_FLAG_TRANSIENT_KEY) + && (keygen_flags & KEYGEN_FLAG_NO_PROTECTION))? + "(transient-key)" : "" ); if (!keyparms) - { - err = gpg_error_from_syserror (); - log_error ("ecc pk_ecc_build_key_params failed: %s\n", - gpg_strerror (err)); - } + err = gpg_error_from_syserror (); else { - err = common_gen (keyparms, algo, - algo == PUBKEY_ALGO_ECDSA? "cq" : "cqp", + err = common_gen (keyparms, algo, "", pub_root, timestamp, expireval, is_subkey, keygen_flags, cache_nonce_addr); xfree (keyparms); } - return 0; + return err; } @@ -2428,7 +2449,7 @@ get_parameter_algo( struct para_data_s *para, enum para_name key, || !strcmp (r->u.value, "ELG")) i = GCRY_PK_ELG_E; else - i = gcry_pk_map_name (r->u.value); + i = map_pk_gcry_to_openpgp (gcry_pk_map_name (r->u.value)); if (i == PUBKEY_ALGO_RSA_E || i == PUBKEY_ALGO_RSA_S) i = 0; /* we don't want to allow generation of these algorithms */ diff --git a/g10/keyid.c b/g10/keyid.c index 0405b8b2f..6571a51c0 100644 --- a/g10/keyid.c +++ b/g10/keyid.c @@ -724,17 +724,20 @@ keygrip_from_pk (PKT_public_key *pk, unsigned char *array) 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]); + { + char *curve = openpgp_oid_to_str (pk->pkey[0]); + if (!curve) + err = gpg_error_from_syserror (); + else + { + err = gcry_sexp_build (&s_pkey, NULL, + "(public-key(ecc(curve%s)(q%m)))", + curve, pk->pkey[1]); + xfree (curve); + } + } 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 4cec61f9d..d76d96bc4 100644 --- a/g10/main.h +++ b/g10/main.h @@ -97,6 +97,7 @@ 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 map_pk_gcry_to_openpgp (enum gcry_pk_algos 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 ); @@ -154,15 +155,21 @@ int is_valid_mailbox (const char *name); const char *get_libexecdir (void); int path_access(const char *file,int mode); -/* Temporary helpers. */ int pubkey_get_npkey( int algo ); int pubkey_get_nskey( int algo ); int pubkey_get_nsig( int algo ); int pubkey_get_nenc( int algo ); + +/* Temporary helpers. */ unsigned int pubkey_nbits( int algo, gcry_mpi_t *pkey ); int mpi_print (estream_t stream, gcry_mpi_t a, int mode); unsigned int ecdsa_qbits_from_Q (unsigned int qbits); +/* Other stuff */ +gpg_error_t openpgp_oid_from_str (const char *string, gcry_mpi_t *r_mpi); +char *openpgp_oid_to_str (gcry_mpi_t a); + + /*-- status.c --*/ void set_status_fd ( int fd ); int is_status_enabled ( void ); @@ -300,7 +307,7 @@ gpg_error_t export_pubkey_buffer (ctrl_t ctrl, const char *keyspec, int export_seckeys (ctrl_t ctrl, strlist_t users); int export_secsubkeys (ctrl_t ctrl, strlist_t users); -/* dearmor.c --*/ +/*-- dearmor.c --*/ int dearmor_file( const char *fname ); int enarmor_file( const char *fname ); diff --git a/g10/misc.c b/g10/misc.c index dc2f73be4..2052e96c7 100644 --- a/g10/misc.c +++ b/g10/misc.c @@ -379,6 +379,19 @@ map_pk_openpgp_to_gcry (int algo) } } +/* Map Gcrypt public key algorithm numbers to those used by + OpenPGP. */ +int +map_pk_gcry_to_openpgp (enum gcry_pk_algos algo) +{ + switch (algo) + { + case GCRY_PK_ECDSA: return PUBKEY_ALGO_ECDSA; + case GCRY_PK_ECDH: return PUBKEY_ALGO_ECDH; + default: return algo < 110 ? algo : 0; + } +} + /* Return the block length of an OpenPGP cipher algorithm. */ int @@ -1347,35 +1360,44 @@ path_access(const char *file,int mode) -/* Temporary helper. */ +/* Return the number of public key parameters as used by OpenPGP. */ int -pubkey_get_npkey( int algo ) +pubkey_get_npkey (int algo) { size_t n; + /* ECC is special. */ + if (algo == PUBKEY_ALGO_ECDSA) + return 2; + else if (algo == PUBKEY_ALGO_ECDH) + return 3; + + /* All other algorithms match those of Libgcrypt. */ 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)) + + if (gcry_pk_algo_info (algo, GCRYCTL_GET_ALGO_NPKEY, NULL, &n)) n = 0; return n; } -/* Temporary helper. */ + +/* Return the number of secret key parameters as used by OpenPGP. */ int -pubkey_get_nskey( int algo ) +pubkey_get_nskey (int algo) { size_t n; + /* ECC is special. */ + if (algo == PUBKEY_ALGO_ECDSA) + return 3; + else if (algo == PUBKEY_ALGO_ECDH) + return 4; + + /* All other algorithms match those of Libgcrypt. */ 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; @@ -1383,33 +1405,40 @@ pubkey_get_nskey( int algo ) /* Temporary helper. */ int -pubkey_get_nsig( int algo ) +pubkey_get_nsig (int algo) { size_t n; + /* ECC is special. */ + if (algo == PUBKEY_ALGO_ECDSA) + return 2; + else if (algo == PUBKEY_ALGO_ECDH) + return 0; + 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; } + /* Temporary helper. */ int -pubkey_get_nenc( int algo ) +pubkey_get_nenc (int algo) { size_t n; + /* ECC is special. */ + if (algo == PUBKEY_ALGO_ECDSA) + return 0; + else if (algo == PUBKEY_ALGO_ECDH) + return 2; + 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; @@ -1442,9 +1471,16 @@ pubkey_nbits( int algo, gcry_mpi_t *key ) 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' */ ); + char *curve = openpgp_oid_to_str (key[0]); + if (!curve) + rc = gpg_error_from_syserror (); + else + { + rc = gcry_sexp_build (&sexp, NULL, + "(public-key(ecc(curve%s)(q%m)))", + curve, key[1]); + xfree (curve); + } } else return 0; @@ -1472,6 +1508,19 @@ mpi_print (estream_t fp, gcry_mpi_t a, int mode) n1 = gcry_mpi_get_nbits(a); n += es_fprintf (fp, "[%u bits]", n1); } + else if (gcry_mpi_get_flag (a, GCRYMPI_FLAG_OPAQUE)) + { + unsigned int nbits; + unsigned char *p = gcry_mpi_get_opaque (a, &nbits); + if (!p) + n += es_fprintf (fp, "[invalid opaque value]"); + else + { + nbits = (nbits + 7)/8; + for (; nbits; nbits--, p++) + n += es_fprintf (fp, "%02X", *p); + } + } else { unsigned char *buffer; @@ -1501,3 +1550,206 @@ ecdsa_qbits_from_Q (unsigned int qbits) qbits /= 2; return qbits; } + + + +/* Helper for openpgp_oid_from_str. */ +static size_t +make_flagged_int (unsigned long value, char *buf, size_t buflen) +{ + int more = 0; + int shift; + + /* fixme: figure out the number of bits in an ulong and start with + that value as shift (after making it a multiple of 7) a more + straigtforward implementation is to do it in reverse order using + a temporary buffer - saves a lot of compares */ + for (more=0, shift=28; shift > 0; shift -= 7) + { + if (more || value >= (1<> shift); + value -= (value >> shift) << shift; + more = 1; + } + } + buf[buflen++] = value; + return buflen; +} + + +/* Convert the OID given in dotted decimal form in STRING to an DER + * encoding and store it as an opaque value at R_MPI. The format of + * the DER encoded is not a regular ASN.1 object but the modified + * format as used by OpenPGP for the ECC curve description. On error + * the function returns and error code an NULL is stored at R_BUG. + * Note that scanning STRING stops at the first white space + * character. */ +gpg_error_t +openpgp_oid_from_str (const char *string, gcry_mpi_t *r_mpi) +{ + unsigned char *buf; + size_t buflen; + unsigned long val1, val; + const char *endp; + int arcno; + + *r_mpi = NULL; + + if (!string || !*string) + return gpg_error (GPG_ERR_INV_VALUE); + + /* We can safely assume that the encoded OID is shorter than the string. */ + buf = xtrymalloc (1 + strlen (string) + 2); + if (!buf) + return gpg_error_from_syserror (); + /* Save the first byte for the length. */ + buflen = 1; + + val1 = 0; /* Avoid compiler warning. */ + arcno = 0; + do { + arcno++; + val = strtoul (string, (char**)&endp, 10); + if (!digitp (string) || !(*endp == '.' || !*endp)) + { + xfree (buf); + return gpg_error (GPG_ERR_INV_OID_STRING); + } + if (*endp == '.') + string = endp+1; + + if (arcno == 1) + { + if (val > 2) + break; /* Not allowed, error catched below. */ + val1 = val; + } + else if (arcno == 2) + { /* Need to combine the first two arcs in one octet. */ + if (val1 < 2) + { + if (val > 39) + { + xfree (buf); + return gpg_error (GPG_ERR_INV_OID_STRING); + } + buf[buflen++] = val1*40 + val; + } + else + { + val += 80; + buflen = make_flagged_int (val, buf, buflen); + } + } + else + { + buflen = make_flagged_int (val, buf, buflen); + } + } while (*endp == '.'); + + if (arcno == 1 || buflen < 2 || buflen > 254 ) + { /* It is not possible to encode only the first arc. */ + xfree (buf); + return gpg_error (GPG_ERR_INV_OID_STRING); + } + + *buf = buflen - 1; + *r_mpi = gcry_mpi_set_opaque (NULL, buf, buflen * 8); + if (!*r_mpi) + { + xfree (buf); + return gpg_error_from_syserror (); + } + return 0; +} + + +/* Return a malloced string represenation of the OID in the opaque MPI + A. In case of an error NULL is returned and ERRNO is set. */ +char * +openpgp_oid_to_str (gcry_mpi_t a) +{ + const unsigned char *buf; + size_t length; + char *string, *p; + int n = 0; + unsigned long val, valmask; + + valmask = (unsigned long)0xfe << (8 * (sizeof (valmask) - 1)); + + if (!a || !gcry_mpi_get_flag (a, GCRYMPI_FLAG_OPAQUE)) + { + gpg_err_set_errno (EINVAL); + return NULL; + } + + buf = gcry_mpi_get_opaque (a, &length); + length = (length+7)/8; + + /* The first bytes gives the length; check consistency. */ + if (!length || buf[0] != length -1) + { + gpg_err_set_errno (EINVAL); + return NULL; + } + /* Skip length byte. */ + length--; + buf++; + + /* To calculate the length of the string we can safely assume an + upper limit of 3 decimal characters per byte. Two extra bytes + account for the special first octect */ + string = p = xtrymalloc (length*(1+3)+2+1); + if (!string) + return NULL; + if (!buf || !length) + { + *p = 0; + return string; + } + + if (buf[0] < 40) + p += sprintf (p, "0.%d", buf[n]); + else if (buf[0] < 80) + p += sprintf (p, "1.%d", buf[n]-40); + else { + val = buf[n] & 0x7f; + while ( (buf[n]&0x80) && ++n < length ) + { + if ( (val & valmask) ) + goto badoid; /* Overflow. */ + val <<= 7; + val |= buf[n] & 0x7f; + } + val -= 80; + sprintf (p, "2.%lu", val); + p += strlen (p); + } + for (n++; n < length; n++) + { + val = buf[n] & 0x7f; + while ( (buf[n]&0x80) && ++n < length ) + { + if ( (val & valmask) ) + goto badoid; /* Overflow. */ + val <<= 7; + val |= buf[n] & 0x7f; + } + sprintf (p, ".%lu", val); + p += strlen (p); + } + + *p = 0; + return string; + + badoid: + /* Return a special OID (gnu.gnupg.badoid) to indicate the error + case. The OID is broken and thus we return one which can't do + any harm. Formally this does not need to be a bad OID but an OID + with an arc that can't be represented in a 32 bit word is more + than likely corrupt. */ + xfree (string); + return xtrystrdup ("1.3.6.1.4.1.11591.2.12242973"); +} + diff --git a/g10/parse-packet.c b/g10/parse-packet.c index a0844c7ac..83be15d8c 100644 --- a/g10/parse-packet.c +++ b/g10/parse-packet.c @@ -741,51 +741,57 @@ read_rest (IOBUF inp, size_t pktlen, int partial) } -/* - * 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). - */ -static int -read_size_body (iobuf_t inp, byte *body, int body_max_size, - int pktlen, gcry_mpi_t *out ) +/* Read a special size+body from INP. On success store an opaque MPI + with it at R_DATA. On error return an error code and store NULL at + R_DATA. Even in the error case store the number of read bytes at + R_NREAD. The caller shall pass the remaining size of the packet in + PKTLEN. */ +static gpg_error_t +read_size_body (iobuf_t inp, int pktlen, size_t *r_nread, + gcry_mpi_t *r_data) { - unsigned int n; - int rc; - gcry_mpi_t result; + char buffer[256]; + char *tmpbuf; + int i, c, nbytes; + + *r_nread = 0; + *r_data = NULL; + + if (!pktlen) + return gpg_error (GPG_ERR_INV_PACKET); + c = iobuf_readbyte (inp); + if (c < 0) + return gpg_error (GPG_ERR_INV_PACKET); + pktlen--; + ++*r_nread; + nbytes = c; + if (nbytes < 2 || nbytes > 254) + return gpg_error (GPG_ERR_INV_PACKET); + if (nbytes > pktlen) + return gpg_error (GPG_ERR_INV_PACKET); - *out = NULL; + buffer[0] = nbytes; - 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) + for (i = 0; i < nbytes; i++) { - log_error("invalid size+body field\n"); - return G10ERR_INVALID_PACKET; + c = iobuf_get (inp); + if (c < 0) + return gpg_error (GPG_ERR_INV_PACKET); + ++*r_nread; + buffer[1+i] = c; } - if (n+1 > pktlen) + + tmpbuf = xtrymalloc (1 + nbytes); + if (!tmpbuf) + return gpg_error_from_syserror (); + memcpy (tmpbuf, buffer, 1 + nbytes); + *r_data = gcry_mpi_set_opaque (NULL, tmpbuf, 8 * (1 + nbytes)); + if (!*r_data) { - log_error("size+body field is larger than the packet\n"); - return G10ERR_INVALID_PACKET; + xfree (tmpbuf); + return gpg_error_from_syserror (); } - 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; + return 0; } @@ -988,46 +994,29 @@ parse_pubkeyenc (IOBUF inp, int pkttype, unsigned long pktlen, } else { - if (k->pubkey_algo == PUBKEY_ALGO_ECDH) + for (i = 0; i < ndata; i++) { - byte encr_buf[255]; - - assert (ndata == 2); - n = pktlen; - k->data[0] = mpi_read (inp, &n, 0); - pktlen -= n; - rc = read_size_body (inp, encr_buf, sizeof(encr_buf), - pktlen, k->data+1); - if (rc) - goto leave; - - if (list_mode) + if (k->pubkey_algo == PUBKEY_ALGO_ECDH && i == 1) { - 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); + rc = read_size_body (inp, pktlen, &n, k->data+i); + pktlen -= n; } - pktlen -= (encr_buf[0]+1); - } - else - { - for (i = 0; i < ndata; i++) + else { 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 (rc) + goto leave; + if (list_mode) + { + es_fprintf (listfp, "\tdata: "); + mpi_print (listfp, k->data[i], mpi_print_mode); + es_putc ('\n', listfp); + } } } @@ -1989,7 +1978,6 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen, unknown_pubkey_warning (algorithm); } - if (!npkey) { /* Unknown algorithm - put data into an opaque MPI. */ @@ -2001,79 +1989,32 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen, } else { - /* Fill in public key parameters. */ - if (algorithm == PUBKEY_ALGO_ECDSA || algorithm == PUBKEY_ALGO_ECDH) + for (i = 0; i < npkey; i++) { - /* FIXME: The code in this function ignores the errors. */ - byte name_oid[256]; - - err = 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) + if ((algorithm == PUBKEY_ALGO_ECDSA + || algorithm == PUBKEY_ALGO_ECDH) && (i==0 || i == 2)) { - es_fprintf (listfp, "\tpkey[1]: "); - mpi_print (listfp, pk->pkey[1], mpi_print_mode); - es_putc ('\n', listfp); - } - /* One more field for ECDH. */ - if (algorithm == PUBKEY_ALGO_ECDH) - { - /* (NAMEOID holds the KEK params.) */ - err = 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); + err = read_size_body (inp, pktlen, &n, pk->pkey+i); + pktlen -= n; } - } - else - { - for (i = 0; i < npkey; i++) + else { 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; + if (list_mode) + { + es_fprintf (listfp, "\tpkey[%d]: ", i); + mpi_print (listfp, pk->pkey[i], mpi_print_mode); + es_putc ('\n', listfp); + } } - if (err) - goto leave; } - if (list_mode) keyid_from_pk (pk, keyid); diff --git a/g10/pkglue.c b/g10/pkglue.c index 3aba4e4c1..27ee239a4 100644 --- a/g10/pkglue.c +++ b/g10/pkglue.c @@ -79,8 +79,16 @@ pk_verify (int algo, gcry_mpi_t hash, gcry_mpi_t *data, gcry_mpi_t *pkey) } 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]); + char *curve = openpgp_oid_to_str (pkey[0]); + if (!curve) + rc = gpg_error_from_syserror (); + else + { + rc = gcry_sexp_build (&s_pkey, NULL, + "(public-key(ecdsa(curve %s)(q%m)))", + curve, pkey[1]); + xfree (curve); + } } else return GPG_ERR_PUBKEY_ALGO; @@ -174,18 +182,27 @@ pk_encrypt (int algo, gcry_mpi_t *resarr, gcry_mpi_t data, else if (algo == PUBKEY_ALGO_ECDH) { gcry_mpi_t k; + char *curve; rc = pk_ecdh_generate_ephemeral_key (pkey, &k); if (rc) return rc; - /* Now use the ephemeral secret to compute the shared point. */ - rc = gcry_sexp_build (&s_pkey, NULL, - "(public-key(ecdh(c%m)(q%m)(p%m)))", - pkey[0], pkey[1], pkey[2]); - /* Put K into a simplified S-expression. */ - if (rc || gcry_sexp_build (&s_data, NULL, "%m", k)) - BUG (); + curve = openpgp_oid_to_str (pkey[0]); + if (!curve) + rc = gpg_error_from_syserror (); + else + { + /* Now use the ephemeral secret to compute the shared point. */ + rc = gcry_sexp_build (&s_pkey, NULL, + "(public-key(ecdh(curve%s)(q%m)))", + curve, pkey[1]); + xfree (curve); + /* FIXME: Take care of RC. */ + /* Put K into a simplified S-expression. */ + if (rc || gcry_sexp_build (&s_data, NULL, "%m", k)) + BUG (); + } } else return gpg_error (GPG_ERR_PUBKEY_ALGO); @@ -272,9 +289,16 @@ pk_check_secret_key (int algo, gcry_mpi_t *skey) } 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] ); + char *curve = openpgp_oid_to_str (skey[0]); + if (!curve) + rc = gpg_error_from_syserror (); + else + { + rc = gcry_sexp_build (&s_skey, NULL, + "(private-key(ecdsa(curve%s)(q%m)(d%m)))", + curve, skey[1], skey[2]); + xfree (curve); + } } else return GPG_ERR_PUBKEY_ALGO; diff --git a/g10/pkglue.h b/g10/pkglue.h index 98d8c1440..eb0d7c1dc 100644 --- a/g10/pkglue.h +++ b/g10/pkglue.h @@ -32,7 +32,7 @@ int pk_check_secret_key (int algo, gcry_mpi_t *skey); /*-- ecdh.c --*/ -byte *pk_ecdh_default_params (unsigned int qbits, size_t *sizeout); +gcry_mpi_t pk_ecdh_default_params (unsigned int qbits); gpg_error_t pk_ecdh_generate_ephemeral_key (gcry_mpi_t *pkey, gcry_mpi_t *r_k); gpg_error_t pk_ecdh_encrypt_with_shared_point /* */ (int is_encrypt, gcry_mpi_t shared_mpi, -- cgit v1.2.3 From 328a642aa5ed971870a2667b06307f760fa251dc Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Mon, 31 Jan 2011 15:44:24 +0100 Subject: Fixed the ECC interface to Libgcrypt to be ABI compatible with the previous version. Quite some changes were needed but in the end we have less code than before. Instead of trying to do everything with MPIs and pass them back and forth between Libgcrypt and GnuPG, we know use the S-expression based interface and make heavy use of our opaque MPI feature. Encryption, decryption, signing and verification work with self-generared keys. Import and export does not yet work; thus it was not possible to check the test keys at https://sites.google.com/site/brainhub/pgpecckeys . --- g10/ChangeLog | 7 ++ g10/build-packet.c | 51 ++------------ g10/ecdh.c | 191 +++++++++++++++++++++++++++-------------------------- g10/encrypt.c | 2 +- g10/packet.h | 2 +- g10/pkglue.c | 4 +- g10/pubkey-enc.c | 2 +- 7 files changed, 115 insertions(+), 144 deletions(-) (limited to 'g10') diff --git a/g10/ChangeLog b/g10/ChangeLog index f6c144d7c..587c9f158 100644 --- a/g10/ChangeLog +++ b/g10/ChangeLog @@ -1,3 +1,10 @@ +2011-01-31 Werner Koch + + * ecdh.c (pk_ecdh_encrypt_with_shared_point): Return an opaque MPI. + + * build-packet.c (mpi_write): Rename to gpg_mpi_write and make global. + (write_size_body_mpi): Remove. + 2011-01-30 Werner Koch diff --git a/g10/build-packet.c b/g10/build-packet.c index 122ef15e7..1cdf9616c 100644 --- a/g10/build-packet.c +++ b/g10/build-packet.c @@ -157,8 +157,8 @@ build_packet( IOBUF out, PACKET *pkt ) /* * Write the mpi A to OUT. */ -static int -mpi_write (iobuf_t out, gcry_mpi_t a) +gpg_error_t +gpg_mpi_write (iobuf_t out, gcry_mpi_t a) { int rc; @@ -191,45 +191,6 @@ mpi_write (iobuf_t out, gcry_mpi_t a) } -/* - * 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. - */ -gpg_error_t -write_size_body_mpi (iobuf_t out, gcry_mpi_t a) -{ - gpg_error_t err; - byte buffer[256]; /* Fixed buffer for a public parameter, max possible */ - size_t nbytes = (mpi_get_nbits (a)+7)/8; - - if (nbytes > sizeof(buffer)) - { - log_error("mpi with size+body is too large (%u bytes)\n", nbytes); - return gpg_error (GPG_ERR_TOO_LARGE); - } - - err = gcry_mpi_print (GCRYMPI_FMT_USG, buffer, sizeof(buffer), &nbytes, a); - if (err) - { - log_error ("failed to exported size+body mpi\n"); - return err; - } - 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); -} - - /* Calculate the length of a packet described by PKT. */ u32 calc_packet_length( PACKET *pkt ) @@ -341,7 +302,7 @@ do_key (iobuf_t out, int ctb, PKT_public_key *pk) for (i=0; i < npkey; i++ ) { - err = mpi_write (a, pk->pkey[i]); + err = gpg_mpi_write (a, pk->pkey[i]); if (err) goto leave; } @@ -436,7 +397,7 @@ do_key (iobuf_t out, int ctb, PKT_public_key *pk) { /* Non-protected key. */ for ( ; i < nskey; i++ ) - if ( (err = mpi_write (a, pk->pkey[i]))) + if ( (err = gpg_mpi_write (a, pk->pkey[i]))) goto leave; write_16 (a, ski->csum ); } @@ -512,7 +473,7 @@ do_pubkey_enc( IOBUF out, int ctb, PKT_pubkey_enc *enc ) write_fake_data( a, enc->data[0] ); for (i=0; i < n && !rc ; i++ ) - rc = mpi_write (a, enc->data[i]); + rc = gpg_mpi_write (a, enc->data[i]); if (!rc) { @@ -1170,7 +1131,7 @@ do_signature( IOBUF out, int ctb, PKT_signature *sig ) if ( !n ) write_fake_data( a, sig->data[0] ); for (i=0; i < n && !rc ; i++ ) - rc = mpi_write(a, sig->data[i] ); + rc = gpg_mpi_write (a, sig->data[i] ); if (!rc) { diff --git a/g10/ecdh.c b/g10/ecdh.c index cf002b957..09ab3ed16 100644 --- a/g10/ecdh.c +++ b/g10/ecdh.c @@ -1,5 +1,5 @@ /* ecdh.c - ECDH public key operations used in public key glue code - * Copyright (C) 2010 Free Software Foundation, Inc. + * Copyright (C) 2010, 2011 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -105,11 +105,13 @@ pk_ecdh_encrypt_with_shared_point (int is_encrypt, gcry_mpi_t shared_mpi, gpg_error_t err; byte *secret_x; int secret_x_size; - byte kdf_params[256]; - int kdf_params_size=0; - int nbits; + unsigned int nbits; + const unsigned char *kdf_params; + size_t kdf_params_size; int kdf_hash_algo; int kdf_encr_algo; + unsigned char message[256]; + size_t message_size; *r_result = NULL; @@ -137,12 +139,11 @@ pk_ecdh_encrypt_with_shared_point (int is_encrypt, gcry_mpi_t shared_mpi, return err; } - /* fixme: explain what we are doing. */ 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 ); } @@ -155,38 +156,34 @@ pk_ecdh_encrypt_with_shared_point (int is_encrypt, gcry_mpi_t shared_mpi, * current secret_x with a value derived from it. This will become * a KEK. */ - { - IOBUF obuf = iobuf_temp(); - err = 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; - } + if (!gcry_mpi_get_flag (pkey[2], GCRYMPI_FLAG_OPAQUE)) + return GPG_ERR_BUG; + kdf_params = gcry_mpi_get_opaque (pkey[2], &nbits); + kdf_params_size = (nbits+7)/8; + + if (DBG_CIPHER) + log_printhex ("ecdh KDF params:", 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", + openpgp_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. */ { @@ -194,18 +191,17 @@ pk_ecdh_encrypt_with_shared_point (int is_encrypt, gcry_mpi_t shared_mpi, obuf = iobuf_temp(); /* variable-length field 1, curve name OID */ - err = write_size_body_mpi (obuf, pkey[0]); + err = gpg_mpi_write (obuf, pkey[0]); /* fixed-length field 2 */ iobuf_put (obuf, PUBKEY_ALGO_ECDH); /* variable-length field 3, KDF params */ - err = (err ? err : write_size_body_mpi ( obuf, pkey[2] )); + err = (err ? err : gpg_mpi_write (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)); + message_size = iobuf_temp_to_buffer (obuf, message, sizeof message); iobuf_close (obuf); if (err) return err; @@ -223,10 +219,10 @@ pk_ecdh_encrypt_with_shared_point (int is_encrypt, gcry_mpi_t shared_mpi, err = gcry_md_open (&h, kdf_hash_algo, 0); if(err) log_bug ("gcry_md_open failed for algo %d: %s", - kdf_hash_algo, gpg_strerror (gcry_error(err))); - 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 */ + kdf_hash_algo, gpg_strerror (err)); + 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); @@ -320,13 +316,13 @@ pk_ecdh_encrypt_with_shared_point (int is_encrypt, gcry_mpi_t shared_mpi, if (DBG_CIPHER) log_printhex ("ecdh encrypted to:", data_buf+1, data_buf[0] ); - err = gcry_mpi_scan (&result, GCRYMPI_FMT_USG, - data_buf, 1+data_buf[0], NULL); - /* (byte)size + aeswrap of DEK */ - xfree( data_buf ); - if (err) + result = gcry_mpi_set_opaque (NULL, data_buf, 8 * (1+data_buf[0])); + if (!result) { - log_error ("ecdh failed to create an MPI: %s\n", gpg_strerror (err)); + err = gpg_error_from_syserror (); + xfree (data_buf); + log_error ("ecdh failed to create an MPI: %s\n", + gpg_strerror (err)); return err; } @@ -335,55 +331,62 @@ pk_ecdh_encrypt_with_shared_point (int is_encrypt, gcry_mpi_t shared_mpi, else { byte *in; + const void *p; - err = 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) + p = gcry_mpi_get_opaque (data, &nbits); + nbytes = (nbits+7)/8; + if (!p || nbytes > data_buf_size || !nbytes) + { + xfree (data_buf); + return GPG_ERR_BAD_MPI; + } + memcpy (data_buf, p, nbytes); + if (data_buf[0] != nbytes-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); - - err = gcry_cipher_decrypt (hd, in, data_buf_size, data_buf+1, - data_buf_size); - gcry_cipher_close (hd); - if (err) - { - log_error ("ecdh failed in gcry_cipher_decrypt: %s\n", - gpg_strerror (err)); - xfree (data_buf); - return err; - } - - 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; */ - /* } */ + 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); + + err = gcry_cipher_decrypt (hd, in, data_buf_size, data_buf+1, + data_buf_size); + gcry_cipher_close (hd); + if (err) + { + log_error ("ecdh failed in gcry_cipher_decrypt: %s\n", + gpg_strerror (err)); + xfree (data_buf); + return err; + } + + 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; */ + /* } */ - err = gcry_mpi_scan ( &result, GCRYMPI_FMT_USG, in, data_buf_size, NULL); - xfree (data_buf); - if (err) - { - log_error ("ecdh failed to create a plain text MPI: %s\n", - gpg_strerror (err)); - return err; - } - - *r_result = result; + err = gcry_mpi_scan (&result, GCRYMPI_FMT_USG, in, data_buf_size, NULL); + xfree (data_buf); + if (err) + { + log_error ("ecdh failed to create a plain text MPI: %s\n", + gpg_strerror (err)); + return err; + } + + *r_result = result; } } diff --git a/g10/encrypt.c b/g10/encrypt.c index f52921582..8548a5739 100644 --- a/g10/encrypt.c +++ b/g10/encrypt.c @@ -921,7 +921,7 @@ write_pubkey_enc_from_list (PK_LIST pk_list, DEK *dek, iobuf_t out) { char *ustr = get_user_id_string_native (enc->keyid); log_info (_("%s/%s encrypted for: \"%s\"\n"), - gcry_pk_algo_name (enc->pubkey_algo), + openpgp_pk_algo_name (enc->pubkey_algo), openpgp_cipher_algo_name (dek->algo), ustr ); xfree (ustr); diff --git a/g10/packet.h b/g10/packet.h index d06c4c7e7..5411e524a 100644 --- a/g10/packet.h +++ b/g10/packet.h @@ -444,7 +444,7 @@ PACKET *create_gpg_control ( ctrlpkttype_t type, /*-- build-packet.c --*/ int build_packet( iobuf_t inp, PACKET *pkt ); -gpg_error_t write_size_body_mpi (iobuf_t out, gcry_mpi_t a); +gpg_error_t gpg_mpi_write (iobuf_t out, gcry_mpi_t a); u32 calc_packet_length( PACKET *pkt ); void build_sig_subpkt( PKT_signature *sig, sigsubpkttype_t type, const byte *buffer, size_t buflen ); diff --git a/g10/pkglue.c b/g10/pkglue.c index 27ee239a4..66ba48b60 100644 --- a/g10/pkglue.c +++ b/g10/pkglue.c @@ -220,8 +220,8 @@ pk_encrypt (int algo, gcry_mpi_t *resarr, gcry_mpi_t data, gcry_mpi_t shared, public, result; /* Get the shared point and the ephemeral public key. */ - shared = mpi_from_sexp (s_ciph, "a"); - public = mpi_from_sexp (s_ciph, "b"); + shared = mpi_from_sexp (s_ciph, "s"); + public = mpi_from_sexp (s_ciph, "e"); gcry_sexp_release (s_ciph); s_ciph = NULL; if (DBG_CIPHER) diff --git a/g10/pubkey-enc.c b/g10/pubkey-enc.c index 43f5419db..d45b4a217 100644 --- a/g10/pubkey-enc.c +++ b/g10/pubkey-enc.c @@ -176,7 +176,7 @@ get_it (PKT_pubkey_enc *enc, DEK *dek, PKT_public_key *sk, u32 *keyid) 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)))", + err = gcry_sexp_build (&s_data, NULL, "(enc-val(ecdh(s%m)(e%m)))", enc->data[0], enc->data[1]); } else -- cgit v1.2.3 From e0d4139e19ceed9375cc7e7ba801a965d3376f7d Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Mon, 31 Jan 2011 18:19:14 +0100 Subject: Move OpenPGP OID helpers to common/. This is needed so that the agent will be able to export and import OpenPGP secret keys. Add test case. Removed unused function. --- g10/ChangeLog | 3 + g10/main.h | 4 -- g10/misc.c | 201 ---------------------------------------------------------- 3 files changed, 3 insertions(+), 205 deletions(-) (limited to 'g10') diff --git a/g10/ChangeLog b/g10/ChangeLog index 587c9f158..2a284e765 100644 --- a/g10/ChangeLog +++ b/g10/ChangeLog @@ -1,5 +1,8 @@ 2011-01-31 Werner Koch + * misc.c (make_flagged_int, openpgp_oid_from_str) + (openpgp_oid_to_str): Move to ../common/openpgp-oids.c. + * ecdh.c (pk_ecdh_encrypt_with_shared_point): Return an opaque MPI. * build-packet.c (mpi_write): Rename to gpg_mpi_write and make global. diff --git a/g10/main.h b/g10/main.h index d76d96bc4..d70c16901 100644 --- a/g10/main.h +++ b/g10/main.h @@ -165,10 +165,6 @@ unsigned int pubkey_nbits( int algo, gcry_mpi_t *pkey ); int mpi_print (estream_t stream, gcry_mpi_t a, int mode); unsigned int ecdsa_qbits_from_Q (unsigned int qbits); -/* Other stuff */ -gpg_error_t openpgp_oid_from_str (const char *string, gcry_mpi_t *r_mpi); -char *openpgp_oid_to_str (gcry_mpi_t a); - /*-- status.c --*/ void set_status_fd ( int fd ); diff --git a/g10/misc.c b/g10/misc.c index 2052e96c7..dcd2bd10b 100644 --- a/g10/misc.c +++ b/g10/misc.c @@ -1552,204 +1552,3 @@ ecdsa_qbits_from_Q (unsigned int qbits) } - -/* Helper for openpgp_oid_from_str. */ -static size_t -make_flagged_int (unsigned long value, char *buf, size_t buflen) -{ - int more = 0; - int shift; - - /* fixme: figure out the number of bits in an ulong and start with - that value as shift (after making it a multiple of 7) a more - straigtforward implementation is to do it in reverse order using - a temporary buffer - saves a lot of compares */ - for (more=0, shift=28; shift > 0; shift -= 7) - { - if (more || value >= (1<> shift); - value -= (value >> shift) << shift; - more = 1; - } - } - buf[buflen++] = value; - return buflen; -} - - -/* Convert the OID given in dotted decimal form in STRING to an DER - * encoding and store it as an opaque value at R_MPI. The format of - * the DER encoded is not a regular ASN.1 object but the modified - * format as used by OpenPGP for the ECC curve description. On error - * the function returns and error code an NULL is stored at R_BUG. - * Note that scanning STRING stops at the first white space - * character. */ -gpg_error_t -openpgp_oid_from_str (const char *string, gcry_mpi_t *r_mpi) -{ - unsigned char *buf; - size_t buflen; - unsigned long val1, val; - const char *endp; - int arcno; - - *r_mpi = NULL; - - if (!string || !*string) - return gpg_error (GPG_ERR_INV_VALUE); - - /* We can safely assume that the encoded OID is shorter than the string. */ - buf = xtrymalloc (1 + strlen (string) + 2); - if (!buf) - return gpg_error_from_syserror (); - /* Save the first byte for the length. */ - buflen = 1; - - val1 = 0; /* Avoid compiler warning. */ - arcno = 0; - do { - arcno++; - val = strtoul (string, (char**)&endp, 10); - if (!digitp (string) || !(*endp == '.' || !*endp)) - { - xfree (buf); - return gpg_error (GPG_ERR_INV_OID_STRING); - } - if (*endp == '.') - string = endp+1; - - if (arcno == 1) - { - if (val > 2) - break; /* Not allowed, error catched below. */ - val1 = val; - } - else if (arcno == 2) - { /* Need to combine the first two arcs in one octet. */ - if (val1 < 2) - { - if (val > 39) - { - xfree (buf); - return gpg_error (GPG_ERR_INV_OID_STRING); - } - buf[buflen++] = val1*40 + val; - } - else - { - val += 80; - buflen = make_flagged_int (val, buf, buflen); - } - } - else - { - buflen = make_flagged_int (val, buf, buflen); - } - } while (*endp == '.'); - - if (arcno == 1 || buflen < 2 || buflen > 254 ) - { /* It is not possible to encode only the first arc. */ - xfree (buf); - return gpg_error (GPG_ERR_INV_OID_STRING); - } - - *buf = buflen - 1; - *r_mpi = gcry_mpi_set_opaque (NULL, buf, buflen * 8); - if (!*r_mpi) - { - xfree (buf); - return gpg_error_from_syserror (); - } - return 0; -} - - -/* Return a malloced string represenation of the OID in the opaque MPI - A. In case of an error NULL is returned and ERRNO is set. */ -char * -openpgp_oid_to_str (gcry_mpi_t a) -{ - const unsigned char *buf; - size_t length; - char *string, *p; - int n = 0; - unsigned long val, valmask; - - valmask = (unsigned long)0xfe << (8 * (sizeof (valmask) - 1)); - - if (!a || !gcry_mpi_get_flag (a, GCRYMPI_FLAG_OPAQUE)) - { - gpg_err_set_errno (EINVAL); - return NULL; - } - - buf = gcry_mpi_get_opaque (a, &length); - length = (length+7)/8; - - /* The first bytes gives the length; check consistency. */ - if (!length || buf[0] != length -1) - { - gpg_err_set_errno (EINVAL); - return NULL; - } - /* Skip length byte. */ - length--; - buf++; - - /* To calculate the length of the string we can safely assume an - upper limit of 3 decimal characters per byte. Two extra bytes - account for the special first octect */ - string = p = xtrymalloc (length*(1+3)+2+1); - if (!string) - return NULL; - if (!buf || !length) - { - *p = 0; - return string; - } - - if (buf[0] < 40) - p += sprintf (p, "0.%d", buf[n]); - else if (buf[0] < 80) - p += sprintf (p, "1.%d", buf[n]-40); - else { - val = buf[n] & 0x7f; - while ( (buf[n]&0x80) && ++n < length ) - { - if ( (val & valmask) ) - goto badoid; /* Overflow. */ - val <<= 7; - val |= buf[n] & 0x7f; - } - val -= 80; - sprintf (p, "2.%lu", val); - p += strlen (p); - } - for (n++; n < length; n++) - { - val = buf[n] & 0x7f; - while ( (buf[n]&0x80) && ++n < length ) - { - if ( (val & valmask) ) - goto badoid; /* Overflow. */ - val <<= 7; - val |= buf[n] & 0x7f; - } - sprintf (p, ".%lu", val); - p += strlen (p); - } - - *p = 0; - return string; - - badoid: - /* Return a special OID (gnu.gnupg.badoid) to indicate the error - case. The OID is broken and thus we return one which can't do - any harm. Formally this does not need to be a bad OID but an OID - with an arc that can't be represented in a 32 bit word is more - than likely corrupt. */ - xfree (string); - return xtrystrdup ("1.3.6.1.4.1.11591.2.12242973"); -} - -- cgit v1.2.3 From 4659c923a08002a72cb4bb5b3c4e6a02d7484767 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Wed, 2 Feb 2011 15:48:54 +0100 Subject: Sample ECC keys and message do now work. Import and export of secret keys does now work. Encryption has been fixed to be compatible with the sample messages. This version tests for new Libgcrypt function and thus needs to be build with a new Libgcrypt installed. --- g10/ChangeLog | 15 ++++ g10/ecdh.c | 81 +++++++++--------- g10/export.c | 148 ++++++++++++++++++++++++-------- g10/gpg.c | 3 + g10/import.c | 250 +++++++++++++++++++++++++++++++++++++++++-------------- g10/keygen.c | 57 +++++++++---- g10/keyid.c | 97 +++++++++++---------- g10/main.h | 1 + g10/passphrase.c | 104 +++++++++++------------ 9 files changed, 504 insertions(+), 252 deletions(-) (limited to 'g10') diff --git a/g10/ChangeLog b/g10/ChangeLog index 2a284e765..bb55ff85f 100644 --- a/g10/ChangeLog +++ b/g10/ChangeLog @@ -1,3 +1,18 @@ +2011-02-02 Werner Koch + + * import.c (transfer_secret_keys): Make sure keyids are available. + + * keyid.c (hash_public_key): Adjust for the ECC case. + +2011-02-01 Werner Koch + + * import.c (transfer_secret_keys): Implement ECC case. + + * gpg.c (main): Call setup_libgcrypt_logging. + + * keygen.c (gpg_curve_to_oid): New. + (ecckey_from_sexp): Factor curve name mapping out to new function. + 2011-01-31 Werner Koch * misc.c (make_flagged_int, openpgp_oid_from_str) diff --git a/g10/ecdh.c b/g10/ecdh.c index 09ab3ed16..f97667ae3 100644 --- a/g10/ecdh.c +++ b/g10/ecdh.c @@ -36,7 +36,7 @@ static const struct unsigned int qbits; int openpgp_hash_id; /* KEK digest algorithm. */ int openpgp_cipher_id; /* KEK cipher algorithm. */ -} kek_params_table[] = +} kek_params_table[] = /* Note: Must be sorted by ascending values for QBITS. */ { { 256, DIGEST_ALGO_SHA256, CIPHER_ALGO_AES }, @@ -60,8 +60,8 @@ pk_ecdh_default_params (unsigned int qbits) if (!kek_params) return NULL; kek_params[0] = 3; /* Number of bytes to follow. */ - kek_params[1] = 1; /* Version for KDF+AESWRAP. */ - + kek_params[1] = 1; /* Version for KDF+AESWRAP. */ + /* Search for matching KEK parameter. Defaults to the strongest possible choices. Performance is not an issue here, only interoperability. */ @@ -78,7 +78,7 @@ pk_ecdh_default_params (unsigned int qbits) assert (i < DIM (kek_params_table)); if (DBG_CIPHER) log_printhex ("ECDH KEK params are", kek_params, sizeof(kek_params) ); - + return gcry_mpi_set_opaque (NULL, kek_params, 4 * 8); } @@ -88,16 +88,16 @@ pk_ecdh_default_params (unsigned int qbits) key_derivation+key_wrapping. If IS_ENCRYPT is true the function encrypts; if false, it decrypts. On success the result is stored at R_RESULT; on failure NULL is stored at R_RESULT and an error - code returned. + code returned. FIXME: explain PKEY and PK_FP. */ - + /* TODO: memory leaks (x_secret). */ gpg_error_t -pk_ecdh_encrypt_with_shared_point (int is_encrypt, gcry_mpi_t shared_mpi, +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 *r_result) @@ -106,8 +106,8 @@ pk_ecdh_encrypt_with_shared_point (int is_encrypt, gcry_mpi_t shared_mpi, byte *secret_x; int secret_x_size; unsigned int nbits; - const unsigned char *kdf_params; - size_t kdf_params_size; + const unsigned char *kek_params; + size_t kek_params_size; int kdf_hash_algo; int kdf_encr_algo; unsigned char message[256]; @@ -139,11 +139,11 @@ pk_ecdh_encrypt_with_shared_point (int is_encrypt, gcry_mpi_t shared_mpi, return err; } - secret_x_size = (nbits+7)/8; + 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 ); } @@ -158,24 +158,24 @@ pk_ecdh_encrypt_with_shared_point (int is_encrypt, gcry_mpi_t shared_mpi, */ if (!gcry_mpi_get_flag (pkey[2], GCRYMPI_FLAG_OPAQUE)) return GPG_ERR_BUG; - kdf_params = gcry_mpi_get_opaque (pkey[2], &nbits); - kdf_params_size = (nbits+7)/8; - + kek_params = gcry_mpi_get_opaque (pkey[2], &nbits); + kek_params_size = (nbits+7)/8; + if (DBG_CIPHER) - log_printhex ("ecdh KDF params:", kdf_params, kdf_params_size); - + log_printhex ("ecdh KDF params:", kek_params, kek_params_size); + /* Expect 4 bytes 03 01 hash_alg symm_alg. */ - if (kdf_params_size != 4 || kdf_params[0] != 3 || kdf_params[1] != 1) + if (kek_params_size != 4 || kek_params[0] != 3 || kek_params[1] != 1) return GPG_ERR_BAD_PUBKEY; - - kdf_hash_algo = kdf_params[2]; - kdf_encr_algo = kdf_params[3]; - + + kdf_hash_algo = kek_params[2]; + kdf_encr_algo = kek_params[3]; + if (DBG_CIPHER) log_debug ("ecdh KDF algorithms %s+%s with aeswrap\n", openpgp_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) @@ -199,7 +199,7 @@ pk_ecdh_encrypt_with_shared_point (int is_encrypt, gcry_mpi_t shared_mpi, /* fixed-length field 4 */ iobuf_write (obuf, "Anonymous Sender ", 20); /* fixed-length field 5, recipient fp */ - iobuf_write (obuf, pk_fp, 20); + iobuf_write (obuf, pk_fp, 20); message_size = iobuf_temp_to_buffer (obuf, message, sizeof message); iobuf_close (obuf); @@ -207,11 +207,10 @@ pk_ecdh_encrypt_with_shared_point (int is_encrypt, gcry_mpi_t shared_mpi, return err; if(DBG_CIPHER) - log_printhex ("ecdh KDF message params are:", - kdf_params, kdf_params_size ); + log_printhex ("ecdh KDF message params are:", message, message_size); } - /* Derive a KEK (key wrapping key) using kdf_params and secret_x. */ + /* Derive a KEK (key wrapping key) using MESSAGE and SECRET_X. */ { gcry_md_hd_t h; int old_size; @@ -222,7 +221,7 @@ pk_ecdh_encrypt_with_shared_point (int is_encrypt, gcry_mpi_t shared_mpi, kdf_hash_algo, gpg_strerror (err)); 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_write(h, message, message_size);/* KDF parameters */ gcry_md_final (h); @@ -242,7 +241,7 @@ pk_ecdh_encrypt_with_shared_point (int is_encrypt, gcry_mpi_t shared_mpi, 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; @@ -284,7 +283,7 @@ pk_ecdh_encrypt_with_shared_point (int is_encrypt, gcry_mpi_t shared_mpi, 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. */ err = gcry_mpi_print (GCRYMPI_FMT_USG, in, @@ -296,7 +295,7 @@ pk_ecdh_encrypt_with_shared_point (int is_encrypt, gcry_mpi_t shared_mpi, xfree (data_buf); return err; } - + if (DBG_CIPHER) log_printhex ("ecdh encrypting :", in, data_buf_size ); @@ -325,14 +324,14 @@ pk_ecdh_encrypt_with_shared_point (int is_encrypt, gcry_mpi_t shared_mpi, gpg_strerror (err)); return err; } - + *r_result = result; } else { byte *in; const void *p; - + p = gcry_mpi_get_opaque (data, &nbits); nbytes = (nbits+7)/8; if (!p || nbytes > data_buf_size || !nbytes) @@ -349,10 +348,10 @@ pk_ecdh_encrypt_with_shared_point (int is_encrypt, gcry_mpi_t shared_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); - + err = gcry_cipher_decrypt (hd, in, data_buf_size, data_buf+1, data_buf_size); gcry_cipher_close (hd); @@ -363,12 +362,12 @@ pk_ecdh_encrypt_with_shared_point (int is_encrypt, gcry_mpi_t shared_mpi, xfree (data_buf); return err; } - + 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 ) */ /* { */ @@ -376,7 +375,7 @@ pk_ecdh_encrypt_with_shared_point (int is_encrypt, gcry_mpi_t shared_mpi, /* in[data_buf_size-1] ); */ /* return GPG_ERR_BAD_KEY; */ /* } */ - + err = gcry_mpi_scan (&result, GCRYMPI_FMT_USG, in, data_buf_size, NULL); xfree (data_buf); if (err) @@ -385,11 +384,11 @@ pk_ecdh_encrypt_with_shared_point (int is_encrypt, gcry_mpi_t shared_mpi, gpg_strerror (err)); return err; } - + *r_result = result; } } - + return err; } @@ -453,5 +452,3 @@ pk_ecdh_decrypt (gcry_mpi_t * result, const byte sk_fp[MAX_FINGERPRINT_LEN], sk_fp, data/*encr data as an MPI*/, skey, result); } - - diff --git a/g10/export.c b/g10/export.c index 1eb0baa8b..91a6c87f1 100644 --- a/g10/export.c +++ b/g10/export.c @@ -107,7 +107,7 @@ export_pubkeys_stream (ctrl_t ctrl, iobuf_t out, strlist_t users, kbnode_t *keyblock_out, unsigned int options ) { int any, rc; - + rc = do_export_stream (ctrl, out, users, 0, keyblock_out, options, &any); if (!rc && !any) rc = -1; @@ -197,9 +197,9 @@ do_export (ctrl_t ctrl, strlist_t users, int secret, unsigned int options ) int any, rc; armor_filter_context_t *afx = NULL; compress_filter_context_t zfx; - + memset( &zfx, 0, sizeof zfx); - + rc = open_outfile (GNUPG_INVALID_FD, NULL, 0, &out ); if (rc) return rc; @@ -251,7 +251,7 @@ subkey_in_list_p (subkey_list_t list, KBNODE node) u32 kid[2]; keyid_from_pk (node->pkt->pkt.public_key, kid); - + for (; list; list = list->next) if (list->kid[0] == kid[0] && list->kid[1] == kid[1]) return 1; @@ -293,17 +293,17 @@ exact_subkey_match_p (KEYDB_SEARCH_DESC *desc, KBNODE node) case KEYDB_SEARCH_MODE_LONG_KID: keyid_from_pk (node->pkt->pkt.public_key, kid); break; - + case KEYDB_SEARCH_MODE_FPR16: case KEYDB_SEARCH_MODE_FPR20: case KEYDB_SEARCH_MODE_FPR: fingerprint_from_pk (node->pkt->pkt.public_key, fpr,&fprlen); break; - + default: break; } - + switch(desc->mode) { case KEYDB_SEARCH_MODE_SHORT_KID: @@ -346,7 +346,7 @@ canon_pubkey_algo (int algo) case GCRY_PK_RSA: case GCRY_PK_RSA_E: case GCRY_PK_RSA_S: return GCRY_PK_RSA; - case GCRY_PK_ELG: + case GCRY_PK_ELG: case GCRY_PK_ELG_E: return GCRY_PK_ELG; default: return algo; } @@ -354,7 +354,7 @@ canon_pubkey_algo (int algo) /* Use the key transfer format given in S_PGP to create the secinfo - structure in PK and chnage the parameter array in PK to include the + structure in PK and change the parameter array in PK to include the secret parameters. */ static gpg_error_t transfer_format_to_openpgp (gcry_sexp_t s_pgp, PKT_public_key *pk) @@ -415,7 +415,7 @@ transfer_format_to_openpgp (gcry_sexp_t s_pgp, PKT_public_key *pk) goto bad_seckey; protect_algo = gcry_cipher_map_name (string); xfree (string); - + value = gcry_sexp_nth_data (list, 3, &valuelen); if (!value || !valuelen || valuelen > sizeof iv) goto bad_seckey; @@ -460,6 +460,7 @@ transfer_format_to_openpgp (gcry_sexp_t s_pgp, PKT_public_key *pk) || gcry_pk_algo_info (pubkey_algo, GCRYCTL_GET_ALGO_NSKEY, NULL, &nskey) || !npkey || npkey >= nskey || nskey > PUBKEY_MAX_NSKEY) goto bad_seckey; + pubkey_algo = map_pk_gcry_to_openpgp (pubkey_algo); gcry_sexp_release (list); list = gcry_sexp_find_token (top_list, "skey", 0); @@ -557,6 +558,77 @@ transfer_format_to_openpgp (gcry_sexp_t s_pgp, PKT_public_key *pk) goto leave; } + /* We need to change the received parameters for ECC algorithms. + The transfer format has all parameters but OpenPGP defines that + only the OID of the curve is to be used. */ + if (pubkey_algo == PUBKEY_ALGO_ECDSA || pubkey_algo == PUBKEY_ALGO_ECDH) + { + gcry_sexp_t s_pubkey; + const char *curvename, *curveoidstr; + gcry_mpi_t mpi; + + /* We build an S-expression with the public key parameters and + ask Libgcrypt to return the matching curve name. */ + if (npkey != 6 || !skey[0] || !skey[1] || !skey[2] + || !skey[3] || !skey[4] || !skey[5] + || !skey[6] || skey[7]) + { + err = gpg_error (GPG_ERR_INTERNAL); + goto leave; + } + err = gcry_sexp_build (&s_pubkey, NULL, + "(public-key(ecc(p%m)(a%m)(b%m)(g%m)(n%m)))", + skey[0], skey[1], skey[2], skey[3], skey[4]); + if (err) + goto leave; +#ifdef HAVE_GCRY_PK_GET_CURVE + curvename = gcry_pk_get_curve (s_pubkey, 0, NULL); +#else + curvename = "?"; +#endif + gcry_sexp_release (s_pubkey); + curveoidstr = gpg_curve_to_oid (curvename, NULL); + if (!curveoidstr) + { + log_error ("no OID known for curve `%s'\n", curvename); + err = gpg_error (GPG_ERR_UNKNOWN_NAME); + goto leave; + } + err = openpgp_oid_from_str (curveoidstr, &mpi); + if (err) + goto leave; + + /* Now replace the curve parameters by the OID and shift the + rest of the parameters. */ + gcry_mpi_release (skey[0]); + skey[0] = mpi; + for (idx=1; idx <= 4; idx++) + gcry_mpi_release (skey[idx]); + skey[1] = skey[5]; + skey[2] = skey[6]; + for (idx=3; idx <= 6; idx++) + skey[idx] = NULL; + + /* Fixup the NPKEY and NSKEY to match OpenPGP reality. */ + npkey = 2; + nskey = 3; + + /* for (idx=0; skey[idx]; idx++) */ + /* { */ + /* log_info ("YYY skey[%d]:", idx); */ + /* if (gcry_mpi_get_flag (skey[idx], GCRYMPI_FLAG_OPAQUE)) */ + /* { */ + /* void *p; */ + /* unsigned int nbits; */ + /* p = gcry_mpi_get_opaque (skey[idx], &nbits); */ + /* log_printhex (NULL, p, (nbits+7)/8); */ + /* } */ + /* else */ + /* gcry_mpi_dump (skey[idx]); */ + /* log_printf ("\n"); */ + /* } */ + } + /* Do some sanity checks. */ if (s2k_count <= 1024) { @@ -576,11 +648,17 @@ transfer_format_to_openpgp (gcry_sexp_t s_pgp, PKT_public_key *pk) err = openpgp_md_test_algo (s2k_algo); if (err) goto leave; - - /* Check that the public key parameters match. */ + + /* Check that the public key parameters match. Since Libgcrypt 1.5 + and the gcry_pk_get_curve function, gcry_mpi_cmp handles opaque + MPI correctly and thus we don't need to to do the extra + opaqueness checks. */ for (idx=0; idx < npkey; idx++) - if (gcry_mpi_get_flag (pk->pkey[idx], GCRYMPI_FLAG_OPAQUE) + if (0 +#ifndef HAVE_GCRY_PK_GET_CURVE + gcry_mpi_get_flag (pk->pkey[idx], GCRYMPI_FLAG_OPAQUE) || gcry_mpi_get_flag (skey[idx], GCRYMPI_FLAG_OPAQUE) +#endif || gcry_mpi_cmp (pk->pkey[idx], skey[idx])) { err = gpg_error (GPG_ERR_BAD_PUBKEY); @@ -607,7 +685,7 @@ transfer_format_to_openpgp (gcry_sexp_t s_pgp, PKT_public_key *pk) err = gpg_error_from_syserror (); goto leave; } - + ski->is_protected = 1; ski->sha1chk = 1; ski->algo = protect_algo; @@ -636,7 +714,7 @@ transfer_format_to_openpgp (gcry_sexp_t s_pgp, PKT_public_key *pk) bad_seckey: err = gpg_error (GPG_ERR_BAD_SECKEY); goto leave; - + outofmem: err = gpg_error (GPG_ERR_ENOMEM); goto leave; @@ -671,7 +749,7 @@ do_export_stream (ctrl_t ctrl, iobuf_t out, strlist_t users, int secret, init_packet (&pkt); kdbhd = keydb_new (); - if (!users) + if (!users) { ndesc = 1; desc = xcalloc (ndesc, sizeof *desc); @@ -679,10 +757,10 @@ do_export_stream (ctrl_t ctrl, iobuf_t out, strlist_t users, int secret, } else { - for (ndesc=0, sl=users; sl; sl = sl->next, ndesc++) + for (ndesc=0, sl=users; sl; sl = sl->next, ndesc++) ; desc = xmalloc ( ndesc * sizeof *desc); - + for (ndesc=0, sl=users; sl; sl = sl->next) { if (!(err=classify_user_id (sl->d, desc+ndesc))) @@ -708,7 +786,7 @@ do_export_stream (ctrl_t ctrl, iobuf_t out, strlist_t users, int secret, goto leave; } #endif - + /* For secret key export we need to setup a decryption context. */ if (secret) { @@ -721,7 +799,7 @@ do_export_stream (ctrl_t ctrl, iobuf_t out, strlist_t users, int secret, log_error ("error getting the KEK: %s\n", gpg_strerror (err)); goto leave; } - + /* Prepare a cipher context. */ err = gcry_cipher_open (&cipherhd, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_AESWRAP, 0); @@ -737,20 +815,20 @@ do_export_stream (ctrl_t ctrl, iobuf_t out, strlist_t users, int secret, kek = NULL; } - while (!(err = keydb_search2 (kdbhd, desc, ndesc, &descindex))) + while (!(err = keydb_search2 (kdbhd, desc, ndesc, &descindex))) { int skip_until_subkey = 0; u32 keyid[2]; PKT_public_key *pk; - if (!users) + if (!users) desc[0].mode = KEYDB_SEARCH_MODE_NEXT; /* Read the keyblock. */ release_kbnode (keyblock); keyblock = NULL; err = keydb_get_keyblock (kdbhd, &keyblock); - if (err) + if (err) { log_error (_("error reading keyblock: %s\n"), gpg_strerror (err)); goto leave; @@ -802,7 +880,7 @@ do_export_stream (ctrl_t ctrl, iobuf_t out, strlist_t users, int secret, clean_key (keyblock, opt.verbose, (options&EXPORT_MINIMAL), NULL, NULL); /* And write it. */ - for (kbctx=NULL; (node = walk_kbnode (keyblock, &kbctx, 0)); ) + for (kbctx=NULL; (node = walk_kbnode (keyblock, &kbctx, 0)); ) { if (skip_until_subkey) { @@ -835,7 +913,7 @@ do_export_stream (ctrl_t ctrl, iobuf_t out, strlist_t users, int secret, subkey and include that subkey into the output too. Need to add this subkey to a list so that it won't get processed a second time. - + So the first step here is to check that list and skip in any case if the key is in that list. @@ -843,7 +921,7 @@ do_export_stream (ctrl_t ctrl, iobuf_t out, strlist_t users, int secret, function of GnuPG < 2.1 is not able to merge secret keys and thus it is useless to output them as two separate keys and have import merge them. */ - if (subkey_in_list_p (subkey_list, node)) + if (subkey_in_list_p (subkey_list, node)) skip_until_subkey = 1; /* Already processed this one. */ else { @@ -854,7 +932,7 @@ do_export_stream (ctrl_t ctrl, iobuf_t out, strlist_t users, int secret, && exact_subkey_match_p (desc+j, node)) break; if (!(j < ndesc)) - skip_until_subkey = 1; /* No other one matching. */ + skip_until_subkey = 1; /* No other one matching. */ } } @@ -885,7 +963,7 @@ do_export_stream (ctrl_t ctrl, iobuf_t out, strlist_t users, int secret, && node->pkt->pkt.signature->revkey) { int i; - + for (i=0;ipkt->pkt.signature->numrevkeys;i++) if ( (node->pkt->pkt.signature->revkey[i]->class & 0x40)) break; @@ -904,7 +982,7 @@ do_export_stream (ctrl_t ctrl, iobuf_t out, strlist_t users, int secret, or a signature on an attrib */ while (kbctx->next && kbctx->next->pkt->pkttype==PKT_SIGNATURE) kbctx = kbctx->next; - + continue; } @@ -913,7 +991,7 @@ do_export_stream (ctrl_t ctrl, iobuf_t out, strlist_t users, int secret, { u32 subkidbuf[2], *subkid; char *hexgrip, *serialno; - + pk = node->pkt->pkt.public_key; if (node->pkt->pkttype == PKT_PUBLIC_KEY) subkid = NULL; @@ -930,7 +1008,7 @@ do_export_stream (ctrl_t ctrl, iobuf_t out, strlist_t users, int secret, skip_until_subkey = 1; continue; } - + err = hexkeygrip_from_pk (pk, &hexgrip); if (err) { @@ -970,7 +1048,7 @@ do_export_stream (ctrl_t ctrl, iobuf_t out, strlist_t users, int secret, /* Create a key stub. */ struct seckey_info *ski; const char *s; - + pk->seckey_info = ski = xtrycalloc (1, sizeof *ski); if (!ski) { @@ -989,7 +1067,7 @@ do_export_stream (ctrl_t ctrl, iobuf_t out, strlist_t users, int secret, ski->ivlen++, s += 2) ski->iv[ski->ivlen] = xtoi_2 (s); } - + if ((options&EXPORT_SEXP_FORMAT)) err = build_sexp (out, node->pkt, &indent); else @@ -1032,7 +1110,7 @@ do_export_stream (ctrl_t ctrl, iobuf_t out, strlist_t users, int secret, realkeylen = gcry_sexp_canon_len (key, keylen, NULL, &err); if (!realkeylen) goto unwraperror; /* Invalid csexp. */ - + err = gcry_sexp_sscan (&s_skey, NULL, key, realkeylen); xfree (key); key = NULL; @@ -1252,7 +1330,7 @@ build_sexp_seckey (iobuf_t out, PACKET *pkt, int *indent) /* For some packet types we write them in a S-expression format. This is still EXPERIMENTAL and subject to change. */ -static int +static int build_sexp (iobuf_t out, PACKET *pkt, int *indent) { int rc; diff --git a/g10/gpg.c b/g10/gpg.c index 47e8b361f..6daa144be 100644 --- a/g10/gpg.c +++ b/g10/gpg.c @@ -1959,6 +1959,9 @@ main (int argc, char **argv) NEED_LIBGCRYPT_VERSION, gcry_check_version (NULL) ); } + /* Use our own logging handler for Libcgrypt. */ + setup_libgcrypt_logging (); + /* Put random number into secure memory */ gcry_control (GCRYCTL_USE_SECURE_RNDPOOL); diff --git a/g10/import.c b/g10/import.c index 88abafd6a..99398c762 100644 --- a/g10/import.c +++ b/g10/import.c @@ -1,6 +1,6 @@ /* import.c - import a key into our key storage. * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, - * 2007, 2010 Free Software Foundation, Inc. + * 2007, 2010, 2011 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -200,7 +200,7 @@ import_keys_internal (ctrl_t ctrl, iobuf_t inp, char **fnames, int nnames, rc = import (ctrl, inp2, fname, stats, fpr, fpr_len, options); iobuf_close(inp2); /* Must invalidate that ugly cache to actually close it. */ - iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE, + iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE, 0, (char*)fname); if( rc ) log_error("import from `%s' failed: %s\n", fname, @@ -294,7 +294,7 @@ import (ctrl_t ctrl, IOBUF inp, const char* fname,struct stats_s *stats, if( keyblock->pkt->pkttype == PKT_PUBLIC_KEY ) rc = import_one (ctrl, fname, keyblock, stats, fpr, fpr_len, options, 0); - else if( keyblock->pkt->pkttype == PKT_SECRET_KEY ) + else if( keyblock->pkt->pkttype == PKT_SECRET_KEY ) rc = import_secret_one (ctrl, fname, keyblock, stats, options); else if( keyblock->pkt->pkttype == PKT_SIGNATURE && keyblock->pkt->pkt.signature->sig_class == 0x20 ) @@ -647,7 +647,7 @@ check_prefs (ctrl_t ctrl, kbnode_t keyblock) kbnode_t node; PKT_public_key *pk; int problem=0; - + merge_keys_and_selfsig(keyblock); pk=keyblock->pkt->pkt.public_key; @@ -672,9 +672,9 @@ check_prefs (ctrl_t ctrl, kbnode_t keyblock) { if (openpgp_cipher_test_algo (prefs->value)) { - const char *algo = + const char *algo = (openpgp_cipher_test_algo (prefs->value) - ? num + ? num : openpgp_cipher_algo_name (prefs->value)); if(!problem) check_prefs_warning(pk); @@ -689,7 +689,7 @@ check_prefs (ctrl_t ctrl, kbnode_t keyblock) { const char *algo = (gcry_md_test_algo (prefs->value) - ? num + ? num : gcry_md_algo_name (prefs->value)); if(!problem) check_prefs_warning(pk); @@ -801,7 +801,7 @@ import_one (ctrl_t ctrl, log_error( _("key %s: no user ID\n"), keystr_from_pk(pk)); return 0; } - + if (opt.interactive) { if(is_status_enabled()) print_import_check (pk, uidnode->pkt->pkt.user_id); @@ -938,7 +938,7 @@ import_one (ctrl_t ctrl, size_t an; fingerprint_from_pk (pk_orig, afp, &an); - while (an < MAX_FINGERPRINT_LEN) + while (an < MAX_FINGERPRINT_LEN) afp[an++] = 0; rc = keydb_search_fpr (hd, afp); } @@ -962,7 +962,7 @@ import_one (ctrl_t ctrl, n_sigs_cleaned = fix_bad_direct_key_sigs (keyblock_orig, keyid); if (n_sigs_cleaned) commit_kbnode (&keyblock_orig); - + /* and try to merge the block */ clear_kbnode_flags( keyblock_orig ); clear_kbnode_flags( keyblock ); @@ -1032,13 +1032,13 @@ import_one (ctrl_t ctrl, stats->n_sigs_cleaned +=n_sigs_cleaned; stats->n_uids_cleaned +=n_uids_cleaned; - if (is_status_enabled ()) + if (is_status_enabled ()) print_import_ok (pk, ((n_uids?2:0)|(n_sigs?4:0)|(n_subk?8:0))); } else { same_key = 1; - if (is_status_enabled ()) + if (is_status_enabled ()) print_import_ok (pk, 0); if( !opt.quiet ) @@ -1107,6 +1107,37 @@ import_one (ctrl_t ctrl, } +/* Extract one MPI value from the S-expression PKEY which is expected + to hold a "public-key". Returns NULL on error. */ +static gcry_mpi_t +one_mpi_from_pkey (gcry_sexp_t pkey, const char *name, size_t namelen) +{ + gcry_sexp_t list, l2; + gcry_mpi_t a; + + list = gcry_sexp_find_token (pkey, "public-key", 0); + if (!list) + return NULL; + l2 = gcry_sexp_cadr (list); + gcry_sexp_release (list); + list = l2; + if (!list) + return NULL; + + l2 = gcry_sexp_find_token (list, name, namelen); + if (!l2) + { + gcry_sexp_release (list); + return NULL; + } + a = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG); + gcry_sexp_release (l2); + gcry_sexp_release (list); + + return a; +} + + /* Transfer all the secret keys in SEC_KEYBLOCK to the gpg-agent. The function prints diagnostics and returns an error code. */ static gpg_error_t @@ -1133,6 +1164,7 @@ transfer_secret_keys (ctrl_t ctrl, struct stats_s *stats, kbnode_t sec_keyblock) unsigned char *wrappedkey = NULL; size_t wrappedkeylen; char *cache_nonce = NULL; + gcry_mpi_t ecc_params[5] = {NULL, NULL, NULL, NULL, NULL}; /* Get the current KEK. */ err = agent_keywrap_key (ctrl, 0, &kek, &keklen); @@ -1148,7 +1180,8 @@ transfer_secret_keys (ctrl_t ctrl, struct stats_s *stats, kbnode_t sec_keyblock) if (!err) err = gcry_cipher_setkey (cipherhd, kek, keklen); if (err) - goto leave; xfree (kek); + goto leave; + xfree (kek); kek = NULL; main_pk = NULL; @@ -1161,6 +1194,20 @@ transfer_secret_keys (ctrl_t ctrl, struct stats_s *stats, kbnode_t sec_keyblock) if (!main_pk) main_pk = pk; + /* Make sure the keyids are available. */ + keyid_from_pk (pk, NULL); + if (node->pkt->pkttype == PKT_SECRET_KEY) + { + pk->main_keyid[0] = pk->keyid[0]; + pk->main_keyid[1] = pk->keyid[1]; + } + else + { + pk->main_keyid[0] = main_pk->keyid[0]; + pk->main_keyid[1] = main_pk->keyid[1]; + } + + ski = pk->seckey_info; if (!ski) BUG (); @@ -1191,34 +1238,109 @@ transfer_secret_keys (ctrl_t ctrl, struct stats_s *stats, kbnode_t sec_keyblock) init_membuf (&mbuf, 50); put_membuf_str (&mbuf, "(skey"); - for (i=j=0; i < nskey; i++) + if (pk->pubkey_algo == PUBKEY_ALGO_ECDSA + || pk->pubkey_algo == PUBKEY_ALGO_ECDH) { - if (!pk->pkey[i]) - ; /* Protected keys only have NPKEY+1 elements. */ - else if (gcry_mpi_get_flag (pk->pkey[i], GCRYMPI_FLAG_OPAQUE)) + /* We need special treatment for ECC algorithms. OpenPGP + stores only the curve name but the agent expects a full + key. This is so that we can keep all curve name + validation code out of gpg-agent. */ +#if PUBKEY_MAX_NSKEY < 7 +#error PUBKEY_MAX_NSKEY too low for ECC +#endif + char *curve = openpgp_oid_to_str (pk->pkey[0]); + if (!curve) + err = gpg_error_from_syserror (); + else { - put_membuf_str (&mbuf, " e %b"); - format_args_buf_ptr[i] = gcry_mpi_get_opaque (pk->pkey[i], &n); - format_args_buf_int[i] = (n+7)/8; - format_args[j++] = format_args_buf_int + i; - format_args[j++] = format_args_buf_ptr + i; +#ifdef HAVE_GCRY_PK_GET_CURVE /* Also ensures availability of get_param. */ + gcry_sexp_t cparam = gcry_pk_get_param (GCRY_PK_ECDSA, curve); +#else + gcry_sexp_t cparam = NULL; +#endif + xfree (curve); + if (!cparam) + err = gpg_error (GPG_ERR_UNKNOWN_CURVE); + else + { + const char *s; + + /* Append the curve parameters P, A, B, G and N. */ + for (i=j=0; !err && *(s = "pabgn"+i); i++) + { + ecc_params[i] = one_mpi_from_pkey (cparam, s, 1); + if (!ecc_params[i]) + err = gpg_error (GPG_ERR_INV_CURVE); + else + { + put_membuf_str (&mbuf, " _ %m"); + format_args[j++] = ecc_params+i; + } + } + gcry_sexp_release (cparam); + if (!err) + { + /* Append the public key element Q. */ + put_membuf_str (&mbuf, " _ %m"); + format_args[j++] = pk->pkey + 1; + + /* Append the secret key element D. Note that + for ECDH we need to skip PKEY[2] because this + holds the KEK which is not needed. */ + i = pk->pubkey_algo == PUBKEY_ALGO_ECDH? 3 : 2; + if (gcry_mpi_get_flag (pk->pkey[i], GCRYMPI_FLAG_OPAQUE)) + { + put_membuf_str (&mbuf, " e %b"); + format_args_buf_ptr[i] + = gcry_mpi_get_opaque (pk->pkey[i],&n); + format_args_buf_int[i] = (n+7)/8; + format_args[j++] = format_args_buf_int + i; + format_args[j++] = format_args_buf_ptr + i; + } + else + { + put_membuf_str (&mbuf, " _ %m"); + format_args[j++] = pk->pkey + i; + } + } + } } - else + } + else + { + /* Standard case for the old (non-ECC) algorithms. */ + for (i=j=0; i < nskey; i++) { - put_membuf_str (&mbuf, " _ %m"); - format_args[j++] = pk->pkey + i; + if (!pk->pkey[i]) + ; /* Protected keys only have NPKEY+1 elements. */ + else if (gcry_mpi_get_flag (pk->pkey[i], GCRYMPI_FLAG_OPAQUE)) + { + put_membuf_str (&mbuf, " e %b"); + format_args_buf_ptr[i] = gcry_mpi_get_opaque (pk->pkey[i],&n); + format_args_buf_int[i] = (n+7)/8; + format_args[j++] = format_args_buf_int + i; + format_args[j++] = format_args_buf_ptr + i; + } + else + { + put_membuf_str (&mbuf, " _ %m"); + format_args[j++] = pk->pkey + i; + } } } put_membuf_str (&mbuf, ")\n"); put_membuf (&mbuf, "", 1); - { - char *format = get_membuf (&mbuf, NULL); - if (!format) - err = gpg_error_from_syserror (); - else - err = gcry_sexp_build_array (&skey, NULL, format, format_args); - xfree (format); - } + if (err) + xfree (get_membuf (&mbuf, NULL)); + else + { + char *format = get_membuf (&mbuf, NULL); + if (!format) + err = gpg_error_from_syserror (); + else + err = gcry_sexp_build_array (&skey, NULL, format, format_args); + xfree (format); + } if (err) { log_error ("error building skey array: %s\n", gpg_strerror (err)); @@ -1228,7 +1350,7 @@ transfer_secret_keys (ctrl_t ctrl, struct stats_s *stats, kbnode_t sec_keyblock) if (ski->is_protected) { char countbuf[35]; - + /* Note that the IVLEN may be zero if we are working on a dummy key. We can't express that in an S-expression and thus we send dummy data for the IV. */ @@ -1289,9 +1411,9 @@ transfer_secret_keys (ctrl_t ctrl, struct stats_s *stats, kbnode_t sec_keyblock) transferkey = NULL; /* Send the wrapped key to the agent. */ - { + { char *desc = gpg_format_keydesc (pk, 1, 1); - err = agent_import_key (ctrl, desc, &cache_nonce, + err = agent_import_key (ctrl, desc, &cache_nonce, wrappedkey, wrappedkeylen); xfree (desc); } @@ -1328,6 +1450,8 @@ transfer_secret_keys (ctrl_t ctrl, struct stats_s *stats, kbnode_t sec_keyblock) } leave: + for (i=0; i < DIM (ecc_params); i++) + gcry_mpi_release (ecc_params[i]); xfree (cache_nonce); xfree (wrappedkey); xfree (transferkey); @@ -1392,7 +1516,7 @@ sec_to_pub_keyblock (kbnode_t sec_keyblock) * with the trust calculation. */ static int -import_secret_one (ctrl_t ctrl, const char *fname, KBNODE keyblock, +import_secret_one (ctrl_t ctrl, const char *fname, KBNODE keyblock, struct stats_s *stats, unsigned int options) { PKT_public_key *pk; @@ -1400,17 +1524,17 @@ import_secret_one (ctrl_t ctrl, const char *fname, KBNODE keyblock, KBNODE node, uidnode; u32 keyid[2]; int rc = 0; - + /* Get the key and print some info about it */ node = find_kbnode (keyblock, PKT_SECRET_KEY); if (!node) BUG (); - + pk = node->pkt->pkt.public_key; keyid_from_pk (pk, keyid); uidnode = find_next_kbnode (keyblock, PKT_USER_ID); - + if (opt.verbose) { log_info ("sec %4u%c/%s %s ", @@ -1423,7 +1547,7 @@ import_secret_one (ctrl_t ctrl, const char *fname, KBNODE keyblock, log_printf ("\n"); } stats->secret_read++; - + if (!uidnode) { log_error( _("key %s: no user ID\n"), keystr_from_pk (pk)); @@ -1456,10 +1580,10 @@ import_secret_one (ctrl_t ctrl, const char *fname, KBNODE keyblock, log_error (_("importing secret keys not allowed\n")); return 0; } -#endif - +#endif + clear_kbnode_flags (keyblock); - + if (!(options&IMPORT_MERGE_ONLY) || !have_secret_key_with_kid (keyid) ) { /* We don't have this key, insert as a new key. */ @@ -1477,7 +1601,7 @@ import_secret_one (ctrl_t ctrl, const char *fname, KBNODE keyblock, /* Fixme: We should check for an invalid keyblock and cancel the secret key import in this case. */ release_kbnode (pub_keyblock); - + /* Read the keyblock again to get the effects of a merge. */ /* Fixme: we should do this based on the fingerprint or even better let import_one return the merged @@ -1493,7 +1617,7 @@ import_secret_one (ctrl_t ctrl, const char *fname, KBNODE keyblock, if (!opt.quiet) log_info (_("key %s: secret key imported\n"), keystr_from_pk (pk)); - if (is_status_enabled ()) + if (is_status_enabled ()) print_import_ok (pk, 1|16); check_prefs (ctrl, node); } @@ -1502,11 +1626,11 @@ import_secret_one (ctrl_t ctrl, const char *fname, KBNODE keyblock, } } else - { + { /* We don't want to merge the secret keys. */ log_error (_("key %s: secret key part already available\n"), keystr_from_pk (pk)); - if (is_status_enabled ()) + if (is_status_enabled ()) print_import_ok (pk, 16); } @@ -1556,9 +1680,9 @@ import_revoke_cert( const char *fname, KBNODE node, struct stats_s *stats ) { byte afp[MAX_FINGERPRINT_LEN]; size_t an; - + fingerprint_from_pk (pk, afp, &an); - while (an < MAX_FINGERPRINT_LEN) + while (an < MAX_FINGERPRINT_LEN) afp[an++] = 0; rc = keydb_search_fpr (hd, afp); } @@ -1654,11 +1778,11 @@ chk_self_sigs (const char *fname, kbnode_t keyblock, int rc; u32 bsdate=0, rsdate=0; kbnode_t bsnode = NULL, rsnode = NULL; - + (void)fname; (void)pk; - for (n=keyblock; (n = find_next_kbnode (n, 0)); ) + for (n=keyblock; (n = find_next_kbnode (n, 0)); ) { if (n->pkt->pkttype == PKT_PUBLIC_SUBKEY) { @@ -1672,7 +1796,7 @@ chk_self_sigs (const char *fname, kbnode_t keyblock, if ( n->pkt->pkttype != PKT_SIGNATURE ) continue; - + sig = n->pkt->pkt.signature; if ( keyid[0] != sig->keyid[0] || keyid[1] != sig->keyid[1] ) { @@ -1684,7 +1808,7 @@ chk_self_sigs (const char *fname, kbnode_t keyblock, import a fully-cached key which speeds things up. */ if (!opt.no_sig_cache) check_key_signature (keyblock, n, NULL); - + if ( IS_UID_SIG(sig) || IS_UID_REV(sig) ) { KBNODE unode = find_prev_kbnode( keyblock, n, PKT_USER_ID ); @@ -1694,16 +1818,16 @@ chk_self_sigs (const char *fname, kbnode_t keyblock, keystr(keyid)); return -1; /* The complete keyblock is invalid. */ } - + /* If it hasn't been marked valid yet, keep trying. */ - if (!(unode->flag&1)) + if (!(unode->flag&1)) { rc = check_key_signature (keyblock, n, NULL); if ( rc ) { if ( opt.verbose ) { - char *p = utf8_to_native + char *p = utf8_to_native (unode->pkt->pkt.user_id->name, strlen (unode->pkt->pkt.user_id->name),0); log_info (gpg_err_code(rc) == G10ERR_PUBKEY_ALGO ? @@ -1732,7 +1856,7 @@ chk_self_sigs (const char *fname, kbnode_t keyblock, n->flag |= 4; } } - else if ( IS_SUBKEY_SIG (sig) ) + else if ( IS_SUBKEY_SIG (sig) ) { /* Note that this works based solely on the timestamps like the rest of gpg. If the standard gets revocation @@ -1761,19 +1885,19 @@ chk_self_sigs (const char *fname, kbnode_t keyblock, else { /* It's valid, so is it newer? */ - if (sig->timestamp >= bsdate) + if (sig->timestamp >= bsdate) { knode->flag |= 1; /* The subkey is valid. */ if (bsnode) { /* Delete the last binding sig since this one is newer */ - bsnode->flag |= 4; + bsnode->flag |= 4; if (opt.verbose) log_info (_("key %s: removed multiple subkey" " binding\n"),keystr(keyid)); } - + bsnode = n; bsdate = sig->timestamp; } @@ -1818,12 +1942,12 @@ chk_self_sigs (const char *fname, kbnode_t keyblock, { /* Delete the last revocation sig since this one is newer. */ - rsnode->flag |= 4; + rsnode->flag |= 4; if (opt.verbose) log_info (_("key %s: removed multiple subkey" " revocation\n"),keystr(keyid)); } - + rsnode = n; rsdate = sig->timestamp; } diff --git a/g10/keygen.c b/g10/keygen.c index 4d911f0b9..d8535fa61 100644 --- a/g10/keygen.c +++ b/g10/keygen.c @@ -1080,6 +1080,40 @@ write_keybinding (KBNODE root, PKT_public_key *pri_psk, PKT_public_key *sub_psk, return err; } +/* Map the Libgcrypt ECC curve NAME to an OID. If R_NBITS is not NULL + store the bit size of the curve there. Returns NULL for unknown + curve names. */ +const char * +gpg_curve_to_oid (const char *name, unsigned int *r_nbits) +{ + unsigned int nbits = 0; + const char *oidstr; + + if (!name) + oidstr = NULL; + else if (!strcmp (name, "NIST P-256")) + { + oidstr = "1.2.840.10045.3.1.7"; + nbits = 256; + } + else if (!strcmp (name, "NIST P-384")) + { + oidstr = "1.3.132.0.34"; + nbits = 384; + } + else if (!strcmp (name, "NIST P-521")) + { + oidstr = "1.3.132.0.35"; + nbits = 521; + } + else + oidstr = NULL; + + if (r_nbits) + *r_nbits = nbits; + return oidstr; +} + static gpg_error_t ecckey_from_sexp (gcry_mpi_t *array, gcry_sexp_t sexp, int algo) @@ -1117,23 +1151,11 @@ ecckey_from_sexp (gcry_mpi_t *array, gcry_sexp_t sexp, int algo) goto leave; } gcry_sexp_release (l2); - if (!strcmp (curve, "NIST P-256")) - { - oidstr = "1.2.840.10045.3.1.7"; - nbits = 256; - } - else if (!strcmp (curve, "NIST P-384")) - { - oidstr = "1.3.132.0.34"; - nbits = 384; - } - else if (!strcmp (curve, "NIST P-521")) - { - oidstr = "1.3.132.0.35"; - nbits = 521; - } - else + oidstr = gpg_curve_to_oid (curve, &nbits); + if (!oidstr) { + /* That can't happen because we used one of the curves + gpg_curve_to_oid knows about. */ err = gpg_error (GPG_ERR_INV_OBJ); goto leave; } @@ -1445,7 +1467,8 @@ gen_ecc (int algo, unsigned int nbits, kbnode_t pub_root, assert (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_ECDH); - /* For now we may only use one of the 3 NISY curves. */ + /* For now we may only use one of the 3 NIST curves. See also + gpg_curve_to_oid. */ if (nbits <= 256) curve = "NIST P-256"; else if (nbits <= 384) diff --git a/g10/keyid.c b/g10/keyid.c index 6571a51c0..cbcc971c3 100644 --- a/g10/keyid.c +++ b/g10/keyid.c @@ -39,7 +39,7 @@ #ifdef HAVE_UNSIGNED_TIME_T # define IS_INVALID_TIME_T(a) ((a) == (time_t)(-1)) -#else +#else /* Error or 32 bit time_t and value after 2038-01-19. */ # define IS_INVALID_TIME_T(a) ((a) < 0) #endif @@ -81,6 +81,11 @@ hash_public_key (gcry_md_hd_t md, PKT_public_key *pk) if(pk->version<4) n+=2; + /* FIXME: We can avoid the extra malloc by calling only the first + mpi_print here which computes the required length and calling the + real mpi_print only at the end. The speed advantage would only be + for ECC (opaque MPIs) or if we could implement an mpi_print + variant with a callback handler to do the hashing. */ if (npkey==0 && pk->pkey[0] && gcry_mpi_get_flag (pk->pkey[0], GCRYMPI_FLAG_OPAQUE)) { @@ -92,25 +97,32 @@ hash_public_key (gcry_md_hd_t md, PKT_public_key *pk) { for (i=0; i < npkey; 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. */ + if (gcry_mpi_get_flag (pk->pkey[i], GCRYMPI_FLAG_OPAQUE)) + { + size_t nbits; + const void *p; + + p = gcry_mpi_get_opaque (pk->pkey[i], &nbits); + pp[i] = xmalloc ((nbits+7)/8); + memcpy (pp[i], p, (nbits+7)/8); + nn[i] = (nbits+7)/8; + n += nn[i]; + } 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 (fmt, pp[i], nbytes, &nbytes, pk->pkey[i])) - BUG (); - nn[i] = nbytes; - n += nn[i]; + { + if (gcry_mpi_print (GCRYMPI_FMT_PGP, 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])) + 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 */ @@ -127,7 +139,7 @@ hash_public_key (gcry_md_hd_t md, PKT_public_key *pk) u16 days=0; if(pk->expiredate) days=(u16)((pk->expiredate - pk->timestamp) / 86400L); - + gcry_md_putc ( md, days >> 8 ); gcry_md_putc ( md, days ); } @@ -178,7 +190,7 @@ v3_keyid (gcry_mpi_t a, u32 *ki) BUG (); if (nbytes < 8) /* oops */ ki[0] = ki[1] = 0; - else + else { p = buffer + nbytes - 8; ki[0] = (p[0] << 24) | (p[1] <<16) | (p[2] << 8) | p[3]; @@ -215,7 +227,7 @@ keystrlen(void) const char * keystr (u32 *keyid) -{ +{ static char keyid_str[KEYID_STR_SIZE]; switch (opt.keyid_format) @@ -226,7 +238,7 @@ keystr (u32 *keyid) case KF_LONG: if (keyid[0]) - snprintf (keyid_str, sizeof keyid_str, "%08lX%08lX", + snprintf (keyid_str, sizeof keyid_str, "%08lX%08lX", (ulong)keyid[0], (ulong)keyid[1]); else snprintf (keyid_str, sizeof keyid_str, "%08lX", (ulong)keyid[1]); @@ -238,12 +250,12 @@ keystr (u32 *keyid) case KF_0xLONG: if(keyid[0]) - snprintf (keyid_str, sizeof keyid_str, "0x%08lX%08lX", + snprintf (keyid_str, sizeof keyid_str, "0x%08lX%08lX", (ulong)keyid[0],(ulong)keyid[1]); else snprintf (keyid_str, sizeof keyid_str, "0x%08lX", (ulong)keyid[1]); break; - + default: BUG(); } @@ -254,7 +266,7 @@ keystr (u32 *keyid) const char * keystr_with_sub (u32 *main_kid, u32 *sub_kid) -{ +{ static char buffer[KEYID_STR_SIZE+1+KEYID_STR_SIZE]; char *p; @@ -408,7 +420,7 @@ keyid_from_fingerprint( const byte *fprint, size_t fprint_len, u32 *keyid ) else keyid_from_pk (&pk, keyid); } - else + else { const byte *dp = fprint; keyid[0] = dp[12] << 24 | dp[13] << 16 | dp[14] << 8 | dp[15] ; @@ -422,7 +434,7 @@ keyid_from_fingerprint( const byte *fprint, size_t fprint_len, u32 *keyid ) u32 keyid_from_sig (PKT_signature *sig, u32 *keyid) { - if( keyid ) + if( keyid ) { keyid[0] = sig->keyid[0]; keyid[1] = sig->keyid[1]; @@ -437,13 +449,13 @@ namehash_from_uid (PKT_user_id *uid) if (!uid->namehash) { uid->namehash = xmalloc (20); - + if (uid->attrib_data) rmd160_hash_buffer (uid->namehash, uid->attrib_data, uid->attrib_len); else rmd160_hash_buffer (uid->namehash, uid->name, uid->len); } - + return uid->namehash; } @@ -465,7 +477,7 @@ mk_datestr (char *buffer, time_t atime) if (IS_INVALID_TIME_T (atime)) strcpy (buffer, "????" "-??" "-??"); /* Mark this as invalid. */ - else + else { tp = gmtime (&atime); sprintf (buffer,"%04d-%02d-%02d", @@ -485,7 +497,7 @@ datestr_from_pk (PKT_public_key *pk) { static char buffer[11+5]; time_t atime = pk->timestamp; - + return mk_datestr (buffer, atime); } @@ -518,7 +530,7 @@ expirestr_from_sig (PKT_signature *sig) { static char buffer[11+5]; time_t atime; - + if (!sig->expiredate) return _("never "); atime=sig->expiredate; @@ -591,7 +603,7 @@ const char * colon_datestr_from_sig (PKT_signature *sig) { static char buf[20]; - + snprintf (buf, sizeof buf, "%lu", (ulong)sig->timestamp); return buf; } @@ -621,21 +633,21 @@ fingerprint_from_pk (PKT_public_key *pk, byte *array, size_t *ret_len) const byte *dp; size_t len, nbytes; int i; - + if ( pk->version < 4 ) { if ( is_RSA(pk->pubkey_algo) ) { /* RSA in version 3 packets is special. */ gcry_md_hd_t md; - + if (gcry_md_open (&md, DIGEST_ALGO_MD5, 0)) BUG (); - if ( pubkey_get_npkey (pk->pubkey_algo) > 1 ) + if ( pubkey_get_npkey (pk->pubkey_algo) > 1 ) { for (i=0; i < 2; i++) { - if (gcry_mpi_print (GCRYMPI_FMT_USG, NULL, 0, + if (gcry_mpi_print (GCRYMPI_FMT_USG, NULL, 0, &nbytes, pk->pkey[i])) BUG (); /* fixme: Better allocate BUF on the stack */ @@ -662,10 +674,10 @@ fingerprint_from_pk (PKT_public_key *pk, byte *array, size_t *ret_len) memset (array,0,16); } } - else + else { gcry_md_hd_t md; - + md = do_fingerprint_md(pk); dp = gcry_md_read( md, 0 ); len = gcry_md_get_algo_dlen (gcry_md_get_algo (md)); @@ -677,7 +689,7 @@ fingerprint_from_pk (PKT_public_key *pk, byte *array, size_t *ret_len) pk->keyid[1] = dp[16] << 24 | dp[17] << 16 | dp[18] << 8 | dp[19] ; gcry_md_close( md); } - + *ret_len = len; return array; } @@ -694,7 +706,7 @@ keygrip_from_pk (PKT_public_key *pk, unsigned char *array) { gpg_error_t err; gcry_sexp_t s_pkey; - + if (DBG_PACKET) log_debug ("get_keygrip for public key\n"); @@ -742,7 +754,7 @@ keygrip_from_pk (PKT_public_key *pk, unsigned char *array) err = gpg_error (GPG_ERR_PUBKEY_ALGO); break; } - + if (err) return err; @@ -758,7 +770,7 @@ keygrip_from_pk (PKT_public_key *pk, unsigned char *array) /* FIXME: Save the keygrip in PK. */ } gcry_sexp_release (s_pkey); - + return 0; } @@ -786,4 +798,3 @@ hexkeygrip_from_pk (PKT_public_key *pk, char **r_grip) } return err; } - diff --git a/g10/main.h b/g10/main.h index d70c16901..920d82c97 100644 --- a/g10/main.h +++ b/g10/main.h @@ -236,6 +236,7 @@ void keyedit_passwd (ctrl_t ctrl, const char *username); void show_basic_key_info (KBNODE keyblock); /*-- keygen.c --*/ +const char *gpg_curve_to_oid (const char *name, unsigned int *r_nbits); u32 parse_expire_string(const char *string); u32 ask_expire_interval(int object,const char *def_expire); u32 ask_expiredate(void); diff --git a/g10/passphrase.c b/g10/passphrase.c index f29fca72f..8065810c9 100644 --- a/g10/passphrase.c +++ b/g10/passphrase.c @@ -74,7 +74,7 @@ encode_s2k_iterations (int iterations) if (err && gpg_err_code (err) != GPG_ERR_ASS_PARAMETER) log_error (_("problem with the agent: %s\n"), gpg_strerror (err)); /* Default to 65536 which we used up to 2.0.13. */ - return 96; + return 96; } else if (mycnt >= 65011712) return 255; /* Largest possible value. */ @@ -87,7 +87,7 @@ encode_s2k_iterations (int iterations) if (iterations >= 65011712) return 255; - + /* Need count to be in the range 16-31 */ for (count=iterations>>6; count>=32; count>>=1) c++; @@ -96,13 +96,13 @@ encode_s2k_iterations (int iterations) if (S2K_DECODE_COUNT(result) < iterations) result++; - + return result; } -/* Hash a passphrase using the supplied s2k. +/* Hash a passphrase using the supplied s2k. Always needs: dek->algo, s2k->mode, s2k->hash_algo. */ static void hash_passphrase ( DEK *dek, char *pw, STRING2KEY *s2k) @@ -119,20 +119,20 @@ hash_passphrase ( DEK *dek, char *pw, STRING2KEY *s2k) if (gcry_md_open (&md, s2k->hash_algo, 1)) BUG (); - for (pass=0; used < dek->keylen ; pass++ ) + for (pass=0; used < dek->keylen ; pass++ ) { - if ( pass ) + if ( pass ) { gcry_md_reset (md); for (i=0; i < pass; i++ ) /* Preset the hash context. */ gcry_md_putc (md, 0 ); } - if ( s2k->mode == 1 || s2k->mode == 3 ) + if ( s2k->mode == 1 || s2k->mode == 3 ) { int len2 = pwlen + 8; ulong count = len2; - + if ( s2k->mode == 3 ) { count = S2K_DECODE_COUNT(s2k->count); @@ -146,7 +146,7 @@ hash_passphrase ( DEK *dek, char *pw, STRING2KEY *s2k) /* A little bit complicated because we need a ulong for count. */ while ( count > len2 ) /* maybe iterated+salted */ - { + { gcry_md_write ( md, s2k->salt, 8 ); gcry_md_write ( md, pw, pwlen ); count -= len2; @@ -231,7 +231,7 @@ read_passphrase_from_fd( int fd ) int i, len; char *pw; - if ( !opt.batch ) + if ( !opt.batch ) { /* Not used but we have to do a dummy read, so that it won't end up at the begin of the message if the quite usual trick to prepend the passphtrase to the message is used. */ @@ -240,12 +240,12 @@ read_passphrase_from_fd( int fd ) while (!(read (fd, buf, 1) != 1 || *buf == '\n' )) ; *buf = 0; - return; + return; } - for (pw = NULL, i = len = 100; ; i++ ) + for (pw = NULL, i = len = 100; ; i++ ) { - if (i >= len-1 ) + if (i >= len-1 ) { char *pw2 = pw; len += 100; @@ -311,35 +311,35 @@ passphrase_get ( u32 *keyid, int mode, const char *cacheid, int repeat, if( keyid && get_pubkey( pk, keyid ) ) { if (pk) - free_public_key( pk ); + free_public_key( pk ); pk = NULL; /* oops: no key for some reason */ } - + orig_codeset = i18n_switchto_utf8 (); if (custom_description) atext = native_to_utf8 (custom_description); else if ( !mode && pk && keyid ) - { + { char *uid; size_t uidlen; const char *algo_name = openpgp_pk_algo_name ( pk->pubkey_algo ); const char *timestr; char *maink; - + if ( !algo_name ) algo_name = "?"; #define KEYIDSTRING _(" (main key ID %s)") maink = xmalloc ( strlen (KEYIDSTRING) + keystrlen() + 20 ); - if( keyid[2] && keyid[3] && keyid[0] != keyid[2] + if( keyid[2] && keyid[3] && keyid[0] != keyid[2] && keyid[1] != keyid[3] ) sprintf( maink, KEYIDSTRING, keystr(&keyid[2]) ); else *maink = 0; - - uid = get_user_id ( keyid, &uidlen ); + + uid = get_user_id ( keyid, &uidlen ); timestr = strtimestamp (pk->timestamp); #undef KEYIDSTRING @@ -350,7 +350,7 @@ passphrase_get ( u32 *keyid, int mode, const char *cacheid, int repeat, "%u-bit %s key, ID %s,\n" \ "created %s%s.\n" ) - atext = xmalloc ( 100 + strlen (PROMPTSTRING) + atext = xmalloc ( 100 + strlen (PROMPTSTRING) + uidlen + 15 + strlen(algo_name) + keystrlen() + strlen (timestr) + strlen (maink) ); sprintf (atext, PROMPTSTRING, @@ -362,16 +362,16 @@ passphrase_get ( u32 *keyid, int mode, const char *cacheid, int repeat, #undef PROMPTSTRING - { + { size_t dummy; fingerprint_from_pk( pk, fpr, &dummy ); have_fpr = 1; } - + } else atext = xstrdup ( _("Enter passphrase\n") ); - + if (!mode && cacheid) my_cacheid = cacheid; @@ -387,7 +387,7 @@ passphrase_get ( u32 *keyid, int mode, const char *cacheid, int repeat, rc = agent_get_passphrase (my_cacheid, tryagain_text, my_prompt, atext, repeat, check, &pw); - + xfree (my_prompt); xfree (atext); atext = NULL; @@ -396,14 +396,14 @@ passphrase_get ( u32 *keyid, int mode, const char *cacheid, int repeat, if (!rc) ; - else if (gpg_err_code (rc) == GPG_ERR_CANCELED + else if (gpg_err_code (rc) == GPG_ERR_CANCELED || gpg_err_code (rc) == GPG_ERR_FULLY_CANCELED) { log_info (_("cancelled by user\n") ); if (canceled) *canceled = 1; } - else + else { log_error (_("problem with the agent: %s\n"), gpg_strerror (rc)); /* Due to limitations in the API of the upper layers they @@ -412,7 +412,7 @@ passphrase_get ( u32 *keyid, int mode, const char *cacheid, int repeat, definitely not happen and let it continue without requiring a passphrase. Given that now all the upper layers handle a cancel correctly, we simply set the cancel flag now for all - errors from the agent. */ + errors from the agent. */ if (canceled) *canceled = 1; @@ -440,7 +440,7 @@ passphrase_clear_cache ( u32 *keyid, const char *cacheid, int algo ) int rc; (void)algo; - + if (!cacheid) { PKT_public_key *pk; @@ -450,7 +450,7 @@ passphrase_clear_cache ( u32 *keyid, const char *cacheid, int algo ) byte fpr[MAX_FINGERPRINT_LEN]; char hexfprbuf[2*20+1]; size_t dummy; - + pk = xcalloc (1, sizeof *pk); if ( !keyid || get_pubkey( pk, keyid ) ) { @@ -478,7 +478,7 @@ passphrase_clear_cache ( u32 *keyid, const char *cacheid, int algo ) NULL, sets it to true. MODE 0: Allow cached passphrase - 1: Ignore cached passphrase + 1: Ignore cached passphrase 2: Ditto, but create a new key 3: Allow cached passphrase; use the S2K salt as the cache ID 4: Ditto, but create a new key @@ -486,7 +486,7 @@ passphrase_clear_cache ( u32 *keyid, const char *cacheid, int algo ) DEK * passphrase_to_dek_ext (u32 *keyid, int pubkey_algo, int cipher_algo, STRING2KEY *s2k, int mode, - const char *tryagain_text, + const char *tryagain_text, const char *custdesc, const char *custprompt, int *canceled) { @@ -499,11 +499,11 @@ passphrase_to_dek_ext (u32 *keyid, int pubkey_algo, if (!canceled) canceled = &dummy_canceled; *canceled = 0; - + if ( !s2k ) { assert (mode != 3 && mode != 4); - /* This is used for the old rfc1991 mode + /* This is used for the old rfc1991 mode * Note: This must match the code in encode.c with opt.rfc1991 set */ s2k = &help_s2k; s2k->mode = 0; @@ -529,16 +529,16 @@ passphrase_to_dek_ext (u32 *keyid, int pubkey_algo, /* If we do not have a passphrase available in NEXT_PW and status information are request, we print them now. */ - if ( !next_pw && is_status_enabled() ) + if ( !next_pw && is_status_enabled() ) { char buf[50]; - + if ( keyid ) { u32 used_kid[2]; char *us; - - if ( keyid[2] && keyid[3] ) + + if ( keyid[2] && keyid[3] ) { used_kid[0] = keyid[2]; used_kid[1] = keyid[3]; @@ -548,16 +548,16 @@ passphrase_to_dek_ext (u32 *keyid, int pubkey_algo, used_kid[0] = keyid[0]; used_kid[1] = keyid[1]; } - + us = get_long_user_id_string ( keyid ); write_status_text ( STATUS_USERID_HINT, us ); xfree(us); - + snprintf (buf, sizeof buf -1, "%08lX%08lX %08lX%08lX %d 0", (ulong)keyid[0], (ulong)keyid[1], (ulong)used_kid[0], (ulong)used_kid[1], pubkey_algo ); - + write_status_text ( STATUS_NEED_PASSPHRASE, buf ); } else @@ -576,7 +576,7 @@ passphrase_to_dek_ext (u32 *keyid, int pubkey_algo, { PKT_public_key *pk = xmalloc_clear( sizeof *pk ); char *p; - + p = get_user_id_native(keyid); tty_printf ("\n"); tty_printf (_("You need a passphrase to unlock the secret key for\n" @@ -586,7 +586,7 @@ passphrase_to_dek_ext (u32 *keyid, int pubkey_algo, if ( !get_pubkey( pk, keyid ) ) { 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), strtimestamp(pk->timestamp) ); @@ -610,19 +610,19 @@ passphrase_to_dek_ext (u32 *keyid, int pubkey_algo, free_public_key( pk ); } - if ( next_pw ) + if ( next_pw ) { /* Simply return the passphrase we already have in NEXT_PW. */ pw = next_pw; next_pw = NULL; } - else if ( have_static_passphrase () ) + else if ( have_static_passphrase () ) { /* Return the passphrase we have stored in FD_PASSWD. */ pw = xmalloc_secure ( strlen(fd_passwd)+1 ); strcpy ( pw, fd_passwd ); } - else + else { if ((mode == 3 || mode == 4) && (s2k->mode == 1 || s2k->mode == 3)) { @@ -643,7 +643,7 @@ passphrase_to_dek_ext (u32 *keyid, int pubkey_algo, return NULL; } } - + if ( !pw || !*pw ) write_status( STATUS_MISSING_PASSPHRASE ); @@ -689,15 +689,15 @@ gpg_format_keydesc (PKT_public_key *pk, int mode, int escaped) char *maink; char *desc; const char *prompt; - + algo_name = openpgp_pk_algo_name (pk->pubkey_algo); timestr = strtimestamp (pk->timestamp); - uid = get_user_id (pk->keyid, &uidlen); + uid = get_user_id (pk->keyid, &uidlen); orig_codeset = i18n_switchto_utf8 (); if (pk->main_keyid[0] && pk->main_keyid[1] - && pk->keyid[0] != pk->main_keyid[0] + && pk->keyid[0] != pk->main_keyid[0] && pk->keyid[1] != pk->main_keyid[1]) maink = xtryasprintf (_(" (main key ID %s)"), keystr (pk->main_keyid)); else @@ -724,7 +724,7 @@ gpg_format_keydesc (PKT_public_key *pk, int mode, int escaped) "created %s%s.\n"), prompt, (int)uidlen, uid, - nbits_from_pk (pk), algo_name, + nbits_from_pk (pk), algo_name, keystr (pk->keyid), timestr, maink?maink:"" ); xfree (maink); @@ -735,7 +735,7 @@ gpg_format_keydesc (PKT_public_key *pk, int mode, int escaped) if (escaped) { char *tmp = percent_plus_escape (desc); - xfree (desc); + xfree (desc); desc = tmp; } -- cgit v1.2.3 From 20f429f735d6e965e0660484f8286fe24fb6162b Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Wed, 2 Feb 2011 17:40:32 +0100 Subject: Compute the fingerprint for ECDH only on demand. This also fixes a failed assertion when using a v3 key where the fingerprint size is not 20. --- g10/ChangeLog | 6 +++ g10/encrypt.c | 129 ++++++++++++++++++++++++++++------------------------------ g10/pkglue.c | 27 ++++++++---- g10/pkglue.h | 7 ++-- 4 files changed, 90 insertions(+), 79 deletions(-) (limited to 'g10') diff --git a/g10/ChangeLog b/g10/ChangeLog index bb55ff85f..b3358c9d9 100644 --- a/g10/ChangeLog +++ b/g10/ChangeLog @@ -1,5 +1,11 @@ 2011-02-02 Werner Koch + * encrypt.c (write_pubkey_enc_from_list): Don't compute the + fingerprint. + * pkglue.c (pk_encrypt): Replace PK_FP by PK and compute the + fingerprint only when needed. + * pkglue.h: Include packet.h. + * import.c (transfer_secret_keys): Make sure keyids are available. * keyid.c (hash_public_key): Adjust for the ECC case. diff --git a/g10/encrypt.c b/g10/encrypt.c index 8548a5739..83b43a89e 100644 --- a/g10/encrypt.c +++ b/g10/encrypt.c @@ -84,7 +84,7 @@ encrypt_seskey (DEK *dek, DEK **seskey, byte *enckey) /* The encrypted session key is prefixed with a one-octet algorithm id. */ buf[0] = (*seskey)->algo; memcpy( buf + 1, (*seskey)->key, (*seskey)->keylen ); - + /* We only pass already checked values to the following fucntion, thus we consider any failure as fatal. */ if (openpgp_cipher_open (&hd, dek->algo, GCRY_CIPHER_MODE_CFB, 1)) @@ -119,7 +119,7 @@ use_mdc(PK_LIST pk_list,int algo) if(select_mdc_from_pklist(pk_list)) return 1; - + /* The keys don't support MDC, so now we do a bit of a hack - if any of the AESes or TWOFISH are in the prefs, we assume that the user can handle a MDC. This is valid for PGP 7, which can handle MDCs @@ -181,7 +181,7 @@ encrypt_simple (const char *filename, int mode, int use_seskey) memset( &zfx, 0, sizeof zfx); memset( &tfx, 0, sizeof tfx); init_packet(&pkt); - + /* Prepare iobufs. */ inp = iobuf_open(filename); if (inp) @@ -200,23 +200,23 @@ encrypt_simple (const char *filename, int mode, int use_seskey) release_progress_context (pfx); return rc; } - + handle_progress (pfx, inp, filename); - + if (opt.textmode) iobuf_push_filter( inp, text_filter, &tfx ); - + /* Due the the fact that we use don't use an IV to encrypt the session key we can't use the new mode with RFC1991 because it has no S2K salt. RFC1991 always uses simple S2K. */ if ( RFC1991 && use_seskey ) use_seskey = 0; - + cfx.dek = NULL; - if ( mode ) + if ( mode ) { int canceled; - + s2k = xmalloc_clear( sizeof *s2k ); s2k->mode = RFC1991? 0:opt.s2k_mode; s2k->hash_algo = S2K_DIGEST_ALGO; @@ -233,37 +233,37 @@ encrypt_simple (const char *filename, int mode, int use_seskey) release_progress_context (pfx); return rc; } - if (use_seskey && s2k->mode != 1 && s2k->mode != 3) + if (use_seskey && s2k->mode != 1 && s2k->mode != 3) { use_seskey = 0; log_info (_("can't use a symmetric ESK packet " "due to the S2K mode\n")); } - + if ( use_seskey ) { DEK *dek = NULL; - + seskeylen = openpgp_cipher_get_algo_keylen (default_cipher_algo ()); encrypt_seskey( cfx.dek, &dek, enckey ); xfree( cfx.dek ); cfx.dek = dek; } - + if (opt.verbose) log_info(_("using cipher %s\n"), openpgp_cipher_algo_name (cfx.dek->algo)); - + cfx.dek->use_mdc=use_mdc(NULL,cfx.dek->algo); } - + if (do_compress && cfx.dek && cfx.dek->use_mdc && is_file_compressed(filename, &rc)) { if (opt.verbose) log_info(_("`%s' already compressed\n"), filename); - do_compress = 0; + do_compress = 0; } - + if ( rc || (rc = open_outfile (-1, filename, opt.armor? 1:0, &out ))) { iobuf_cancel (inp); @@ -272,7 +272,7 @@ encrypt_simple (const char *filename, int mode, int use_seskey) release_progress_context (pfx); return rc; } - + if ( opt.armor ) { afx = new_armor_context (); @@ -296,7 +296,7 @@ encrypt_simple (const char *filename, int mode, int use_seskey) log_error("build symkey packet failed: %s\n", g10_errstr(rc) ); xfree (enc); } - + if (!opt.no_literal) pt = setup_plaintext_name (filename, inp); @@ -347,7 +347,7 @@ encrypt_simple (const char *filename, int mode, int use_seskey) pkt.pkttype = 0; pkt.pkt.generic = NULL; } - + /* Register the cipher filter. */ if (mode) iobuf_push_filter ( out, cipher_filter, &cfx ); @@ -359,14 +359,14 @@ encrypt_simple (const char *filename, int mode, int use_seskey) zfx.new_ctb = 1; push_compress_filter (out, &zfx, default_compress_algo()); } - + /* Do the work. */ if (!opt.no_literal) { if ( (rc = build_packet( out, &pkt )) ) log_error("build_packet failed: %s\n", g10_errstr(rc) ); } - else + else { /* User requested not to create a literal packet, so we copy the plain data. */ @@ -380,12 +380,12 @@ encrypt_simple (const char *filename, int mode, int use_seskey) } wipememory (copy_buffer, 4096); /* burn buffer */ } - + /* Finish the stuff. */ iobuf_close (inp); if (rc) iobuf_cancel(out); - else + else { iobuf_close (out); /* fixme: check returncode */ if (mode) @@ -425,7 +425,7 @@ setup_symkey (STRING2KEY **symkey_s2k,DEK **symkey_dek) static int -write_symkey_enc (STRING2KEY *symkey_s2k, DEK *symkey_dek, DEK *dek, +write_symkey_enc (STRING2KEY *symkey_s2k, DEK *symkey_dek, DEK *dek, iobuf_t out) { int rc, seskeylen = openpgp_cipher_get_algo_keylen (dek->algo); @@ -492,7 +492,7 @@ encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename, memset( &tfx, 0, sizeof tfx); init_packet(&pkt); - if (use_symkey + if (use_symkey && (rc=setup_symkey(&symkey_s2k,&symkey_dek))) { release_progress_context (pfx); @@ -509,7 +509,7 @@ encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename, return rc; } } - + if(PGP2) { for (work_list=pk_list; work_list; work_list=work_list->next) @@ -560,17 +560,17 @@ encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename, rc = open_outfile (outputfd, filename, opt.armor? 1:0, &out); if (rc) goto leave; - + if (opt.armor) { afx = new_armor_context (); push_armor_filter (afx, out); } - + /* Create a session key. */ cfx.dek = xmalloc_secure_clear (sizeof *cfx.dek); if (!opt.def_cipher_algo) - { + { /* Try to get it from the prefs. */ cfx.dek->algo = select_algo_from_prefs (pk_list, PREFTYPE_SYM, -1, NULL); /* The only way select_algo_from_prefs can fail here is when @@ -582,7 +582,7 @@ encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename, if (cfx.dek->algo == -1) { cfx.dek->algo = CIPHER_ALGO_3DES; - + if (PGP2) { log_info(_("unable to use the IDEA cipher for all of the keys " @@ -610,12 +610,12 @@ encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename, openpgp_cipher_algo_name (opt.def_cipher_algo), opt.def_cipher_algo); } - + cfx.dek->algo = opt.def_cipher_algo; } - + cfx.dek->use_mdc = use_mdc (pk_list,cfx.dek->algo); - + /* Only do the is-file-already-compressed check if we are using a MDC. This forces compressed files to be re-compressed if we do not have a MDC to give some protection against chosen ciphertext @@ -625,7 +625,7 @@ encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename, { if (opt.verbose) log_info(_("`%s' already compressed\n"), filename); - do_compress = 0; + do_compress = 0; } if (rc2) { @@ -636,7 +636,7 @@ encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename, make_session_key (cfx.dek); if (DBG_CIPHER) log_printhex ("DEK is: ", cfx.dek->key, cfx.dek->keylen ); - + rc = write_pubkey_enc_from_list (pk_list, cfx.dek, out); if (rc) goto leave; @@ -647,16 +647,16 @@ encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename, secret key needed to decrypt. */ if(use_symkey && (rc = write_symkey_enc(symkey_s2k,symkey_dek,cfx.dek,out))) goto leave; - + if (!opt.no_literal) pt = setup_plaintext_name (filename, inp); - + if (filefd != -1 && !iobuf_is_pipe_filename (filename) && *filename && !opt.textmode ) { off_t tmpsize; int overflow; - + if ( !(tmpsize = iobuf_get_filelength(inp, &overflow)) && !overflow && opt.verbose) log_info(_("WARNING: `%s' is an empty file\n"), filename ); @@ -672,7 +672,7 @@ encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename, else filesize = opt.set_filesize ? opt.set_filesize : 0; /* stdin */ - if (!opt.no_literal) + if (!opt.no_literal) { pt->timestamp = make_timestamp(); pt->mode = opt.textmode ? 't' : 'b'; @@ -693,7 +693,7 @@ encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename, if (do_compress) { int compr_algo = opt.compress_algo; - + if (compr_algo == -1) { compr_algo = select_algo_from_prefs (pk_list, PREFTYPE_ZIP, -1, NULL); @@ -702,7 +702,7 @@ encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename, /* Theoretically impossible to get here since uncompressed is implicit. */ } - else if (!opt.expert + else if (!opt.expert && select_algo_from_prefs(pk_list, PREFTYPE_ZIP, compr_algo, NULL) != compr_algo) { @@ -710,7 +710,7 @@ encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename, " violates recipient preferences\n"), compress_algo_to_string(compr_algo), compr_algo); } - + /* Algo 0 means no compression. */ if (compr_algo) { @@ -719,7 +719,7 @@ encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename, push_compress_filter (out,&zfx,compr_algo); } } - + /* Do the work. */ if (!opt.no_literal) { @@ -750,7 +750,7 @@ encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename, iobuf_close (inp); if (rc) iobuf_cancel (out); - else + else { iobuf_close (out); /* fixme: check returncode */ write_status (STATUS_END_ENCRYPTION); @@ -779,7 +779,7 @@ encrypt_filter (void *opaque, int control, size_t size = *ret_len; encrypt_filter_context_t *efx = opaque; int rc = 0; - + if (control == IOBUFCTRL_UNDERFLOW) /* decrypt */ { BUG(); /* not used */ @@ -789,19 +789,19 @@ encrypt_filter (void *opaque, int control, if ( !efx->header_okay ) { efx->cfx.dek = xmalloc_secure_clear ( sizeof *efx->cfx.dek ); - if ( !opt.def_cipher_algo ) + if ( !opt.def_cipher_algo ) { /* Try to get it from the prefs. */ efx->cfx.dek->algo = select_algo_from_prefs (efx->pk_list, PREFTYPE_SYM, -1, NULL); - if (efx->cfx.dek->algo == -1 ) + if (efx->cfx.dek->algo == -1 ) { /* Because 3DES is implicitly in the prefs, this can only happen if we do not have any public keys in the list. */ efx->cfx.dek->algo = DEFAULT_CIPHER_ALGO; } - + /* In case 3DES has been selected, print a warning if any key does not have a preference for AES. This should help to indentify why encrypting to several @@ -810,7 +810,7 @@ encrypt_filter (void *opaque, int control, && efx->cfx.dek->algo == CIPHER_ALGO_3DES) warn_missing_aes_from_pklist (efx->pk_list); } - else + else { if (!opt.expert && select_algo_from_prefs (efx->pk_list,PREFTYPE_SYM, @@ -820,12 +820,12 @@ encrypt_filter (void *opaque, int control, "violates recipient preferences\n"), openpgp_cipher_algo_name (opt.def_cipher_algo), opt.def_cipher_algo); - + efx->cfx.dek->algo = opt.def_cipher_algo; } - + efx->cfx.dek->use_mdc = use_mdc (efx->pk_list,efx->cfx.dek->algo); - + make_session_key ( efx->cfx.dek ); if (DBG_CIPHER) log_printhex ("DEK is: ", efx->cfx.dek->key, efx->cfx.dek->keylen); @@ -841,13 +841,13 @@ encrypt_filter (void *opaque, int control, if(rc) return rc; } - + iobuf_push_filter (a, cipher_filter, &efx->cfx); - + efx->header_okay = 1; } rc = iobuf_write (a, buf, size); - + } else if (control == IOBUFCTRL_FREE) { @@ -876,11 +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 ); enc = xmalloc_clear ( sizeof *enc ); enc->pubkey_algo = pk->pubkey_algo; @@ -894,9 +892,6 @@ 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 @@ -909,9 +904,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 (pk->pubkey_algo, 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, fp, pk->pkey); + rc = pk_encrypt (pk->pubkey_algo, enc->data, frame, pk, pk->pkey); gcry_mpi_release (frame); if (rc) log_error ("pubkey_encrypt failed: %s\n", gpg_strerror (rc) ); @@ -932,7 +927,7 @@ write_pubkey_enc_from_list (PK_LIST pk_list, DEK *dek, iobuf_t out) pkt.pkt.pubkey_enc = enc; rc = build_packet (out, &pkt); if (rc) - log_error ("build_packet(pubkey_enc) failed: %s\n", + log_error ("build_packet(pubkey_enc) failed: %s\n", g10_errstr (rc)); } free_pubkey_enc(enc); @@ -951,9 +946,9 @@ encrypt_crypt_files (ctrl_t ctrl, int nfiles, char **files, strlist_t remusr) if (opt.outfile) { log_error(_("--output doesn't work for this command\n")); - return; + return; } - + if (!nfiles) { char line[2048]; diff --git a/g10/pkglue.c b/g10/pkglue.c index 66ba48b60..836c2c3e2 100644 --- a/g10/pkglue.c +++ b/g10/pkglue.c @@ -37,7 +37,7 @@ 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); @@ -151,10 +151,11 @@ pk_verify (int algo, gcry_mpi_t hash, 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. + * PK is only required to compute the fingerprint for ECDH. */ int pk_encrypt (int algo, gcry_mpi_t *resarr, gcry_mpi_t data, - const byte pk_fp[MAX_FINGERPRINT_LEN], gcry_mpi_t *pkey) + PKT_public_key *pk, gcry_mpi_t *pkey) { gcry_sexp_t s_ciph, s_data, s_pkey; int rc; @@ -179,15 +180,17 @@ pk_encrypt (int algo, gcry_mpi_t *resarr, gcry_mpi_t data, if (rc || gcry_sexp_build (&s_data, NULL, "%m", data)) BUG (); } - else if (algo == PUBKEY_ALGO_ECDH) + else if (algo == PUBKEY_ALGO_ECDH) { gcry_mpi_t k; char *curve; + byte fp[MAX_FINGERPRINT_LEN]; + size_t fpn; rc = pk_ecdh_generate_ephemeral_key (pkey, &k); if (rc) return rc; - + curve = openpgp_oid_to_str (pkey[0]); if (!curve) rc = gpg_error_from_syserror (); @@ -215,12 +218,14 @@ pk_encrypt (int algo, gcry_mpi_t *resarr, gcry_mpi_t data, if (rc) ; - else if (algo == PUBKEY_ALGO_ECDH) + else if (algo == PUBKEY_ALGO_ECDH) { gcry_mpi_t shared, public, result; + byte fp[MAX_FINGERPRINT_LEN]; + size_t fpn; /* Get the shared point and the ephemeral public key. */ - shared = mpi_from_sexp (s_ciph, "s"); + shared = mpi_from_sexp (s_ciph, "s"); public = mpi_from_sexp (s_ciph, "e"); gcry_sexp_release (s_ciph); s_ciph = NULL; @@ -230,10 +235,14 @@ pk_encrypt (int algo, gcry_mpi_t *resarr, gcry_mpi_t data, gcry_mpi_dump (public); log_printf ("\n"); } - + result = NULL; - rc = pk_ecdh_encrypt_with_shared_point (1 /*=encrypton*/, shared, - pk_fp, data, pkey, &result); + fingerprint_from_pk (pk, fp, &fpn); + if (fpn != 20) + rc = gpg_error (GPG_ERR_INV_LENGTH); + else + rc = pk_ecdh_encrypt_with_shared_point (1 /*=encrypton*/, shared, + fp, data, pkey, &result); gcry_mpi_release (shared); if (!rc) { diff --git a/g10/pkglue.h b/g10/pkglue.h index eb0d7c1dc..e5165f73b 100644 --- a/g10/pkglue.h +++ b/g10/pkglue.h @@ -20,14 +20,15 @@ #ifndef GNUPG_G10_PKGLUE_H #define GNUPG_G10_PKGLUE_H +#include "packet.h" /* For PKT_public_key. */ + /*-- pkglue.c --*/ 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); + PKT_public_key *pk, gcry_mpi_t *pkey); int pk_check_secret_key (int algo, gcry_mpi_t *skey); @@ -35,7 +36,7 @@ int pk_check_secret_key (int algo, gcry_mpi_t *skey); gcry_mpi_t pk_ecdh_default_params (unsigned int qbits); gpg_error_t pk_ecdh_generate_ephemeral_key (gcry_mpi_t *pkey, gcry_mpi_t *r_k); gpg_error_t pk_ecdh_encrypt_with_shared_point -/* */ (int is_encrypt, gcry_mpi_t shared_mpi, +/* */ (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); -- cgit v1.2.3 From 0b5bcb40cf17a0e1032c113af6024c08b47d7a5c Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Thu, 3 Feb 2011 16:31:42 +0100 Subject: Finished ECC integration. Wrote the ChangeLog 2011-01-13 entry for Andrey's orginal work modulo the cleanups I did in the last week. Adjusted my own ChangeLog entries to be consistent with that entry. Nuked quite some trailing spaces; again sorry for that, I will better take care of not saving them in the future. "git diff -b" is useful to read the actual changes ;-). The ECC-INTEGRATION-2-1 branch can be closed now. --- g10/ChangeLog | 105 ++++++++++++++-------- g10/keygen.c | 265 ++++++++++++++++++++++++++++--------------------------- g10/pkglue.c | 2 - g10/pubkey-enc.c | 10 ++- g10/seskey.c | 28 +++--- 5 files changed, 219 insertions(+), 191 deletions(-) (limited to 'g10') diff --git a/g10/ChangeLog b/g10/ChangeLog index b3358c9d9..d6f93239a 100644 --- a/g10/ChangeLog +++ b/g10/ChangeLog @@ -1,3 +1,8 @@ +2011-02-03 Werner Koch + + Finished ECC integration. + Wrote change description for 2011-01-13. + 2011-02-02 Werner Koch * encrypt.c (write_pubkey_enc_from_list): Don't compute the @@ -12,36 +17,31 @@ 2011-02-01 Werner Koch - * import.c (transfer_secret_keys): Implement ECC case. - * gpg.c (main): Call setup_libgcrypt_logging. + * import.c (transfer_secret_keys): Implement ECC case. + (one_mpi_from_pkey): New. + * export.c (transfer_format_to_openpgp): Ditto. * keygen.c (gpg_curve_to_oid): New. (ecckey_from_sexp): Factor curve name mapping out to new function. 2011-01-31 Werner Koch - * misc.c (make_flagged_int, openpgp_oid_from_str) - (openpgp_oid_to_str): Move to ../common/openpgp-oids.c. - * ecdh.c (pk_ecdh_encrypt_with_shared_point): Return an opaque MPI. * build-packet.c (mpi_write): Rename to gpg_mpi_write and make global. - (write_size_body_mpi): Remove. 2011-01-30 Werner Koch - * keyid.c (keygrip_from_pk): Adjust ECC cases. * pkglue.c (pk_verify): Ditto. - * parse-packet.c (read_size_body): Rewrite. - (parse_key): Simply ECC case. + * parse-packet.c (parse_key): Simply ECC case. (parse_pubkeyenc): Ditto. * misc.c (pubkey_get_npkey): Special case ECC. (pubkey_get_nskey): Ditto. - (mpi_print): Support printfing of opaque values. + (mpi_print): Support printing of opaque values. (openpgp_oid_to_str): New. (pubkey_nbits): For ECC pass curve parameter. @@ -61,19 +61,6 @@ (get_parameter_algo): Map algo number. (ecckey_from_sexp): New. * misc.c (map_pk_gcry_to_openpgp): New. - (openpgp_oid_from_str): New. Based on libksba code. - -2011-01-26 Werner Koch - - * misc.c (ecdsa_qbits_from_Q): Use unsigned int. - - * misc.c (iobuf_read_size_body): Move and rename to .. - * parse-packet.c (read_size_body): .. here. Make static. - * misc.c (iobuf_write_size_body_mpi): Move and rename to .. - * build-packet.c (write_size_body_mpi): .. here. - (iobuf_name_oid_write, ecdh_kek_params_write, ecdh_esk_write): - Remove macros. Replace users by direct calls to - write_size_body_mpi. 2011-01-25 Werner Koch @@ -84,28 +71,17 @@ (pk_ecdh_encrypt): Remove. (pk_ecdh_encrypt_with_shared_point): Make public. - * pubkey-enc.c (get_it): Fix assertion. - Use GPG_ERR_WRONG_SECKEY instead of log_fatal. Add safety checks - for NFRAME. - - * main.h (KEYGEN_FLAG_NO_PROTECTION, KEYGEN_FLAG_TRANSIENT_KEY): - Move back to .. - * keygen.c: .. here. - (pk_ecc_keypair_gen): Make static. - (common_key_gen): Fold back into .. - (common_gen): .. this. - (delme__pk_ecc_build_sexp): Remove unused function. - (pk_ecc_keypair_gen): Fold it into .. - (gen_ecc): .. this. + * pubkey-enc.c (get_it): Fix assertion. Use GPG_ERR_WRONG_SECKEY + instead of log_fatal. Add safety checks for NFRAME. + + * keygen.c (pk_ecc_keypair_gen): Make static. (ask_keysize): Use proper rounding for ECC. (pk_ecc_build_key_params): Remove NBITSSTR. - * verify-stubs.c: Remove. - 2011-01-20 Werner Koch * keyserver.c: Rewrite most stuff for use with dirmngr. Get rid - of all spawn code. Work work pending. + of all spawn code. More work pending. * export.c (export_pubkeys_buffer): New. @@ -116,6 +92,57 @@ * gpg.c: Include call-dirmngr.h. (gpg_deinit_default_ctrl): Call gpg_dirmngr_deinit_session_data. +2011-01-13 Andrey Jivsov (wk) + + Integrated ECC support. Below are the changes finally merged into + the git master after some cleanup by wk until 2011-02-03. + + * ecdh.c: New. + + * sign.c (mpi_from_sexp): Remove. + (match_dsa_hash): Uses SHA-512 for ECDSA with 521 bits. + (hash_for): Support ECDSA. + (make_keysig_packet): Ditto. + + * seskey.c (encode_session_key): Add arg OPENPGP_PK_ALGO. Support + ECDH. + (encode_md_value): Map pkalgo. Extend size checks to ECDSA. + + * pubkey-enc.c (get_it): Support ECDH. + + * pkglue.c (mpi_from_sexp): Make global. + (pk_verify, pk_encrypt, pk_check_secret_key): Support ECC. + + * parse-packet.c (read_size_body): New. + (parse_pubkeyenc): Support ECC. + (parse_key): Ditto. + + * misc.c (map_pk_openpgp_to_gcry, map_pk_gcry_to_openpgp): New. + (openpgp_pk_test_algo, openpgp_pk_test_algo2): Map algo numbers. + (openpgp_pk_algo_usage): Support ECDH and ECDSA. + (openpgp_pk_algo_name): Simplify. + (ecdsa_qbits_from_Q): New. + + * mainproc.c (proc_pubkey_enc): Support ECC. + + * keyid.c (pubkey_letter): Add 'E' and 'e'. + (keygrip_from_pk): Supporf ECC. + + * keygen.c: Include pkglue.h. + (ask_algo): Add option 9 for ECDSA and ECDH. + (ask_keysize): Support ECDSA and ECDH. + (do_create): Ditto. + (gen_ecc): New. + (pk_ecc_build_key_params): New. + + * getkey.c (cache_public_key): Support ECC. + + * encrypt.c (write_pubkey_enc_from_list): Pass PK to PK_ENCRYPT + and the pkalgo to encode_session_key. + + * build-packet.c (do_key, do_pubkey_enc): Support ECC. + (write_size_body_mpi): New. + 2011-01-06 Werner Koch * gpg.c (main): Use keyserver_spec_t. diff --git a/g10/keygen.c b/g10/keygen.c index d8535fa61..fdae6fb83 100644 --- a/g10/keygen.c +++ b/g10/keygen.c @@ -55,7 +55,7 @@ #define KEYGEN_FLAG_TRANSIENT_KEY 2 /* Maximum number of supported algorithm preferences. */ -#define MAX_PREFS 30 +#define MAX_PREFS 30 enum para_name { pKEYTYPE, @@ -148,7 +148,7 @@ print_status_key_created (int letter, PKT_public_key *pk, const char *handle) byte array[MAX_FINGERPRINT_LEN], *s; char *buf, *p; size_t i, n; - + if (!handle) handle = ""; @@ -216,7 +216,7 @@ do_add_key_flags (PKT_signature *sig, unsigned int use) if (use & PUBKEY_USAGE_AUTH) buf[0] |= 0x20; - if (!buf[0]) + if (!buf[0]) return; build_sig_subpkt (sig, SIGSUBPKT_KEY_FLAGS, buf, 1); @@ -229,14 +229,14 @@ keygen_add_key_expire (PKT_signature *sig, void *opaque) PKT_public_key *pk = opaque; byte buf[8]; u32 u; - + if (pk->expiredate) { if (pk->expiredate > pk->timestamp) u = pk->expiredate - pk->timestamp; else u = 1; - + buf[0] = (u >> 24) & 0xff; buf[1] = (u >> 16) & 0xff; buf[2] = (u >> 8) & 0xff; @@ -258,7 +258,7 @@ static int keygen_add_key_flags_and_expire (PKT_signature *sig, void *opaque) { struct opaque_data_usage_and_pk *oduap = opaque; - + do_add_key_flags (sig, oduap->usage); return keygen_add_key_expire (sig, oduap->pk); } @@ -325,7 +325,7 @@ keygen_set_std_prefs (const char *string,int personal) gpg -r pgpkey -r gpgkey ---gives--> AES256 gpg -r gpgkey -r pgpkey ---gives--> AES - + Note that by using --personal-cipher-preferences it is possible to prefer AES128. */ @@ -392,7 +392,7 @@ keygen_set_std_prefs (const char *string,int personal) strcat(dummy_string,"Z1 "); any_compress = 1; } - + /* In case we have no compress algo at all, declare that we prefer no compresssion. */ if (!any_compress) @@ -676,18 +676,18 @@ int keygen_upd_std_prefs (PKT_signature *sig, void *opaque) { (void)opaque; - + if (!prefs_initialized) keygen_set_std_prefs (NULL, 0); - - if (nsym_prefs) + + if (nsym_prefs) build_sig_subpkt (sig, SIGSUBPKT_PREF_SYM, sym_prefs, nsym_prefs); else { delete_sig_subpkt (sig->hashed, SIGSUBPKT_PREF_SYM); delete_sig_subpkt (sig->unhashed, SIGSUBPKT_PREF_SYM); } - + if (nhash_prefs) build_sig_subpkt (sig, SIGSUBPKT_PREF_HASH, hash_prefs, nhash_prefs); else @@ -703,7 +703,7 @@ keygen_upd_std_prefs (PKT_signature *sig, void *opaque) delete_sig_subpkt (sig->hashed, SIGSUBPKT_PREF_COMPR); delete_sig_subpkt (sig->unhashed, SIGSUBPKT_PREF_COMPR); } - + /* Make sure that the MDC feature flag is set if needed. */ add_feature_mdc (sig,mdc_available); add_keyserver_modify (sig,ks_modify); @@ -721,12 +721,12 @@ int keygen_add_std_prefs (PKT_signature *sig, void *opaque) { PKT_public_key *pk = opaque; - + do_add_key_flags (sig, pk->pubkey_usage); keygen_add_key_expire (sig, opaque ); keygen_upd_std_prefs (sig, opaque); keygen_add_keyserver_url (sig,NULL); - + return 0; } @@ -840,7 +840,7 @@ make_backsig (PKT_signature *sig, PKT_public_key *pk, /* Get it into a binary packed form. */ IOBUF backsig_out = iobuf_temp(); PACKET backsig_pkt; - + init_packet (&backsig_pkt); backsig_pkt.pkttype = PKT_SIGNATURE; backsig_pkt.pkt.signature = backsig; @@ -852,15 +852,15 @@ make_backsig (PKT_signature *sig, PKT_public_key *pk, { size_t pktlen = 0; byte *buf = iobuf_get_temp_buffer (backsig_out); - + /* Remove the packet header. */ if(buf[0]&0x40) { if (buf[1] < 192) { pktlen = buf[1]; - buf += 2; - } + buf += 2; + } else if(buf[1] < 224) { pktlen = (buf[1]-192)*256; @@ -881,34 +881,34 @@ make_backsig (PKT_signature *sig, PKT_public_key *pk, else { int mark = 1; - + switch (buf[0]&3) { case 3: BUG (); break; - + case 2: pktlen = buf[mark++] << 24; pktlen |= buf[mark++] << 16; - + case 1: pktlen |= buf[mark++] << 8; - + case 0: pktlen |= buf[mark++]; } - + buf += mark; } - + /* Now make the binary blob into a subpacket. */ build_sig_subpkt (sig, SIGSUBPKT_SIGNATURE, buf, pktlen); iobuf_close (backsig_out); } } - + return err; } @@ -949,7 +949,7 @@ write_direct_sig (KBNODE root, PKT_public_key *psk, log_error ("make_keysig_packet failed: %s\n", g10_errstr (err) ); return err; } - + pkt = xmalloc_clear (sizeof *pkt); pkt->pkttype = PKT_SIGNATURE; pkt->pkt.signature = sig; @@ -990,7 +990,7 @@ write_selfsigs (KBNODE root, PKT_public_key *psk, /* The usage has not yet been set - do it now. */ pk->pubkey_usage = use; - + /* We have to cache the key, so that the verification of the signature creation is able to retrieve the public key. */ cache_public_key (pk); @@ -999,7 +999,7 @@ write_selfsigs (KBNODE root, PKT_public_key *psk, err = make_keysig_packet (&sig, pk, uid, NULL, psk, 0x13, 0, 0, timestamp, 0, keygen_add_std_prefs, pk, cache_nonce); - if (err) + if (err) { log_error ("make_keysig_packet failed: %s\n", g10_errstr (err)); return err; @@ -1041,10 +1041,10 @@ write_keybinding (KBNODE root, PKT_public_key *pri_psk, PKT_public_key *sub_psk, /* We have to cache the key, so that the verification of the * signature creation is able to retrieve the public key. */ cache_public_key (pri_pk); - + /* Find the last subkey. */ sub_pk = NULL; - for (node = root; node; node = node->next ) + for (node = root; node; node = node->next ) { if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) sub_pk = node->pkt->pkt.public_key; @@ -1055,11 +1055,11 @@ write_keybinding (KBNODE root, PKT_public_key *pri_psk, PKT_public_key *sub_psk, /* Make the signature. */ oduap.usage = use; oduap.pk = sub_pk; - err = make_keysig_packet (&sig, pri_pk, NULL, sub_pk, pri_psk, 0x18, + err = make_keysig_packet (&sig, pri_pk, NULL, sub_pk, pri_psk, 0x18, 0, 0, timestamp, 0, keygen_add_key_flags_and_expire, &oduap, cache_nonce); - if (err) + if (err) { log_error ("make_keysig_packet failed: %s\n", g10_errstr (err)); return err; @@ -1072,7 +1072,7 @@ write_keybinding (KBNODE root, PKT_public_key *pri_psk, PKT_public_key *sub_psk, if (err) return err; } - + pkt = xmalloc_clear ( sizeof *pkt ); pkt->pkttype = PKT_SIGNATURE; pkt->pkt.signature = sig; @@ -1203,7 +1203,7 @@ ecckey_from_sexp (gcry_mpi_t *array, gcry_sexp_t sexp, int algo) /* Extract key parameters from SEXP and store them in ARRAY. ELEMS is a string where each character denotes a parameter name. TOPNAME is - the name of the top element above the elements. */ + the name of the top element above the elements. */ static int key_from_sexp (gcry_mpi_t *array, gcry_sexp_t sexp, const char *topname, const char *elems) @@ -1232,7 +1232,7 @@ key_from_sexp (gcry_mpi_t *array, gcry_sexp_t sexp, } array[idx] = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG); gcry_sexp_release (l2); - if (!array[idx]) + if (!array[idx]) { rc = gpg_error (GPG_ERR_INV_OBJ); /* required parameter invalid */ goto leave; @@ -1272,7 +1272,7 @@ common_gen (const char *keyparms, int algo, const char *algoelem, log_error ("agent_genkey failed: %s\n", gpg_strerror (err) ); return err; } - + pk = xtrycalloc (1, sizeof *pk); if (!pk) { @@ -1283,7 +1283,7 @@ common_gen (const char *keyparms, int algo, const char *algoelem, pk->timestamp = timestamp; pk->version = 4; - if (expireval) + if (expireval) pk->expiredate = pk->timestamp + expireval; pk->pubkey_algo = algo; @@ -1291,14 +1291,15 @@ common_gen (const char *keyparms, int algo, const char *algoelem, err = ecckey_from_sexp (pk->pkey, s_key, algo); else err = key_from_sexp (pk->pkey, s_key, "public-key", algoelem); - if (err) + if (err) { log_error ("key_from_sexp failed: %s\n", gpg_strerror (err) ); gcry_sexp_release (s_key); free_public_key (pk); return err; } - + gcry_sexp_release (s_key); + pkt = xtrycalloc (1, sizeof *pkt); if (!pkt) { @@ -1326,7 +1327,7 @@ gen_elg (int algo, unsigned int nbits, KBNODE pub_root, int err; char *keyparms; char nbitsstr[35]; - + assert (is_ELGAMAL (algo)); if (nbits < 512) @@ -1355,7 +1356,7 @@ gen_elg (int algo, unsigned int nbits, KBNODE pub_root, err = gpg_error_from_syserror (); else { - err = common_gen (keyparms, algo, "pgy", + err = common_gen (keyparms, algo, "pgy", pub_root, timestamp, expireval, is_subkey, keygen_flags, cache_nonce_addr); xfree (keyparms); @@ -1369,7 +1370,7 @@ gen_elg (int algo, unsigned int nbits, KBNODE pub_root, * Generate an DSA key */ static gpg_error_t -gen_dsa (unsigned int nbits, KBNODE pub_root, +gen_dsa (unsigned int nbits, KBNODE pub_root, u32 timestamp, u32 expireval, int is_subkey, int keygen_flags, char **cache_nonce_addr) { @@ -1379,7 +1380,7 @@ gen_dsa (unsigned int nbits, KBNODE pub_root, char nbitsstr[35]; char qbitsstr[35]; - if ( nbits < 512) + if ( nbits < 512) { nbits = 1024; log_info(_("keysize invalid; using %u bits\n"), nbits ); @@ -1406,26 +1407,26 @@ gen_dsa (unsigned int nbits, KBNODE pub_root, /* Figure out a q size based on the key size. FIPS 180-3 says: - + L = 1024, N = 160 L = 2048, N = 224 L = 2048, N = 256 L = 3072, N = 256 - + 2048/256 is an odd pair since there is also a 2048/224 and 3072/256. Matching sizes is not a very exact science. - + We'll do 256 qbits for nbits over 2047, 224 for nbits over 1024 but less than 2048, and 160 for 1024 (DSA1). */ - + if (nbits > 2047) qbits = 256; else if ( nbits > 1024) qbits = 224; else qbits = 160; - + if (qbits != 160 ) log_info (_("WARNING: some OpenPGP programs can't" " handle a DSA key with this digest size\n")); @@ -1442,7 +1443,7 @@ gen_dsa (unsigned int nbits, KBNODE pub_root, err = gpg_error_from_syserror (); else { - err = common_gen (keyparms, PUBKEY_ALGO_DSA, "pqgy", + err = common_gen (keyparms, PUBKEY_ALGO_DSA, "pqgy", pub_root, timestamp, expireval, is_subkey, keygen_flags, cache_nonce_addr); xfree (keyparms); @@ -1473,10 +1474,10 @@ gen_ecc (int algo, unsigned int nbits, kbnode_t pub_root, curve = "NIST P-256"; else if (nbits <= 384) curve = "NIST P-384"; - else + else curve = "NIST P-521"; - keyparms = xtryasprintf ("(genkey(%s(curve %zu:%s)%s))", + keyparms = xtryasprintf ("(genkey(%s(curve %zu:%s)%s))", algo == PUBKEY_ALGO_ECDSA ? "ecdsa" : "ecdh", strlen (curve), curve, ((keygen_flags & KEYGEN_FLAG_TRANSIENT_KEY) @@ -1496,7 +1497,7 @@ gen_ecc (int algo, unsigned int nbits, kbnode_t pub_root, } -/* +/* * Generate an RSA key. */ static int @@ -1513,12 +1514,12 @@ gen_rsa (int algo, unsigned int nbits, KBNODE pub_root, if (!nbits) nbits = DEFAULT_STD_KEYSIZE; - if (nbits < 1024) + if (nbits < 1024) { nbits = 1024; log_info (_("keysize invalid; using %u bits\n"), nbits ); } - + if ((nbits % 32)) { nbits = ((nbits + 31) / 32) * 32; @@ -1526,7 +1527,7 @@ gen_rsa (int algo, unsigned int nbits, KBNODE pub_root, } snprintf (nbitsstr, sizeof nbitsstr, "%u", nbits); - keyparms = xtryasprintf ("(genkey(rsa(nbits %zu:%s)%s))", + keyparms = xtryasprintf ("(genkey(rsa(nbits %zu:%s)%s))", strlen (nbitsstr), nbitsstr, ((keygen_flags & KEYGEN_FLAG_TRANSIENT_KEY) && (keygen_flags & KEYGEN_FLAG_NO_PROTECTION))? @@ -1535,7 +1536,7 @@ gen_rsa (int algo, unsigned int nbits, KBNODE pub_root, err = gpg_error_from_syserror (); else { - err = common_gen (keyparms, algo, "ne", + err = common_gen (keyparms, algo, "ne", pub_root, timestamp, expireval, is_subkey, keygen_flags, cache_nonce_addr); xfree (keyparms); @@ -1704,7 +1705,7 @@ ask_algo (int addmode, int *r_subkey_algo, unsigned int *r_usage) if (!r_subkey_algo) r_subkey_algo = &dummy_algo; - + tty_printf (_("Please select what kind of key you want:\n")); if (!addmode) @@ -1725,7 +1726,7 @@ ask_algo (int addmode, int *r_subkey_algo, unsigned int *r_usage) tty_printf (_(" (%d) DSA (set your own capabilities)\n"), 7 ); tty_printf (_(" (%d) RSA (set your own capabilities)\n"), 8 ); } - + tty_printf (_(" (%d) ECDSA and ECDH\n"), 9 ); for(;;) @@ -1793,7 +1794,7 @@ ask_algo (int addmode, int *r_subkey_algo, unsigned int *r_usage) else tty_printf (_("Invalid selection.\n")); } - + return algo; } @@ -1863,7 +1864,7 @@ ask_keysize (int algo, unsigned int primary_keysize) nbits = *answer? atoi (answer): def; xfree(prompt); xfree(answer); - + if(nbitsmax) tty_printf(_("%s keysizes must be in the range %u-%u\n"), openpgp_pk_algo_name (algo), min, max); @@ -1923,7 +1924,7 @@ parse_expire_string( const char *string ) u32 abs_date = 0; u32 curtime = make_timestamp (); time_t tt; - + if (!*string) seconds = 0; else if (!strncmp (string, "seconds=", 8)) @@ -1937,7 +1938,7 @@ parse_expire_string( const char *string ) seconds = atoi (string) * 86400L * mult; else seconds = (u32)(-1); - + return seconds; } @@ -1947,7 +1948,7 @@ static u32 parse_creation_string (const char *string) { u32 seconds; - + if (!*string) seconds = 0; else if ( !strncmp (string, "seconds=", 8) ) @@ -2242,7 +2243,7 @@ ask_user_id (int mode, KBNODE keyblock) lower and uppercase. Below you will find the matching string which should be translated accordingly and the letter changed to match the one in the answer string. - + n = Change name c = Change comment e = Change email @@ -2403,7 +2404,7 @@ PKT_user_id * generate_user_id (KBNODE keyblock) { char *p; - + p = ask_user_id (1, keyblock); if (!p) return NULL; /* Canceled. */ @@ -2415,7 +2416,7 @@ static void release_parameter_list (struct para_data_s *r) { struct para_data_s *r2; - + for (; r ; r = r2) { r2 = r->next; @@ -2423,7 +2424,7 @@ release_parameter_list (struct para_data_s *r) xfree (r->u.dek); else if (r->key == pPASSPHRASE_S2K ) xfree (r->u.s2k); - + xfree (r); } } @@ -2446,7 +2447,7 @@ get_parameter_value( struct para_data_s *para, enum para_name key ) } static int -get_parameter_algo( struct para_data_s *para, enum para_name key, +get_parameter_algo( struct para_data_s *para, enum para_name key, int *r_default) { int i; @@ -2479,7 +2480,7 @@ get_parameter_algo( struct para_data_s *para, enum para_name key, return i; } -/* +/* * Parse the usage parameter and set the keyflags. Returns -1 on * error, 0 for no usage given or 1 for usage available. */ @@ -2493,7 +2494,7 @@ parse_parameter_usage (const char *fname, if( !r ) return 0; /* none (this is an optional parameter)*/ - + use = 0; pn = r->u.value; while ( (p = strsep (&pn, " \t,")) ) { @@ -2581,7 +2582,7 @@ get_parameter_u32( struct para_data_s *para, enum para_name key ) return r->u.expire; if( r->key == pKEYUSAGE || r->key == pSUBKEYUSAGE ) return r->u.usage; - + return (unsigned int)strtoul( r->u.value, NULL, 10 ); } @@ -2775,7 +2776,7 @@ proc_parameter_file( struct para_data_s *para, const char *fname, para = r; } - if (canceled) + if (canceled) { log_error ("%s:%d: key generation canceled\n", fname, r->lnr ); return -1; @@ -2791,7 +2792,7 @@ proc_parameter_file( struct para_data_s *para, const char *fname, * but because we do this always, why not here. */ STRING2KEY *s2k; DEK *dek; - + s2k = xmalloc_secure ( sizeof *s2k ); s2k->mode = opt.s2k_mode; s2k->hash_algo = S2K_DIGEST_ALGO; @@ -2801,7 +2802,7 @@ proc_parameter_file( struct para_data_s *para, const char *fname, set_next_passphrase (NULL ); assert (dek); memset (r->u.value, 0, strlen(r->u.value)); - + r = xmalloc_clear (sizeof *r); r->key = pPASSPHRASE_S2K; r->u.s2k = s2k; @@ -2958,7 +2959,7 @@ read_parameter_file( const char *fname ) else if( !ascii_strcasecmp( keyword, "%commit" ) ) { outctrl.lnr = lnr; if (proc_parameter_file( para, fname, &outctrl, 0 )) - print_status_key_not_created + print_status_key_not_created (get_parameter_value (para, pHANDLE)); release_parameter_list( para ); para = NULL; @@ -3052,7 +3053,7 @@ read_parameter_file( const char *fname ) /* Must invalidate that ugly cache to actually close it. */ if (outctrl.pub.fname) - iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE, + iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE, 0, (char*)outctrl.pub.fname); xfree( outctrl.pub.fname ); @@ -3073,7 +3074,7 @@ read_parameter_file( const char *fname ) * imported to the card and a backup file created by gpg-agent. */ void -generate_keypair (const char *fname, const char *card_serialno, +generate_keypair (const char *fname, const char *card_serialno, int card_backup_key) { unsigned int nbits; @@ -3085,16 +3086,16 @@ generate_keypair (const char *fname, const char *card_serialno, struct para_data_s *para = NULL; struct para_data_s *r; struct output_control_s outctrl; - + memset( &outctrl, 0, sizeof( outctrl ) ); - + if (opt.batch && card_serialno) { /* We don't yet support unattended key generation. */ log_error (_("can't do this in batch mode\n")); return; } - + if (opt.batch) { read_parameter_file( fname ); @@ -3109,9 +3110,9 @@ generate_keypair (const char *fname, const char *card_serialno, strcpy( r->u.value, card_serialno); r->next = para; para = r; - + algo = PUBKEY_ALGO_RSA; - + r = xcalloc (1, sizeof *r + 20 ); r->key = pKEYTYPE; sprintf( r->u.value, "%d", algo ); @@ -3122,7 +3123,7 @@ generate_keypair (const char *fname, const char *card_serialno, strcpy (r->u.value, "sign"); r->next = para; para = r; - + r = xcalloc (1, sizeof *r + 20 ); r->key = pSUBKEYTYPE; sprintf( r->u.value, "%d", algo ); @@ -3133,7 +3134,7 @@ generate_keypair (const char *fname, const char *card_serialno, strcpy (r->u.value, "encrypt"); r->next = para; para = r; - + r = xcalloc (1, sizeof *r + 20 ); r->key = pAUTHKEYTYPE; sprintf( r->u.value, "%d", algo ); @@ -3152,11 +3153,11 @@ generate_keypair (const char *fname, const char *card_serialno, } else { - int subkey_algo; + int subkey_algo; algo = ask_algo (0, &subkey_algo, &use); if (subkey_algo) - { + { /* Create primary and subkey at once. */ both = 1; r = xmalloc_clear( sizeof *r + 20 ); @@ -3175,7 +3176,7 @@ generate_keypair (const char *fname, const char *card_serialno, strcpy( r->u.value, "sign" ); r->next = para; para = r; - + r = xmalloc_clear( sizeof *r + 20 ); r->key = pSUBKEYTYPE; sprintf( r->u.value, "%d", subkey_algo); @@ -3187,14 +3188,14 @@ generate_keypair (const char *fname, const char *card_serialno, r->next = para; para = r; } - else + else { r = xmalloc_clear( sizeof *r + 20 ); r->key = pKEYTYPE; sprintf( r->u.value, "%d", algo ); r->next = para; para = r; - + if (use) { r = xmalloc_clear( sizeof *r + 25 ); @@ -3216,7 +3217,7 @@ generate_keypair (const char *fname, const char *card_serialno, r->next = para; para = r; } - + expire = ask_expire_interval(0,NULL); r = xmalloc_clear( sizeof *r + 20 ); r->key = pKEYEXPIRE; @@ -3230,7 +3231,7 @@ generate_keypair (const char *fname, const char *card_serialno, para = r; uid = ask_user_id (0, NULL); - if( !uid ) + if( !uid ) { log_error(_("Key generation canceled.\n")); release_parameter_list( para ); @@ -3241,7 +3242,7 @@ generate_keypair (const char *fname, const char *card_serialno, strcpy( r->u.value, uid ); r->next = para; para = r; - + proc_parameter_file( para, "[internal]", &outctrl, !!card_serialno); release_parameter_list( para ); } @@ -3276,7 +3277,7 @@ generate_raw_key (int algo, unsigned int nbits, u32 created_at, log_info (_("keysize invalid; using %u bits\n"), nbits ); } - if ((nbits % 32)) + if ((nbits % 32)) { nbits = ((nbits + 31) / 32) * 32; log_info(_("keysize rounded up to %u bits\n"), nbits ); @@ -3314,16 +3315,16 @@ generate_raw_key (int algo, unsigned int nbits, u32 created_at, } rc = key_from_sexp (sk->skey, s_key, "private-key", "nedpqu"); gcry_sexp_release (s_key); - if (rc) + if (rc) { log_error ("key_from_sexp failed: %s\n", gpg_strerror (rc) ); goto leave; } - + for (i=npkey; i < nskey; i++) sk->csum += checksum_mpi (sk->skey[i]); - if (r_sk_unprotected) + if (r_sk_unprotected) *r_sk_unprotected = copy_secret_key (NULL, sk); rc = genhelp_protect (dek, s2k, sk); @@ -3377,10 +3378,10 @@ do_generate_keypair (struct para_data_s *para, log_info("dry-run mode - key generation skipped\n"); return; } - - if ( outctrl->use_files ) + + if ( outctrl->use_files ) { - if ( outctrl->pub.newfname ) + if ( outctrl->pub.newfname ) { iobuf_close(outctrl->pub.stream); outctrl->pub.stream = NULL; @@ -3390,8 +3391,8 @@ do_generate_keypair (struct para_data_s *para, xfree( outctrl->pub.fname ); outctrl->pub.fname = outctrl->pub.newfname; outctrl->pub.newfname = NULL; - - if (is_secured_filename (outctrl->pub.fname) ) + + if (is_secured_filename (outctrl->pub.fname) ) { outctrl->pub.stream = NULL; gpg_err_set_errno (EPERM); @@ -3420,7 +3421,7 @@ do_generate_keypair (struct para_data_s *para, structure we create is known in advance we simply generate a linked list. The first packet is a dummy packet which we flag as deleted. The very first packet must always be a KEY packet. */ - + start_tree (&pub_root); timestamp = get_parameter_u32 (para, pKEYCREATIONDATE); @@ -3441,7 +3442,7 @@ do_generate_keypair (struct para_data_s *para, get_parameter_uint( para, pKEYLENGTH ), pub_root, timestamp, - get_parameter_u32( para, pKEYEXPIRE ), 0, + get_parameter_u32( para, pKEYEXPIRE ), 0, outctrl->keygen_flags, &cache_nonce); else err = gen_card_key (PUBKEY_ALGO_RSA, 1, 1, pub_root, @@ -3490,7 +3491,7 @@ do_generate_keypair (struct para_data_s *para, { err = do_create (get_parameter_algo (para, pSUBKEYTYPE, NULL), get_parameter_uint (para, pSUBKEYLENGTH), - pub_root, + pub_root, timestamp, get_parameter_u32 (para, pSUBKEYEXPIRE), 1, outctrl->keygen_flags, &cache_nonce); @@ -3498,7 +3499,7 @@ do_generate_keypair (struct para_data_s *para, if (!err) { kbnode_t node; - + for (node = pub_root; node; node = node->next) if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) sub_psk = node->pkt->pkt.public_key; @@ -3542,26 +3543,26 @@ do_generate_keypair (struct para_data_s *para, KEYDB_HANDLE pub_hd = keydb_new (); err = keydb_locate_writable (pub_hd, NULL); - if (err) + if (err) log_error (_("no writable public keyring found: %s\n"), g10_errstr (err)); - + if (!err && opt.verbose) { log_info (_("writing public key to `%s'\n"), keydb_get_resource_name (pub_hd)); } - - if (!err) + + if (!err) { err = keydb_insert_keyblock (pub_hd, pub_root); if (err) log_error (_("error writing public keyring `%s': %s\n"), keydb_get_resource_name (pub_hd), g10_errstr(err)); } - + keydb_release (pub_hd); - + if (!err) { int no_enc_rsa; @@ -3581,14 +3582,14 @@ do_generate_keypair (struct para_data_s *para, update_ownertrust (pk, ((get_ownertrust (pk) & ~TRUST_MASK) | TRUST_ULTIMATE )); - if (!opt.batch) + if (!opt.batch) { tty_printf (_("public and secret key created and signed.\n") ); tty_printf ("\n"); list_keyblock (pub_root, 0, 1, NULL); } - - + + if (!opt.batch && (get_parameter_algo (para, pKEYTYPE, NULL) == PUBKEY_ALGO_DSA || no_enc_rsa ) @@ -3613,12 +3614,12 @@ do_generate_keypair (struct para_data_s *para, } else { - PKT_public_key *pk = find_kbnode (pub_root, + PKT_public_key *pk = find_kbnode (pub_root, PKT_PUBLIC_KEY)->pkt->pkt.public_key; print_status_key_created (did_sub? 'B':'P', pk, get_parameter_value (para, pHANDLE)); } - + release_kbnode (pub_root); xfree (cache_nonce); } @@ -3643,7 +3644,7 @@ generate_subkeypair (KBNODE keyblock) /* Break out the primary key. */ node = find_kbnode (keyblock, PKT_PUBLIC_KEY); - if (!node) + if (!node) { log_error ("Oops; primary key missing in keyblock!\n"); err = gpg_error (GPG_ERR_BUG); @@ -3667,7 +3668,7 @@ generate_subkeypair (KBNODE keyblock) } } - if (pri_psk->version < 4) + if (pri_psk->version < 4) { log_info (_("NOTE: creating subkeys for v3 keys " "is not OpenPGP compliant\n")); @@ -3695,7 +3696,7 @@ generate_subkeypair (KBNODE keyblock) { err = gpg_error (GPG_ERR_CANCELED); goto leave; - } + } err = do_create (algo, nbits, keyblock, cur_time, expire, 1, 0, NULL); if (err) @@ -3803,7 +3804,7 @@ generate_card_subkeypair (kbnode_t pub_keyblock, if (!err) { PKT_public_key *sub_pk = NULL; - + for (node = pub_keyblock; node; node = node->next) if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) sub_pk = node->pkt->pkt.public_key; @@ -3848,7 +3849,7 @@ write_keyblock( IOBUF out, KBNODE node ) /* Note that timestamp is an in/out arg. */ static gpg_error_t -gen_card_key (int algo, int keyno, int is_primary, kbnode_t pub_root, +gen_card_key (int algo, int keyno, int is_primary, kbnode_t pub_root, u32 *timestamp, u32 expireval) { #ifdef ENABLE_CARD_SUPPORT @@ -3869,11 +3870,11 @@ gen_card_key (int algo, int keyno, int is_primary, kbnode_t pub_root, xfree (pk); return gpg_error_from_syserror (); } - + /* Note: SCD knows the serialnumber, thus there is no point in passing it. */ err = agent_scd_genkey (&info, keyno, 1, NULL, *timestamp); - /* The code below is not used because we force creation of - * the a card key (3rd arg). + /* The code below is not used because we force creation of + * the a card key (3rd arg). * if (gpg_err_code (rc) == GPG_ERR_EEXIST) * { * tty_printf ("\n"); @@ -3898,7 +3899,7 @@ gen_card_key (int algo, int keyno, int is_primary, kbnode_t pub_root, xfree (pk); return err; } - + if (*timestamp != info.created_at) log_info ("NOTE: the key does not use the suggested creation date\n"); *timestamp = info.created_at; @@ -3909,7 +3910,7 @@ gen_card_key (int algo, int keyno, int is_primary, kbnode_t pub_root, pk->expiredate = pk->timestamp + expireval; pk->pubkey_algo = algo; pk->pkey[0] = info.n; - pk->pkey[1] = info.e; + pk->pkey[1] = info.e; pkt->pkttype = is_primary ? PKT_PUBLIC_KEY : PKT_PUBLIC_SUBKEY; pkt->pkt.public_key = pk; @@ -3937,11 +3938,11 @@ gen_card_key_with_backup (int algo, int keyno, int is_primary, size_t n; int i; unsigned int nbits; - + /* Get the size of the key directly from the card. */ { struct agent_card_info_s info; - + memset (&info, 0, sizeof info); if (!agent_scd_getattr ("KEY-ATTR", &info) && info.key_attr[1].algo) @@ -4007,7 +4008,7 @@ gen_card_key_with_backup (int algo, int keyno, int is_primary, else fp = iobuf_create (fname); umask (oldmask); - if (!fp) + if (!fp) { rc = gpg_error_from_syserror (); log_error (_("can't create backup file `%s': %s\n"), @@ -4033,7 +4034,7 @@ gen_card_key_with_backup (int algo, int keyno, int is_primary, { unsigned char array[MAX_FINGERPRINT_LEN]; char *fprbuf, *p; - + iobuf_close (fp); iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE, 0, (char*)fname); log_info (_("NOTE: backup of card key saved to `%s'\n"), fname); @@ -4150,7 +4151,7 @@ save_unprotected_key_to_card (PKT_public_key *sk, int keyno) p = stpcpy (stpcpy (stpcpy (p, numbuf), numbuf2), "))"); /* Fixme: Unfortunately we don't have the serialnumber available - - thus we can't pass it down to the agent. */ + thus we can't pass it down to the agent. */ rc = agent_scd_writekey (keyno, NULL, sexp, p - sexp); leave: diff --git a/g10/pkglue.c b/g10/pkglue.c index 836c2c3e2..05f7167c2 100644 --- a/g10/pkglue.c +++ b/g10/pkglue.c @@ -184,8 +184,6 @@ pk_encrypt (int algo, gcry_mpi_t *resarr, gcry_mpi_t data, { gcry_mpi_t k; char *curve; - byte fp[MAX_FINGERPRINT_LEN]; - size_t fpn; rc = pk_ecdh_generate_ephemeral_key (pkey, &k); if (rc) diff --git a/g10/pubkey-enc.c b/g10/pubkey-enc.c index d45b4a217..1b94af54f 100644 --- a/g10/pubkey-enc.c +++ b/g10/pubkey-enc.c @@ -160,7 +160,7 @@ get_it (PKT_pubkey_enc *enc, DEK *dek, PKT_public_key *sk, u32 *keyid) if (!enc->data[0] || !enc->data[1]) err = gpg_error (GPG_ERR_BAD_MPI); else - err = gcry_sexp_build (&s_data, NULL, "(enc-val(elg(a%m)(b%m)))", + err = gcry_sexp_build (&s_data, NULL, "(enc-val(elg(a%m)(b%m)))", enc->data[0], enc->data[1]); } else if (pkalgo == GCRY_PK_RSA || pkalgo == GCRY_PK_RSA_E) @@ -185,9 +185,11 @@ get_it (PKT_pubkey_enc *enc, DEK *dek, PKT_public_key *sk, u32 *keyid) if (err) goto leave; - /* fixme: only needed for ECDH. Don't compute always. */ - fingerprint_from_pk (sk, fp, &fpn); - assert (fpn == 20); + if (sk->pubkey_algo == PUBKEY_ALGO_ECDH) + { + fingerprint_from_pk (sk, fp, &fpn); + assert (fpn == 20); + } /* Decrypt. */ desc = gpg_format_keydesc (sk, 0, 1); diff --git a/g10/seskey.c b/g10/seskey.c index 2d7918d39..1f3e8ab49 100644 --- a/g10/seskey.c +++ b/g10/seskey.c @@ -49,10 +49,10 @@ make_session_key( DEK *dek ) 0 : GCRY_CIPHER_ENABLE_SYNC))) ) BUG(); gcry_randomize (dek->key, dek->keylen, GCRY_STRONG_RANDOM ); - for (i=0; i < 16; i++ ) + for (i=0; i < 16; i++ ) { rc = gcry_cipher_setkey (chd, dek->key, dek->keylen); - if (!rc) + if (!rc) { gcry_cipher_close (chd); return; @@ -82,7 +82,7 @@ encode_session_key (int openpgp_pk_algo, DEK *dek, unsigned int nbits) int i,n; u16 csum; gcry_mpi_t a; - + if (DBG_CIPHER) log_debug ("encode_session_key: encoding %d byte DEK", dek->keylen); @@ -102,10 +102,10 @@ encode_session_key (int openpgp_pk_algo, DEK *dek, unsigned int nbits) */ 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; @@ -122,13 +122,13 @@ encode_session_key (int openpgp_pk_algo, DEK *dek, unsigned int nbits) "[%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. @@ -136,7 +136,7 @@ encode_session_key (int openpgp_pk_algo, DEK *dek, unsigned int nbits) 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) @@ -163,7 +163,7 @@ encode_session_key (int openpgp_pk_algo, DEK *dek, unsigned int nbits) { int j, k; byte *pp; - + /* Count the zero bytes. */ for (j=k=0; j < i; j++ ) if (!p[j]) @@ -273,9 +273,9 @@ encode_md_value (PKT_public_key *pk, gcry_md_hd_t md, int hash_algo) i.e. 04 */ if (pkalgo == GCRY_PK_ECDSA) qbytes = ecdsa_qbits_from_Q (qbytes); - + /* Make sure it is a multiple of 8 bits. */ - + if (qbytes%8) { log_error(_("DSA requires the hash length to be a" @@ -295,11 +295,11 @@ encode_md_value (PKT_public_key *pk, gcry_md_hd_t md, int hash_algo) 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. + automatically left-truncate. FIXME: Check against FIPS. This checks would require the use of SHA512 with ECDSA 512. I @@ -308,7 +308,7 @@ encode_md_value (PKT_public_key *pk, gcry_md_hd_t md, int hash_algo) 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) + if (gcry_md_get_algo_dlen (hash_algo) < ((pkalgo == GCRY_PK_ECDSA && qbytes > (521)/8) ? 512/8 : qbytes)) { log_error (_("%s key %s requires a %zu bit or larger hash " -- cgit v1.2.3