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,
-                                  &timestamp,
-                                  get_parameter_u32 (para, pKEYEXPIRE));
-            }
+          err = gen_card_key (PUBKEY_ALGO_RSA, 2, 0, pub_root, &timestamp,
+                              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