1
0
mirror of git://git.gnupg.org/gnupg.git synced 2024-05-31 22:18:03 +02:00

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; const char *algoname;
int npkey, nskey; int npkey, nskey;
gcry_mpi_t array[10]; gcry_mpi_t array[10];
gcry_sexp_t curve = NULL;
char protect_iv[16]; char protect_iv[16];
char salt[8]; char salt[8];
unsigned long s2k_count; 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")) 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. */ algoname = "ecc"; /* Decide later by checking the usage. */
npkey = 6; npkey = 1;
nskey = 7; nskey = 2;
err = gcry_sexp_extract_param (list, NULL, "pabgnqd", iob.data = iobbuf;
array+0, array+1, array+2, array+3, iob.size = sizeof iobbuf - 1;
array+4, array+5, array+6, NULL); 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")) 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); err = gpg_error (GPG_ERR_PUBKEY_ALGO);
} }
xfree (name); xfree (name);
gcry_sexp_release (list); gcry_sexp_release (list); list = NULL;
if (err) if (err)
return err; {
gcry_sexp_release (curve);
return err;
}
gcry_create_nonce (protect_iv, sizeof protect_iv); gcry_create_nonce (protect_iv, sizeof protect_iv);
gcry_create_nonce (salt, sizeof salt); 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" "(openpgp-private-key\n"
" (version 1:4)\n" " (version 1:4)\n"
" (algo %s)\n" " (algo %s)\n"
" %S\n" " %S%S\n"
" (protection sha1 aes %b 1:3 sha1 %b %s))\n", " (protection sha1 aes %b 1:3 sha1 %b %s))\n",
algoname, algoname,
curve,
tmpkey, tmpkey,
(int)sizeof protect_iv, protect_iv, (int)sizeof protect_iv, protect_iv,
(int)sizeof salt, salt, (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++) for (i=0; i < DIM (array); i++)
gcry_mpi_release (array[i]); gcry_mpi_release (array[i]);
gcry_sexp_release (curve);
return err; return err;
} }

View File

@ -1,6 +1,7 @@
/* export.c - Export keys in the OpenPGP defined format. /* export.c - Export keys in the OpenPGP defined format.
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004,
* 2005, 2010 Free Software Foundation, Inc. * 2005, 2010 Free Software Foundation, Inc.
* Copyright (C) 2014 Werner Koch
* *
* This file is part of GnuPG. * 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 /* Return a canonicalized public key algoithms. This is used to
compare different flavors of algorithms (e.g. ELG and ELG_E are compare different flavors of algorithms (e.g. ELG and ELG_E are
considered the same). */ considered the same). */
static int static enum gcry_pk_algos
canon_pubkey_algo (int algo) canon_pk_algo (enum gcry_pk_algos algo)
{ {
switch (algo) switch (algo)
{ {
@ -348,6 +349,9 @@ canon_pubkey_algo (int algo)
case GCRY_PK_RSA_S: return GCRY_PK_RSA; case GCRY_PK_RSA_S: return GCRY_PK_RSA;
case GCRY_PK_ELG: case GCRY_PK_ELG:
case GCRY_PK_ELG_E: return 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; default: return algo;
} }
} }
@ -362,12 +366,13 @@ transfer_format_to_openpgp (gcry_sexp_t s_pgp, PKT_public_key *pk)
gpg_error_t err; gpg_error_t err;
gcry_sexp_t top_list; gcry_sexp_t top_list;
gcry_sexp_t list = NULL; gcry_sexp_t list = NULL;
char *curve = NULL;
const char *value; const char *value;
size_t valuelen; size_t valuelen;
char *string; char *string;
int idx; int idx;
int is_v4, is_protected; int is_v4, is_protected;
int pubkey_algo; enum gcry_pk_algos pk_algo;
int protect_algo = 0; int protect_algo = 0;
char iv[16]; char iv[16];
int ivlen = 0; int ivlen = 0;
@ -375,11 +380,13 @@ transfer_format_to_openpgp (gcry_sexp_t s_pgp, PKT_public_key *pk)
int s2k_algo = 0; int s2k_algo = 0;
byte s2k_salt[8]; byte s2k_salt[8];
u32 s2k_count = 0; u32 s2k_count = 0;
int is_ecdh = 0;
size_t npkey, nskey; size_t npkey, nskey;
gcry_mpi_t skey[10]; /* We support up to 9 parameters. */ gcry_mpi_t skey[10]; /* We support up to 9 parameters. */
int skeyidx = 0; int skeyidx = 0;
struct seckey_info *ski; struct seckey_info *ski;
/* gcry_log_debugsxp ("transferkey", s_pgp); */
top_list = gcry_sexp_find_token (s_pgp, "openpgp-private-key", 0); top_list = gcry_sexp_find_token (s_pgp, "openpgp-private-key", 0);
if (!top_list) if (!top_list)
goto bad_seckey; goto bad_seckey;
@ -445,6 +452,7 @@ transfer_format_to_openpgp (gcry_sexp_t s_pgp, PKT_public_key *pk)
xfree (string); xfree (string);
} }
/* Parse the gcrypt PK algo and check that it is okay. */
gcry_sexp_release (list); gcry_sexp_release (list);
list = gcry_sexp_find_token (top_list, "algo", 0); list = gcry_sexp_find_token (top_list, "algo", 0);
if (!list) 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); string = gcry_sexp_nth_string (list, 1);
if (!string) if (!string)
goto bad_seckey; goto bad_seckey;
pubkey_algo = gcry_pk_map_name (string); pk_algo = gcry_pk_map_name (string);
xfree (string); xfree (string); string = NULL;
if (gcry_pk_algo_info (pk_algo, GCRYCTL_GET_ALGO_NPKEY, NULL, &npkey)
if (gcry_pk_algo_info (pubkey_algo, GCRYCTL_GET_ALGO_NPKEY, NULL, &npkey) || gcry_pk_algo_info (pk_algo, GCRYCTL_GET_ALGO_NSKEY, NULL, &nskey)
|| gcry_pk_algo_info (pubkey_algo, GCRYCTL_GET_ALGO_NSKEY, NULL, &nskey)
|| !npkey || npkey >= nskey || nskey > PUBKEY_MAX_NSKEY) || !npkey || npkey >= nskey || nskey > PUBKEY_MAX_NSKEY)
goto bad_seckey; 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); gcry_sexp_release (list);
list = gcry_sexp_find_token (top_list, "skey", 0); list = gcry_sexp_find_token (top_list, "skey", 0);
if (!list) 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; 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); */ /* list = gcry_sexp_find_token (top_list, "csum", 0); */
/* if (list) */ /* if (list) */
/* { */ /* { */
@ -523,6 +568,14 @@ transfer_format_to_openpgp (gcry_sexp_t s_pgp, PKT_public_key *pk)
/* desired_csum = 0; */ /* desired_csum = 0; */
/* gcry_sexp_release (list); list = NULL; */ /* 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; gcry_sexp_release (top_list); top_list = NULL;
/* log_debug ("XXX is_v4=%d\n", is_v4); */ /* 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. /* We need to change the received parameters for ECC algorithms.
The transfer format has all parameters but OpenPGP defines that The transfer format has the curve name and the parameters
only the OID of the curve is to be used. */ separate. We put them all into the SKEY array. */
if (pubkey_algo == PUBKEY_ALGO_ECDSA if (canon_pk_algo (pk_algo) == GCRY_PK_ECC)
|| pubkey_algo == PUBKEY_ALGO_EDDSA
|| pubkey_algo == PUBKEY_ALGO_ECDH)
{ {
gcry_sexp_t s_pubkey; const char *oidstr;
const char *curvename, *curveoidstr;
gcry_mpi_t mpi;
/* We build an S-expression with the public key parameters and /* Assert that all required parameters are available. We also
ask Libgcrypt to return the matching curve name. */ check that the array does not contain more parameters than
if (npkey != 6 || !skey[0] || !skey[1] || !skey[2] needed (this was used by some beta versions of 2.1. */
|| !skey[3] || !skey[4] || !skey[5] if (!curve || !skey[0] || !skey[1] || skey[2])
|| !skey[6] || skey[7])
{ {
err = gpg_error (GPG_ERR_INTERNAL); err = gpg_error (GPG_ERR_INTERNAL);
goto leave; goto leave;
} }
err = gcry_sexp_build (&s_pubkey, NULL,
"(public-key(ecc(p%m)(a%m)(b%m)(g%m)(n%m)))", oidstr = openpgp_curve_to_oid (curve, NULL);
skey[0], skey[1], skey[2], skey[3], skey[4]); if (!oidstr)
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)
{ {
log_error ("no OID known for curve '%s'\n", curvename); log_error ("no OID known for curve '%s'\n", curve);
err = gpg_error (GPG_ERR_UNKNOWN_NAME); err = gpg_error (GPG_ERR_UNKNOWN_CURVE);
goto leave; 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) if (err)
goto leave; 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. */ /* Fixup the NPKEY and NSKEY to match OpenPGP reality. */
npkey = 2; npkey = 2 + is_ecdh;
nskey = 3; nskey = 3 + is_ecdh;
/* for (idx=0; skey[idx]; idx++) */ /* 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); err = gpg_error (GPG_ERR_INV_DATA);
goto leave; 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); err = openpgp_cipher_test_algo (protect_algo);
if (err) if (err)
goto leave; goto leave;
@ -695,6 +735,7 @@ transfer_format_to_openpgp (gcry_sexp_t s_pgp, PKT_public_key *pk)
/* That's it. */ /* That's it. */
leave: leave:
gcry_free (curve);
gcry_sexp_release (list); gcry_sexp_release (list);
gcry_sexp_release (top_list); gcry_sexp_release (top_list);
for (idx=0; idx < skeyidx; idx++) for (idx=0; idx < skeyidx; idx++)