aboutsummaryrefslogtreecommitdiffstats
path: root/g10/keygen.c
diff options
context:
space:
mode:
authorAndrey Jivsov <[email protected]>2011-01-06 01:33:17 +0000
committerAndrey Jivsov <[email protected]>2011-01-06 01:33:17 +0000
commite0972d3d962548972872d889b362560e499340d1 (patch)
tree26c597a42968ecef26bb7c36b9850b26cb17ebf5 /g10/keygen.c
parentMake sure that --disable-optimization works in its attempt to replace -Ox wit... (diff)
downloadgnupg-e0972d3d962548972872d889b362560e499340d1.tar.gz
gnupg-e0972d3d962548972872d889b362560e499340d1.zip
Integrating http://code.google.com/p/gnupg-ecc/source/detail?r=15 .
The following works: gpg2 --gen-key (ECC) gpg2 --list-keys gpg2 --list-packets ~/.gnupg/pubring.gpg gpg2 --list-packets <private key from http://sites.google.com/site/brainhub/pgpecckeys> ECDH doesn't work yet as the code must be re-written to adjust for gpg-agent refactoring.
Diffstat (limited to 'g10/keygen.c')
-rw-r--r--g10/keygen.c281
1 files changed, 252 insertions, 29 deletions
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);