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/ecdh.c | 477 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 477 insertions(+) create mode 100644 g10/ecdh.c (limited to 'g10/ecdh.c') 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; +} + + -- 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/ecdh.c | 82 ++++++++++++++++++++++++++------------------------------------ 1 file changed, 34 insertions(+), 48 deletions(-) (limited to 'g10/ecdh.c') 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 ); } -- 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/ecdh.c | 451 +++++++++++++++++++++++++++++++++++-------------------------- 1 file changed, 261 insertions(+), 190 deletions(-) (limited to 'g10/ecdh.c') 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); } -- 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/ecdh.c | 104 ++++++++++++++++++------------------------------------------- 1 file changed, 30 insertions(+), 74 deletions(-) (limited to 'g10/ecdh.c') 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; } -- 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/ecdh.c | 212 ++++++++++++++++++++++++++----------------------------------- 1 file changed, 90 insertions(+), 122 deletions(-) (limited to 'g10/ecdh.c') 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], -- 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/ecdh.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'g10/ecdh.c') 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 */ -- 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/ecdh.c | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) (limited to 'g10/ecdh.c') 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; -- 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/ecdh.c | 191 +++++++++++++++++++++++++++++++------------------------------ 1 file changed, 97 insertions(+), 94 deletions(-) (limited to 'g10/ecdh.c') 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; } } -- 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/ecdh.c | 81 ++++++++++++++++++++++++++++++-------------------------------- 1 file changed, 39 insertions(+), 42 deletions(-) (limited to 'g10/ecdh.c') 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); } - - -- cgit v1.2.3