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 + + * protect.c (protect_info): Adjust ECDSA and ECDH parameter names. + Add "ecc". + 2011-01-19 Werner Koch * 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 + + + * 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 + + * 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 * 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]); + err = mpi_write (a, pk->pkey[i]); 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 = 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. */ - } + /* 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"; - 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)) ); + 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); + 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; - *out = NULL; + *r_nread = 0; + *r_data = 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)); + 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 = result; + buffer[0] = nbytes; - return rc; + for (i = 0; i < nbytes; i++) + { + c = iobuf_get (inp); + if (c < 0) + return gpg_error (GPG_ERR_INV_PACKET); + ++*r_nread; + buffer[1+i] = c; + } + + 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) + { + xfree (tmpbuf); + return gpg_error_from_syserror (); + } + 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,