g10: fix regression of card key generation with backup
NIIBE Yutaka
gniibe at fsij.org
Fri Dec 18 02:11:11 CET 2015
On 12/15/2015 11:32 AM, NIIBE Yutaka wrote:
> I'm looking the issue 2169.
>
> Smartcard card-edit generate fails when off-card backup
> of encryption key is selected:
> https://bugs.gnupg.org/gnupg/issue2169
>
> If we keep the same usage of GnuPG,
I consider that fixing this regression makes sense (than asking users
new way of handling private keys by gpg-agent).
Here is the change. I intentionally don't remove unused functions,
so that we can focus the change. Removal of unused functions will
be done in next commit.
It builds successfully. I tested lightly. It is somewhat difficult
to do extensive testing because it takes time to generate keys.
I'm pushing this.
diff --git a/g10/keygen.c b/g10/keygen.c
index a1f449e..9259ff3 100644
--- a/g10/keygen.c
+++ b/g10/keygen.c
@@ -141,9 +141,6 @@ static int write_keyblock (iobuf_t out, kbnode_t node);
static gpg_error_t gen_card_key (int algo, int keyno, int is_primary,
kbnode_t pub_root,
u32 *timestamp, u32 expireval);
-static int gen_card_key_with_backup (int algo, int keyno, int is_primary,
- kbnode_t pub_root, u32 timestamp,
- u32 expireval, struct para_data_s *para);
static void
@@ -3964,6 +3961,166 @@ start_tree(KBNODE *tree)
}
+/* Write the *protected* secret key to the file. */
+static gpg_error_t
+card_write_key_to_backup_file (PKT_public_key *sk, const char *backup_dir)
+{
+ gpg_error_t err = 0;
+ int rc;
+ char name_buffer[50];
+ char *fname;
+ IOBUF fp;
+ mode_t oldmask;
+ PACKET *pkt = NULL;
+
+ keyid_from_pk (sk, NULL);
+ snprintf (name_buffer, sizeof name_buffer, "sk_%08lX%08lX.gpg",
+ (ulong)sk->keyid[0], (ulong)sk->keyid[1]);
+
+ fname = make_filename (backup_dir, name_buffer, NULL);
+ /* Note that the umask call is not anymore needed because
+ iobuf_create now takes care of it. However, it does not harm
+ and thus we keep it. */
+ oldmask = umask (077);
+ if (is_secured_filename (fname))
+ {
+ fp = NULL;
+ gpg_err_set_errno (EPERM);
+ }
+ else
+ fp = iobuf_create (fname, 1);
+ umask (oldmask);
+ if (!fp)
+ {
+ err = gpg_error_from_syserror ();
+ log_error (_("can't create backup file '%s': %s\n"), fname, strerror (errno) );
+ goto leave;
+ }
+
+ pkt = xcalloc (1, sizeof *pkt);
+ pkt->pkttype = PKT_SECRET_KEY;
+ pkt->pkt.secret_key = sk;
+
+ rc = build_packet (fp, pkt);
+ if (rc)
+ {
+ log_error ("build packet failed: %s\n", gpg_strerror (rc));
+ iobuf_cancel (fp);
+ }
+ else
+ {
+ unsigned char array[MAX_FINGERPRINT_LEN];
+ char *fprbuf, *p;
+ int n;
+ int i;
+
+ iobuf_close (fp);
+ iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE, 0, (char*)fname);
+ log_info (_("Note: backup of card key saved to '%s'\n"), fname);
+
+ fingerprint_from_pk (sk, array, &n);
+ p = fprbuf = xmalloc (MAX_FINGERPRINT_LEN*2 + 1 + 1);
+ if (!p)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+
+ for (i=0; i < n ; i++, p += 2)
+ sprintf (p, "%02X", array[i]);
+ *p++ = ' ';
+ *p = 0;
+
+ write_status_text_and_buffer (STATUS_BACKUP_KEY_CREATED, fprbuf,
+ fname, strlen (fname), 0);
+ xfree (fprbuf);
+ }
+
+ leave:
+ xfree (pkt);
+ xfree (fname);
+ return err;
+}
+
+
+/* Store key to card and make a backup file in OpenPGP format. */
+static gpg_error_t
+card_store_key_with_backup (ctrl_t ctrl, PKT_public_key *sub_psk,
+ const char *backup_dir)
+{
+ PKT_public_key *sk;
+ gnupg_isotime_t timestamp;
+ gpg_error_t err;
+ char *hexgrip;
+ int rc;
+ struct agent_card_info_s info;
+ gcry_cipher_hd_t cipherhd = NULL;
+ char *cache_nonce = NULL;
+ void *kek = NULL;
+ size_t keklen;
+
+ sk = copy_public_key (NULL, sub_psk);
+ if (!sk)
+ return gpg_error_from_syserror ();
+
+ epoch2isotime (timestamp, (time_t)sk->timestamp);
+ err = hexkeygrip_from_pk (sk, &hexgrip);
+ if (err)
+ return err;
+
+ memset(&info, 0, sizeof (info));
+ rc = agent_scd_getattr ("SERIALNO", &info);
+ if (rc)
+ return (gpg_error_t)rc;
+
+ rc = agent_keytocard (hexgrip, 2, 1, info.serialno, timestamp);
+ xfree (info.serialno);
+ if (rc)
+ {
+ err = (gpg_error_t)rc;
+ goto leave;
+ }
+
+ err = agent_keywrap_key (ctrl, 1, &kek, &keklen);
+ if (err)
+ {
+ log_error ("error getting the KEK: %s\n", gpg_strerror (err));
+ goto leave;
+ }
+
+ err = gcry_cipher_open (&cipherhd, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_AESWRAP, 0);
+ if (!err)
+ err = gcry_cipher_setkey (cipherhd, kek, keklen);
+ if (err)
+ {
+ log_error ("error setting up an encryption context: %s\n", gpg_strerror (err));
+ goto leave;
+ }
+
+ err = receive_seckey_from_agent (ctrl, cipherhd, &cache_nonce, hexgrip, sk);
+ if (err)
+ {
+ log_error ("error getting secret key from agent: %s\n", gpg_strerror (err));
+ goto leave;
+ }
+
+ err = card_write_key_to_backup_file (sk, backup_dir);
+ if (err)
+ log_error ("writing card key to backup file: %s\n", gpg_strerror (err));
+ else
+ /* Remove secret key data in agent side. */
+ agent_scd_learn (NULL, 1);
+
+ leave:
+ xfree (cache_nonce);
+ gcry_cipher_close (cipherhd);
+ xfree (kek);
+ xfree (hexgrip);
+ free_public_key (sk);
+ return err;
+}
+
+
static void
do_generate_keypair (ctrl_t ctrl, struct para_data_s *para,
struct output_control_s *outctrl, int card)
@@ -4095,7 +4252,7 @@ do_generate_keypair (ctrl_t ctrl, struct para_data_s *para,
if (!err && get_parameter (para, pSUBKEYTYPE))
{
sub_psk = NULL;
- if (!card)
+ if (!card || (s = get_parameter_value (para, pCARDBACKUPKEY)))
{
err = do_create (get_parameter_algo (para, pSUBKEYTYPE, NULL),
get_parameter_uint (para, pSUBKEYLENGTH),
@@ -4103,7 +4260,7 @@ do_generate_keypair (ctrl_t ctrl, struct para_data_s *para,
pub_root,
timestamp,
get_parameter_u32 (para, pSUBKEYEXPIRE), 1,
- outctrl->keygen_flags,
+ s ? KEYGEN_FLAG_NO_PROTECTION : outctrl->keygen_flags,
get_parameter_passphrase (para),
&cache_nonce);
/* Get the pointer to the generated public subkey packet. */
@@ -4115,25 +4272,15 @@ do_generate_keypair (ctrl_t ctrl, struct para_data_s *para,
if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY)
sub_psk = node->pkt->pkt.public_key;
assert (sub_psk);
+
+ if (s)
+ err = card_store_key_with_backup (ctrl, sub_psk, opt.homedir);
}
}
else
{
- if ((s = get_parameter_value (para, pCARDBACKUPKEY)))
- {
- /* A backup of the encryption key has been requested.
- Generate the key in software and import it then to
- the card. Write a backup file. */
- err = gen_card_key_with_backup
- (PUBKEY_ALGO_RSA, 2, 0, pub_root, timestamp,
- get_parameter_u32 (para, pKEYEXPIRE), para);
- }
- else
- {
- err = gen_card_key (PUBKEY_ALGO_RSA, 2, 0, pub_root,
- ×tamp,
- get_parameter_u32 (para, pKEYEXPIRE));
- }
+ err = gen_card_key (PUBKEY_ALGO_RSA, 2, 0, pub_root, ×tamp,
+ get_parameter_u32 (para, pKEYEXPIRE));
}
if (!err)
diff --git a/g10/main.h b/g10/main.h
index e4c6e8f..60bbd24 100644
--- a/g10/main.h
+++ b/g10/main.h
@@ -363,6 +363,10 @@ gpg_error_t export_pubkey_buffer (ctrl_t ctrl, const char *keyspec,
kbnode_t *r_keyblock,
void **r_data, size_t *r_datalen);
+gpg_error_t receive_seckey_from_agent (ctrl_t ctrl, gcry_cipher_hd_t cipherhd,
+ char **cache_nonce_addr, const char *hexgrip,
+ PKT_public_key *pk);
+
/*-- dearmor.c --*/
int dearmor_file( const char *fname );
int enarmor_file( const char *fname );
--
More information about the Gnupg-devel
mailing list