summaryrefslogtreecommitdiffstats
path: root/g10
diff options
context:
space:
mode:
Diffstat (limited to 'g10')
-rw-r--r--g10/Makefile.am6
-rw-r--r--g10/armor.c2
-rw-r--r--g10/build-packet.c46
-rw-r--r--g10/call-agent.c3
-rw-r--r--g10/call-agent.h1
-rw-r--r--g10/ecdh.c477
-rw-r--r--g10/encrypt.c11
-rw-r--r--g10/export.c12
-rw-r--r--g10/getkey.c5
-rw-r--r--g10/gpg.c4
-rw-r--r--g10/keygen.c281
-rw-r--r--g10/keyid.c26
-rw-r--r--g10/main.h13
-rw-r--r--g10/mainproc.c6
-rw-r--r--g10/misc.c147
-rw-r--r--g10/parse-packet.c115
-rw-r--r--g10/passphrase.c6
-rw-r--r--g10/pkglue.c151
-rw-r--r--g10/pkglue.h12
-rw-r--r--g10/pubkey-enc.c62
-rw-r--r--g10/seskey.c69
-rw-r--r--g10/sign.c38
-rw-r--r--g10/verify-stubs.c30
23 files changed, 1370 insertions, 153 deletions
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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "gpg.h"
+#include "util.h"
+#include "pkglue.h"
+#include "main.h"
+#include "options.h"
+
+gcry_mpi_t
+pk_ecdh_default_params_to_mpi( int qbits ) {
+ gpg_error_t err;
+ gcry_mpi_t result;
+ /* Defaults are the strongest possible choices. Performance is not an issue here, only interoperability. */
+ byte kek_params[4] = {
+ 3 /*size of following field*/,
+ 1 /*fixed version for KDF+AESWRAP*/,
+ DIGEST_ALGO_SHA512 /* KEK MD */,
+ CIPHER_ALGO_AES256 /*KEK AESWRAP alg*/
+ };
+ int i;
+
+ static const struct {
+ int qbits;
+ int openpgp_hash_id;
+ int openpgp_cipher_id;
+ } kek_params_table[] = {
+ { 256, DIGEST_ALGO_SHA256, CIPHER_ALGO_AES },
+ { 384, DIGEST_ALGO_SHA384, CIPHER_ALGO_AES256 },
+ { 528, DIGEST_ALGO_SHA512, CIPHER_ALGO_AES256 } // 528 is 521 rounded to the 8 bit boundary
+ };
+
+ for( i=0; i<sizeof(kek_params_table)/sizeof(kek_params_table[0]); i++ ) {
+ if( kek_params_table[i].qbits >= qbits ) {
+ kek_params[2] = kek_params_table[i].openpgp_hash_id;
+ kek_params[3] = kek_params_table[i].openpgp_cipher_id;
+ break;
+ }
+ }
+ if( DBG_CIPHER )
+ log_printhex ("ecdh kek params are", kek_params, sizeof(kek_params) );
+
+ err = gcry_mpi_scan (&result, GCRYMPI_FMT_USG, kek_params, sizeof(kek_params), NULL);
+ if (err)
+ log_fatal ("mpi_scan failed: %s\n", gpg_strerror (err));
+
+ return result;
+}
+
+/* returns allocated (binary) KEK parameters; the size is returned in sizeout.
+ * The caller must free 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<sizeof(kek_params_table)/sizeof(kek_params_table[0]); i++ ) {
+ if( kek_params_table[i].qbits >= qbits ) {
+ kek_params[2] = kek_params_table[i].openpgp_hash_id;
+ kek_params[3] = kek_params_table[i].openpgp_cipher_id;
+ break;
+ }
+ }
+ if( DBG_CIPHER )
+ log_printhex ("ecdh kek params are", kek_params, sizeof(kek_params) );
+
+ p = xtrymalloc( sizeof(kek_params) );
+ if( p == 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(nbits<min || nbits>max)
tty_printf(_("%s keysizes must be in the range %u-%u\n"),
- gcry_pk_algo_name (algo), min, max);
+ openpgp_pk_algo_name (algo), min, max);
else
break;
}
@@ -1692,10 +1904,18 @@ ask_keysize (int algo, unsigned int primary_keysize)
leave:
if( algo == PUBKEY_ALGO_DSA && (nbits % 64) )
{
- nbits = ((nbits + 63) / 64) * 64;
- if (!autocomp)
- tty_printf(_("rounded up to %u bits\n"), nbits );
+ if( !(algo == PUBKEY_ALGO_ECDSA && nbits==521) ) {
+ nbits = ((nbits + 63) / 64) * 64;
+ if (!autocomp)
+ tty_printf(_("rounded up to %u bits\n"), nbits );
+ }
}
+ else if( algo == PUBKEY_ALGO_ECDH || algo == PUBKEY_ALGO_ECDSA ) {
+ if( nbits != 256 && nbits != 384 && nbits != 521 ) {
+ nbits = min;
+ tty_printf(_("unsupported ECDH value, corrected to the minimum %u bits\n"), nbits );
+ }
+ }
else if( (nbits % 32) )
{
nbits = ((nbits + 31) / 32) * 32;
@@ -2185,6 +2405,9 @@ do_create (int algo, unsigned int nbits, KBNODE pub_root,
else if (algo == PUBKEY_ALGO_DSA)
err = gen_dsa (nbits, pub_root, timestamp, expiredate, is_subkey,
keygen_flags, cache_nonce_addr);
+ else if( algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_ECDH )
+ err = gen_ecc (algo, nbits, pub_root, timestamp, expiredate, is_subkey,
+ keygen_flags, cache_nonce_addr);
else if (algo == PUBKEY_ALGO_RSA)
err = gen_rsa (algo, nbits, pub_root, timestamp, expiredate, is_subkey,
keygen_flags, cache_nonce_addr);
diff --git a/g10/keyid.c b/g10/keyid.c
index 62ce03685..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 <assert.h>
static int
string_count_chr (const char *string, int c)
@@ -294,7 +295,7 @@ print_pubkey_algo_note( int algo )
{
warn=1;
log_info (_("WARNING: using experimental public key algorithm %s\n"),
- gcry_pk_algo_name (algo));
+ openpgp_cipher_algo_name (algo));
}
}
else if (algo == 20)
@@ -365,6 +366,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 <x> <y> */
+int ecdsa_qbits_from_Q( int qbits ) {
+ if( qbits%8>3 ) {
+ log_error(_("ECDSA public key is expected to be in SEC encoding multiple of 8 bits\n"));
+ return 0;
+ }
+ qbits -= qbits%8;
+ qbits /= 2;
+ return qbits;
+}
+
+
+
+
diff --git a/g10/parse-packet.c b/g10/parse-packet.c
index 3714739d4..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 <x> <y> */
+ 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include "gpg.h"
+#include "main.h"
+
+int
+pk_ecc_keypair_gen( PKT_public_key **pk_out, int algo, int keygen_flags, char **cache_nonce_addr, unsigned nbits) {
+ return GPG_ERR_NOT_IMPLEMENTED;
+}