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