Note: I have not fully tested the new key creation due to a pc/sc
authorWerner Koch <wk@gnupg.org>
Thu, 23 Sep 2004 19:34:45 +0000 (19:34 +0000)
committerWerner Koch <wk@gnupg.org>
Thu, 23 Sep 2004 19:34:45 +0000 (19:34 +0000)
error.  However the backupfile has been created successfully.

* rsa.c (rsa_generate): Return the dummy list of factors only if
the caller asked for it.

* card_util.c (generate_card_keys): ask whether backup should be
created.
(card_store_subkey): Factored some code out to ..
* keygen.c (save_unprotected_key_to_card): .. new function.
(gen_card_key_with_backup): New.
(generate_raw_key): New.
(generate_keypair): New arg BACKUP_ENCRYPTION_DIR.  Changed all
callers.
(do_generate_keypair): Divert to gen_card_key_with_backup when
desired.

cipher/ChangeLog
cipher/rsa.c
g10/ChangeLog
g10/app-openpgp.c
g10/card-util.c
g10/g10.c
g10/keygen.c
g10/main.h

index 25cc265..47f09ca 100644 (file)
@@ -1,3 +1,8 @@
+2004-09-23  Werner Koch  <wk@g10code.com>
+
+       * rsa.c (rsa_generate): Return the dummy list of factors only if
+       the caller asked for it.
+
 2004-05-20  David Shaw  <dshaw@jabberwocky.com>
 
        * dsa.c (verify): s/exp/exponent/ to fix a compiler warning.  From
index 7eb757a..e41ff24 100644 (file)
@@ -353,7 +353,8 @@ rsa_generate( int algo, unsigned nbits, MPI *skey, MPI **retfactors )
     skey[4] = sk.q;
     skey[5] = sk.u;
     /* make an empty list of factors */
-    *retfactors = m_alloc_clear( 1 * sizeof **retfactors );
+    if (retfactors)
+      *retfactors = m_alloc_clear( 1 * sizeof **retfactors );
     return 0;
 }
 
index 667115d..96e724e 100644 (file)
@@ -1,5 +1,16 @@
 2004-09-23  Werner Koch  <wk@g10code.com>
 
+       * card_util.c (generate_card_keys): ask whether backup should be
+       created.
+       (card_store_subkey): Factored some code out to ..
+       * keygen.c (save_unprotected_key_to_card): .. new function.
+       (gen_card_key_with_backup): New.
+       (generate_raw_key): New.
+       (generate_keypair): New arg BACKUP_ENCRYPTION_DIR.  Changed all
+       callers.
+       (do_generate_keypair): Divert to gen_card_key_with_backup when
+       desired.
+
        * apdu.c (open_pcsc_reader): Do not print empty reader string.
 
        * keygen.c (ask_algo): Allow creation of AUTH keys.
@@ -7,6 +18,9 @@
        * keyid.c (usagestr_from_pk): New.
 
        * app-openpgp.c (app_openpgp_storekey): Call flush_cache.
+       (get_cached_data): Move local data initialization to ..
+       (app_select_openpgp): .. here. Read some flags for later use.
+       (do_getattr): New read-only attribute EXTCAP.
 
        * keyedit.c (keyedit_menu): New command "keytocard"
        (keyedit_menu): Bad hack for the not_with_sk element.
@@ -15,6 +29,7 @@
 
        * card-util.c (card_store_subkey): New.
        (copy_mpi): New.
+
        * cardglue.c (agent_openpgp_storekey): New.
 
 2004-09-22  Werner Koch  <wk@g10code.com>
index 096e66e..e4c1477 100644 (file)
@@ -63,11 +63,11 @@ static struct {
   { 0x006E, 1,    0, 1, 0, 0, "Application Related Data" },
   { 0x004F, 0, 0x6E, 1, 0, 0, "AID" },
   { 0x0073, 1,    0, 1, 0, 0, "Discretionary Data Objects" },
-  { 0x0047, 0, 0x6E, 1, 0, 0, "Card Capabilities" },
-  { 0x00C0, 0, 0x6E, 1, 0, 0, "Extended Card Capabilities" },
-  { 0x00C1, 0, 0x6E, 1, 0, 0, "Algorithm Attributes Signature" },
-  { 0x00C2, 0, 0x6E, 1, 0, 0, "Algorithm Attributes Decryption" },
-  { 0x00C3, 0, 0x6E, 1, 0, 0, "Algorithm Attributes Authentication" },
+  { 0x0047, 0, 0x6E, 1, 1, 0, "Card Capabilities" },
+  { 0x00C0, 0, 0x6E, 1, 1, 0, "Extended Card Capabilities" },
+  { 0x00C1, 0, 0x6E, 1, 1, 0, "Algorithm Attributes Signature" },
+  { 0x00C2, 0, 0x6E, 1, 1, 0, "Algorithm Attributes Decryption" },
+  { 0x00C3, 0, 0x6E, 1, 1, 0, "Algorithm Attributes Authentication" },
   { 0x00C4, 0, 0x6E, 1, 0, 1, "CHV Status Bytes" },
   { 0x00C5, 0, 0x6E, 1, 0, 0, "Fingerprints" },
   { 0x00C6, 0, 0x6E, 1, 0, 0, "CA Fingerprints" },
@@ -86,6 +86,13 @@ struct cache_s {
 
 struct app_local_s {
   struct cache_s *cache;
+  struct 
+  {
+    unsigned int get_challenge:1;
+    unsigned int key_import:1;
+    unsigned int change_force_chv:1;
+    unsigned int private_dos:1;
+  } extcap;
 };
 
 
@@ -127,25 +134,23 @@ get_cached_data (app_t app, int tag,
   *result = NULL;
   *resultlen = 0;
 
-  if (app->app_local)
-    {
-      for (c=app->app_local->cache; c; c = c->next)
-        if (c->tag == tag)
+  for (c=app->app_local->cache; c; c = c->next)
+    if (c->tag == tag)
+      {
+        if(c->length)
           {
-           if(c->length)
-             {
-               p = xtrymalloc (c->length);
-               if (!p)
-                 return gpg_error (gpg_err_code_from_errno (errno));
-               memcpy (p, c->data, c->length);
-               *result = p;
-             }
-
-           *resultlen = c->length;
-
-           return 0;
+            p = xtrymalloc (c->length);
+            if (!p)
+              return gpg_error (gpg_err_code_from_errno (errno));
+            memcpy (p, c->data, c->length);
+            *result = p;
           }
-    }
+
+        *resultlen = c->length;
+
+        return 0;
+      }
   
   err = iso7816_get_data (app->slot, tag, &p, &len);
   if (err)
@@ -163,24 +168,18 @@ get_cached_data (app_t app, int tag,
       }
 
   /* No, cache it. */
-  if (!app->app_local)
-    app->app_local = xtrycalloc (1, sizeof *app->app_local);
 
-  /* Note that we can safely ignore out of core errors. */
-  if (app->app_local)
+  for (c=app->app_local->cache; c; c = c->next)
+    assert (c->tag != tag);
+  
+  c = xtrymalloc (sizeof *c + len);
+  if (c)
     {
-      for (c=app->app_local->cache; c; c = c->next)
-        assert (c->tag != tag);
-      
-      c = xtrymalloc (sizeof *c + len);
-      if (c)
-        {
-          memcpy (c->data, p, len);
-          c->length = len;
-          c->tag = tag;
-          c->next = app->app_local->cache;
-          app->app_local->cache = c;
-        }
+      memcpy (c->data, p, len);
+      c->length = len;
+      c->tag = tag;
+      c->next = app->app_local->cache;
+      app->app_local->cache = c;
     }
 
   return 0;
@@ -505,6 +504,7 @@ do_getattr (APP app, CTRL ctrl, const char *name)
     { "SIG-COUNTER",  0x0093, 2 },
     { "SERIALNO",     0x004F, -1 },
     { "AID",          0x004F },
+    { "EXTCAP",       0x0000, -2 },
     { NULL, 0 }
   };
   int idx, i;
@@ -539,6 +539,18 @@ do_getattr (APP app, CTRL ctrl, const char *name)
         }
       return 0;
     }
+  if (table[idx].special == -2)
+    {
+      char tmp[50];
+
+      sprintf (tmp, "gc=%d ki=%d fc=%d pd=%d", 
+               app->app_local->extcap.get_challenge,
+               app->app_local->extcap.key_import,
+               app->app_local->extcap.change_force_chv,
+               app->app_local->extcap.private_dos);
+      send_status_info (ctrl, table[idx].name, tmp, strlen (tmp), NULL, 0);
+      return 0;
+    }
 
   relptr = get_one_do (app, table[idx].tag, &value, &valuelen);
   if (relptr)
@@ -578,6 +590,7 @@ do_getattr (APP app, CTRL ctrl, const char *name)
 static int
 do_learn_status (APP app, CTRL ctrl)
 {
+  do_getattr (app, ctrl, "EXTCAP");
   do_getattr (app, ctrl, "DISP-NAME");
   do_getattr (app, ctrl, "DISP-LANG");
   do_getattr (app, ctrl, "DISP-SEX");
@@ -1378,11 +1391,14 @@ app_select_openpgp (APP app)
   rc = iso7816_select_application (slot, aid, sizeof aid);
   if (!rc)
     {
+      unsigned int manufacturer;
+
       app->apptype = "OPENPGP";
 
       app->did_chv1 = 0;
       app->did_chv2 = 0;
       app->did_chv3 = 0;
+      app->app_local = NULL;
 
       /* The OpenPGP card returns the serial number as part of the
          AID; because we prefer to use OpenPGP serial numbers, we
@@ -1400,10 +1416,18 @@ app_select_openpgp (APP app)
 
       app->card_version = buffer[6] << 8;
       app->card_version |= buffer[7];
+      manufacturer = (buffer[8]<<8 | buffer[9]);
+
       xfree (app->serialno);
       app->serialno = buffer;
       app->serialnolen = buflen;
       buffer = NULL;
+      app->app_local = xtrycalloc (1, sizeof *app->app_local);
+      if (!app->app_local)
+        {
+          rc = gpg_error (gpg_err_code_from_errno (errno));
+          goto leave;
+        }
 
       relptr = get_one_do (app, 0x00C4, &buffer, &buflen);
       if (!relptr)
@@ -1413,7 +1437,29 @@ app_select_openpgp (APP app)
         }
       app->force_chv1 = (buflen && *buffer == 0);
       xfree (relptr);
-        
+
+      relptr = get_one_do (app, 0x00C0, &buffer, &buflen);
+      if (!relptr)
+        {
+          log_error ("can't access Extended Capability Flags - "
+                     "invalid OpenPGP card?\n");
+          goto leave;
+        }
+      if (buflen)
+        {
+          app->app_local->extcap.get_challenge    = !!(*buffer & 0x40);
+          app->app_local->extcap.key_import       = !!(*buffer & 0x20);
+          app->app_local->extcap.change_force_chv = !!(*buffer & 0x10);
+          app->app_local->extcap.private_dos      = !!(*buffer & 0x08);
+        }
+      xfree (relptr);
+      
+      /* Some of the first cards accidently don't set the
+         CHANGE_FORCE_CHV bit but allow it anyway. */
+      if (app->card_version <= 0x0100 && manufacturer == 1)
+        app->app_local->extcap.change_force_chv = 1;
+
+
       if (opt.verbose > 1)
         dump_all_do (slot);
 
@@ -1431,6 +1477,8 @@ app_select_openpgp (APP app)
    }
 
 leave:
+  if (rc)
+    do_deinit (app);
   return rc;
 }
 
index c5cd344..5b3a33e 100644 (file)
 
 #define CONTROL_D ('D' - 'A' + 1)
 
-#if GNUPG_MAJOR_VERSION == 1
-#define GET_NBITS(a)  mpi_get_nbits (a)
-#else
-#define GET_NBITS(a)  gcry_mpi_get_nbits (a)
-#endif
-
-  
-static int
-copy_mpi (MPI a, unsigned char *buffer, size_t len, size_t *ncopied)
-{
-  int rc;
-#if GNUPG_MAJOR_VERSION == 1
-  unsigned char *tmp;
-  unsigned int n;
-
-  tmp = mpi_get_secure_buffer (a, &n, NULL);
-  if (n > len)
-    rc = G10ERR_GENERAL;
-  else
-    {
-      rc = 0;
-      memcpy (buffer, tmp, n);
-      *ncopied = n;
-    }
-  xfree (tmp);
-#else /* GNUPG_MAJOR_VERSION != 1 */
-  rc = gcry_mpi_print (GCRYMPI_FMT_USG, buffer, len, ncopied, a);
-#endif /* GNUPG_MAJOR_VERSION != 1 */
-  if (rc)
-    log_error ("mpi_copy failed: %s\n", gpg_strerror (rc));
-  return rc;
-}
-
 
 /* Change the PIN of a an OpenPGP card.  This is an interactive
    function. */
@@ -897,10 +864,15 @@ generate_card_keys (const char *serialno)
 {
   struct agent_card_info_s info;
   int forced_chv1;
+  int want_backup;
 
   if (get_info_for_key_operation (&info))
     return;
 
+  want_backup = !(cpr_get_answer_is_yes 
+                  ( "cardedit.genkeys.backup_enc",
+                    _("Inhibit creation of encryption key backup? ")));
+
   if ( (info.fpr1valid && !fpr_is_zero (info.fpr1))
        || (info.fpr2valid && !fpr_is_zero (info.fpr2))
        || (info.fpr3valid && !fpr_is_zero (info.fpr3)))
@@ -928,7 +900,8 @@ generate_card_keys (const char *serialno)
   if (check_pin_for_key_operation (&info, &forced_chv1))
     goto leave;
   
-  generate_keypair (NULL, info.serialno);
+  generate_keypair (NULL, info.serialno,
+                    want_backup? opt.homedir:NULL);
 
  leave:
   agent_release_card_info (&info);
@@ -1003,12 +976,6 @@ card_store_subkey (KBNODE node, int use)
   PKT_secret_key *copied_sk = NULL;
   PKT_secret_key *sk;
   size_t n;
-  MPI rsa_n, rsa_e, rsa_p, rsa_q;
-  unsigned int nbits;
-  unsigned char *template = NULL;
-  unsigned char *tp;
-  unsigned char m[128], e[4];
-  size_t mlen, elen;
   const char *s;
   int allow_keyno[3];
 
@@ -1087,94 +1054,9 @@ card_store_subkey (KBNODE node, int use)
         goto leave;
     }
 
-  /* Some basic checks on the key parameters. */
-  rsa_n = sk->skey[0];
-  rsa_e = sk->skey[1];
-  rsa_p = sk->skey[3];
-  rsa_q = sk->skey[4];
-
-  nbits = GET_NBITS (rsa_n);
-  if (nbits != 1024)
-    {
-      log_error (_("length of RSA modulus is not %d\n"), 1024);
-      goto leave;
-    }
-  nbits = GET_NBITS (rsa_e);
-  if (nbits < 2 || nbits > 32)
-    {
-      log_error (_("public exponent too large (more than 32 bits)\n"));
-      goto leave;
-    }
-  nbits = GET_NBITS (rsa_p);
-  if (nbits != 512)
-    {
-      log_error (_("length of an RSA prime is not %d\n"), 512);
-      goto leave;
-    }
-  nbits = GET_NBITS (rsa_q);
-  if (nbits != 512)
-    {
-      log_error (_("length of an RSA prime is not %d\n"), 512);
-      goto leave;
-    }
-
-  
-  /* We need the modulus later to calculate the fingerprint. */
-  rc = copy_mpi (rsa_n, m, 128, &n);
-  if (rc)
-    goto leave;
-  assert (n == 128);
-  mlen = 128;
-
-  /* Build the private key template as described in section 4.3.3.6 of
-     the OpenPGP card specs:
-         0xC0   <length> public exponent
-         0xC1   <length> prime p 
-         0xC2   <length> prime q 
-  */
-  template = tp = xmalloc_secure (1+2 + 1+1+4 + 1+1+(512/8) + 1+1+(512/8));
-  *tp++ = 0xC0;
-  *tp++ = 4;
-  rc = copy_mpi (rsa_e, tp, 4, &n);
-  if (rc)
-    goto leave;
-  assert (n <= 4);
-  memcpy (e, tp, n);  /* Save a copy of the exponent for later use.  */
-  elen = n;
-  if (n != 4)
-    {
-      memmove (tp+4-n, tp, 4-n);
-      memset (tp, 0, 4-n);
-    }                 
-  tp += 4;
-
-  *tp++ = 0xC1;
-  *tp++ = 64;
-  rc = copy_mpi (rsa_p, tp, 64, &n);
-  if (rc)
-    goto leave;
-  assert (n == 64);
-  tp += 64;
-
-  *tp++ = 0xC2;
-  *tp++ = 64;
-  rc = copy_mpi (rsa_q, tp, 64, &n);
-  if (rc)
-    goto leave;
-  assert (n == 64);
-  tp += 64;
-  assert (tp - template == 138);
-
-  rc = agent_openpgp_storekey (keyno,
-                               template, tp - template,
-                               sk->timestamp,
-                               m, mlen,
-                               e, elen);
-
+  rc = save_unprotected_key_to_card (sk, keyno);
   if (rc)
     goto leave;
-  xfree (template);
-  template = NULL;
 
   /* Get back to the maybe protected original secret key.  */
   if (copied_sk)
@@ -1205,7 +1087,6 @@ card_store_subkey (KBNODE node, int use)
  leave:
   if (copied_sk)
     free_secret_key (copied_sk);
-  xfree (template);
   agent_release_card_info (&info);
   return okay;
 }
index 66692f7..d6b65a0 100644 (file)
--- a/g10/g10.c
+++ b/g10/g10.c
@@ -3181,12 +3181,12 @@ main( int argc, char **argv )
        if( opt.batch ) {
            if( argc > 1 )
                wrong_args("--gen-key [parameterfile]");
-           generate_keypair( argc? *argv : NULL, NULL );
+           generate_keypair( argc? *argv : NULL, NULL, NULL );
        }
        else {
            if( argc )
                wrong_args("--gen-key");
-           generate_keypair(NULL, NULL);
+           generate_keypair(NULL, NULL, NULL);
        }
        break;
 
index 23bbed1..96afad4 100644 (file)
@@ -63,7 +63,8 @@ enum para_name {
   pPASSPHRASE,
   pPASSPHRASE_DEK,
   pPASSPHRASE_S2K,
-  pSERIALNO
+  pSERIALNO,
+  pBACKUPENCDIR
 };
 
 struct para_data_s {
@@ -120,6 +121,47 @@ static int  write_keyblock( IOBUF out, KBNODE node );
 static int gen_card_key (int algo, int keyno, int is_primary,
                          KBNODE pub_root, KBNODE sec_root,
                          u32 expireval, struct para_data_s *para);
+static int gen_card_key_with_backup (int algo, int keyno, int is_primary,
+                                     KBNODE pub_root, KBNODE sec_root,
+                                     u32 expireval, struct para_data_s *para,
+                                     const char *backup_dir);
+
+
+#if GNUPG_MAJOR_VERSION == 1
+#define GET_NBITS(a)  mpi_get_nbits (a)
+#else
+#define GET_NBITS(a)  gcry_mpi_get_nbits (a)
+#endif
+
+  
+static int
+copy_mpi (MPI a, unsigned char *buffer, size_t len, size_t *ncopied)
+{
+  int rc;
+#if GNUPG_MAJOR_VERSION == 1
+  unsigned char *tmp;
+  unsigned int n;
+
+  tmp = mpi_get_secure_buffer (a, &n, NULL);
+  if (n > len)
+    rc = G10ERR_GENERAL;
+  else
+    {
+      rc = 0;
+      memcpy (buffer, tmp, n);
+      *ncopied = n;
+    }
+  xfree (tmp);
+#else /* GNUPG_MAJOR_VERSION != 1 */
+  rc = gcry_mpi_print (GCRYMPI_FMT_USG, buffer, len, ncopied, a);
+#endif /* GNUPG_MAJOR_VERSION != 1 */
+  if (rc)
+    log_error ("mpi_copy failed: %s\n", gpg_strerror (rc));
+  return rc;
+}
+
+
+
 
 static void
 write_uid( KBNODE root, const char *s )
@@ -1622,6 +1664,7 @@ ask_user_id( int mode )
 }
 
 
+/* FIXME: We need a way to cancel this prompt. */
 static DEK *
 do_ask_passphrase( STRING2KEY **ret_s2k )
 {
@@ -2213,10 +2256,14 @@ read_parameter_file( const char *fname )
 /*
  * Generate a keypair (fname is only used in batch mode) If
  * CARD_SERIALNO is not NULL the fucntion will create the keys on an
- * OpenPGP Card.
+ * OpenPGP Card.  If BACKUP_ENCRYPTION_DIR has been set and
+ * CARD_SERIALNO is NOT NULL, the encryption key for the card gets
+ * generate in software, imported to the card and a backup file
+ * written to directory given by this argument .
  */
 void
-generate_keypair( const char *fname, const char *card_serialno )
+generate_keypair (const char *fname, const char *card_serialno, 
+                  const char *backup_encryption_dir)
 {
   unsigned int nbits;
   char *uid = NULL;
@@ -2239,158 +2286,245 @@ generate_keypair( const char *fname, const char *card_serialno )
       return;
     }
   
-   if (opt.batch)
-     {
-       read_parameter_file( fname );
-       return;
-     }
-
-   if (card_serialno)
-     {
+  if (opt.batch)
+    {
+      read_parameter_file( fname );
+      return;
+    }
+
+  if (card_serialno)
+    {
 #ifdef ENABLE_CARD_SUPPORT
-       r = xcalloc (1, sizeof *r + strlen (card_serialno) );
-       r->key = pSERIALNO;
-       strcpy( r->u.value, card_serialno);
-       r->next = para;
-       para = r;
+      r = xcalloc (1, sizeof *r + strlen (card_serialno) );
+      r->key = pSERIALNO;
+      strcpy( r->u.value, card_serialno);
+      r->next = para;
+      para = r;
        
-       algo = PUBKEY_ALGO_RSA;
+      algo = PUBKEY_ALGO_RSA;
        
-       r = xcalloc (1, sizeof *r + 20 );
-       r->key = pKEYTYPE;
-       sprintf( r->u.value, "%d", algo );
-       r->next = para;
-       para = r;
-       r = xcalloc (1, sizeof *r + 20 );
-       r->key = pKEYUSAGE;
-       strcpy (r->u.value, "sign");
-       r->next = para;
-       para = r;
+      r = xcalloc (1, sizeof *r + 20 );
+      r->key = pKEYTYPE;
+      sprintf( r->u.value, "%d", algo );
+      r->next = para;
+      para = r;
+      r = xcalloc (1, sizeof *r + 20 );
+      r->key = pKEYUSAGE;
+      strcpy (r->u.value, "sign");
+      r->next = para;
+      para = r;
        
-       r = xcalloc (1, sizeof *r + 20 );
-       r->key = pSUBKEYTYPE;
-       sprintf( r->u.value, "%d", algo );
-       r->next = para;
-       para = r;
-       r = xcalloc (1, sizeof *r + 20 );
-       r->key = pSUBKEYUSAGE;
-       strcpy (r->u.value, "encrypt");
-       r->next = para;
-       para = r;
+      r = xcalloc (1, sizeof *r + 20 );
+      r->key = pSUBKEYTYPE;
+      sprintf( r->u.value, "%d", algo );
+      r->next = para;
+      para = r;
+      r = xcalloc (1, sizeof *r + 20 );
+      r->key = pSUBKEYUSAGE;
+      strcpy (r->u.value, "encrypt");
+      r->next = para;
+      para = r;
        
-       r = xcalloc (1, sizeof *r + 20 );
-       r->key = pAUTHKEYTYPE;
-       sprintf( r->u.value, "%d", algo );
-       r->next = para;
-       para = r;
+      r = xcalloc (1, sizeof *r + 20 );
+      r->key = pAUTHKEYTYPE;
+      sprintf( r->u.value, "%d", algo );
+      r->next = para;
+      para = r;
+
+      if (backup_encryption_dir)
+        {
+          r = xcalloc (1, sizeof *r + strlen (backup_encryption_dir) );
+          r->key = pBACKUPENCDIR;
+          strcpy (r->u.value, backup_encryption_dir);
+          r->next = para;
+          para = r;
+        }
 #endif /*ENABLE_CARD_SUPPORT*/
-     }
-   else
-     {
-       algo = ask_algo( 0, &use );
-       if( !algo )
-         { /* default: DSA with ElG subkey of the specified size */
-           both = 1;
-           r = m_alloc_clear( sizeof *r + 20 );
-           r->key = pKEYTYPE;
-           sprintf( r->u.value, "%d", PUBKEY_ALGO_DSA );
-           r->next = para;
-           para = r;
-           tty_printf(_("DSA keypair will have 1024 bits.\n"));
-           r = m_alloc_clear( sizeof *r + 20 );
-           r->key = pKEYLENGTH;
-           strcpy( r->u.value, "1024" );
-           r->next = para;
-           para = r;
-           r = m_alloc_clear( sizeof *r + 20 );
-           r->key = pKEYUSAGE;
-           strcpy( r->u.value, "sign" );
-           r->next = para;
-           para = r;
+    }
+  else
+    {
+      algo = ask_algo( 0, &use );
+      if( !algo )
+        { /* default: DSA with ElG subkey of the specified size */
+          both = 1;
+          r = m_alloc_clear( sizeof *r + 20 );
+          r->key = pKEYTYPE;
+          sprintf( r->u.value, "%d", PUBKEY_ALGO_DSA );
+          r->next = para;
+          para = r;
+          tty_printf(_("DSA keypair will have 1024 bits.\n"));
+          r = m_alloc_clear( sizeof *r + 20 );
+          r->key = pKEYLENGTH;
+          strcpy( r->u.value, "1024" );
+          r->next = para;
+          para = r;
+          r = m_alloc_clear( sizeof *r + 20 );
+          r->key = pKEYUSAGE;
+          strcpy( r->u.value, "sign" );
+          r->next = para;
+          para = r;
            
-           algo = PUBKEY_ALGO_ELGAMAL_E;
-           r = m_alloc_clear( sizeof *r + 20 );
-           r->key = pSUBKEYTYPE;
-           sprintf( r->u.value, "%d", algo );
-           r->next = para;
-           para = r;
-           r = m_alloc_clear( sizeof *r + 20 );
-           r->key = pSUBKEYUSAGE;
-           strcpy( r->u.value, "encrypt" );
-           r->next = para;
-           para = r;
-         }
-       else 
-         {
-           r = m_alloc_clear( sizeof *r + 20 );
-           r->key = pKEYTYPE;
-           sprintf( r->u.value, "%d", algo );
-           r->next = para;
-           para = r;
+          algo = PUBKEY_ALGO_ELGAMAL_E;
+          r = m_alloc_clear( sizeof *r + 20 );
+          r->key = pSUBKEYTYPE;
+          sprintf( r->u.value, "%d", algo );
+          r->next = para;
+          para = r;
+          r = m_alloc_clear( sizeof *r + 20 );
+          r->key = pSUBKEYUSAGE;
+          strcpy( r->u.value, "encrypt" );
+          r->next = para;
+          para = r;
+        }
+      else 
+        {
+          r = m_alloc_clear( sizeof *r + 20 );
+          r->key = pKEYTYPE;
+          sprintf( r->u.value, "%d", algo );
+          r->next = para;
+          para = r;
            
-           if (use)
-             {
-               r = m_alloc_clear( sizeof *r + 20 );
-               r->key = pKEYUSAGE;
-               sprintf( r->u.value, "%s%s",
-                        (use & PUBKEY_USAGE_SIG)? "sign ":"",
-                        (use & PUBKEY_USAGE_ENC)? "encrypt ":"" );
-               r->next = para;
-               para = r;
-             }
+          if (use)
+            {
+              r = m_alloc_clear( sizeof *r + 20 );
+              r->key = pKEYUSAGE;
+              sprintf( r->u.value, "%s%s",
+                       (use & PUBKEY_USAGE_SIG)? "sign ":"",
+                       (use & PUBKEY_USAGE_ENC)? "encrypt ":"" );
+              r->next = para;
+              para = r;
+            }
            
-         }
+        }
        
-       nbits = ask_keysize( algo );
-       r = m_alloc_clear( sizeof *r + 20 );
-       r->key = both? pSUBKEYLENGTH : pKEYLENGTH;
-       sprintf( r->u.value, "%u", nbits);
-       r->next = para;
-       para = r;
-     }
-
-   expire = ask_expire_interval(0);
-    r = m_alloc_clear( sizeof *r + 20 );
-    r->key = pKEYEXPIRE;
-    r->u.expire = expire;
-    r->next = para;
-    para = r;
-    r = m_alloc_clear( sizeof *r + 20 );
-    r->key = pSUBKEYEXPIRE;
-    r->u.expire = expire;
-    r->next = para;
-    para = r;
-
-    uid = ask_user_id(0);
-    if( !uid ) 
-      {
-       log_error(_("Key generation canceled.\n"));
-       release_parameter_list( para );
-       return;
-      }
-    r = m_alloc_clear( sizeof *r + strlen(uid) );
-    r->key = pUSERID;
-    strcpy( r->u.value, uid );
-    r->next = para;
-    para = r;
+      nbits = ask_keysize( algo );
+      r = m_alloc_clear( sizeof *r + 20 );
+      r->key = both? pSUBKEYLENGTH : pKEYLENGTH;
+      sprintf( r->u.value, "%u", nbits);
+      r->next = para;
+      para = r;
+    }
+   
+  expire = ask_expire_interval(0);
+  r = m_alloc_clear( sizeof *r + 20 );
+  r->key = pKEYEXPIRE;
+  r->u.expire = expire;
+  r->next = para;
+  para = r;
+  r = m_alloc_clear( sizeof *r + 20 );
+  r->key = pSUBKEYEXPIRE;
+  r->u.expire = expire;
+  r->next = para;
+  para = r;
+
+  uid = ask_user_id(0);
+  if( !uid ) 
+    {
+      log_error(_("Key generation canceled.\n"));
+      release_parameter_list( para );
+      return;
+    }
+  r = m_alloc_clear( sizeof *r + strlen(uid) );
+  r->key = pUSERID;
+  strcpy( r->u.value, uid );
+  r->next = para;
+  para = r;
     
-    dek = card_serialno? NULL : do_ask_passphrase( &s2k );
-    if( dek )
-      {
-       r = m_alloc_clear( sizeof *r );
-       r->key = pPASSPHRASE_DEK;
-       r->u.dek = dek;
-       r->next = para;
-       para = r;
-       r = m_alloc_clear( sizeof *r );
-       r->key = pPASSPHRASE_S2K;
-       r->u.s2k = s2k;
-       r->next = para;
-       para = r;
-      }
+  dek = card_serialno? NULL : do_ask_passphrase( &s2k );
+  if( dek )
+    {
+      r = m_alloc_clear( sizeof *r );
+      r->key = pPASSPHRASE_DEK;
+      r->u.dek = dek;
+      r->next = para;
+      para = r;
+      r = m_alloc_clear( sizeof *r );
+      r->key = pPASSPHRASE_S2K;
+      r->u.s2k = s2k;
+      r->next = para;
+      para = r;
+    }
     
-    proc_parameter_file( para, "[internal]", &outctrl, !!card_serialno);
-    release_parameter_list( para );
+  proc_parameter_file( para, "[internal]", &outctrl, !!card_serialno);
+  release_parameter_list( para );
+}
+
+
+/* Generate a raw key and return it as a secret key packet.  The
+   function will ask for the passphrase and return a protected as well
+   as an unprotected copy of a new secret key packet.  0 is returned
+   on success and the caller must then free the returned values.  */
+static int
+generate_raw_key (int algo, unsigned int nbits, u32 created_at,
+                  PKT_secret_key **r_sk_unprotected,
+                  PKT_secret_key **r_sk_protected)
+{
+  int rc;
+  DEK *dek = NULL;
+  STRING2KEY *s2k = NULL;
+  PKT_secret_key *sk = NULL;
+  int i;
+  size_t nskey, npkey;
+
+  npkey = pubkey_get_npkey (algo);
+  nskey = pubkey_get_nskey (algo);
+  assert (nskey <= PUBKEY_MAX_NSKEY && npkey < nskey);
+
+  if (nbits < 512)
+    {
+      nbits = 512;
+      log_info (_("keysize invalid; using %u bits\n"), nbits );
+    }
+
+  if ((nbits % 32)) 
+    {
+      nbits = ((nbits + 31) / 32) * 32;
+      log_info(_("keysize rounded up to %u bits\n"), nbits );
+    }
+
+  dek = do_ask_passphrase (&s2k);
+
+  sk = m_alloc_clear (sizeof *sk);
+  sk->timestamp = created_at;
+  sk->version = 4;
+  sk->pubkey_algo = algo;
+
+  rc = pubkey_generate (algo, nbits, sk->skey, NULL);
+  if (rc)
+    {
+      log_error("pubkey_generate failed: %s\n", g10_errstr(rc) );
+      goto leave;
+    }
+
+  for (i=npkey; i < nskey; i++)
+    sk->csum += checksum_mpi (sk->skey[i]);
+
+  if (r_sk_unprotected) 
+    *r_sk_unprotected = copy_secret_key (NULL, sk);
+
+  if (dek)
+    {
+      sk->protect.algo = dek->algo;
+      sk->protect.s2k = *s2k;
+      rc = protect_secret_key (sk, dek);
+      if (rc)
+        {
+          log_error ("protect_secret_key failed: %s\n", g10_errstr(rc));
+          goto leave;
+       }
+    }
+  if (r_sk_protected)
+    {
+      *r_sk_protected = sk;
+      sk = NULL;
+    }
+
+ leave:
+  if (sk)
+    free_secret_key (sk);
+  m_free (dek);
+  m_free (s2k);
+  return rc;
 }
 
 
@@ -2552,8 +2686,20 @@ do_generate_keypair( struct para_data_s *para,
           }
         else
           {
-            rc = gen_card_key (PUBKEY_ALGO_RSA, 2, 0, pub_root, sec_root,
-                               get_parameter_u32 (para, pKEYEXPIRE), para);
+            if ((s = get_parameter_value (para, pBACKUPENCDIR)))
+              {
+                /* A backup of the encryption key has been requested.
+                   Generate the key i software and import it then to
+                   the card.  Write a backup file. */
+                rc = gen_card_key_with_backup (PUBKEY_ALGO_RSA, 2, 0,
+                                               pub_root, sec_root,
+                                               get_parameter_u32 (para,
+                                                                  pKEYEXPIRE),
+                                               para, s);
+              }
+            else
+              rc = gen_card_key (PUBKEY_ALGO_RSA, 2, 0, pub_root, sec_root,
+                                 get_parameter_u32 (para, pKEYEXPIRE), para);
           }
 
         if( !rc )
@@ -2943,6 +3089,7 @@ gen_card_key (int algo, int keyno, int is_primary,
   PKT_public_key *pk;
 
   assert (algo == PUBKEY_ALGO_RSA);
+  
 
   rc = agent_scd_genkey (&info, keyno, 1);
 /*    if (gpg_err_code (rc) == GPG_ERR_EEXIST) */
@@ -3006,3 +3153,241 @@ gen_card_key (int algo, int keyno, int is_primary,
   return -1;
 #endif /*!ENABLE_CARD_SUPPORT*/
 }
+
+
+
+static int
+gen_card_key_with_backup (int algo, int keyno, int is_primary,
+                          KBNODE pub_root, KBNODE sec_root,
+                          u32 expireval, struct para_data_s *para,
+                          const char *backup_dir)
+{
+#ifdef ENABLE_CARD_SUPPORT
+  int rc;
+  const char *s;
+  PACKET *pkt;
+  PKT_secret_key *sk, *sk_unprotected, *sk_protected;
+  PKT_public_key *pk;
+  size_t n;
+  int i;
+
+  rc = generate_raw_key (algo, 1024, make_timestamp (),
+                         &sk_unprotected, &sk_protected);
+  if (rc)
+    return rc;
+
+  /* First, store the key to the card. */
+  rc = save_unprotected_key_to_card (sk_unprotected, keyno);
+  if (rc)
+    {
+      log_error (_("storing key onto card failed: %s\n"), g10_errstr (rc));
+      free_secret_key (sk_unprotected);
+      free_secret_key (sk_protected);
+      return rc;
+    }
+
+  /* Get rid of the secret key parameters and store the serial numer. */
+  sk = sk_unprotected;
+  n = pubkey_get_nskey (sk->pubkey_algo);
+  for (i=pubkey_get_npkey (sk->pubkey_algo); i < n; i++)
+    {
+      mpi_free (sk->skey[i]);
+      sk->skey[i] = NULL;
+    }
+  i = pubkey_get_npkey (sk->pubkey_algo);
+  sk->skey[i] = mpi_set_opaque (NULL, xstrdup ("dummydata"), 10);
+  sk->is_protected = 1;
+  sk->protect.s2k.mode = 1002;
+  s = get_parameter_value (para, pSERIALNO);
+  assert (s);
+  for (sk->protect.ivlen=0; sk->protect.ivlen < 16 && *s && s[1];
+       sk->protect.ivlen++, s += 2)
+    sk->protect.iv[sk->protect.ivlen] = xtoi_2 (s);
+
+  /* Now write the *protected* secret key to the file.  */
+  {
+    char name_buffer[50];
+    char *fname;
+    IOBUF fp;
+    mode_t oldmask;
+
+    keyid_from_sk (sk, NULL);
+    sprintf (name_buffer,"sk_%08lX%08lX.gpg",
+             (ulong)sk->keyid[0], (ulong)sk->keyid[1]);
+
+    fname = make_filename (backup_dir, name_buffer, NULL);
+    oldmask = umask (077);
+    fp = iobuf_create (fname);
+    umask (oldmask);
+    if (!fp) 
+      {
+       log_error (_("can't create backup file `%s': %s\n"),
+                   fname, strerror(errno) );
+        m_free (fname);
+        free_secret_key (sk_unprotected);
+        free_secret_key (sk_protected);
+        return G10ERR_OPEN_FILE;
+      }
+
+    pkt = xcalloc (1, sizeof *pkt);
+    pkt->pkttype = PKT_SECRET_KEY;
+    pkt->pkt.secret_key = sk_protected;
+    sk_protected = NULL;
+
+    rc = build_packet (fp, pkt);
+    if (rc)
+      {
+        log_error("build packet failed: %s\n", g10_errstr(rc) );
+        iobuf_cancel (fp);
+      }
+    else
+      {
+        iobuf_close (fp);
+        iobuf_ioctl (NULL, 2, 0, (char*)fname);
+        log_info (_("NOTE: backup of card key saved to `%s'\n"), fname);
+      }
+    free_packet (pkt);
+    m_free (pkt);
+    m_free (fname);
+    if (rc)
+      {
+        free_secret_key (sk_unprotected);
+        return rc;
+      }
+  }
+
+  /* Create the public key from the secret key. */
+  pk = xcalloc (1, sizeof *pk );
+  pk->timestamp = sk->timestamp;
+  pk->version = sk->version;
+  if (expireval)
+      pk->expiredate = sk->expiredate = sk->timestamp + expireval;
+  pk->pubkey_algo = sk->pubkey_algo;
+  n = pubkey_get_npkey (sk->pubkey_algo);
+  for (i=0; i < n; i++)
+    pk->pkey[i] = mpi_copy (sk->skey[i]);
+
+  /* Build packets and add them to the node lists.  */
+  pkt = xcalloc (1,sizeof *pkt);
+  pkt->pkttype = is_primary ? PKT_PUBLIC_KEY : PKT_PUBLIC_SUBKEY;
+  pkt->pkt.public_key = pk;
+  add_kbnode(pub_root, new_kbnode( pkt ));
+
+  pkt = xcalloc (1,sizeof *pkt);
+  pkt->pkttype = is_primary ? PKT_SECRET_KEY : PKT_SECRET_SUBKEY;
+  pkt->pkt.secret_key = sk;
+  add_kbnode(sec_root, new_kbnode( pkt ));
+
+  return 0;
+#else
+  return -1;
+#endif /*!ENABLE_CARD_SUPPORT*/
+}
+
+
+#ifdef ENABLE_CARD_SUPPORT
+int
+save_unprotected_key_to_card (PKT_secret_key *sk, int keyno)
+{
+  int rc;
+  size_t n;
+  MPI rsa_n, rsa_e, rsa_p, rsa_q;
+  unsigned int nbits;
+  unsigned char *template = NULL;
+  unsigned char *tp;
+  unsigned char m[128], e[4];
+  size_t mlen, elen;
+
+  assert (is_RSA (sk->pubkey_algo));
+  assert (!sk->is_protected);
+
+  rc = -1;
+  /* Some basic checks on the key parameters. */
+  rsa_n = sk->skey[0];
+  rsa_e = sk->skey[1];
+  rsa_p = sk->skey[3];
+  rsa_q = sk->skey[4];
+
+  nbits = GET_NBITS (rsa_n);
+  if (nbits != 1024)
+    {
+      log_error (_("length of RSA modulus is not %d\n"), 1024);
+      goto leave;
+    }
+  nbits = GET_NBITS (rsa_e);
+  if (nbits < 2 || nbits > 32)
+    {
+      log_error (_("public exponent too large (more than 32 bits)\n"));
+      goto leave;
+    }
+  nbits = GET_NBITS (rsa_p);
+  if (nbits != 512)
+    {
+      log_error (_("length of an RSA prime is not %d\n"), 512);
+      goto leave;
+    }
+  nbits = GET_NBITS (rsa_q);
+  if (nbits != 512)
+    {
+      log_error (_("length of an RSA prime is not %d\n"), 512);
+      goto leave;
+    }
+
+  
+  /* We need the modulus later to calculate the fingerprint. */
+  rc = copy_mpi (rsa_n, m, 128, &n);
+  if (rc)
+    goto leave;
+  assert (n == 128);
+  mlen = 128;
+
+  /* Build the private key template as described in section 4.3.3.6 of
+     the OpenPGP card specs:
+         0xC0   <length> public exponent
+         0xC1   <length> prime p 
+         0xC2   <length> prime q 
+  */
+  template = tp = xmalloc_secure (1+2 + 1+1+4 + 1+1+(512/8) + 1+1+(512/8));
+  *tp++ = 0xC0;
+  *tp++ = 4;
+  rc = copy_mpi (rsa_e, tp, 4, &n);
+  if (rc)
+    goto leave;
+  assert (n <= 4);
+  memcpy (e, tp, n);  /* Save a copy of the exponent for later use.  */
+  elen = n;
+  if (n != 4)
+    {
+      memmove (tp+4-n, tp, 4-n);
+      memset (tp, 0, 4-n);
+    }                 
+  tp += 4;
+
+  *tp++ = 0xC1;
+  *tp++ = 64;
+  rc = copy_mpi (rsa_p, tp, 64, &n);
+  if (rc)
+    goto leave;
+  assert (n == 64);
+  tp += 64;
+
+  *tp++ = 0xC2;
+  *tp++ = 64;
+  rc = copy_mpi (rsa_q, tp, 64, &n);
+  if (rc)
+    goto leave;
+  assert (n == 64);
+  tp += 64;
+  assert (tp - template == 138);
+
+  rc = agent_openpgp_storekey (keyno,
+                               template, tp - template,
+                               sk->timestamp,
+                               m, mlen,
+                               e, elen);
+
+ leave:
+  xfree (template);
+  return rc;
+}
+#endif /*ENABLE_CARD_SUPPORT*/
index 0205f7e..1326bd4 100644 (file)
@@ -153,7 +153,8 @@ void show_basic_key_info (KBNODE keyblock);
 /*-- keygen.c --*/
 u32 ask_expire_interval(int object);
 u32 ask_expiredate(void);
-void generate_keypair( const char *fname, const char *card_serialno );
+void generate_keypair( const char *fname, const char *card_serialno,
+                       const char *backup_encryption_dir );
 int keygen_set_std_prefs (const char *string,int personal);
 PKT_user_id *keygen_get_std_prefs (void);
 int keygen_add_key_expire( PKT_signature *sig, void *opaque );
@@ -165,6 +166,7 @@ int generate_subkeypair( KBNODE pub_keyblock, KBNODE sec_keyblock );
 #ifdef ENABLE_CARD_SUPPORT
 int generate_card_subkeypair (KBNODE pub_keyblock, KBNODE sec_keyblock,
                               int keyno, const char *serialno);
+int save_unprotected_key_to_card (PKT_secret_key *sk, int keyno);
 #endif
 
 /*-- openfile.c --*/