aboutsummaryrefslogtreecommitdiffstats
path: root/g10/export.c
diff options
context:
space:
mode:
Diffstat (limited to 'g10/export.c')
-rw-r--r--g10/export.c232
1 files changed, 185 insertions, 47 deletions
diff --git a/g10/export.c b/g10/export.c
index 493513b63..7c7869fb8 100644
--- a/g10/export.c
+++ b/g10/export.c
@@ -564,7 +564,8 @@ match_curve_skey_pk (gcry_sexp_t s_key, PKT_public_key *pk)
if (!(pk->pubkey_algo==PUBKEY_ALGO_ECDH
|| pk->pubkey_algo==PUBKEY_ALGO_ECDSA
- || pk->pubkey_algo==PUBKEY_ALGO_EDDSA))
+ || pk->pubkey_algo==PUBKEY_ALGO_EDDSA
+ || pk->pubkey_algo==PUBKEY_ALGO_KYBER))
return gpg_error (GPG_ERR_PUBKEY_ALGO);
curve = gcry_sexp_find_token (s_key, "curve", 0);
@@ -643,9 +644,10 @@ canon_pk_algo (enum gcry_pk_algos algo)
/* Take an s-expression with the public and private key and change the
- * parameter array in PK to include the secret parameters. */
+ * parameter array in PK to include the secret parameters. With
+ * IS_PART2 set the second key from a dual key is merged into PK. */
static gpg_error_t
-secret_key_to_mode1003 (gcry_sexp_t s_key, PKT_public_key *pk)
+secret_key_to_mode1003 (gcry_sexp_t s_key, PKT_public_key *pk, int is_part2)
{
gpg_error_t err;
gcry_sexp_t list = NULL;
@@ -668,8 +670,6 @@ secret_key_to_mode1003 (gcry_sexp_t s_key, PKT_public_key *pk)
goto leave;
}
- log_assert (!pk->seckey_info);
-
/* Parse the gcrypt PK algo and check that it is okay. */
l2 = gcry_sexp_cadr (list);
if (!l2)
@@ -688,8 +688,7 @@ secret_key_to_mode1003 (gcry_sexp_t s_key, PKT_public_key *pk)
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)
+ || !npkey)
{
err = gpg_error (GPG_ERR_BAD_SECKEY);
goto leave;
@@ -697,6 +696,7 @@ secret_key_to_mode1003 (gcry_sexp_t s_key, PKT_public_key *pk)
/* Check that the pubkey algo and the received parameters matches
* those from the public key. */
+
switch (canon_pk_algo (pk_algo))
{
case GCRY_PK_RSA:
@@ -736,7 +736,8 @@ secret_key_to_mode1003 (gcry_sexp_t s_key, PKT_public_key *pk)
err = 0;
if (!(pk->pubkey_algo == PUBKEY_ALGO_ECDSA
|| pk->pubkey_algo == PUBKEY_ALGO_ECDH
- || pk->pubkey_algo == PUBKEY_ALGO_EDDSA))
+ || pk->pubkey_algo == PUBKEY_ALGO_EDDSA
+ || pk->pubkey_algo == PUBKEY_ALGO_KYBER))
{
err = gpg_error (GPG_ERR_PUBKEY_ALGO); /* Does not match. */
goto leave;
@@ -755,6 +756,26 @@ secret_key_to_mode1003 (gcry_sexp_t s_key, PKT_public_key *pk)
err = gpg_error (GPG_ERR_BAD_PUBKEY);
break;
+ case GCRY_PK_KEM: /* This is Kyber768 or Kyber1024 */
+ err = 0;
+ if (!(pk->pubkey_algo == PUBKEY_ALGO_KYBER) || !is_part2)
+ {
+ /* The algorithm does not match or this function has not
+ * been called with the flag for the second (Kyber) part. */
+ err = gpg_error (GPG_ERR_PUBKEY_ALGO);
+ goto leave;
+ }
+ log_assert (npkey == 1);
+ err = gcry_sexp_extract_param (list, NULL, "/p", &pub_params[0], NULL);
+ if (err)
+ goto leave;
+ if (gcry_mpi_cmp (pk->pkey[2], pub_params[0]))
+ {
+ err = gpg_error (GPG_ERR_BAD_PUBKEY);
+ goto leave;
+ }
+ break;
+
default:
err = gpg_error (GPG_ERR_PUBKEY_ALGO); /* Unknown. */
break;
@@ -762,16 +783,10 @@ secret_key_to_mode1003 (gcry_sexp_t s_key, PKT_public_key *pk)
if (err)
goto leave;
- nskey = npkey + 1; /* We only have one skey param. */
- if (nskey > PUBKEY_MAX_NSKEY)
- {
- err = gpg_error (GPG_ERR_BAD_SECKEY);
- goto leave;
- }
-
- /* Check that the public key parameters match. For ECC we already
- * did this in the switch above. */
- if (canon_pk_algo (pk_algo) != GCRY_PK_ECC)
+ /* Check that the public key parameters match. For ECC and Kyber we
+ * already did this in the switch above. */
+ if (canon_pk_algo (pk_algo) != GCRY_PK_ECC
+ && canon_pk_algo (pk_algo) != GCRY_PK_KEM)
{
for (idx=0; idx < npkey; idx++)
if (gcry_mpi_cmp (pk->pkey[idx], pub_params[idx]))
@@ -781,35 +796,71 @@ secret_key_to_mode1003 (gcry_sexp_t s_key, PKT_public_key *pk)
}
}
- /* Store the maybe protected secret key as an s-expression. */
- pk->seckey_info = ski = xtrycalloc (1, sizeof *ski);
- if (!ski)
+ /* For ECC+Kyber we need to adjust the npkey. */
+ if (pk->pubkey_algo == PUBKEY_ALGO_KYBER)
+ npkey = 3; /* ECC + Kyber */
+
+ /* Note that we always have just one secret key parameter in
+ * mode1003. That one is the entire s-expression as received from
+ * the agent and thus also includes the public part. For a dual key
+ * both s-expressions are simply concatenated. */
+ nskey = npkey + 1;
+ if (nskey > PUBKEY_MAX_NSKEY)
{
- err = gpg_error_from_syserror ();
+ err = gpg_error (GPG_ERR_BAD_SECKEY);
goto leave;
}
- pk->seckey_info = ski = xtrycalloc (1, sizeof *ski);
- if (!ski)
+ /* Store the maybe protected secret key as an s-expression. */
+ if (!is_part2)
{
- err = gpg_error_from_syserror ();
- goto leave;
+ pk->seckey_info = ski = xtrycalloc (1, sizeof *ski);
+ if (!ski)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ ski->is_protected = 1;
+ ski->s2k.mode = 1003;
}
+ else
+ log_assert (pk->seckey_info);
- ski->is_protected = 1;
- ski->s2k.mode = 1003;
+ /* Store the entire s-expression as the secret parameter. */
+ if (1)
+ {
+ unsigned char *buf;
+ size_t buflen;
- {
- unsigned char *buf;
- size_t buflen;
+ err = make_canon_sexp (s_key, &buf, &buflen);
+ if (err)
+ goto leave;
+ if (is_part2) /* Append secret s-expr to the existing one. */
+ {
+ const unsigned char *oldbuf;
+ unsigned int oldbuflen;
+ unsigned char *newbuf;
- err = make_canon_sexp (s_key, &buf, &buflen);
- if (err)
- goto leave;
- pk->pkey[npkey] = gcry_mpi_set_opaque (NULL, buf, buflen*8);
- for (idx=npkey+1; idx < PUBKEY_MAX_NSKEY; idx++)
+ log_assert (pk->pkey[npkey] &&
+ gcry_mpi_get_flag (pk->pkey[npkey], GCRYMPI_FLAG_OPAQUE));
+ oldbuf = gcry_mpi_get_opaque (pk->pkey[npkey], &oldbuflen);
+ oldbuflen = (oldbuflen +7)/8; /* Fixup bits to bytes */
+ newbuf = xtrymalloc_secure (oldbuflen + buflen);
+ if (!newbuf)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ memcpy (newbuf, oldbuf, oldbuflen);
+ memcpy (newbuf+oldbuflen, buf, buflen);
+ buf = newbuf;
+ buflen = oldbuflen + buflen;
+ }
+ /* Note that BUF is consumed by gcry_mpi_set_opaque. */
+ pk->pkey[npkey] = gcry_mpi_set_opaque (NULL, buf, buflen*8);
+ for (idx=npkey+1; idx < PUBKEY_MAX_NSKEY; idx++)
pk->pkey[idx] = NULL;
- }
+ }
leave:
gcry_sexp_release (list);
@@ -1431,7 +1482,8 @@ print_status_exported (PKT_public_key *pk)
* If MODE1003 is set, the key is requested in raw GnuPG format from
* the agent. This usually does not require a passphrase unless the
* gpg-agent has not yet used the key and needs to convert it to its
- * internal format first.
+ * internal format first. For the second key of a dual key IS_PART2
+ * needs to be set.
*
* CACHE_NONCE_ADDR is used to share nonce for multiple key retrievals.
*
@@ -1440,7 +1492,7 @@ print_status_exported (PKT_public_key *pk)
*/
gpg_error_t
receive_seckey_from_agent (ctrl_t ctrl, gcry_cipher_hd_t cipherhd,
- int cleartext, int mode1003,
+ int cleartext, int mode1003, int is_part2,
char **cache_nonce_addr, const char *hexgrip,
PKT_public_key *pk, gcry_sexp_t *r_key)
{
@@ -1500,7 +1552,7 @@ receive_seckey_from_agent (ctrl_t ctrl, gcry_cipher_hd_t cipherhd,
if (!err)
{
if (pk && mode1003)
- err = secret_key_to_mode1003 (s_skey, pk);
+ err = secret_key_to_mode1003 (s_skey, pk, is_part2);
else if (pk && cleartext)
err = cleartext_secret_key_to_openpgp (s_skey, pk);
else if (pk)
@@ -1816,8 +1868,12 @@ do_export_one_keyblock (ctrl_t ctrl, kbnode_t keyblock, u32 *keyid,
subkey_list_t subkey_list = NULL; /* Track already processed subkeys. */
int skip_until_subkey = 0;
int cleartext = 0;
+ int cleartext2 = 0;
char *hexgrip = NULL;
+ char *p;
+ const char *hexgrip2;
char *serialno = NULL;
+ char *serialno2 = NULL;
PKT_public_key *pk;
u32 subkidbuf[2], *subkid;
kbnode_t kbctx, node;
@@ -1958,14 +2014,20 @@ do_export_one_keyblock (ctrl_t ctrl, kbnode_t keyblock, u32 *keyid,
err = 0;
continue;
}
- if (strchr (hexgrip, ','))
+
+ /* In case of a dual key let hexgrip2 point to the second. */
+ if ((p = strchr (hexgrip, ',')))
{
- log_error ("exporting a secret dual key is not yet supported\n");
- return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+ *p = 0;
+ hexgrip2 = p+1;
}
+ else
+ hexgrip2 = NULL;
xfree (serialno);
serialno = NULL;
+ xfree (serialno2);
+ serialno2 = NULL;
if (secret == 2 && node->pkt->pkttype == PKT_PUBLIC_KEY)
{
/* We are asked not to export the secret parts of the
@@ -1974,10 +2036,83 @@ do_export_one_keyblock (ctrl_t ctrl, kbnode_t keyblock, u32 *keyid,
err = GPG_ERR_NOT_FOUND;
}
else
- err = agent_get_keyinfo (ctrl, hexgrip, &serialno, &cleartext);
+ {
+ err = agent_get_keyinfo (ctrl, hexgrip, &serialno, &cleartext);
+ if (!err && hexgrip2)
+ err = agent_get_keyinfo (ctrl,hexgrip2,&serialno2,&cleartext2);
+ }
+
+ if (!err && hexgrip2)
+ {
+ /* Dual key with two keygrips needs a special case. For
+ * example, we may have one key on card and the other on
+ * disk or even both on disk or both on card. There are
+ * two many combinations and thus we resort to using
+ * mode1003 which stores both s-expression received from
+ * gpg-agent in the OpenPGP format (the special 1003
+ * mode). That means that they stub is also stored. We
+ * do this even if both keys are on card. */
+ struct seckey_info *ski;
+
+ if (pk->pubkey_algo != PUBKEY_ALGO_KYBER)
+ err = gpg_error (GPG_ERR_NOT_SUPPORTED);
+
+ if (!err)
+ err = receive_seckey_from_agent (ctrl, cipherhd,
+ cleartext,
+ 1, /* Force mode1003 */
+ 0, /* First part. */
+ &cache_nonce,
+ hexgrip, pk, NULL);
+ if (!err)
+ err = receive_seckey_from_agent (ctrl, cipherhd,
+ cleartext,
+ 1, /* Force mode1003 */
+ 1, /* Second part. */
+ &cache_nonce,
+ hexgrip2, pk, NULL);
+
+ if (err)
+ {
+ /* If we receive a fully canceled error we stop
+ * immediately. If we receive a cancel for a public
+ * key we also stop immediately because a
+ * public/secret key is always required first If we
+ * receive a subkey we skip to the next subkey. */
+ if (gpg_err_code (err) == GPG_ERR_FULLY_CANCELED
+ || (node->pkt->pkttype == PKT_PUBLIC_KEY
+ && gpg_err_code (err) == GPG_ERR_CANCELED))
+ goto leave;
+ write_status_error ("export_keys.secret", err);
+ skip_until_subkey = 1;
+ err = 0;
+ }
+ else
+ {
+ pk->seckey_info = ski = xtrycalloc (1, sizeof *ski);
+ if (!ski)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+
+ ski->is_protected = 1; /* Required for mode1003. */
+ ski->s2k.mode = 1003; /* Mode 1003. */
- if ((!err && serialno)
- && secret == 2 && node->pkt->pkttype == PKT_PUBLIC_KEY)
+ if ((options & EXPORT_BACKUP))
+ err = build_packet_and_meta (out, node->pkt);
+ else
+ err = build_packet (out, node->pkt);
+ if (!err && node->pkt->pkttype == PKT_PUBLIC_KEY)
+ {
+ stats->exported++;
+ if (!(options & EXPORT_NO_STATUS))
+ print_status_exported (node->pkt->pkt.public_key);
+ }
+ }
+ }
+ else if ((!err && (serialno || serialno2))
+ && secret == 2 && node->pkt->pkttype == PKT_PUBLIC_KEY)
{
/* It does not make sense to export a key with its
* primary key on card using a non-key stub. Thus we
@@ -2027,6 +2162,7 @@ do_export_one_keyblock (ctrl_t ctrl, kbnode_t keyblock, u32 *keyid,
err = receive_seckey_from_agent (ctrl, cipherhd,
cleartext,
!!(options & EXPORT_MODE1003),
+ 0,
&cache_nonce,
hexgrip, pk, NULL);
if (err)
@@ -2095,6 +2231,7 @@ do_export_one_keyblock (ctrl_t ctrl, kbnode_t keyblock, u32 *keyid,
}
}
+
if (err)
{
log_error ("build_packet(%d) failed: %s\n",
@@ -2109,6 +2246,7 @@ do_export_one_keyblock (ctrl_t ctrl, kbnode_t keyblock, u32 *keyid,
leave:
release_subkey_list (subkey_list);
xfree (serialno);
+ xfree (serialno2);
xfree (hexgrip);
xfree (cache_nonce);
return err;
@@ -2986,7 +3124,7 @@ export_secret_ssh_key (ctrl_t ctrl, const char *userid)
if ((err = get_keywrap_key (ctrl, &cipherhd)))
goto leave;
- err = receive_seckey_from_agent (ctrl, cipherhd, 0, 0, NULL, hexgrip, NULL,
+ err = receive_seckey_from_agent (ctrl, cipherhd, 0, 0, 0, NULL, hexgrip, NULL,
&skey);
if (err)
goto leave;