aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--agent/ChangeLog5
-rw-r--r--agent/protect.c5
-rw-r--r--g10/ChangeLog34
-rw-r--r--g10/build-packet.c93
-rw-r--r--g10/ecdh.c26
-rw-r--r--g10/keygen.c229
-rw-r--r--g10/keyid.c21
-rw-r--r--g10/main.h11
-rw-r--r--g10/misc.c304
-rw-r--r--g10/parse-packet.c201
-rw-r--r--g10/pkglue.c48
-rw-r--r--g10/pkglue.h2
12 files changed, 621 insertions, 358 deletions
diff --git a/agent/ChangeLog b/agent/ChangeLog
index 7dace3aef..432803fa3 100644
--- a/agent/ChangeLog
+++ b/agent/ChangeLog
@@ -1,3 +1,8 @@
+2011-01-27 Werner Koch <[email protected]>
+
+ * protect.c (protect_info): Adjust ECDSA and ECDH parameter names.
+ Add "ecc".
+
2011-01-19 Werner Koch <[email protected]>
* trustlist.c (read_one_trustfile): Also chop an CR.
diff --git a/agent/protect.c b/agent/protect.c
index d0a5fe9e3..61ed8ee38 100644
--- a/agent/protect.c
+++ b/agent/protect.c
@@ -52,8 +52,9 @@ static struct {
{ "rsa", "nedpqu", 2, 5 },
{ "dsa", "pqgyx", 4, 4 },
{ "elg", "pgyx", 3, 3 },
- { "ecdsa","cqd", 2, 2 },
- { "ecdh", "cqpd", 3, 3 },
+ { "ecdsa","pabgnqd", 6, 6 },
+ { "ecdh", "pabgnqd", 6, 6 },
+ { "ecc", "pabgnqd", 6, 6 },
{ NULL }
};
diff --git a/g10/ChangeLog b/g10/ChangeLog
index b27601563..f6c144d7c 100644
--- a/g10/ChangeLog
+++ b/g10/ChangeLog
@@ -1,3 +1,37 @@
+2011-01-30 Werner Koch <[email protected]>
+
+
+ * keyid.c (keygrip_from_pk): Adjust ECC cases.
+ * pkglue.c (pk_verify): Ditto.
+
+ * parse-packet.c (read_size_body): Rewrite.
+ (parse_key): Simply ECC case.
+ (parse_pubkeyenc): Ditto.
+
+ * misc.c (pubkey_get_npkey): Special case ECC.
+ (pubkey_get_nskey): Ditto.
+ (mpi_print): Support printfing of opaque values.
+ (openpgp_oid_to_str): New.
+ (pubkey_nbits): For ECC pass curve parameter.
+
+ * ecdh.c (pk_ecdh_default_params): Change to return an opaque MPI.
+
+ * build-packet.c (do_key): Automatically handle real and opaque
+ key parameters.
+ (write_fake_data): Return an error code.
+ (mpi_write): Support writing opaque MPIs.
+ (do_pubkey_enc): Simplify ECC handling.
+
+2011-01-28 Werner Koch <[email protected]>
+
+ * keygen.c (gen_ecc): Rewrite. Select a named curve and create a
+ keyspec based on that.
+ (pk_ecc_build_key_params): Remove.
+ (get_parameter_algo): Map algo number.
+ (ecckey_from_sexp): New.
+ * misc.c (map_pk_gcry_to_openpgp): New.
+ (openpgp_oid_from_str): New. Based on libksba code.
+
2011-01-26 Werner Koch <[email protected]>
* misc.c (ecdsa_qbits_from_Q): Use unsigned int.
diff --git a/g10/build-packet.c b/g10/build-packet.c
index e2bbdb511..122ef15e7 100644
--- a/g10/build-packet.c
+++ b/g10/build-packet.c
@@ -1,6 +1,6 @@
/* build-packet.c - assemble packets and write them
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
- * 2006, 2010 Free Software Foundation, Inc.
+ * 2006, 2010, 2011 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
@@ -160,19 +160,31 @@ build_packet( IOBUF out, PACKET *pkt )
static int
mpi_write (iobuf_t out, gcry_mpi_t a)
{
- char buffer[(MAX_EXTERN_MPI_BITS+7)/8+2]; /* 2 is for the mpi length. */
- size_t nbytes;
int rc;
- nbytes = DIM(buffer);
- rc = gcry_mpi_print (GCRYMPI_FMT_PGP, buffer, nbytes, &nbytes, a );
- if( !rc )
- rc = iobuf_write( out, buffer, nbytes );
- else if (gpg_err_code(rc) == GPG_ERR_TOO_SHORT )
+ if (gcry_mpi_get_flag (a, GCRYMPI_FLAG_OPAQUE))
{
- log_info ("mpi too large (%u bits)\n", gcry_mpi_get_nbits (a));
- /* The buffer was too small. We better tell the user about the MPI. */
- rc = gpg_error (GPG_ERR_TOO_LARGE);
+ size_t nbits;
+ const void *p;
+
+ p = gcry_mpi_get_opaque (a, &nbits);
+ rc = iobuf_write (out, p, (nbits+7)/8);
+ }
+ else
+ {
+ char buffer[(MAX_EXTERN_MPI_BITS+7)/8+2]; /* 2 is for the mpi length. */
+ size_t nbytes;
+
+ nbytes = DIM(buffer);
+ rc = gcry_mpi_print (GCRYMPI_FMT_PGP, buffer, nbytes, &nbytes, a );
+ if( !rc )
+ rc = iobuf_write( out, buffer, nbytes );
+ else if (gpg_err_code(rc) == GPG_ERR_TOO_SHORT )
+ {
+ log_info ("mpi too large (%u bits)\n", gcry_mpi_get_nbits (a));
+ /* The buffer was too small. We better tell the user about the MPI. */
+ rc = gpg_error (GPG_ERR_TOO_LARGE);
+ }
}
return rc;
@@ -252,19 +264,20 @@ calc_packet_length( PACKET *pkt )
return n;
}
-static void
+
+static gpg_error_t
write_fake_data (IOBUF out, gcry_mpi_t a)
{
- if (a)
- {
- unsigned int n;
- void *p;
+ unsigned int n;
+ void *p;
- p = gcry_mpi_get_opaque ( a, &n );
- iobuf_write (out, p, (n+7)/8 );
- }
+ if (!a)
+ return 0;
+ p = gcry_mpi_get_opaque ( a, &n);
+ return iobuf_write (out, p, (n+7)/8 );
}
+
static int
do_user_id( IOBUF out, int ctb, PKT_user_id *uid )
{
@@ -326,33 +339,11 @@ do_key (iobuf_t out, int ctb, PKT_public_key *pk)
}
assert (npkey < nskey);
- /* Writing the public parameters is easy. Except if we do an
- adjustment for ECC OID and possibly KEK params for ECDH. */
- if (pk->pubkey_algo == PUBKEY_ALGO_ECDSA
- || pk->pubkey_algo == PUBKEY_ALGO_ECDH)
+ for (i=0; i < npkey; i++ )
{
- /* Write DER of OID with preceeding length byte. */
- err = write_size_body_mpi (a, pk->pkey[0]);
- if (err)
- goto leave;
- /* Write point Q, the public key. */
- err = mpi_write (a, pk->pkey[1]);
+ err = mpi_write (a, pk->pkey[i]);
if (err)
goto leave;
-
- /* Write one more public field for ECDH. */
- if (pk->pubkey_algo == PUBKEY_ALGO_ECDH)
- {
- err = write_size_body_mpi (a, pk->pkey[2]);
- if (err)
- goto leave;
- }
- }
- else
- {
- for (i=0; i < npkey; i++ )
- if ((err = mpi_write (a, pk->pkey[i])))
- goto leave;
}
@@ -520,20 +511,8 @@ do_pubkey_enc( IOBUF out, int ctb, PKT_pubkey_enc *enc )
if ( !n )
write_fake_data( a, enc->data[0] );
- if (enc->pubkey_algo == PUBKEY_ALGO_ECDH )
- {
- /* The second field persists as a LEN+field structure, even
- * though it is stored for uniformity as an MPI internally. */
- assert (n == 2);
- rc = mpi_write (a, enc->data[0]);
- if (!rc)
- rc = write_size_body_mpi (a, enc->data[1]);
- }
- else
- {
- for (i=0; i < n && !rc ; i++ )
- rc = mpi_write(a, enc->data[i] );
- }
+ for (i=0; i < n && !rc ; i++ )
+ rc = mpi_write (a, enc->data[i]);
if (!rc)
{
diff --git a/g10/ecdh.c b/g10/ecdh.c
index 95bd8668f..cf002b957 100644
--- a/g10/ecdh.c
+++ b/g10/ecdh.c
@@ -48,16 +48,17 @@ static const struct
-/* Returns allocated (binary) KEK parameters; the size is returned in
- sizeout. The caller must free the returned value. Returns NULL
- and sets ERRNO on error. */
-byte *
-pk_ecdh_default_params (unsigned int qbits, size_t *sizeout)
+/* Return KEK parameters as an opaque MPI The caller must free the
+ returned value. Returns NULL and sets ERRNO on error. */
+gcry_mpi_t
+pk_ecdh_default_params (unsigned int qbits)
{
- byte kek_params[4];
+ byte *kek_params;
int i;
- byte *buffer;
+ kek_params = xtrymalloc (4);
+ if (!kek_params)
+ return NULL;
kek_params[0] = 3; /* Number of bytes to follow. */
kek_params[1] = 1; /* Version for KDF+AESWRAP. */
@@ -78,12 +79,7 @@ pk_ecdh_default_params (unsigned int qbits, size_t *sizeout)
if (DBG_CIPHER)
log_printhex ("ECDH KEK params are", kek_params, sizeof(kek_params) );
- buffer = xtrymalloc (sizeof(kek_params));
- if (!buffer)
- return NULL;
- memcpy (buffer, kek_params, sizeof (kek_params));
- *sizeout = sizeof (kek_params);
- return buffer;
+ return gcry_mpi_set_opaque (NULL, kek_params, 4 * 8);
}
@@ -411,8 +407,8 @@ gen_k (unsigned nbits)
unsigned char *buffer;
if (gcry_mpi_aprint (GCRYMPI_FMT_HEX, &buffer, NULL, k))
BUG ();
- log_debug("ephemeral scalar MPI #0: %s\n", buffer);
- gcry_free( buffer );
+ log_debug ("ephemeral scalar MPI #0: %s\n", buffer);
+ gcry_free (buffer);
}
return k;
diff --git a/g10/keygen.c b/g10/keygen.c
index b42121b28..4d911f0b9 100644
--- a/g10/keygen.c
+++ b/g10/keygen.c
@@ -1081,7 +1081,107 @@ write_keybinding (KBNODE root, PKT_public_key *pri_psk, PKT_public_key *sub_psk,
}
+static gpg_error_t
+ecckey_from_sexp (gcry_mpi_t *array, gcry_sexp_t sexp, int algo)
+{
+ gpg_error_t err;
+ gcry_sexp_t list, l2;
+ char *curve;
+ int i;
+ const char *oidstr;
+ unsigned int nbits;
+
+ array[0] = NULL;
+ array[1] = NULL;
+ array[2] = NULL;
+
+ list = gcry_sexp_find_token (sexp, "public-key", 0);
+ if (!list)
+ return gpg_error (GPG_ERR_INV_OBJ);
+ l2 = gcry_sexp_cadr (list);
+ gcry_sexp_release (list);
+ list = l2;
+ if (!list)
+ return gpg_error (GPG_ERR_NO_OBJ);
+
+ l2 = gcry_sexp_find_token (list, "curve", 0);
+ if (!l2)
+ {
+ err = gpg_error (GPG_ERR_NO_OBJ);
+ goto leave;
+ }
+ curve = gcry_sexp_nth_string (l2, 1);
+ if (!curve)
+ {
+ err = gpg_error (GPG_ERR_NO_OBJ);
+ goto leave;
+ }
+ gcry_sexp_release (l2);
+ if (!strcmp (curve, "NIST P-256"))
+ {
+ oidstr = "1.2.840.10045.3.1.7";
+ nbits = 256;
+ }
+ else if (!strcmp (curve, "NIST P-384"))
+ {
+ oidstr = "1.3.132.0.34";
+ nbits = 384;
+ }
+ else if (!strcmp (curve, "NIST P-521"))
+ {
+ oidstr = "1.3.132.0.35";
+ nbits = 521;
+ }
+ else
+ {
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ goto leave;
+ }
+ err = openpgp_oid_from_str (oidstr, &array[0]);
+ if (err)
+ goto leave;
+
+ l2 = gcry_sexp_find_token (list, "q", 0);
+ if (!l2)
+ {
+ err = gpg_error (GPG_ERR_NO_OBJ);
+ goto leave;
+ }
+ array[1] = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG);
+ gcry_sexp_release (l2);
+ if (!array[1])
+ {
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ goto leave;
+ }
+ gcry_sexp_release (list);
+ if (algo == PUBKEY_ALGO_ECDH)
+ {
+ array[2] = pk_ecdh_default_params (nbits);
+ if (!array[2])
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ }
+
+ leave:
+ if (err)
+ {
+ for (i=0; i < 3; i++)
+ {
+ gcry_mpi_release (array[i]);
+ array[i] = NULL;
+ }
+ }
+ return 0;
+}
+
+
+/* Extract key parameters from SEXP and store them in ARRAY. ELEMS is
+ a string where each character denotes a parameter name. TOPNAME is
+ the name of the top element above the elements. */
static int
key_from_sexp (gcry_mpi_t *array, gcry_sexp_t sexp,
const char *topname, const char *elems)
@@ -1165,7 +1265,10 @@ common_gen (const char *keyparms, int algo, const char *algoelem,
pk->expiredate = pk->timestamp + expireval;
pk->pubkey_algo = algo;
- err = key_from_sexp (pk->pkey, s_key, "public-key", algoelem);
+ if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_ECDH)
+ err = ecckey_from_sexp (pk->pkey, s_key, algo);
+ else
+ err = key_from_sexp (pk->pkey, s_key, "public-key", algoelem);
if (err)
{
log_error ("key_from_sexp failed: %s\n", gpg_strerror (err) );
@@ -1173,7 +1276,6 @@ common_gen (const char *keyparms, int algo, const char *algoelem,
free_public_key (pk);
return err;
}
- gcry_sexp_release (s_key);
pkt = xtrycalloc (1, sizeof *pkt);
if (!pkt)
@@ -1329,126 +1431,45 @@ gen_dsa (unsigned int nbits, KBNODE pub_root,
-/* Create an S-expression string out of QBITS, ALGO and the TRANSIENT
- flag. On success a malloced string is returned, on failure NULL
- and ERRNO is set. */
-static char *
-pk_ecc_build_key_params (int qbits, int algo, int transient)
-{
- byte *kek_params = NULL;
- size_t kek_params_size;
- char qbitsstr[35];
- char *result;
- size_t n;
-
- /* KEK parameters are only needed for long term key generation. */
- if (!transient && algo == PUBKEY_ALGO_ECDH)
- {
- kek_params = pk_ecdh_default_params (qbits, &kek_params_size);
- if (!kek_params)
- return NULL;
- }
- else
- kek_params = NULL;
-
- snprintf (qbitsstr, sizeof qbitsstr, "%u", qbits);
- if (algo == PUBKEY_ALGO_ECDSA || !kek_params)
- {
- result = xtryasprintf ("(genkey(%s(nbits %zu:%s)"
- /**/ "(qbits %zu:%s)"
- /**/ "(transient-key 1:%d)))",
- algo == PUBKEY_ALGO_ECDSA ? "ecdsa" : "ecdh",
- strlen (qbitsstr), qbitsstr,
- strlen (qbitsstr), qbitsstr,
- transient);
- }
- else
- {
- char *tmpstr;
-
- assert (kek_params);
- tmpstr = xtryasprintf ("(genkey(ecdh(nbits %zu:%s)"
- /**/ "(qbits %zu:%s)"
- /**/ "(transient-key 1:%d)"
- /**/ "(kek-params %zu:",
- strlen (qbitsstr), qbitsstr,
- strlen (qbitsstr), qbitsstr,
- transient,
- kek_params_size);
- if (!tmpstr)
- {
- xfree (kek_params);
- return NULL;
- }
- /* Append the binary KEK parmas. */
- n = strlen (tmpstr);
- result = xtryrealloc (tmpstr, n + kek_params_size + 4);
- if (!result)
- {
- xfree (tmpstr);
- xfree (kek_params);
- return NULL;
- }
- memcpy (result + n, kek_params, kek_params_size);
- strcpy (result + n + kek_params_size, ")))");
- }
- xfree (kek_params);
- return result;
-}
-
-
/*
* Generate an ECC key
*/
static gpg_error_t
-gen_ecc (int algo, unsigned int nbits, KBNODE pub_root,
+gen_ecc (int algo, unsigned int nbits, kbnode_t pub_root,
u32 timestamp, u32 expireval, int is_subkey,
int keygen_flags, char **cache_nonce_addr)
{
- int err;
- unsigned int qbits;
+ gpg_error_t err;
+ const char *curve;
char *keyparms;
assert (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_ECDH);
- if (pubkey_get_npkey (PUBKEY_ALGO_ECDSA) != 2
- || pubkey_get_nskey (PUBKEY_ALGO_ECDSA) != 3
- || pubkey_get_npkey (PUBKEY_ALGO_ECDH) != 3
- || pubkey_get_nskey (PUBKEY_ALGO_ECDH) != 4)
- {
- log_error ("broken version of Libgcrypt\n");
- return gpg_error (GPG_ERR_INTERNAL); /* ABI silently changed. */
- }
-
- if (nbits != 256 && nbits != 384 && nbits != 521)
- {
- log_info (_("keysize invalid; using %u bits\n"), 256);
- /* FIXME: Where do we set it to 256? */
- }
-
- /* Figure out a Q size based on the key size. See gen_dsa for more
- details. Due to 8-bit rounding we may get 528 here instead of 521. */
- nbits = qbits = (nbits < 521 ? nbits : 521 );
-
- keyparms = pk_ecc_build_key_params
- (qbits, algo, !!( (keygen_flags & KEYGEN_FLAG_TRANSIENT_KEY)
- && (keygen_flags & KEYGEN_FLAG_NO_PROTECTION)) );
+ /* For now we may only use one of the 3 NISY curves. */
+ if (nbits <= 256)
+ curve = "NIST P-256";
+ else if (nbits <= 384)
+ curve = "NIST P-384";
+ else
+ curve = "NIST P-521";
+
+ keyparms = xtryasprintf ("(genkey(%s(curve %zu:%s)%s))",
+ algo == PUBKEY_ALGO_ECDSA ? "ecdsa" : "ecdh",
+ strlen (curve), curve,
+ ((keygen_flags & KEYGEN_FLAG_TRANSIENT_KEY)
+ && (keygen_flags & KEYGEN_FLAG_NO_PROTECTION))?
+ "(transient-key)" : "" );
if (!keyparms)
- {
- err = gpg_error_from_syserror ();
- log_error ("ecc pk_ecc_build_key_params failed: %s\n",
- gpg_strerror (err));
- }
+ err = gpg_error_from_syserror ();
else
{
- err = common_gen (keyparms, algo,
- algo == PUBKEY_ALGO_ECDSA? "cq" : "cqp",
+ err = common_gen (keyparms, algo, "",
pub_root, timestamp, expireval, is_subkey,
keygen_flags, cache_nonce_addr);
xfree (keyparms);
}
- return 0;
+ return err;
}
@@ -2428,7 +2449,7 @@ get_parameter_algo( struct para_data_s *para, enum para_name key,
|| !strcmp (r->u.value, "ELG"))
i = GCRY_PK_ELG_E;
else
- i = gcry_pk_map_name (r->u.value);
+ i = map_pk_gcry_to_openpgp (gcry_pk_map_name (r->u.value));
if (i == PUBKEY_ALGO_RSA_E || i == PUBKEY_ALGO_RSA_S)
i = 0; /* we don't want to allow generation of these algorithms */
diff --git a/g10/keyid.c b/g10/keyid.c
index 0405b8b2f..6571a51c0 100644
--- a/g10/keyid.c
+++ b/g10/keyid.c
@@ -724,17 +724,20 @@ keygrip_from_pk (PKT_public_key *pk, unsigned char *array)
case PUBKEY_ALGO_ECDSA:
case PUBKEY_ALGO_ECDH:
- err = gcry_sexp_build (&s_pkey, NULL,
- "(public-key(ecc(c%m)(q%m)))",
- pk->pkey[0], pk->pkey[1]);
+ {
+ char *curve = openpgp_oid_to_str (pk->pkey[0]);
+ if (!curve)
+ err = gpg_error_from_syserror ();
+ else
+ {
+ err = gcry_sexp_build (&s_pkey, NULL,
+ "(public-key(ecc(curve%s)(q%m)))",
+ curve, pk->pkey[1]);
+ xfree (curve);
+ }
+ }
break;
- /* case PUBKEY_ALGO_ECDH: */
- /* err = gcry_sexp_build (&s_pkey, NULL, */
- /* "(public-key(ecdh(c%m)(q%m)(p%m)))", */
- /* pk->pkey[0], pk->pkey[1], pk->pkey[2]); */
- /* break; */
-
default:
err = gpg_error (GPG_ERR_PUBKEY_ALGO);
break;
diff --git a/g10/main.h b/g10/main.h
index 4cec61f9d..d76d96bc4 100644
--- a/g10/main.h
+++ b/g10/main.h
@@ -97,6 +97,7 @@ int openpgp_cipher_blocklen (int algo);
int openpgp_cipher_test_algo( int algo );
const char *openpgp_cipher_algo_name (int algo);
int map_pk_openpgp_to_gcry (int algo);
+int map_pk_gcry_to_openpgp (enum gcry_pk_algos algo);
int openpgp_pk_test_algo( int algo );
int openpgp_pk_test_algo2 ( int algo, unsigned int use );
int openpgp_pk_algo_usage ( int algo );
@@ -154,15 +155,21 @@ int is_valid_mailbox (const char *name);
const char *get_libexecdir (void);
int path_access(const char *file,int mode);
-/* Temporary helpers. */
int pubkey_get_npkey( int algo );
int pubkey_get_nskey( int algo );
int pubkey_get_nsig( int algo );
int pubkey_get_nenc( int algo );
+
+/* Temporary helpers. */
unsigned int pubkey_nbits( int algo, gcry_mpi_t *pkey );
int mpi_print (estream_t stream, gcry_mpi_t a, int mode);
unsigned int ecdsa_qbits_from_Q (unsigned int qbits);
+/* Other stuff */
+gpg_error_t openpgp_oid_from_str (const char *string, gcry_mpi_t *r_mpi);
+char *openpgp_oid_to_str (gcry_mpi_t a);
+
+
/*-- status.c --*/
void set_status_fd ( int fd );
int is_status_enabled ( void );
@@ -300,7 +307,7 @@ gpg_error_t export_pubkey_buffer (ctrl_t ctrl, const char *keyspec,
int export_seckeys (ctrl_t ctrl, strlist_t users);
int export_secsubkeys (ctrl_t ctrl, strlist_t users);
-/* dearmor.c --*/
+/*-- dearmor.c --*/
int dearmor_file( const char *fname );
int enarmor_file( const char *fname );
diff --git a/g10/misc.c b/g10/misc.c
index dc2f73be4..2052e96c7 100644
--- a/g10/misc.c
+++ b/g10/misc.c
@@ -379,6 +379,19 @@ map_pk_openpgp_to_gcry (int algo)
}
}
+/* Map Gcrypt public key algorithm numbers to those used by
+ OpenPGP. */
+int
+map_pk_gcry_to_openpgp (enum gcry_pk_algos algo)
+{
+ switch (algo)
+ {
+ case GCRY_PK_ECDSA: return PUBKEY_ALGO_ECDSA;
+ case GCRY_PK_ECDH: return PUBKEY_ALGO_ECDH;
+ default: return algo < 110 ? algo : 0;
+ }
+}
+
/* Return the block length of an OpenPGP cipher algorithm. */
int
@@ -1347,35 +1360,44 @@ path_access(const char *file,int mode)
-/* Temporary helper. */
+/* Return the number of public key parameters as used by OpenPGP. */
int
-pubkey_get_npkey( int algo )
+pubkey_get_npkey (int algo)
{
size_t n;
+ /* ECC is special. */
+ if (algo == PUBKEY_ALGO_ECDSA)
+ return 2;
+ else if (algo == PUBKEY_ALGO_ECDH)
+ return 3;
+
+ /* All other algorithms match those of Libgcrypt. */
if (algo == GCRY_PK_ELG_E)
algo = GCRY_PK_ELG;
- else if (algo == PUBKEY_ALGO_ECDSA)
- algo = GCRY_PK_ECDSA;
- else if (algo == PUBKEY_ALGO_ECDH)
- algo = GCRY_PK_ECDH;
- if (gcry_pk_algo_info( algo, GCRYCTL_GET_ALGO_NPKEY, NULL, &n))
+
+ if (gcry_pk_algo_info (algo, GCRYCTL_GET_ALGO_NPKEY, NULL, &n))
n = 0;
return n;
}
-/* Temporary helper. */
+
+/* Return the number of secret key parameters as used by OpenPGP. */
int
-pubkey_get_nskey( int algo )
+pubkey_get_nskey (int algo)
{
size_t n;
+ /* ECC is special. */
+ if (algo == PUBKEY_ALGO_ECDSA)
+ return 3;
+ else if (algo == PUBKEY_ALGO_ECDH)
+ return 4;
+
+ /* All other algorithms match those of Libgcrypt. */
if (algo == GCRY_PK_ELG_E)
algo = GCRY_PK_ELG;
- else if (algo == PUBKEY_ALGO_ECDSA)
- algo = GCRY_PK_ECDSA;
- else if (algo == PUBKEY_ALGO_ECDH)
- algo = GCRY_PK_ECDH;
+
if (gcry_pk_algo_info( algo, GCRYCTL_GET_ALGO_NSKEY, NULL, &n ))
n = 0;
return n;
@@ -1383,33 +1405,40 @@ pubkey_get_nskey( int algo )
/* Temporary helper. */
int
-pubkey_get_nsig( int algo )
+pubkey_get_nsig (int algo)
{
size_t n;
+ /* ECC is special. */
+ if (algo == PUBKEY_ALGO_ECDSA)
+ return 2;
+ else if (algo == PUBKEY_ALGO_ECDH)
+ return 0;
+
if (algo == GCRY_PK_ELG_E)
algo = GCRY_PK_ELG;
- else if (algo == PUBKEY_ALGO_ECDSA)
- algo = GCRY_PK_ECDSA;
- else if (algo == PUBKEY_ALGO_ECDH)
- algo = GCRY_PK_ECDH;
+
if (gcry_pk_algo_info( algo, GCRYCTL_GET_ALGO_NSIGN, NULL, &n))
n = 0;
return n;
}
+
/* Temporary helper. */
int
-pubkey_get_nenc( int algo )
+pubkey_get_nenc (int algo)
{
size_t n;
+ /* ECC is special. */
+ if (algo == PUBKEY_ALGO_ECDSA)
+ return 0;
+ else if (algo == PUBKEY_ALGO_ECDH)
+ return 2;
+
if (algo == GCRY_PK_ELG_E)
algo = GCRY_PK_ELG;
- else if (algo == PUBKEY_ALGO_ECDSA)
- algo = GCRY_PK_ECDSA;
- else if (algo == PUBKEY_ALGO_ECDH)
- algo = GCRY_PK_ECDH;
+
if (gcry_pk_algo_info( algo, GCRYCTL_GET_ALGO_NENCR, NULL, &n ))
n = 0;
return n;
@@ -1442,9 +1471,16 @@ pubkey_nbits( int algo, gcry_mpi_t *key )
key[0], key[1] );
}
else if( algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_ECDH ) {
- rc = gcry_sexp_build ( &sexp, NULL,
- "(public-key(ecc(c%m)(q%m)))",
- key[0], key[1] /* not affecting the size calculation, so use 'ecc' == 'ecdsa' */ );
+ char *curve = openpgp_oid_to_str (key[0]);
+ if (!curve)
+ rc = gpg_error_from_syserror ();
+ else
+ {
+ rc = gcry_sexp_build (&sexp, NULL,
+ "(public-key(ecc(curve%s)(q%m)))",
+ curve, key[1]);
+ xfree (curve);
+ }
}
else
return 0;
@@ -1472,6 +1508,19 @@ mpi_print (estream_t fp, gcry_mpi_t a, int mode)
n1 = gcry_mpi_get_nbits(a);
n += es_fprintf (fp, "[%u bits]", n1);
}
+ else if (gcry_mpi_get_flag (a, GCRYMPI_FLAG_OPAQUE))
+ {
+ unsigned int nbits;
+ unsigned char *p = gcry_mpi_get_opaque (a, &nbits);
+ if (!p)
+ n += es_fprintf (fp, "[invalid opaque value]");
+ else
+ {
+ nbits = (nbits + 7)/8;
+ for (; nbits; nbits--, p++)
+ n += es_fprintf (fp, "%02X", *p);
+ }
+ }
else
{
unsigned char *buffer;
@@ -1501,3 +1550,206 @@ ecdsa_qbits_from_Q (unsigned int qbits)
qbits /= 2;
return qbits;
}
+
+
+
+/* Helper for openpgp_oid_from_str. */
+static size_t
+make_flagged_int (unsigned long value, char *buf, size_t buflen)
+{
+ int more = 0;
+ int shift;
+
+ /* fixme: figure out the number of bits in an ulong and start with
+ that value as shift (after making it a multiple of 7) a more
+ straigtforward implementation is to do it in reverse order using
+ a temporary buffer - saves a lot of compares */
+ for (more=0, shift=28; shift > 0; shift -= 7)
+ {
+ if (more || value >= (1<<shift))
+ {
+ buf[buflen++] = 0x80 | (value >> shift);
+ value -= (value >> shift) << shift;
+ more = 1;
+ }
+ }
+ buf[buflen++] = value;
+ return buflen;
+}
+
+
+/* Convert the OID given in dotted decimal form in STRING to an DER
+ * encoding and store it as an opaque value at R_MPI. The format of
+ * the DER encoded is not a regular ASN.1 object but the modified
+ * format as used by OpenPGP for the ECC curve description. On error
+ * the function returns and error code an NULL is stored at R_BUG.
+ * Note that scanning STRING stops at the first white space
+ * character. */
+gpg_error_t
+openpgp_oid_from_str (const char *string, gcry_mpi_t *r_mpi)
+{
+ unsigned char *buf;
+ size_t buflen;
+ unsigned long val1, val;
+ const char *endp;
+ int arcno;
+
+ *r_mpi = NULL;
+
+ if (!string || !*string)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ /* We can safely assume that the encoded OID is shorter than the string. */
+ buf = xtrymalloc (1 + strlen (string) + 2);
+ if (!buf)
+ return gpg_error_from_syserror ();
+ /* Save the first byte for the length. */
+ buflen = 1;
+
+ val1 = 0; /* Avoid compiler warning. */
+ arcno = 0;
+ do {
+ arcno++;
+ val = strtoul (string, (char**)&endp, 10);
+ if (!digitp (string) || !(*endp == '.' || !*endp))
+ {
+ xfree (buf);
+ return gpg_error (GPG_ERR_INV_OID_STRING);
+ }
+ if (*endp == '.')
+ string = endp+1;
+
+ if (arcno == 1)
+ {
+ if (val > 2)
+ break; /* Not allowed, error catched below. */
+ val1 = val;
+ }
+ else if (arcno == 2)
+ { /* Need to combine the first two arcs in one octet. */
+ if (val1 < 2)
+ {
+ if (val > 39)
+ {
+ xfree (buf);
+ return gpg_error (GPG_ERR_INV_OID_STRING);
+ }
+ buf[buflen++] = val1*40 + val;
+ }
+ else
+ {
+ val += 80;
+ buflen = make_flagged_int (val, buf, buflen);
+ }
+ }
+ else
+ {
+ buflen = make_flagged_int (val, buf, buflen);
+ }
+ } while (*endp == '.');
+
+ if (arcno == 1 || buflen < 2 || buflen > 254 )
+ { /* It is not possible to encode only the first arc. */
+ xfree (buf);
+ return gpg_error (GPG_ERR_INV_OID_STRING);
+ }
+
+ *buf = buflen - 1;
+ *r_mpi = gcry_mpi_set_opaque (NULL, buf, buflen * 8);
+ if (!*r_mpi)
+ {
+ xfree (buf);
+ return gpg_error_from_syserror ();
+ }
+ return 0;
+}
+
+
+/* Return a malloced string represenation of the OID in the opaque MPI
+ A. In case of an error NULL is returned and ERRNO is set. */
+char *
+openpgp_oid_to_str (gcry_mpi_t a)
+{
+ const unsigned char *buf;
+ size_t length;
+ char *string, *p;
+ int n = 0;
+ unsigned long val, valmask;
+
+ valmask = (unsigned long)0xfe << (8 * (sizeof (valmask) - 1));
+
+ if (!a || !gcry_mpi_get_flag (a, GCRYMPI_FLAG_OPAQUE))
+ {
+ gpg_err_set_errno (EINVAL);
+ return NULL;
+ }
+
+ buf = gcry_mpi_get_opaque (a, &length);
+ length = (length+7)/8;
+
+ /* The first bytes gives the length; check consistency. */
+ if (!length || buf[0] != length -1)
+ {
+ gpg_err_set_errno (EINVAL);
+ return NULL;
+ }
+ /* Skip length byte. */
+ length--;
+ buf++;
+
+ /* To calculate the length of the string we can safely assume an
+ upper limit of 3 decimal characters per byte. Two extra bytes
+ account for the special first octect */
+ string = p = xtrymalloc (length*(1+3)+2+1);
+ if (!string)
+ return NULL;
+ if (!buf || !length)
+ {
+ *p = 0;
+ return string;
+ }
+
+ if (buf[0] < 40)
+ p += sprintf (p, "0.%d", buf[n]);
+ else if (buf[0] < 80)
+ p += sprintf (p, "1.%d", buf[n]-40);
+ else {
+ val = buf[n] & 0x7f;
+ while ( (buf[n]&0x80) && ++n < length )
+ {
+ if ( (val & valmask) )
+ goto badoid; /* Overflow. */
+ val <<= 7;
+ val |= buf[n] & 0x7f;
+ }
+ val -= 80;
+ sprintf (p, "2.%lu", val);
+ p += strlen (p);
+ }
+ for (n++; n < length; n++)
+ {
+ val = buf[n] & 0x7f;
+ while ( (buf[n]&0x80) && ++n < length )
+ {
+ if ( (val & valmask) )
+ goto badoid; /* Overflow. */
+ val <<= 7;
+ val |= buf[n] & 0x7f;
+ }
+ sprintf (p, ".%lu", val);
+ p += strlen (p);
+ }
+
+ *p = 0;
+ return string;
+
+ badoid:
+ /* Return a special OID (gnu.gnupg.badoid) to indicate the error
+ case. The OID is broken and thus we return one which can't do
+ any harm. Formally this does not need to be a bad OID but an OID
+ with an arc that can't be represented in a 32 bit word is more
+ than likely corrupt. */
+ xfree (string);
+ return xtrystrdup ("1.3.6.1.4.1.11591.2.12242973");
+}
+
diff --git a/g10/parse-packet.c b/g10/parse-packet.c
index a0844c7ac..83be15d8c 100644
--- a/g10/parse-packet.c
+++ b/g10/parse-packet.c
@@ -741,51 +741,57 @@ read_rest (IOBUF inp, size_t pktlen, int partial)
}
-/*
- * Read a special size+body from inp into body[body_max_size] and
- * return it in a buffer and as MPI. On success the number of
- * consumed bytes will body[0]+1. The format of the content of the
- * returned MPI is one byte LEN, following by LEN bytes. Caller is
- * expected to pre-allocate fixed-size 255 byte buffer (or smaller
- * when appropriate).
- */
-static int
-read_size_body (iobuf_t inp, byte *body, int body_max_size,
- int pktlen, gcry_mpi_t *out )
+/* Read a special size+body from INP. On success store an opaque MPI
+ with it at R_DATA. On error return an error code and store NULL at
+ R_DATA. Even in the error case store the number of read bytes at
+ R_NREAD. The caller shall pass the remaining size of the packet in
+ PKTLEN. */
+static gpg_error_t
+read_size_body (iobuf_t inp, int pktlen, size_t *r_nread,
+ gcry_mpi_t *r_data)
{
- unsigned int n;
- int rc;
- gcry_mpi_t result;
+ char buffer[256];
+ char *tmpbuf;
+ int i, c, nbytes;
+
+ *r_nread = 0;
+ *r_data = NULL;
+
+ if (!pktlen)
+ return gpg_error (GPG_ERR_INV_PACKET);
+ c = iobuf_readbyte (inp);
+ if (c < 0)
+ return gpg_error (GPG_ERR_INV_PACKET);
+ pktlen--;
+ ++*r_nread;
+ nbytes = c;
+ if (nbytes < 2 || nbytes > 254)
+ return gpg_error (GPG_ERR_INV_PACKET);
+ if (nbytes > pktlen)
+ return gpg_error (GPG_ERR_INV_PACKET);
- *out = NULL;
+ buffer[0] = nbytes;
- if( (n = iobuf_readbyte(inp)) == -1 )
- {
- return G10ERR_INVALID_PACKET;
- }
- if ( n >= body_max_size || n < 2)
- {
- log_error("invalid size+body field\n");
- return G10ERR_INVALID_PACKET;
- }
- body[0] = n;
- if ((n = iobuf_read(inp, body+1, n)) == -1)
+ for (i = 0; i < nbytes; i++)
{
- log_error("invalid size+body field\n");
- return G10ERR_INVALID_PACKET;
+ c = iobuf_get (inp);
+ if (c < 0)
+ return gpg_error (GPG_ERR_INV_PACKET);
+ ++*r_nread;
+ buffer[1+i] = c;
}
- if (n+1 > pktlen)
+
+ tmpbuf = xtrymalloc (1 + nbytes);
+ if (!tmpbuf)
+ return gpg_error_from_syserror ();
+ memcpy (tmpbuf, buffer, 1 + nbytes);
+ *r_data = gcry_mpi_set_opaque (NULL, tmpbuf, 8 * (1 + nbytes));
+ if (!*r_data)
{
- log_error("size+body field is larger than the packet\n");
- return G10ERR_INVALID_PACKET;
+ xfree (tmpbuf);
+ return gpg_error_from_syserror ();
}
- rc = gcry_mpi_scan (&result, GCRYMPI_FMT_USG, body, n+1, NULL);
- if (rc)
- log_fatal ("mpi_scan failed: %s\n", gpg_strerror (rc));
-
- *out = result;
-
- return rc;
+ return 0;
}
@@ -988,46 +994,29 @@ parse_pubkeyenc (IOBUF inp, int pkttype, unsigned long pktlen,
}
else
{
- if (k->pubkey_algo == PUBKEY_ALGO_ECDH)
+ for (i = 0; i < ndata; i++)
{
- byte encr_buf[255];
-
- assert (ndata == 2);
- n = pktlen;
- k->data[0] = mpi_read (inp, &n, 0);
- pktlen -= n;
- rc = read_size_body (inp, encr_buf, sizeof(encr_buf),
- pktlen, k->data+1);
- if (rc)
- goto leave;
-
- if (list_mode)
+ if (k->pubkey_algo == PUBKEY_ALGO_ECDH && i == 1)
{
- es_fprintf (listfp, "\tdata: ");
- mpi_print (listfp, k->data[0], mpi_print_mode );
- es_putc ('\n', listfp);
- es_fprintf (listfp, "\tdata: [% 3d bytes] ", encr_buf[0]+1);
- mpi_print (listfp, k->data[1], mpi_print_mode );
- es_putc ('\n', listfp);
+ rc = read_size_body (inp, pktlen, &n, k->data+i);
+ pktlen -= n;
}
- pktlen -= (encr_buf[0]+1);
- }
- else
- {
- for (i = 0; i < ndata; i++)
+ else
{
n = pktlen;
k->data[i] = mpi_read (inp, &n, 0);
pktlen -= n;
- if (list_mode)
- {
- es_fprintf (listfp, "\tdata: ");
- mpi_print (listfp, k->data[i], mpi_print_mode);
- es_putc ('\n', listfp);
- }
if (!k->data[i])
rc = gpg_error (GPG_ERR_INV_PACKET);
}
+ if (rc)
+ goto leave;
+ if (list_mode)
+ {
+ es_fprintf (listfp, "\tdata: ");
+ mpi_print (listfp, k->data[i], mpi_print_mode);
+ es_putc ('\n', listfp);
+ }
}
}
@@ -1989,7 +1978,6 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen,
unknown_pubkey_warning (algorithm);
}
-
if (!npkey)
{
/* Unknown algorithm - put data into an opaque MPI. */
@@ -2001,79 +1989,32 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen,
}
else
{
- /* Fill in public key parameters. */
- if (algorithm == PUBKEY_ALGO_ECDSA || algorithm == PUBKEY_ALGO_ECDH)
+ for (i = 0; i < npkey; i++)
{
- /* FIXME: The code in this function ignores the errors. */
- byte name_oid[256];
-
- err = read_size_body (inp, name_oid, sizeof(name_oid),
- pktlen, pk->pkey+0);
- if (err)
- goto leave;
- n = name_oid[0];
- if (list_mode)
- es_fprintf (listfp, "\tpkey[0]: curve OID [%d] ...%02x %02x\n",
- n, name_oid[1+n-2], name_oid[1+n-1]);
- pktlen -= (n+1);
- /* Set item [1], which corresponds to the public key; these
- two fields are all we need to uniquely define the key/ */
- n = pktlen;
- pk->pkey[1] = mpi_read( inp, &n, 0 );
- pktlen -=n;
- if (!pk->pkey[1])
- err = gpg_error (GPG_ERR_INV_PACKET);
- else if (list_mode)
+ if ((algorithm == PUBKEY_ALGO_ECDSA
+ || algorithm == PUBKEY_ALGO_ECDH) && (i==0 || i == 2))
{
- es_fprintf (listfp, "\tpkey[1]: ");
- mpi_print (listfp, pk->pkey[1], mpi_print_mode);
- es_putc ('\n', listfp);
- }
- /* One more field for ECDH. */
- if (algorithm == PUBKEY_ALGO_ECDH)
- {
- /* (NAMEOID holds the KEK params.) */
- err = read_size_body (inp, name_oid, sizeof(name_oid),
- pktlen, pk->pkey+2);
- if (err)
- goto leave;
- n = name_oid[0];
- if (name_oid[1] != 1)
- {
- log_error ("invalid ecdh KEK parameters field type in "
- "private key: understand type 1, "
- "but found 0x%02x\n", name_oid[1]);
- err = gpg_error (GPG_ERR_INV_PACKET);
- goto leave;
- }
- if (list_mode)
- es_fprintf (listfp, "\tpkey[2]: KEK params type=01 "
- "hash:%d sym-algo:%d\n",
- name_oid[1+n-2], name_oid[1+n-1]);
- pktlen -= (n+1);
+ err = read_size_body (inp, pktlen, &n, pk->pkey+i);
+ pktlen -= n;
}
- }
- else
- {
- for (i = 0; i < npkey; i++)
+ else
{
n = pktlen;
pk->pkey[i] = mpi_read (inp, &n, 0);
pktlen -= n;
- if (list_mode)
- {
- es_fprintf (listfp, "\tpkey[%d]: ", i);
- mpi_print (listfp, pk->pkey[i], mpi_print_mode);
- es_putc ('\n', listfp);
- }
if (!pk->pkey[i])
err = gpg_error (GPG_ERR_INV_PACKET);
}
+ if (err)
+ goto leave;
+ if (list_mode)
+ {
+ es_fprintf (listfp, "\tpkey[%d]: ", i);
+ mpi_print (listfp, pk->pkey[i], mpi_print_mode);
+ es_putc ('\n', listfp);
+ }
}
- if (err)
- goto leave;
}
-
if (list_mode)
keyid_from_pk (pk, keyid);
diff --git a/g10/pkglue.c b/g10/pkglue.c
index 3aba4e4c1..27ee239a4 100644
--- a/g10/pkglue.c
+++ b/g10/pkglue.c
@@ -79,8 +79,16 @@ pk_verify (int algo, gcry_mpi_t hash, gcry_mpi_t *data, gcry_mpi_t *pkey)
}
else if (pkalgo == GCRY_PK_ECDSA) /* Same as GCRY_PK_ECDH */
{
- rc = gcry_sexp_build (&s_pkey, NULL,
- "(public-key(ecdsa(c%m)(q%m)))", pkey[0], pkey[1]);
+ char *curve = openpgp_oid_to_str (pkey[0]);
+ if (!curve)
+ rc = gpg_error_from_syserror ();
+ else
+ {
+ rc = gcry_sexp_build (&s_pkey, NULL,
+ "(public-key(ecdsa(curve %s)(q%m)))",
+ curve, pkey[1]);
+ xfree (curve);
+ }
}
else
return GPG_ERR_PUBKEY_ALGO;
@@ -174,18 +182,27 @@ pk_encrypt (int algo, gcry_mpi_t *resarr, gcry_mpi_t data,
else if (algo == PUBKEY_ALGO_ECDH)
{
gcry_mpi_t k;
+ char *curve;
rc = pk_ecdh_generate_ephemeral_key (pkey, &k);
if (rc)
return rc;
- /* Now use the ephemeral secret to compute the shared point. */
- rc = gcry_sexp_build (&s_pkey, NULL,
- "(public-key(ecdh(c%m)(q%m)(p%m)))",
- pkey[0], pkey[1], pkey[2]);
- /* Put K into a simplified S-expression. */
- if (rc || gcry_sexp_build (&s_data, NULL, "%m", k))
- BUG ();
+ curve = openpgp_oid_to_str (pkey[0]);
+ if (!curve)
+ rc = gpg_error_from_syserror ();
+ else
+ {
+ /* Now use the ephemeral secret to compute the shared point. */
+ rc = gcry_sexp_build (&s_pkey, NULL,
+ "(public-key(ecdh(curve%s)(q%m)))",
+ curve, pkey[1]);
+ xfree (curve);
+ /* FIXME: Take care of RC. */
+ /* Put K into a simplified S-expression. */
+ if (rc || gcry_sexp_build (&s_data, NULL, "%m", k))
+ BUG ();
+ }
}
else
return gpg_error (GPG_ERR_PUBKEY_ALGO);
@@ -272,9 +289,16 @@ pk_check_secret_key (int algo, gcry_mpi_t *skey)
}
else if (gcry_pkalgo == GCRY_PK_ECDSA || gcry_pkalgo == GCRY_PK_ECDH)
{
- rc = gcry_sexp_build (&s_skey, NULL,
- "(private-key(ecdsa(c%m)(q%m)(d%m)))",
- skey[0], skey[1], skey[2] );
+ char *curve = openpgp_oid_to_str (skey[0]);
+ if (!curve)
+ rc = gpg_error_from_syserror ();
+ else
+ {
+ rc = gcry_sexp_build (&s_skey, NULL,
+ "(private-key(ecdsa(curve%s)(q%m)(d%m)))",
+ curve, skey[1], skey[2]);
+ xfree (curve);
+ }
}
else
return GPG_ERR_PUBKEY_ALGO;
diff --git a/g10/pkglue.h b/g10/pkglue.h
index 98d8c1440..eb0d7c1dc 100644
--- a/g10/pkglue.h
+++ b/g10/pkglue.h
@@ -32,7 +32,7 @@ int pk_check_secret_key (int algo, gcry_mpi_t *skey);
/*-- ecdh.c --*/
-byte *pk_ecdh_default_params (unsigned int qbits, size_t *sizeout);
+gcry_mpi_t pk_ecdh_default_params (unsigned int qbits);
gpg_error_t pk_ecdh_generate_ephemeral_key (gcry_mpi_t *pkey, gcry_mpi_t *r_k);
gpg_error_t pk_ecdh_encrypt_with_shared_point
/* */ (int is_encrypt, gcry_mpi_t shared_mpi,