Allow creating subkeys using an existing key
authorWerner Koch <wk@gnupg.org>
Sun, 6 Nov 2011 16:01:31 +0000 (17:01 +0100)
committerWerner Koch <wk@gnupg.org>
Sun, 6 Nov 2011 16:01:31 +0000 (17:01 +0100)
This works by specifying the keygrip instead of an algorithm (section
number 13) and requires that the option -expert has been used.  It
will be easy to extend this to the primary key.

g10/ChangeLog
g10/call-agent.c
g10/call-agent.h
g10/card-util.c
g10/gpg.c
g10/keyedit.c
g10/keygen.c
g10/main.h

index 4b5f2f1..8928780 100644 (file)
@@ -1,3 +1,15 @@
+2011-11-06  Werner Koch  <wk@g10code.com>
+
+       * card-util.c (generate_card_keys): Add arg CTRL.
+
+       * call-agent.c (agent_readkey): New.
+       * keygen.c (do_create_from_keygrip): New.
+       (ask_algo): Add arg R_KEYGRIP and a prompt to enter it.
+       (generate_subkeypair): Call do_create_from_keygrip if required.
+       (generate_subkeypair): Add arg CTRL.  Change caller.
+       (ask_algo): Add arg CTRL.
+       (generate_keypair): Ditto.
+
 2011-09-23  Werner Koch  <wk@g10code.com>
 
        * gpgv.c (disable_dotlock): Rename to dotlock_disable.
index 5a10dbd..4451029 100644 (file)
@@ -1,6 +1,6 @@
 /* call-agent.c - Divert GPG operations to the agent.
  * Copyright (C) 2001, 2002, 2003, 2006, 2007, 2008, 2009,
- *               2010 Free Software Foundation, Inc.
+ *               2010, 2011 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -1506,55 +1506,52 @@ agent_genkey (ctrl_t ctrl, char **cache_nonce_addr,
 }
 
 
-
 \f
-/* FIXME: Call the agent to read the public key part for a given keygrip.  If
+/* Call the agent to read the public key part for a given keygrip.  If
    FROMCARD is true, the key is directly read from the current
    smartcard. In this case HEXKEYGRIP should be the keyID
    (e.g. OPENPGP.3). */
-/* int */
-/* agent_readkey (ctrl_t ctrl, int fromcard, const char *hexkeygrip, */
-/*                ksba_sexp_t *r_pubkey) */
-/* { */
-/*   int rc; */
-/*   membuf_t data; */
-/*   size_t len; */
-/*   unsigned char *buf; */
-/*   char line[ASSUAN_LINELENGTH]; */
-
-/*   *r_pubkey = NULL; */
-/*   rc = start_agent (ctrl); */
-/*   if (rc) */
-/*     return rc; */
-
-/*   rc = assuan_transact (agent_ctx, "RESET",NULL, NULL, NULL, NULL, NULL, NULL); */
-/*   if (rc) */
-/*     return rc; */
-
-/*   snprintf (line, DIM(line)-1, "%sREADKEY %s", */
-/*             fromcard? "SCD ":"", hexkeygrip); */
-/*   line[DIM(line)-1] = 0; */
-
-/*   init_membuf (&data, 1024); */
-/*   rc = assuan_transact (agent_ctx, line, */
-/*                         membuf_data_cb, &data,  */
-/*                         default_inq_cb, ctrl, NULL, NULL); */
-/*   if (rc) */
-/*     { */
-/*       xfree (get_membuf (&data, &len)); */
-/*       return rc; */
-/*     } */
-/*   buf = get_membuf (&data, &len); */
-/*   if (!buf) */
-/*     return gpg_error (GPG_ERR_ENOMEM); */
-/*   if (!gcry_sexp_canon_len (buf, len, NULL, NULL)) */
-/*     { */
-/*       xfree (buf); */
-/*       return gpg_error (GPG_ERR_INV_SEXP); */
-/*     } */
-/*   *r_pubkey = buf; */
-/*   return 0; */
-/* } */
+gpg_error_t
+agent_readkey (ctrl_t ctrl, int fromcard, const char *hexkeygrip,
+               unsigned char **r_pubkey)
+{
+  gpg_error_t err;
+  membuf_t data;
+  size_t len;
+  unsigned char *buf;
+  char line[ASSUAN_LINELENGTH];
+
+  *r_pubkey = NULL;
+  err = start_agent (ctrl, 0);
+  if (err)
+    return err;
+
+  err = assuan_transact (agent_ctx, "RESET",NULL, NULL, NULL, NULL, NULL, NULL);
+  if (err)
+    return err;
+
+  snprintf (line, DIM(line)-1, "%sREADKEY %s", fromcard? "SCD ":"", hexkeygrip);
+
+  init_membuf (&data, 1024);
+  err = assuan_transact (agent_ctx, line,
+                         membuf_data_cb, &data,
+                         default_inq_cb, NULL, NULL, NULL);
+  if (err)
+    {
+      xfree (get_membuf (&data, &len));
+      return err;
+    }
+  buf = get_membuf (&data, &len);
+  if (!buf)
+    return gpg_error_from_syserror ();
+  if (!gcry_sexp_canon_len (buf, len, NULL, NULL))
+    {
+      xfree (buf);
+      return gpg_error (GPG_ERR_INV_SEXP);
+    }
+  *r_pubkey = buf;
+  return 0;
+}
 
 
 \f
index 1e7e15a..43de14f 100644 (file)
@@ -148,6 +148,10 @@ gpg_error_t agent_genkey (ctrl_t ctrl, char **cache_nonce_addr,
                           const char *keyparms, int no_protection,
                           gcry_sexp_t *r_pubkey);
 
+/* Read a public key.  */
+gpg_error_t agent_readkey (ctrl_t ctrl, int fromcard, const char *hexkeygrip,
+                           unsigned char **r_pubkey);
+
 /* Create a signature.  */
 gpg_error_t agent_pksign (ctrl_t ctrl, const char *cache_nonce,
                           const char *hexkeygrip, const char *desc,
index 9c124bb..14268df 100644 (file)
@@ -1356,7 +1356,7 @@ do_change_keysize (int keyno, unsigned int nbits)
 
 
 static void
-generate_card_keys (void)
+generate_card_keys (ctrl_t ctrl)
 {
   struct agent_card_info_s info;
   int forced_chv1;
@@ -1435,7 +1435,7 @@ generate_card_keys (void)
          the serialnumber and thus it won't harm.  */
     }
 
-  generate_keypair (NULL, info.serialno, want_backup);
+  generate_keypair (ctrl, NULL, info.serialno, want_backup);
 
  leave:
   agent_release_card_info (&info);
@@ -1986,7 +1986,7 @@ card_edit (ctrl_t ctrl, strlist_t commands)
           break;
 
         case cmdGENERATE:
-          generate_card_keys ();
+          generate_card_keys (ctrl);
           break;
 
         case cmdPASSWD:
index c31a558..aa37a88 100644 (file)
--- a/g10/gpg.c
+++ b/g10/gpg.c
@@ -3709,12 +3709,12 @@ main (int argc, char **argv)
        if( opt.batch ) {
            if( argc > 1 )
                wrong_args("--gen-key [parameterfile]");
-           generate_keypair (argc? *argv : NULL, NULL, 0);
+           generate_keypair (ctrl, argc? *argv : NULL, NULL, 0);
        }
        else {
            if( argc )
                wrong_args("--gen-key");
-           generate_keypair (NULL, NULL, 0);
+           generate_keypair (ctrl, NULL, NULL, 0);
        }
        break;
 
index fd42439..26e05a0 100644 (file)
@@ -1794,7 +1794,7 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr,
          break;
 
        case cmdADDKEY:
-         if (!generate_subkeypair (keyblock))
+         if (!generate_subkeypair (ctrl, keyblock))
            {
              redisplay = 1;
              modified = 1;
index a5650a8..55048b1 100644 (file)
@@ -1254,6 +1254,91 @@ key_from_sexp (gcry_mpi_t *array, gcry_sexp_t sexp,
 }
 
 
+/* Create a keyblock using the given KEYGRIP.  ALGO is the OpenPGP
+   algorithm of that keygrip.  */
+static int
+do_create_from_keygrip (ctrl_t ctrl, int algo, const char *hexkeygrip,
+                        kbnode_t pub_root, u32 timestamp, u32 expireval,
+                        int is_subkey)
+{
+  int err;
+  PACKET *pkt;
+  PKT_public_key *pk;
+  gcry_sexp_t s_key;
+  const char *algoelem;
+
+  if (hexkeygrip[0] == '&')
+    hexkeygrip++;
+
+  switch (algo)
+    {
+    case PUBKEY_ALGO_RSA:       algoelem = "ne"; break;
+    case PUBKEY_ALGO_DSA:       algoelem = "pqgy"; break;
+    case PUBKEY_ALGO_ELGAMAL_E: algoelem = "pgy"; break;
+    case PUBKEY_ALGO_ECDH:
+    case PUBKEY_ALGO_ECDSA:     algoelem = ""; break;
+    default: return gpg_error (GPG_ERR_INTERNAL);
+    }
+
+
+  /* Ask the agent for the public key matching HEXKEYGRIP.  */
+  {
+    unsigned char *public;
+
+    err = agent_readkey (ctrl, 0, hexkeygrip, &public);
+    if (err)
+      return err;
+    err = gcry_sexp_sscan (&s_key, NULL,
+                           public, gcry_sexp_canon_len (public, 0, NULL, NULL));
+    xfree (public);
+    if (err)
+      return err;
+  }
+
+  /* Build a public key packet.  */
+  pk = xtrycalloc (1, sizeof *pk);
+  if (!pk)
+    {
+      err = gpg_error_from_syserror ();
+      gcry_sexp_release (s_key);
+      return err;
+    }
+
+  pk->timestamp = timestamp;
+  pk->version = 4;
+  if (expireval)
+    pk->expiredate = pk->timestamp + expireval;
+  pk->pubkey_algo = algo;
+
+  if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_ECDH)
+    err = ecckey_from_sexp (pk->pkey, s_key, algo);
+  else
+    err = key_from_sexp (pk->pkey, s_key, "public-key", algoelem);
+  if (err)
+    {
+      log_error ("key_from_sexp failed: %s\n", gpg_strerror (err) );
+      gcry_sexp_release (s_key);
+      free_public_key (pk);
+      return err;
+    }
+  gcry_sexp_release (s_key);
+
+  pkt = xtrycalloc (1, sizeof *pkt);
+  if (!pkt)
+    {
+      err = gpg_error_from_syserror ();
+      free_public_key (pk);
+      return err;
+    }
+
+  pkt->pkttype = is_subkey ? PKT_PUBLIC_SUBKEY : PKT_PUBLIC_KEY;
+  pkt->pkt.public_key = pk;
+  add_kbnode (pub_root, new_kbnode (pkt));
+
+  return 0;
+}
+
+
 /* Common code for the key generation fucntion gen_xxx.  */
 static int
 common_gen (const char *keyparms, int algo, const char *algoelem,
@@ -1691,14 +1776,53 @@ ask_key_flags(int algo,int subkey)
 }
 
 
+/* Check whether we have a key for the key with HEXGRIP.  Returns 0 if
+   there is no such key or the OpenPGP algo number for the key.  */
+static int
+check_keygrip (ctrl_t ctrl, const char *hexgrip)
+{
+  gpg_error_t err;
+  unsigned char *public;
+  size_t publiclen;
+  int algo;
+
+  if (hexgrip[0] == '&')
+    hexgrip++;
+
+  err = agent_readkey (ctrl, 0, hexgrip, &public);
+  if (err)
+    return 0;
+  publiclen = gcry_sexp_canon_len (public, 0, NULL, NULL);
+
+  get_pk_algo_from_canon_sexp (public, publiclen, &algo);
+  xfree (public);
+
+  switch (algo)
+    {
+    case GCRY_PK_RSA:   return PUBKEY_ALGO_RSA;
+    case GCRY_PK_DSA:   return PUBKEY_ALGO_DSA;
+    case GCRY_PK_ELG_E: return PUBKEY_ALGO_ELGAMAL_E;
+    case GCRY_PK_ECDH:  return PUBKEY_ALGO_ECDH;
+    case GCRY_PK_ECDSA: return PUBKEY_ALGO_ECDSA;
+    default: return 0;
+    }
+}
+
+
+
 /* Ask for an algorithm.  The function returns the algorithm id to
  * create. If ADDMODE is false the function won't show an option to
  * create the primary and subkey combined and won't set R_USAGE
  * either.  If a combined algorithm has been selected, the subkey
- * algorithm is stored at R_SUBKEY_ALGO.  */
+ * algorithm is stored at R_SUBKEY_ALGO.  If R_KEYGRIP is given, the
+ * user has the choice to enter the keygrip of an existing key.  That
+ * keygrip is then stored at this address.  The caller needs to free
+ * it. */
 static int
-ask_algo (int addmode, int *r_subkey_algo, unsigned int *r_usage)
+ask_algo (ctrl_t ctrl, int addmode, int *r_subkey_algo, unsigned int *r_usage,
+          char **r_keygrip)
 {
+  char *keygrip = NULL;
   char *answer;
   int algo;
   int dummy_algo;
@@ -1736,6 +1860,9 @@ ask_algo (int addmode, int *r_subkey_algo, unsigned int *r_usage)
   if (opt.expert && addmode)
     tty_printf (_("  (%d) ECDH (encrypt only)\n"), 12 );
 
+  if (opt.expert && r_keygrip)
+    tty_printf (_("  (%d) Existing key\n"), 13 );
+
   for (;;)
     {
       *r_usage = 0;
@@ -1744,6 +1871,7 @@ ask_algo (int addmode, int *r_subkey_algo, unsigned int *r_usage)
       cpr_kill_prompt ();
       algo = *answer? atoi (answer) : 1;
       xfree(answer);
+      answer = NULL;
       if (algo == 1 && !addmode)
         {
           algo = PUBKEY_ALGO_RSA;
@@ -1816,10 +1944,42 @@ ask_algo (int addmode, int *r_subkey_algo, unsigned int *r_usage)
           *r_usage = PUBKEY_USAGE_ENC;
           break;
        }
+      else if (algo == 13 && opt.expert && r_keygrip)
+        {
+          for (;;)
+            {
+              xfree (answer);
+              answer = tty_get (_("Enter the keygrip: "));
+              tty_kill_prompt ();
+              trim_spaces (answer);
+              if (!*answer)
+                {
+                  xfree (answer);
+                  answer = NULL;
+                  continue;
+                }
+
+              if (strlen (answer) != 40 &&
+                       !(answer[0] == '&' && strlen (answer+1) == 40))
+                tty_printf
+                  (_("Not a valid keygrip (expecting 40 hex digits)\n"));
+              else if (!(algo = check_keygrip (ctrl, answer)) )
+                tty_printf (_("No key with this keygrip\n"));
+              else
+                break; /* Okay.  */
+            }
+          xfree (keygrip);
+          keygrip = answer;
+          answer = NULL;
+          *r_usage = ask_key_flags (algo, addmode);
+          break;
+       }
       else
         tty_printf (_("Invalid selection.\n"));
     }
 
+  if (r_keygrip)
+    *r_keygrip = keygrip;
   return algo;
 }
 
@@ -3099,7 +3259,7 @@ read_parameter_file( const char *fname )
  * imported to the card and a backup file created by gpg-agent.
  */
 void
-generate_keypair (const char *fname, const char *card_serialno,
+generate_keypair (ctrl_t ctrl, const char *fname, const char *card_serialno,
                   int card_backup_key)
 {
   unsigned int nbits;
@@ -3180,7 +3340,11 @@ generate_keypair (const char *fname, const char *card_serialno,
     {
       int subkey_algo;
 
-      algo = ask_algo (0, &subkey_algo, &use);
+      /* Fixme: To support creating a primary key by keygrip we better
+         also define the keyword for the parameter file.  Note that
+         the subkey case will never be asserted if a keygrip has been
+         given.  */
+      algo = ask_algo (ctrl, 0, &subkey_algo, &use, NULL);
       if (subkey_algo)
         {
           /* Create primary and subkey at once.  */
@@ -3653,7 +3817,7 @@ do_generate_keypair (struct para_data_s *para,
 /* Add a new subkey to an existing key.  Returns 0 if a new key has
    been generated and put into the keyblocks.  */
 gpg_error_t
-generate_subkeypair (KBNODE keyblock)
+generate_subkeypair (ctrl_t ctrl, kbnode_t keyblock)
 {
   gpg_error_t err = 0;
   kbnode_t node;
@@ -3712,9 +3876,11 @@ generate_subkeypair (KBNODE keyblock)
   if (serialno)
     tty_printf (_("Secret parts of primary key are stored on-card.\n"));
 
-  algo = ask_algo (1, NULL, &use);
+  xfree (hexgrip);
+  hexgrip = NULL;
+  algo = ask_algo (ctrl, 1, NULL, &use, &hexgrip);
   assert (algo);
-  nbits = ask_keysize (algo, 0);
+  nbits = hexgrip? 0 : ask_keysize (algo, 0);
   expire = ask_expire_interval (0, NULL);
   if (!cpr_enabled() && !cpr_get_answer_is_yes("keygen.sub.okay",
                                                _("Really create? (y/N) ")))
@@ -3723,7 +3889,11 @@ generate_subkeypair (KBNODE keyblock)
       goto leave;
     }
 
-  err = do_create (algo, nbits, keyblock, cur_time, expire, 1, 0, NULL);
+  if (hexgrip)
+    err = do_create_from_keygrip (ctrl, algo, hexgrip,
+                                  keyblock, cur_time, expire, 1);
+  else
+    err = do_create (algo, nbits, keyblock, cur_time, expire, 1, 0, NULL);
   if (err)
     goto leave;
 
index 9548731..7088abe 100644 (file)
@@ -240,8 +240,8 @@ const char *gpg_curve_to_oid (const char *name, unsigned int *r_nbits);
 u32 parse_expire_string(const char *string);
 u32 ask_expire_interval(int object,const char *def_expire);
 u32 ask_expiredate(void);
-void generate_keypair (const char *fname, const char *card_serialno,
-                       int card_backup_key);
+void generate_keypair (ctrl_t ctrl, const char *fname,
+                       const char *card_serialno, int card_backup_key);
 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 );
@@ -253,7 +253,7 @@ int keygen_add_revkey(PKT_signature *sig, void *opaque);
 gpg_error_t make_backsig (PKT_signature *sig, PKT_public_key *pk,
                           PKT_public_key *sub_pk, PKT_public_key *sub_psk,
                           u32 timestamp, const char *cache_nonce);
-gpg_error_t generate_subkeypair (kbnode_t pub_keyblock);
+gpg_error_t generate_subkeypair (ctrl_t ctrl, kbnode_t pub_keyblock);
 #ifdef ENABLE_CARD_SUPPORT
 gpg_error_t generate_card_subkeypair (kbnode_t pub_keyblock,
                                       int keyno, const char *serialno);