gpg: Make export of ECC keys work again.

* agent/cvt-openpgp.c (convert_to_openpgp): Use the curve name instead
of the curve parameters.
* g10/export.c (canon_pubkey_algo): Rename to ...
(canon_pk_algo): this.  Support ECC.
(transfer_format_to_openpgp): Expect curve name.
This commit is contained in:
Werner Koch 2014-06-20 14:54:01 +02:00
parent d6ca407a27
commit f4fcaa2936
2 changed files with 123 additions and 63 deletions

View File

@ -1142,6 +1142,7 @@ convert_to_openpgp (ctrl_t ctrl, gcry_sexp_t s_key, const char *passphrase,
const char *algoname;
int npkey, nskey;
gcry_mpi_t array[10];
gcry_sexp_t curve = NULL;
char protect_iv[16];
char salt[8];
unsigned long s2k_count;
@ -1200,13 +1201,26 @@ convert_to_openpgp (ctrl_t ctrl, gcry_sexp_t s_key, const char *passphrase,
}
else if (!strcmp (name, "ecc"))
{
/* FIXME: We need to use the curve parameter. */
gcry_buffer_t iob;
char iobbuf[32];
algoname = "ecc"; /* Decide later by checking the usage. */
npkey = 6;
nskey = 7;
err = gcry_sexp_extract_param (list, NULL, "pabgnqd",
array+0, array+1, array+2, array+3,
array+4, array+5, array+6, NULL);
npkey = 1;
nskey = 2;
iob.data = iobbuf;
iob.size = sizeof iobbuf - 1;
iob.off = 0;
iob.len = 0;
err = gcry_sexp_extract_param (list, NULL, "&'curve'/qd",
&iob, array+0, array+1, NULL);
if (!err)
{
assert (iob.len < sizeof iobbuf -1);
iobbuf[iob.len] = 0;
err = gcry_sexp_build (&curve, NULL, "(curve %s)", iobbuf);
gcry_log_debugsxp ("at 1", curve);
}
}
else if (!strcmp (name, "ecdsa"))
{
@ -1231,9 +1245,12 @@ convert_to_openpgp (ctrl_t ctrl, gcry_sexp_t s_key, const char *passphrase,
err = gpg_error (GPG_ERR_PUBKEY_ALGO);
}
xfree (name);
gcry_sexp_release (list);
gcry_sexp_release (list); list = NULL;
if (err)
return err;
{
gcry_sexp_release (curve);
return err;
}
gcry_create_nonce (protect_iv, sizeof protect_iv);
gcry_create_nonce (salt, sizeof salt);
@ -1282,9 +1299,10 @@ convert_to_openpgp (ctrl_t ctrl, gcry_sexp_t s_key, const char *passphrase,
"(openpgp-private-key\n"
" (version 1:4)\n"
" (algo %s)\n"
" %S\n"
" %S%S\n"
" (protection sha1 aes %b 1:3 sha1 %b %s))\n",
algoname,
curve,
tmpkey,
(int)sizeof protect_iv, protect_iv,
(int)sizeof salt, salt,
@ -1297,6 +1315,7 @@ 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]);
gcry_sexp_release (curve);
return err;
}

View File

@ -1,6 +1,7 @@
/* export.c - Export keys in the OpenPGP defined format.
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004,
* 2005, 2010 Free Software Foundation, Inc.
* Copyright (C) 2014 Werner Koch
*
* This file is part of GnuPG.
*
@ -338,8 +339,8 @@ exact_subkey_match_p (KEYDB_SEARCH_DESC *desc, KBNODE node)
/* Return a canonicalized public key algoithms. This is used to
compare different flavors of algorithms (e.g. ELG and ELG_E are
considered the same). */
static int
canon_pubkey_algo (int algo)
static enum gcry_pk_algos
canon_pk_algo (enum gcry_pk_algos algo)
{
switch (algo)
{
@ -348,6 +349,9 @@ canon_pubkey_algo (int algo)
case GCRY_PK_RSA_S: return GCRY_PK_RSA;
case GCRY_PK_ELG:
case GCRY_PK_ELG_E: return GCRY_PK_ELG;
case GCRY_PK_ECC:
case GCRY_PK_ECDSA:
case GCRY_PK_ECDH: return GCRY_PK_ECC;
default: return algo;
}
}
@ -362,12 +366,13 @@ transfer_format_to_openpgp (gcry_sexp_t s_pgp, PKT_public_key *pk)
gpg_error_t err;
gcry_sexp_t top_list;
gcry_sexp_t list = NULL;
char *curve = NULL;
const char *value;
size_t valuelen;
char *string;
int idx;
int is_v4, is_protected;
int pubkey_algo;
enum gcry_pk_algos pk_algo;
int protect_algo = 0;
char iv[16];
int ivlen = 0;
@ -375,11 +380,13 @@ transfer_format_to_openpgp (gcry_sexp_t s_pgp, PKT_public_key *pk)
int s2k_algo = 0;
byte s2k_salt[8];
u32 s2k_count = 0;
int is_ecdh = 0;
size_t npkey, nskey;
gcry_mpi_t skey[10]; /* We support up to 9 parameters. */
int skeyidx = 0;
struct seckey_info *ski;
/* gcry_log_debugsxp ("transferkey", s_pgp); */
top_list = gcry_sexp_find_token (s_pgp, "openpgp-private-key", 0);
if (!top_list)
goto bad_seckey;
@ -445,6 +452,7 @@ transfer_format_to_openpgp (gcry_sexp_t s_pgp, PKT_public_key *pk)
xfree (string);
}
/* Parse the gcrypt PK algo and check that it is okay. */
gcry_sexp_release (list);
list = gcry_sexp_find_token (top_list, "algo", 0);
if (!list)
@ -452,15 +460,52 @@ transfer_format_to_openpgp (gcry_sexp_t s_pgp, PKT_public_key *pk)
string = gcry_sexp_nth_string (list, 1);
if (!string)
goto bad_seckey;
pubkey_algo = gcry_pk_map_name (string);
xfree (string);
if (gcry_pk_algo_info (pubkey_algo, GCRYCTL_GET_ALGO_NPKEY, NULL, &npkey)
|| gcry_pk_algo_info (pubkey_algo, GCRYCTL_GET_ALGO_NSKEY, NULL, &nskey)
pk_algo = gcry_pk_map_name (string);
xfree (string); string = NULL;
if (gcry_pk_algo_info (pk_algo, GCRYCTL_GET_ALGO_NPKEY, NULL, &npkey)
|| gcry_pk_algo_info (pk_algo, GCRYCTL_GET_ALGO_NSKEY, NULL, &nskey)
|| !npkey || npkey >= nskey || nskey > PUBKEY_MAX_NSKEY)
goto bad_seckey;
pubkey_algo = map_pk_gcry_to_openpgp (pubkey_algo);
/* Check that the pubkey algo matches the one from the public key. */
switch (canon_pk_algo (pk_algo))
{
case GCRY_PK_RSA:
if (!is_RSA (pk->pubkey_algo))
pk_algo = 0; /* Does not match. */
break;
case GCRY_PK_DSA:
if (!is_DSA (pk->pubkey_algo))
pk_algo = 0; /* Does not match. */
break;
case GCRY_PK_ELG:
if (!is_ELGAMAL (pk->pubkey_algo))
pk_algo = 0; /* Does not match. */
break;
case GCRY_PK_ECC:
if (pk->pubkey_algo == PUBKEY_ALGO_ECDSA)
;
else if (pk->pubkey_algo == PUBKEY_ALGO_ECDH)
is_ecdh = 1;
else if (pk->pubkey_algo == PUBKEY_ALGO_EDDSA)
;
else
pk_algo = 0; /* Does not match. */
/* For ECC we do not have the domain parameters thus fix our info. */
npkey = 1;
nskey = 2;
break;
default:
pk_algo = 0; /* Oops. */
break;
}
if (!pk_algo)
{
err = gpg_error (GPG_ERR_PUBKEY_ALGO);
goto leave;
}
/* Parse the key parameters. */
gcry_sexp_release (list);
list = gcry_sexp_find_token (top_list, "skey", 0);
if (!list)
@ -509,7 +554,7 @@ transfer_format_to_openpgp (gcry_sexp_t s_pgp, PKT_public_key *pk)
gcry_sexp_release (list); list = NULL;
/* We have no need for the CSUM valuel thus we don't parse it. */
/* We have no need for the CSUM value thus we don't parse it. */
/* list = gcry_sexp_find_token (top_list, "csum", 0); */
/* if (list) */
/* { */
@ -523,6 +568,14 @@ transfer_format_to_openpgp (gcry_sexp_t s_pgp, PKT_public_key *pk)
/* desired_csum = 0; */
/* gcry_sexp_release (list); list = NULL; */
/* Get the curve name if any, */
list = gcry_sexp_find_token (top_list, "curve", 0);
if (list)
{
curve = gcry_sexp_nth_string (list, 1);
gcry_sexp_release (list); list = NULL;
}
gcry_sexp_release (top_list); top_list = NULL;
/* log_debug ("XXX is_v4=%d\n", is_v4); */
@ -559,57 +612,49 @@ transfer_format_to_openpgp (gcry_sexp_t s_pgp, PKT_public_key *pk)
}
/* We need to change the received parameters for ECC algorithms.
The transfer format has all parameters but OpenPGP defines that
only the OID of the curve is to be used. */
if (pubkey_algo == PUBKEY_ALGO_ECDSA
|| pubkey_algo == PUBKEY_ALGO_EDDSA
|| pubkey_algo == PUBKEY_ALGO_ECDH)
The transfer format has the curve name and the parameters
separate. We put them all into the SKEY array. */
if (canon_pk_algo (pk_algo) == GCRY_PK_ECC)
{
gcry_sexp_t s_pubkey;
const char *curvename, *curveoidstr;
gcry_mpi_t mpi;
const char *oidstr;
/* We build an S-expression with the public key parameters and
ask Libgcrypt to return the matching curve name. */
if (npkey != 6 || !skey[0] || !skey[1] || !skey[2]
|| !skey[3] || !skey[4] || !skey[5]
|| !skey[6] || skey[7])
/* Assert that all required parameters are available. We also
check that the array does not contain more parameters than
needed (this was used by some beta versions of 2.1. */
if (!curve || !skey[0] || !skey[1] || skey[2])
{
err = gpg_error (GPG_ERR_INTERNAL);
goto leave;
}
err = gcry_sexp_build (&s_pubkey, NULL,
"(public-key(ecc(p%m)(a%m)(b%m)(g%m)(n%m)))",
skey[0], skey[1], skey[2], skey[3], skey[4]);
if (err)
goto leave;
curvename = gcry_pk_get_curve (s_pubkey, 0, NULL);
gcry_sexp_release (s_pubkey);
curveoidstr = openpgp_curve_to_oid (curvename, NULL);
if (!curveoidstr)
oidstr = openpgp_curve_to_oid (curve, NULL);
if (!oidstr)
{
log_error ("no OID known for curve '%s'\n", curvename);
err = gpg_error (GPG_ERR_UNKNOWN_NAME);
log_error ("no OID known for curve '%s'\n", curve);
err = gpg_error (GPG_ERR_UNKNOWN_CURVE);
goto leave;
}
err = openpgp_oid_from_str (curveoidstr, &mpi);
/* Put the curve's OID into into the MPI array. This requires
that we shift Q and D. For ECDH also insert the KDF parms. */
if (is_ecdh)
{
skey[4] = NULL;
skey[3] = skey[1];
skey[2] = gcry_mpi_copy (pk->pkey[2]);
}
else
{
skey[3] = NULL;
skey[2] = skey[1];
}
skey[1] = skey[0];
skey[0] = NULL;
err = openpgp_oid_from_str (oidstr, skey + 0);
if (err)
goto leave;
/* Now replace the curve parameters by the OID and shift the
rest of the parameters. */
gcry_mpi_release (skey[0]);
skey[0] = mpi;
for (idx=1; idx <= 4; idx++)
gcry_mpi_release (skey[idx]);
skey[1] = skey[5];
skey[2] = skey[6];
for (idx=3; idx <= 6; idx++)
skey[idx] = NULL;
/* Fixup the NPKEY and NSKEY to match OpenPGP reality. */
npkey = 2;
nskey = 3;
npkey = 2 + is_ecdh;
nskey = 3 + is_ecdh;
/* for (idx=0; skey[idx]; idx++) */
/* { */
@ -634,11 +679,6 @@ transfer_format_to_openpgp (gcry_sexp_t s_pgp, PKT_public_key *pk)
err = gpg_error (GPG_ERR_INV_DATA);
goto leave;
}
if (canon_pubkey_algo (pubkey_algo) != canon_pubkey_algo (pk->pubkey_algo))
{
err = gpg_error (GPG_ERR_PUBKEY_ALGO);
goto leave;
}
err = openpgp_cipher_test_algo (protect_algo);
if (err)
goto leave;
@ -695,6 +735,7 @@ transfer_format_to_openpgp (gcry_sexp_t s_pgp, PKT_public_key *pk)
/* That's it. */
leave:
gcry_free (curve);
gcry_sexp_release (list);
gcry_sexp_release (top_list);
for (idx=0; idx < skeyidx; idx++)