1
0
mirror of git://git.gnupg.org/gnupg.git synced 2025-04-17 15:44:34 +02:00

g10: Allow receiving cleartext secret keys from agent

* g10/export.c (match_curve_skey_pk): New function, testing whether an
OpenPGP public key and an S-expression use the same curve.
* g10/export.c (cleartext_secret_key_to_openpgp): New function,
filling in the secret key parameters of a PKT_public_key object from
a corresponding cleartext S-expression.
* g10/export.c, g10/main.h (receive_seckey_from_agent): Add cleartext
parameter, enabling retrieval of the secret key, unlocked.
* g10/export.c (do_export_stream): Send cleartext as 0, keeping current
behavior.
* g10/keygen.c (card_store_key_with_backup): Use cleartext=0 to ensure
that smartcard backups are all passphrase-locked.
--

This sets up internal functionality to be capable of exporting
cleartext secret keys, but does not change any existing behavior.

Signed-off-by: Daniel Kahn Gillmor <dkg@fifthhorseman.net>
This commit is contained in:
Daniel Kahn Gillmor 2016-06-10 16:15:35 -04:00 committed by Werner Koch
parent 7de7432076
commit a3cb72af79
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
3 changed files with 223 additions and 4 deletions

View File

@ -390,6 +390,71 @@ exact_subkey_match_p (KEYDB_SEARCH_DESC *desc, KBNODE node)
return result; return result;
} }
/* return an error if the key represented by the S-expression s_key
and the OpenPGP key represented by pk do not use the same curve. */
static gpg_error_t
match_curve_skey_pk (gcry_sexp_t s_key, PKT_public_key *pk)
{
gcry_sexp_t curve = NULL, flags = NULL;
char *curve_str = NULL, *flag;
const char *oidstr = NULL;
gcry_mpi_t curve_as_mpi = NULL;
gpg_error_t err;
int is_eddsa = 0, idx = 0;
if (!(pk->pubkey_algo==PUBKEY_ALGO_ECDH ||
pk->pubkey_algo==PUBKEY_ALGO_ECDSA ||
pk->pubkey_algo==PUBKEY_ALGO_EDDSA))
return gpg_error (GPG_ERR_PUBKEY_ALGO);
curve = gcry_sexp_find_token (s_key, "curve", 0);
if (!curve)
{
log_error ("no reported curve\n");
err = gpg_error (GPG_ERR_UNKNOWN_CURVE);
}
curve_str = gcry_sexp_nth_string (curve, 1);
gcry_sexp_release (curve); curve = NULL;
if (!curve_str)
{
log_error ("no curve name\n");
return gpg_error (GPG_ERR_UNKNOWN_CURVE);
}
oidstr = openpgp_curve_to_oid (curve_str, NULL);
if (!oidstr)
{
log_error ("no OID known for curve '%s'\n", curve_str);
gcry_free (curve_str);
return gpg_error (GPG_ERR_UNKNOWN_CURVE);
}
gcry_free (curve_str);
err = openpgp_oid_from_str (oidstr, &curve_as_mpi);
if (err)
return err;
if (gcry_mpi_cmp(pk->pkey[0], curve_as_mpi))
{
log_error ("curves do not match\n");
err = gpg_error (GPG_ERR_INV_CURVE);
}
gcry_mpi_release (curve_as_mpi);
flags = gcry_sexp_find_token (s_key, "flags", 0);
if (flags)
for (idx = 1; idx < gcry_sexp_length (flags); idx++)
{
flag = gcry_sexp_nth_string (flags, idx);
if (flag && (strcmp ("eddsa", flag) == 0))
is_eddsa = 1;
gcry_free (flag);
}
if (is_eddsa !=
(pk->pubkey_algo==PUBKEY_ALGO_EDDSA))
{
log_error ("disagreement about EdDSA\n");
err = gpg_error (GPG_ERR_INV_CURVE);
}
return err;
}
/* 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
@ -411,6 +476,150 @@ canon_pk_algo (enum gcry_pk_algos algo)
} }
} }
/* take a cleartext dump of a secret key in PK and change the
parameter array in PK to include the secret parameters. */
static gpg_error_t
cleartext_secret_key_to_openpgp (gcry_sexp_t s_key, PKT_public_key *pk)
{
gpg_error_t err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
gcry_sexp_t top_list;
gcry_sexp_t key = NULL;
char *key_type = NULL;
enum gcry_pk_algos pk_algo;
struct seckey_info *ski;
int idx, sec_start;
gcry_mpi_t pub_params[10] = { NULL };
/* we look for a private-key, then the first element in it tells us
the type */
top_list = gcry_sexp_find_token (s_key, "private-key", 0);
if (!top_list)
goto bad_seckey;
if (gcry_sexp_length(top_list) != 2)
goto bad_seckey;
key = gcry_sexp_nth (top_list, 1);
if (!key)
goto bad_seckey;
key_type = gcry_sexp_nth_string(key, 0);
pk_algo = gcry_pk_map_name (key_type);
log_assert(pk->seckey_info == NULL);
pk->seckey_info = ski = xtrycalloc (1, sizeof *ski);
if (!ski)
{
err = gpg_error_from_syserror ();
goto leave;
}
switch (canon_pk_algo (pk_algo))
{
case GCRY_PK_RSA:
if (!is_RSA (pk->pubkey_algo))
goto bad_pubkey_algo;
err = gcry_sexp_extract_param (key, NULL, "ne",
&pub_params[0],
&pub_params[1],
NULL);
for (idx=0; idx < 2 && !err; idx++)
if (gcry_mpi_cmp(pk->pkey[idx], pub_params[idx]))
err = gpg_error (GPG_ERR_BAD_PUBKEY);
if (!err)
err = gcry_sexp_extract_param (key, NULL, "dpqu",
&pk->pkey[2],
&pk->pkey[3],
&pk->pkey[4],
&pk->pkey[5],
NULL);
if (!err)
for (idx = 2; idx < 6; idx++)
ski->csum += checksum_mpi (pk->pkey[idx]);
break;
case GCRY_PK_DSA:
if (!is_DSA (pk->pubkey_algo))
goto bad_pubkey_algo;
err = gcry_sexp_extract_param (key, NULL, "pqgy",
&pub_params[0],
&pub_params[1],
&pub_params[2],
&pub_params[3],
NULL);
for (idx=0; idx < 4 && !err; idx++)
if (gcry_mpi_cmp(pk->pkey[idx], pub_params[idx]))
err = gpg_error (GPG_ERR_BAD_PUBKEY);
if (!err)
err = gcry_sexp_extract_param (key, NULL, "x",
&pk->pkey[4],
NULL);
if (!err)
ski->csum += checksum_mpi (pk->pkey[4]);
break;
case GCRY_PK_ELG:
if (!is_ELGAMAL (pk->pubkey_algo))
goto bad_pubkey_algo;
err = gcry_sexp_extract_param (key, NULL, "pgy",
&pub_params[0],
&pub_params[1],
&pub_params[2],
NULL);
for (idx=0; idx < 3 && !err; idx++)
if (gcry_mpi_cmp(pk->pkey[idx], pub_params[idx]))
err = gpg_error (GPG_ERR_BAD_PUBKEY);
if (!err)
err = gcry_sexp_extract_param (key, NULL, "x",
&pk->pkey[3],
NULL);
if (!err)
ski->csum += checksum_mpi (pk->pkey[3]);
break;
case GCRY_PK_ECC:
err = match_curve_skey_pk (key, pk);
if (err)
goto leave;
if (!err)
err = gcry_sexp_extract_param (key, NULL, "q",
&pub_params[0],
NULL);
if (!err && (gcry_mpi_cmp(pk->pkey[1], pub_params[0])))
err = gpg_error (GPG_ERR_BAD_PUBKEY);
sec_start = 2;
if (pk->pubkey_algo == PUBKEY_ALGO_ECDH)
sec_start += 1;
if (!err)
err = gcry_sexp_extract_param (key, NULL, "d",
&pk->pkey[sec_start],
NULL);
if (!err)
ski->csum += checksum_mpi (pk->pkey[sec_start]);
break;
default:
pk->seckey_info = NULL;
free (ski);
err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
}
leave:
gcry_sexp_release (top_list);
gcry_sexp_release (key);
gcry_free (key_type);
for (idx=0; idx < DIM(pub_params); idx++)
gcry_mpi_release (pub_params[idx]);
return err;
bad_pubkey_algo:
err = gpg_error (GPG_ERR_PUBKEY_ALGO);
goto leave;
bad_seckey:
err = gpg_error (GPG_ERR_BAD_SECKEY);
goto leave;
}
/* Use the key transfer format given in S_PGP to create the secinfo /* Use the key transfer format given in S_PGP to create the secinfo
structure in PK and change the parameter array in PK to include the structure in PK and change the parameter array in PK to include the
@ -833,10 +1042,15 @@ print_status_exported (PKT_public_key *pk)
* Then, parse the decrypted key data in transfer format, and put * Then, parse the decrypted key data in transfer format, and put
* secret parameters into PK. * secret parameters into PK.
* *
* if CLEARTEXT is 0, store the secret key material
* passphrase-protected. otherwise, store secret key material in the
* clear.
*
* CACHE_NONCE_ADDR is used to share nonce for multple key retrievals. * CACHE_NONCE_ADDR is used to share nonce for multple key retrievals.
*/ */
gpg_error_t gpg_error_t
receive_seckey_from_agent (ctrl_t ctrl, gcry_cipher_hd_t cipherhd, receive_seckey_from_agent (ctrl_t ctrl, gcry_cipher_hd_t cipherhd,
int cleartext,
char **cache_nonce_addr, const char *hexgrip, char **cache_nonce_addr, const char *hexgrip,
PKT_public_key *pk) PKT_public_key *pk)
{ {
@ -852,7 +1066,7 @@ receive_seckey_from_agent (ctrl_t ctrl, gcry_cipher_hd_t cipherhd,
log_info ("key %s: asking agent for the secret parts\n", hexgrip); log_info ("key %s: asking agent for the secret parts\n", hexgrip);
prompt = gpg_format_keydesc (pk, FORMAT_KEYDESC_EXPORT,1); prompt = gpg_format_keydesc (pk, FORMAT_KEYDESC_EXPORT,1);
err = agent_export_key (ctrl, hexgrip, prompt, 1, cache_nonce_addr, err = agent_export_key (ctrl, hexgrip, prompt, !cleartext, cache_nonce_addr,
&wrappedkey, &wrappedkeylen); &wrappedkey, &wrappedkeylen);
xfree (prompt); xfree (prompt);
@ -880,7 +1094,10 @@ receive_seckey_from_agent (ctrl_t ctrl, gcry_cipher_hd_t cipherhd,
err = gcry_sexp_sscan (&s_skey, NULL, key, realkeylen); err = gcry_sexp_sscan (&s_skey, NULL, key, realkeylen);
if (!err) if (!err)
{ {
err = transfer_format_to_openpgp (s_skey, pk); if (cleartext)
err = cleartext_secret_key_to_openpgp (s_skey, pk);
else
err = transfer_format_to_openpgp (s_skey, pk);
gcry_sexp_release (s_skey); gcry_sexp_release (s_skey);
} }
@ -1276,7 +1493,8 @@ do_export_stream (ctrl_t ctrl, iobuf_t out, strlist_t users, int secret,
} }
else if (!err) else if (!err)
{ {
err = receive_seckey_from_agent (ctrl, cipherhd, &cache_nonce, err = receive_seckey_from_agent (ctrl, cipherhd,
0, &cache_nonce,
hexgrip, pk); hexgrip, pk);
if (err) if (err)
{ {

View File

@ -4075,7 +4075,7 @@ card_store_key_with_backup (ctrl_t ctrl, PKT_public_key *sub_psk,
goto leave; goto leave;
} }
err = receive_seckey_from_agent (ctrl, cipherhd, &cache_nonce, hexgrip, sk); err = receive_seckey_from_agent (ctrl, cipherhd, 0, &cache_nonce, hexgrip, sk);
if (err) if (err)
{ {
log_error ("error getting secret key from agent: %s\n", gpg_strerror (err)); log_error ("error getting secret key from agent: %s\n", gpg_strerror (err));

View File

@ -389,6 +389,7 @@ gpg_error_t export_pubkey_buffer (ctrl_t ctrl, const char *keyspec,
void **r_data, size_t *r_datalen); void **r_data, size_t *r_datalen);
gpg_error_t receive_seckey_from_agent (ctrl_t ctrl, gcry_cipher_hd_t cipherhd, gpg_error_t receive_seckey_from_agent (ctrl_t ctrl, gcry_cipher_hd_t cipherhd,
int cleartext,
char **cache_nonce_addr, const char *hexgrip, char **cache_nonce_addr, const char *hexgrip,
PKT_public_key *pk); PKT_public_key *pk);