Key generation and signing using the OpenPGP card does rudimentary work.
authorWerner Koch <wk@gnupg.org>
Fri, 27 Jun 2003 20:53:09 +0000 (20:53 +0000)
committerWerner Koch <wk@gnupg.org>
Fri, 27 Jun 2003 20:53:09 +0000 (20:53 +0000)
16 files changed:
NEWS
g10/ChangeLog
g10/Makefile.am
g10/build-packet.c
g10/call-agent.c
g10/call-agent.h
g10/g10.c
g10/gpgv.c
g10/keyedit.c
g10/keygen.c
g10/parse-packet.c
g10/passphrase.c
g10/seckey-cert.c
g10/seskey.c
g10/sign.c
g10/tdbio.c

diff --git a/NEWS b/NEWS
index 5c8a834..794ad11 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,8 @@
 Noteworthy changes in version 1.9.0 (unreleased)
 ------------------------------------------------
 
+ * gpg does now also use libgcrypt, libgpg-error is required.
+
  * New gpgsm commands --call-dirmngr and --call-protect-tool.
 
  * Changing a passphrase is now possible using "gpgsm --passwd"
index b4db7b5..023f66b 100644 (file)
@@ -1,3 +1,25 @@
+2003-06-27  Werner Koch  <wk@gnupg.org>
+
+       * seckey-cert.c (check_secret_key): Bypass the unprotection for
+       mode 1002.
+       * sign.c (do_sign): Handle card case (i.e. mode 1002).
+
+2003-06-26  Werner Koch  <wk@gnupg.org>
+
+       * build-packet.c (do_secret_key): Implement special protection
+       mode 1002.
+       * parse-packet.c (parse_key): Likewise.
+
+       * keygen.c (smartcard_gen_key): New.
+       * call-agent.c (agent_scd_setattr): New.
+
+2003-06-24  Werner Koch  <wk@gnupg.org>
+
+       * Makefile.am: Removed signal.c
+
+       * g10.c (emergency_cleanup): New.
+       (main): Use gnupg_init_signals and register malloc for assuan.
+
 2003-06-23  Werner Koch  <wk@gnupg.org>
 
        * keyid.c (do_fingerprint_md): Made it work again.
index 6940be6..3137bb8 100644 (file)
@@ -63,8 +63,7 @@ common_source =  \
              plaintext.c       \
              sig-check.c       \
              keylist.c         \
-             pkglue.c pkglue.h \
-             signal.c
+             pkglue.c pkglue.h 
 
 gpg_SOURCES  = g10.c           \
              $(common_source)  \
index a317752..afc1236 100644 (file)
@@ -415,8 +415,9 @@ do_secret_key( iobuf_t out, int ctb, PKT_secret_key *sk )
            if( sk->protect.s2k.mode == 3 )
                iobuf_put(a, sk->protect.s2k.count ); 
 
-            /* For out special mode 1001 we do not need an IV */
-           if( sk->protect.s2k.mode != 1001 )
+            /* For out special modes 1001 and 1002 we do not need an IV */
+           if( sk->protect.s2k.mode != 1001
+                && sk->protect.s2k.mode != 1002 )
                iobuf_write(a, sk->protect.iv, sk->protect.ivlen );
        }
     }
@@ -425,6 +426,11 @@ do_secret_key( iobuf_t out, int ctb, PKT_secret_key *sk )
 
     if( sk->protect.s2k.mode == 1001 )
         ; /* GnuPG extension - don't write a secret key at all */ 
+    else if( sk->protect.s2k.mode == 1002 )
+      {  /* GnuPG extension - divert to OpenPGP smartcard. */ 
+       iobuf_put(a, 0 ); /* length of the serial number or 0 for unknown. */
+        /* fixme: write the serial number. */
+      }
     else if( sk->is_protected && sk->version >= 4 ) {
         /* The secret key is protected - write it out as it is */
        byte *p;
index e888820..67aa333 100644 (file)
@@ -47,7 +47,7 @@
 #endif
 
 static ASSUAN_CONTEXT agent_ctx = NULL;
-static int force_pipe_server = 0;
+static int force_pipe_server = 1; /* FIXME: set this back to 0. */
 
 struct cipher_parm_s {
   ASSUAN_CONTEXT ctx;
@@ -61,11 +61,6 @@ struct genkey_parm_s {
   size_t sexplen;
 };
 
-struct learn_parm_s {
-  int error;
-  ASSUAN_CONTEXT ctx;
-  struct membuf *data;
-};
 
 \f
 /* Try to connect to the agent via socket or fork it off and work by
@@ -292,16 +287,59 @@ start_agent (void)
 }
 
 
-static AssuanError
-membuf_data_cb (void *opaque, const void *buffer, size_t length)
+/* Return a new malloced string by unescaping the string S.  Escaping
+   is percent escaping and '+'/space mapping.  A binary nul will
+   silently be replaced by a 0xFF.  Function returns NULL to indicate
+   an out of memory status. */
+static char *
+unescape_status_string (const unsigned char *s)
 {
-  membuf_t *data = opaque;
+  char *buffer, *d;
 
-  if (buffer)
-    put_membuf (data, buffer, length);
-  return 0;
+  buffer = d = xtrymalloc (strlen (s)+1);
+  if (!buffer)
+    return NULL;
+  while (*s)
+    {
+      if (*s == '%' && s[1] && s[2])
+        { 
+          s++;
+          *d = xtoi_2 (s);
+          if (!*d)
+            *d = '\xff';
+          d++;
+          s += 2;
+        }
+      else if (*s == '+')
+        {
+          *d++ = ' ';
+          s++;
+        }
+      else
+        *d++ = *s++;
+    }
+  *d = 0; 
+  return buffer;
+}
+
+/* Take a 20 byte hexencoded string and put it into the the provided
+   20 byte buffer FPR in binary format. */
+static int
+unhexify_fpr (const char *hexstr, unsigned char *fpr)
+{
+  const char *s;
+  int n;
+
+  for (s=hexstr, n=0; hexdigitp (s); s++, n++)
+    ;
+  if (*s || (n != 40))
+    return 0; /* no fingerprint (invalid or wrong length). */
+  n /= 2;
+  for (s=hexstr, n=0; *s; s += 2, n++)
+    fpr[n] = xtoi_2 (s);
+  return 1; /* okay */
 }
-  
+
 
 \f
 #if 0
@@ -391,25 +429,258 @@ agent_havekey (const char *hexkeygrip)
 }
 
 \f
-/* Ask the agent to change the passphrase of the key identified by
-   HEXKEYGRIP. */
+static AssuanError
+learn_status_cb (void *opaque, const char *line)
+{
+  struct agent_card_info_s *parm = opaque;
+  const char *keyword = line;
+  int keywordlen;
+
+  log_debug ("got status line `%s'\n", line);
+  for (keywordlen=0; *line && !spacep (line); line++, keywordlen++)
+    ;
+  while (spacep (line))
+    line++;
+
+  if (keywordlen == 9 && !memcmp (keyword, "DISP-NAME", keywordlen))
+    {
+      parm->disp_name = unescape_status_string (line);
+    }
+  else if (keywordlen == 10 && !memcmp (keyword, "PUBKEY_URL", keywordlen))
+    {
+      parm->pubkey_url = unescape_status_string (line);
+    }
+  else if (keywordlen == 7 && !memcmp (keyword, "KEY-FPR", keywordlen))
+    {
+      int no = atoi (line);
+      while (!spacep (line))
+        line++;
+      while (spacep (line))
+        line++;
+      if (no == 1)
+        parm->fpr1valid = unhexify_fpr (line, parm->fpr1);
+      else if (no == 2)
+        parm->fpr2valid = unhexify_fpr (line, parm->fpr2);
+      else if (no == 3)
+        parm->fpr3valid = unhexify_fpr (line, parm->fpr3);
+    }
+  
+  return 0;
+}
+
+/* Call the agent to learn about a smartcard */
 int
-agent_passwd (const char *hexkeygrip)
+agent_learn (struct agent_card_info_s *info)
 {
   int rc;
-  char line[ASSUAN_LINELENGTH];
 
   rc = start_agent ();
   if (rc)
     return rc;
 
-  if (!hexkeygrip || strlen (hexkeygrip) != 40)
+  memset (info, 0, sizeof *info);
+  rc = assuan_transact (agent_ctx, "LEARN --send",
+                        NULL, NULL, NULL, NULL,
+                        learn_status_cb, info);
+  
+  return map_assuan_err (rc);
+}
+
+
+\f
+/* Send an setattr command to the SCdaemon. */
+int
+agent_scd_setattr (const char *name,
+                   const unsigned char *value, size_t valuelen)
+{
+  int rc;
+  char line[ASSUAN_LINELENGTH];
+  char *p;
+
+  if (!*name || !valuelen)
     return gpg_error (GPG_ERR_INV_VALUE);
 
-  snprintf (line, DIM(line)-1, "PASSWD %s", hexkeygrip);
-  line[DIM(line)-1] = 0;
+  /* We assume that NAME does not need escaping. */
+  if (12 + strlen (name) > DIM(line)-1)
+    return gpg_error (GPG_ERR_TOO_LARGE);
+      
+  p = stpcpy (stpcpy (line, "SCD SETATTR "), name); 
+  *p++ = ' ';
+  for (; valuelen; value++, valuelen--)
+    {
+      if (p >= line + DIM(line)-5 )
+        return gpg_error (GPG_ERR_TOO_LARGE);
+      if (*value < ' ' || *value == '+' || *value == '%')
+        {
+          sprintf (p, "%%%02X", *value);
+          p += 3;
+        }
+      else if (*value == ' ')
+        *p++ = '+';
+      else
+        *p++ = *value;
+    }
+  *p = 0;
+
+  rc = start_agent ();
+  if (rc)
+    return rc;
 
   rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
   return map_assuan_err (rc);
 }
 
+\f
+/* Status callback for the SCD GENKEY command. */
+static AssuanError
+scd_genkey_cb (void *opaque, const char *line)
+{
+  struct agent_card_genkey_s *parm = opaque;
+  const char *keyword = line;
+  int keywordlen;
+  gpg_error_t rc;
+
+  log_debug ("got status line `%s'\n", line);
+  for (keywordlen=0; *line && !spacep (line); line++, keywordlen++)
+    ;
+  while (spacep (line))
+    line++;
+
+  if (keywordlen == 7 && !memcmp (keyword, "KEY-FPR", keywordlen))
+    {
+      parm->fprvalid = unhexify_fpr (line, parm->fpr);
+    }
+  if (keywordlen == 8 && !memcmp (keyword, "KEY-DATA", keywordlen))
+    {
+      gcry_mpi_t a;
+      const char *name = line;
+
+      while (!spacep (line))
+        line++;
+      while (spacep (line))
+        line++;
+
+      rc = gcry_mpi_scan (&a, GCRYMPI_FMT_HEX, line, 0);
+      if (rc)
+        log_error ("error parsing received key data: %s\n", gpg_strerror (rc));
+      else if (*name == 'n' && spacep (name+1))
+        parm->n = a;
+      else if (*name == 'e' && spacep (name+1))
+        parm->e = a;
+      else
+        {
+          log_info ("unknown parameter name in received key data\n");
+          gcry_mpi_release (a);
+        }
+    }
+  else if (keywordlen == 14 && !memcmp (keyword,"KEY-CREATED-AT", keywordlen))
+    {
+      parm->created_at = (u32)strtoul (line, NULL, 10);
+    }
+
+  return 0;
+}
+
+/* Send a GENKEY command to the SCdaemon. */
+int
+agent_scd_genkey (struct agent_card_genkey_s *info, int keyno, int force)
+{
+  int rc;
+  char line[ASSUAN_LINELENGTH];
+
+  rc = start_agent ();
+  if (rc)
+    return rc;
+
+  memset (info, 0, sizeof *info);
+  snprintf (line, DIM(line)-1, "SCD GENKEY %s%d",
+            force? "--force ":"", keyno);
+  line[DIM(line)-1] = 0;
+
+  memset (info, 0, sizeof *info);
+  rc = assuan_transact (agent_ctx, line,
+                        NULL, NULL, NULL, NULL,
+                        scd_genkey_cb, info);
+  
+  return map_assuan_err (rc);
+}
+
+\f
+static AssuanError
+membuf_data_cb (void *opaque, const void *buffer, size_t length)
+{
+  membuf_t *data = opaque;
+
+  if (buffer)
+    put_membuf (data, buffer, length);
+  return 0;
+}
+  
+/* Send a sign command to the scdaemon via gpg-agent's pass thru
+   mechanism. */
+int
+agent_scd_pksign (const char *keyid, int hashalgo,
+                  const unsigned char *indata, size_t indatalen,
+                  char **r_buf, size_t *r_buflen)
+{
+  int rc, i;
+  char *p, line[ASSUAN_LINELENGTH];
+  membuf_t data;
+  size_t len;
+
+  /* Note, hashalgo is not yet used but hardwired to SHA1 in SCdaemon. */
+
+  *r_buf = NULL;
+  *r_buflen = 0;
+
+  rc = start_agent ();
+  if (rc)
+    return rc;
+
+  if (indatalen*2 + 50 > DIM(line))
+    return gpg_error (GPG_ERR_GENERAL);
+
+  sprintf (line, "SCD SETDATA ");
+  p = line + strlen (line);
+  for (i=0; i < indatalen ; i++, p += 2 )
+    sprintf (p, "%02X", indata[i]);
+  rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
+  if (rc)
+    return rc;
+
+  init_membuf (&data, 1024);
+  snprintf (line, DIM(line)-1, "SCD PKSIGN %s", keyid);
+  line[DIM(line)-1] = 0;
+  rc = assuan_transact (agent_ctx, line, membuf_data_cb, &data,
+                        NULL, NULL, NULL, NULL);
+  if (rc)
+    {
+      xfree (get_membuf (&data, &len));
+      return rc;
+    }
+  *r_buf = get_membuf (&data, r_buflen);
+
+  return 0;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
index fbb1195..c620268 100644 (file)
 #ifndef GNUPG_G10_CALL_AGENT_H
 #define GNUPG_G10_CALL_AGENT_H 
 
+
+struct agent_card_info_s {
+  int error;         /* private. */
+  char *disp_name;   /* malloced. */
+  char *pubkey_url;  /* malloced. */
+  char fpr1valid;
+  char fpr2valid;
+  char fpr3valid;
+  char fpr1[20];
+  char fpr2[20];
+  char fpr3[20];
+};
+
+struct agent_card_genkey_s {
+  char fprvalid;
+  char fpr[20];
+  u32  created_at;
+  gcry_mpi_t n;
+  gcry_mpi_t e;
+};
+
+/* Return card info. */
+int agent_learn (struct agent_card_info_s *info);
+
 /* Check whether the secret key for the key identified by HEXKEYGRIP
    is available.  Return 0 for yes or an error code. */
 int agent_havekey (const char *hexkeygrip);
 
+/* Send a SETATTR command to the SCdaemon. */
+int agent_scd_setattr (const char *name,
+                       const unsigned char *value, size_t valuelen);
+
+/* Send a GENKEY command to the SCdaemon. */
+int agent_scd_genkey (struct agent_card_genkey_s *info, int keyno, int force);
+
+/* Send a PKSIGN command to the SCdaemon. */
+int agent_scd_pksign (const char *keyid, int hashalgo,
+                      const unsigned char *indata, size_t indatalen,
+                      char **r_buf, size_t *r_buflen);
+
 /* Ask the agent to let the user change the passphrase of the secret
    key identified by HEXKEYGRIP. */
 int agent_passwd (const char *hexkeygrip);
@@ -31,4 +67,7 @@ int agent_passwd (const char *hexkeygrip);
 
 
 
+
+
 #endif /*GNUPG_G10_CALL_AGENT_H*/
+
index cf6240d..6909e04 100644 (file)
--- a/g10/g10.c
+++ b/g10/g10.c
@@ -639,6 +639,7 @@ static void set_cmd( enum cmd_and_opt_values *ret_cmd,
 static void print_mds( const char *fname, int algo );
 static void add_notation_data( const char *string, int which );
 static void add_policy_url( const char *string, int which );
+static void emergency_cleanup (void);
 
 #ifdef __riscos__
 RISCOS_GLOBAL_STATICS("GnuPG Heap")
@@ -1191,7 +1192,7 @@ main( int argc, char **argv )
     gcry_control (GCRYCTL_USE_SECURE_RNDPOOL);
 
     may_coredump = disable_core_dumps();
-    init_signals (); /* why not gnupg_init_signals. */
+    gnupg_init_signals (0, emergency_cleanup);
     create_dotlock (NULL); /* register locking cleanup */
     i18n_init();
 
@@ -1303,7 +1304,8 @@ main( int argc, char **argv )
     maybe_setuid = 0;
     /* Okay, we are now working under our real uid */
 
-    /* malloc hooks gohere ... */
+    /* malloc hooks go here ... */
+    assuan_set_malloc_hooks (gcry_malloc, gcry_realloc, gcry_free);
 
     set_native_charset (NULL); /* Try to auto set the character set */
 
@@ -2894,6 +2896,13 @@ main( int argc, char **argv )
     return 8; /*NEVER REACHED*/
 }
 
+/* Note: This function is used by signal handlers!. */
+static void
+emergency_cleanup (void)
+{
+  gcry_control (GCRYCTL_TERM_SECMEM );
+}
+
 
 void
 g10_exit( int rc )
index 91574a7..da108d2 100644 (file)
@@ -147,7 +147,7 @@ main( int argc, char **argv )
 
     set_strusage (my_strusage);
     log_set_prefix ("gpgv", 1);
-    init_signals();
+    gnupg_init_signals(0, NULL);
     i18n_init();
     opt.command_fd = -1; /* no command fd */
     opt.pgp2_workarounds = 1;
index 38248a3..11e2d87 100644 (file)
@@ -921,6 +921,10 @@ change_passphrase( KBNODE keyblock )
            tty_printf(_("Secret parts of primary key are not available.\n"));
            no_primary_secrets = 1;
        }
+       else if( sk->protect.s2k.mode == 1002 ) {
+           tty_printf(_("Secret key is actually stored on a card.\n"));
+           no_primary_secrets = 1;
+       }
        else {
            tty_printf(_("Key is protected.\n"));
            rc = check_secret_key( sk, 0 );
index 041a495..328647f 100644 (file)
@@ -38,6 +38,8 @@
 #include "trustdb.h"
 #include "status.h"
 #include "i18n.h"
+#include "call-agent.h"
+
 
 #define MAX_PREFS 30 
 
@@ -111,8 +113,11 @@ static int nzip_prefs;
 static int mdc_available,ks_modify;
 
 static void do_generate_keypair( struct para_data_s *para,
-                                struct output_control_s *outctrl );
-static int  write_keyblock( iobuf_t out, KBNODE node );
+                                struct output_control_s *outctrl, int card);
+static int write_keyblock( iobuf_t out, KBNODE node );
+static int check_smartcard (void);
+static int gen_card_key (int algo, int keyno, KBNODE pub_root, KBNODE sec_root, u32 expireval);
+
 
 
 static void
@@ -1752,7 +1757,7 @@ get_parameter_revkey( struct para_data_s *para, enum para_name key )
 
 static int
 proc_parameter_file( struct para_data_s *para, const char *fname,
-                     struct output_control_s *outctrl )
+                     struct output_control_s *outctrl, int card )
 {
     struct para_data_s *r;
     const char *s1, *s2, *s3;
@@ -1814,8 +1819,8 @@ proc_parameter_file( struct para_data_s *para, const char *fname,
     r = get_parameter( para, pPASSPHRASE );
     if( r && *r->u.value ) {
        /* we have a plain text passphrase - create a DEK from it.
-        * It is a little bit ridiculous to keep it ih secure memory
-        * but becuase we do this alwasy, why not here */
+        * It is a little bit ridiculous to keep it in secure memory
+        * but because we do this always, why not here. */
        STRING2KEY *s2k;
        DEK *dek;
 
@@ -1864,7 +1869,7 @@ proc_parameter_file( struct para_data_s *para, const char *fname,
        return -1;
     }
 
-    do_generate_keypair( para, outctrl );
+    do_generate_keypair( para, outctrl, card);
     return 0;
 }
 
@@ -1948,7 +1953,7 @@ read_parameter_file( const char *fname )
                outctrl.dryrun = 1;
            else if( !ascii_strcasecmp( keyword, "%commit" ) ) {
                outctrl.lnr = lnr;
-               proc_parameter_file( para, fname, &outctrl );
+               proc_parameter_file( para, fname, &outctrl, 0 );
                release_parameter_list( para );
                para = NULL;
            }
@@ -2008,7 +2013,7 @@ read_parameter_file( const char *fname )
 
        if( keywords[i].key == pKEYTYPE && para ) {
            outctrl.lnr = lnr;
-           proc_parameter_file( para, fname, &outctrl );
+           proc_parameter_file( para, fname, &outctrl, 0 );
            release_parameter_list( para );
            para = NULL;
        }
@@ -2036,7 +2041,7 @@ read_parameter_file( const char *fname )
     }
     else if( para ) {
        outctrl.lnr = lnr;
-       proc_parameter_file( para, fname, &outctrl );
+       proc_parameter_file( para, fname, &outctrl, 0 );
     }
 
     if( outctrl.use_files ) { /* close open streams */
@@ -2061,113 +2066,139 @@ read_parameter_file( const char *fname )
 void
 generate_keypair( const char *fname )
 {
-    unsigned int nbits;
-    char *uid = NULL;
-    DEK *dek;
-    STRING2KEY *s2k;
-    int algo;
-    unsigned int use;
-    int both = 0;
-    u32 expire;
-    struct para_data_s *para = NULL;
-    struct para_data_s *r;
-    struct output_control_s outctrl;
-
-    memset( &outctrl, 0, sizeof( outctrl ) );
-
-    if( opt.batch ) {
-       read_parameter_file( fname );
-       return;
+  unsigned int nbits;
+  char *uid = NULL;
+  DEK *dek;
+  STRING2KEY *s2k;
+  int algo;
+  unsigned int use;
+  int both = 0;
+  int card = 0;
+  u32 expire;
+  struct para_data_s *para = NULL;
+  struct para_data_s *r;
+  struct output_control_s outctrl;
+
+  memset (&outctrl, 0, sizeof (outctrl));
+
+  if (opt.batch)
+    {
+      read_parameter_file( fname );
+      return;
     }
 
-    algo = ask_algo( 0, &use );
-    if( !algo ) { /* default: DSA with ElG subkey of the specified size */
-       both = 1;
-       r = xcalloc (1, 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 = xcalloc (1, sizeof *r + 20 );
-       r->key = pKEYLENGTH;
-       strcpy( r->u.value, "1024" );
-       r->next = para;
-       para = r;
-
-       algo = PUBKEY_ALGO_ELGAMAL_E;
-       r = xcalloc (1, sizeof *r + 20 );
-       r->key = pSUBKEYTYPE;
-       sprintf( r->u.value, "%d", algo );
-       r->next = para;
-       para = r;
+  do
+    {
+      card = check_smartcard ();
+      if (card < 0)
+        return;
+      if (card > 1)
+        log_error (_("can't generate subkey here\n"));
     }
-    else {
-       r = xcalloc (1, sizeof *r + 20 );
-       r->key = pKEYTYPE;
-       sprintf( r->u.value, "%d", algo );
-       r->next = para;
-       para = r;
+  while (card > 1);
 
-        if (use) {
-            r = xcalloc (1, 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 (card)
+    {
+      algo = PUBKEY_ALGO_RSA;
+      use = PUBKEY_USAGE_SIG;
+    }
+  else
+    algo = ask_algo (0, &use);
+
+  if (!algo)
+    { /* default: DSA with ElG subkey of the specified size */
+      both = 1;
+      r = xcalloc (1, 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 = xcalloc (1, sizeof *r + 20 );
+      r->key = pKEYLENGTH;
+      strcpy( r->u.value, "1024" );
+      r->next = para;
+      para = r;
+      
+      algo = PUBKEY_ALGO_ELGAMAL_E;
+      r = xcalloc (1, sizeof *r + 20 );
+      r->key = pSUBKEYTYPE;
+      sprintf( r->u.value, "%d", algo );
+      r->next = para;
+      para = r;
+    }
+  else 
+    {
+      r = xcalloc (1, sizeof *r + 20 );
+      r->key = pKEYTYPE;
+      sprintf( r->u.value, "%d", algo );
+      r->next = para;
+      para = r;
+      
+      if (use)
+        {
+          r = xcalloc (1, 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 = xcalloc (1, 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 = xcalloc (1, sizeof *r + 20 );
-    r->key = pKEYEXPIRE;
-    r->u.expire = expire;
-    r->next = para;
-    para = r;
-    r = xcalloc (1, 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 = xcalloc (1, sizeof *r + strlen(uid) );
-    r->key = pUSERID;
-    strcpy( r->u.value, uid );
-    r->next = para;
-    para = r;
-
-    dek = ask_passphrase( &s2k );
-    if( dek ) {
-       r = xcalloc (1, sizeof *r );
-       r->key = pPASSPHRASE_DEK;
-       r->u.dek = dek;
-       r->next = para;
-       para = r;
-       r = xcalloc (1, sizeof *r );
-       r->key = pPASSPHRASE_S2K;
-       r->u.s2k = s2k;
-       r->next = para;
-       para = r;
+  if (!card)
+    {
+      nbits = ask_keysize( algo );
+      r = xcalloc (1, 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 = xcalloc (1, sizeof *r + 20 );
+  r->key = pKEYEXPIRE;
+  r->u.expire = expire;
+  r->next = para;
+  para = r;
+  r = xcalloc (1, 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 = xcalloc (1, sizeof *r + strlen(uid) );
+  r->key = pUSERID;
+  strcpy( r->u.value, uid );
+  r->next = para;
+  para = r;
 
-    proc_parameter_file( para, "[internal]", &outctrl );
-    release_parameter_list( para );
+  dek = card? NULL : ask_passphrase( &s2k );
+  if (dek)
+    {
+      r = xcalloc (1, sizeof *r );
+      r->key = pPASSPHRASE_DEK;
+      r->u.dek = dek;
+      r->next = para;
+      para = r;
+      r = xcalloc (1, sizeof *r );
+      r->key = pPASSPHRASE_S2K;
+      r->u.s2k = s2k;
+      r->next = para;
+      para = r;
+    }
+  
+  proc_parameter_file (para, "[internal]", &outctrl, card);
+  release_parameter_list (para);
 }
 
 
@@ -2189,225 +2220,271 @@ print_status_key_created (int letter, PKT_public_key *pk)
   write_status_text (STATUS_KEY_CREATED, buf);
 }
 
+
+
 static void
-do_generate_keypairstruct para_data_s *para,
-                    struct output_control_s *outctrl )
+do_generate_keypair (struct para_data_s *para,
+                    struct output_control_s *outctrl, int card)
 {
-    KBNODE pub_root = NULL;
-    KBNODE sec_root = NULL;
-    PKT_secret_key *sk = NULL;
-    const char *s;
-    struct revocation_key *revkey;
-    int rc;
-    int did_sub = 0;
-
-    if( outctrl->dryrun ) {
-       log_info("dry-run mode - key generation skipped\n");
-       return;
+  KBNODE pub_root = NULL;
+  KBNODE sec_root = NULL;
+  PKT_secret_key *sk = NULL;
+  const char *s;
+  struct revocation_key *revkey;
+  int rc;
+  int did_sub = 0;
+
+  if (outctrl->dryrun)
+    {
+      log_info ("dry-run mode - key generation skipped\n");
+      return;
     }
 
 
-    if( outctrl->use_files ) {
-       if( outctrl->pub.newfname ) {
-           iobuf_close(outctrl->pub.stream);
-           outctrl->pub.stream = NULL;
-           xfree ( outctrl->pub.fname );
-           outctrl->pub.fname =  outctrl->pub.newfname;
-           outctrl->pub.newfname = NULL;
-
-           outctrl->pub.stream = iobuf_create( outctrl->pub.fname );
-           if( !outctrl->pub.stream ) {
-               log_error("can't create `%s': %s\n", outctrl->pub.newfname,
-                                                    strerror(errno) );
-               return;
+  if (outctrl->use_files)
+    {
+      if (outctrl->pub.newfname)
+       {
+         iobuf_close (outctrl->pub.stream);
+         outctrl->pub.stream = NULL;
+         xfree (outctrl->pub.fname);
+         outctrl->pub.fname = outctrl->pub.newfname;
+         outctrl->pub.newfname = NULL;
+
+         outctrl->pub.stream = iobuf_create (outctrl->pub.fname);
+         if (!outctrl->pub.stream)
+           {
+             log_error ("can't create `%s': %s\n", outctrl->pub.newfname,
+                        strerror (errno));
+             return;
            }
-           if( opt.armor ) {
-               outctrl->pub.afx.what = 1;
-               iobuf_push_filter( outctrl->pub.stream, armor_filter,
-                                                   &outctrl->pub.afx );
+         if (opt.armor)
+           {
+             outctrl->pub.afx.what = 1;
+             iobuf_push_filter (outctrl->pub.stream, armor_filter,
+                                &outctrl->pub.afx);
            }
        }
-       if( outctrl->sec.newfname ) {
-           iobuf_close(outctrl->sec.stream);
-           outctrl->sec.stream = NULL;
-           xfree ( outctrl->sec.fname );
-           outctrl->sec.fname =  outctrl->sec.newfname;
-           outctrl->sec.newfname = NULL;
-
-           outctrl->sec.stream = iobuf_create( outctrl->sec.fname );
-           if( !outctrl->sec.stream ) {
-               log_error("can't create `%s': %s\n", outctrl->sec.newfname,
-                                                    strerror(errno) );
-               return;
+      if (outctrl->sec.newfname)
+       {
+         iobuf_close (outctrl->sec.stream);
+         outctrl->sec.stream = NULL;
+         xfree (outctrl->sec.fname);
+         outctrl->sec.fname = outctrl->sec.newfname;
+         outctrl->sec.newfname = NULL;
+
+         outctrl->sec.stream = iobuf_create (outctrl->sec.fname);
+         if (!outctrl->sec.stream)
+           {
+             log_error ("can't create `%s': %s\n", outctrl->sec.newfname,
+                        strerror (errno));
+             return;
            }
-           if( opt.armor ) {
-               outctrl->sec.afx.what = 5;
-               iobuf_push_filter( outctrl->sec.stream, armor_filter,
-                                                   &outctrl->sec.afx );
+         if (opt.armor)
+           {
+             outctrl->sec.afx.what = 5;
+             iobuf_push_filter (outctrl->sec.stream, armor_filter,
+                                &outctrl->sec.afx);
            }
        }
-       assert( outctrl->pub.stream );
-       assert( outctrl->sec.stream );
-        if( opt.verbose ) {
-            log_info(_("writing public key to `%s'\n"), outctrl->pub.fname );
-            log_info(_("writing secret key to `%s'\n"), outctrl->sec.fname );
-        }
+      assert (outctrl->pub.stream);
+      assert (outctrl->sec.stream);
+      if (opt.verbose)
+       {
+         log_info (_("writing public key to `%s'\n"), outctrl->pub.fname);
+          if (card)
+            log_info (_("writing secret key stub to `%s'\n"),
+                      outctrl->sec.fname);
+          else
+            log_info (_("writing secret key to `%s'\n"), outctrl->sec.fname);
+       }
     }
 
 
-    /* we create the packets as a tree of kbnodes. Because the structure
-     * we create is known in advance we simply generate a linked list
-     * The first packet is a dummy comment packet which we flag
-     * as deleted.  The very first packet must always be a KEY packet.
-     */
-    pub_root = make_comment_node("#"); delete_kbnode(pub_root);
-    sec_root = make_comment_node("#"); delete_kbnode(sec_root);
-
-    rc = do_create( get_parameter_algo( para, pKEYTYPE ),
-                   get_parameter_uint( para, pKEYLENGTH ),
-                   pub_root, sec_root,
-                   get_parameter_dek( para, pPASSPHRASE_DEK ),
-                   get_parameter_s2k( para, pPASSPHRASE_S2K ),
-                   &sk,
-                   get_parameter_u32( para, pKEYEXPIRE ) );
-
-    if(!rc && (revkey=get_parameter_revkey(para,pREVOKER)))
-      {
-       rc=write_direct_sig(pub_root,pub_root,sk,revkey);
-       if(!rc)
-         write_direct_sig(sec_root,pub_root,sk,revkey);
-      }
-
-    if( !rc && (s=get_parameter_value(para, pUSERID)) ) {
-       write_uid(pub_root, s );
-       if( !rc )
-           write_uid(sec_root, s );
-       if( !rc )
-           rc = write_selfsig(pub_root, pub_root, sk,
-                               get_parameter_uint (para, pKEYUSAGE));
-       if( !rc )
-           rc = write_selfsig(sec_root, pub_root, sk,
-                               get_parameter_uint (para, pKEYUSAGE));
-    }
-
-    if( get_parameter( para, pSUBKEYTYPE ) ) {
-       rc = do_create( get_parameter_algo( para, pSUBKEYTYPE ),
-                       get_parameter_uint( para, pSUBKEYLENGTH ),
-                       pub_root, sec_root,
-                       get_parameter_dek( para, pPASSPHRASE_DEK ),
-                       get_parameter_s2k( para, pPASSPHRASE_S2K ),
-                       NULL,
-                       get_parameter_u32( para, pSUBKEYEXPIRE ) );
-       if( !rc )
-           rc = write_keybinding(pub_root, pub_root, sk,
-                                         get_parameter_uint (para, pSUBKEYUSAGE));
-       if( !rc )
-           rc = write_keybinding(sec_root, pub_root, sk,
-                                         get_parameter_uint (para, pSUBKEYUSAGE));
-        did_sub = 1;
+  /* We create the packets as a tree of kbnodes. Because the structure
+   * we create is known in advance we simply generate a linked list.
+   * The first packet is a dummy comment packet which we flag
+   * as deleted.  The very first packet must always be a KEY packet.
+   */
+  pub_root = make_comment_node ("#");
+  delete_kbnode (pub_root);
+  sec_root = make_comment_node ("#");
+  delete_kbnode (sec_root);
+  if (!card)
+    {
+      rc = do_create (get_parameter_algo (para, pKEYTYPE),
+                      get_parameter_uint (para, pKEYLENGTH),
+                      pub_root, sec_root,
+                      get_parameter_dek (para, pPASSPHRASE_DEK),
+                      get_parameter_s2k (para, pPASSPHRASE_S2K),
+                      &sk, get_parameter_u32 (para, pKEYEXPIRE));
+    }
+  else
+    {
+      rc = gen_card_key (PUBKEY_ALGO_RSA, 1, pub_root, sec_root,
+                         get_parameter_u32 (para, pKEYEXPIRE));
+      if (!rc)
+        {
+          sk = sec_root->next->pkt->pkt.secret_key;
+          assert (sk);
+        }
+      
     }
 
+  if (!rc && (revkey = get_parameter_revkey (para, pREVOKER)))
+    {
+      rc = write_direct_sig (pub_root, pub_root, sk, revkey);
+      if (!rc)
+       write_direct_sig (sec_root, pub_root, sk, revkey);
+    }
 
-    if( !rc && outctrl->use_files ) { /* direct write to specified files */
-       rc = write_keyblock( outctrl->pub.stream, pub_root );
-       if( rc )
-           log_error("can't write public key: %s\n", gpg_strerror (rc) );
-       if( !rc ) {
-           rc = write_keyblock( outctrl->sec.stream, sec_root );
-           if( rc )
-               log_error("can't write secret key: %s\n", gpg_strerror (rc) );
+  if (!rc && (s = get_parameter_value (para, pUSERID)))
+    {
+      write_uid (pub_root, s);
+      if (!rc)
+       write_uid (sec_root, s);
+      if (!rc)
+       rc = write_selfsig (pub_root, pub_root, sk,
+                           get_parameter_uint (para, pKEYUSAGE));
+      if (!rc)
+       rc = write_selfsig (sec_root, pub_root, sk,
+                           get_parameter_uint (para, pKEYUSAGE));
+    }
+
+  if (get_parameter (para, pSUBKEYTYPE))
+    {
+      rc = do_create (get_parameter_algo (para, pSUBKEYTYPE),
+                     get_parameter_uint (para, pSUBKEYLENGTH),
+                     pub_root, sec_root,
+                     get_parameter_dek (para, pPASSPHRASE_DEK),
+                     get_parameter_s2k (para, pPASSPHRASE_S2K),
+                     NULL, get_parameter_u32 (para, pSUBKEYEXPIRE));
+      if (!rc)
+       rc = write_keybinding (pub_root, pub_root, sk,
+                              get_parameter_uint (para, pSUBKEYUSAGE));
+      if (!rc)
+       rc = write_keybinding (sec_root, pub_root, sk,
+                              get_parameter_uint (para, pSUBKEYUSAGE));
+      did_sub = 1;
+    }
+
+
+  if (!rc && outctrl->use_files)
+    {                          /* direct write to specified files */
+      rc = write_keyblock (outctrl->pub.stream, pub_root);
+      if (rc)
+       log_error ("can't write public key: %s\n", gpg_strerror (rc));
+      if (!rc)
+       {
+         rc = write_keyblock (outctrl->sec.stream, sec_root);
+         if (rc)
+           log_error ("can't write secret key: %s\n", gpg_strerror (rc));
        }
 
     }
-    else if( !rc ) { /* write to the standard keyrings */
-       KEYDB_HANDLE pub_hd = keydb_new (0);
-       KEYDB_HANDLE sec_hd = keydb_new (1);
+  else if (!rc)
+    {                          /* write to the standard keyrings */
+      KEYDB_HANDLE pub_hd = keydb_new (0);
+      KEYDB_HANDLE sec_hd = keydb_new (1);
 
-        /* FIXME: we may have to create the keyring first */
-        rc = keydb_locate_writable (pub_hd, NULL);
-        if (rc) 
-           log_error (_("no writable public keyring found: %s\n"),
-                       gpg_strerror (rc));
+      /* FIXME: we may have to create the keyring first */
+      rc = keydb_locate_writable (pub_hd, NULL);
+      if (rc)
+       log_error (_("no writable public keyring found: %s\n"),
+                  gpg_strerror (rc));
 
-        if (!rc) {  
-            rc = keydb_locate_writable (sec_hd, NULL);
-            if (rc) 
-                log_error (_("no writable secret keyring found: %s\n"),
-                           gpg_strerror (rc));
-        }
+      if (!rc)
+       {
+         rc = keydb_locate_writable (sec_hd, NULL);
+         if (rc)
+           log_error (_("no writable secret keyring found: %s\n"),
+                      gpg_strerror (rc));
+       }
 
-        if (!rc && opt.verbose) {
-            log_info(_("writing public key to `%s'\n"),
-                     keydb_get_resource_name (pub_hd));
-            log_info(_("writing secret key to `%s'\n"),
-                     keydb_get_resource_name (sec_hd));
-        }
+      if (!rc && opt.verbose)
+       {
+         log_info (_("writing public key to `%s'\n"),
+                   keydb_get_resource_name (pub_hd));
+          if (card)
+            log_info (_("writing secret key stub to `%s'\n"),
+                      keydb_get_resource_name (sec_hd));
+          else
+            log_info (_("writing secret key to `%s'\n"),
+                      keydb_get_resource_name (sec_hd));
+       }
 
-        if (!rc) {
-           rc = keydb_insert_keyblock (pub_hd, pub_root);
-            if (rc)
-                log_error (_("error writing public keyring `%s': %s\n"),
-                           keydb_get_resource_name (pub_hd), gpg_strerror (rc));
-        }
+      if (!rc)
+       {
+         rc = keydb_insert_keyblock (pub_hd, pub_root);
+         if (rc)
+           log_error (_("error writing public keyring `%s': %s\n"),
+                      keydb_get_resource_name (pub_hd), gpg_strerror (rc));
+       }
 
-        if (!rc) {
-           rc = keydb_insert_keyblock (sec_hd, sec_root);
-            if (rc)
-                log_error (_("error writing secret keyring `%s': %s\n"),
-                           keydb_get_resource_name (pub_hd), gpg_strerror (rc));
-        }
+      if (!rc)
+       {
+         rc = keydb_insert_keyblock (sec_hd, sec_root);
+         if (rc)
+           log_error (_("error writing secret keyring `%s': %s\n"),
+                      keydb_get_resource_name (pub_hd), gpg_strerror (rc));
+       }
+
+      keydb_release (pub_hd);
+      keydb_release (sec_hd);
+
+      if (!rc)
+       {
+         int no_enc_rsa =
+           get_parameter_algo (para, pKEYTYPE) == PUBKEY_ALGO_RSA
+           && get_parameter_uint (para, pKEYUSAGE)
+           && !(get_parameter_uint (para, pKEYUSAGE) & PUBKEY_USAGE_ENC);
+         PKT_public_key *pk = find_kbnode (pub_root,
+                                           PKT_PUBLIC_KEY)->pkt->pkt.
+           public_key;
+
+         update_ownertrust (pk,
+                            ((get_ownertrust (pk) & ~TRUST_MASK)
+                             | TRUST_ULTIMATE));
+
+         if (!opt.batch)
+           {
+             tty_printf (_("public and secret key created and signed.\n"));
+             tty_printf (_("key marked as ultimately trusted.\n"));
+             tty_printf ("\n");
+             list_keyblock (pub_root, 0, 1, NULL);
+           }
 
-        keydb_release (pub_hd);
-        keydb_release (sec_hd);
-
-       if (!rc) {
-            int no_enc_rsa =
-                get_parameter_algo(para, pKEYTYPE) == PUBKEY_ALGO_RSA
-                && get_parameter_uint( para, pKEYUSAGE )
-                && !(get_parameter_uint( para,pKEYUSAGE) & PUBKEY_USAGE_ENC);
-            PKT_public_key *pk = find_kbnode (pub_root, 
-                                    PKT_PUBLIC_KEY)->pkt->pkt.public_key;
-            
-            update_ownertrust (pk,
-                               ((get_ownertrust (pk) & ~TRUST_MASK)
-                                | TRUST_ULTIMATE ));
-
-           if (!opt.batch) {
-                tty_printf(_("public and secret key created and signed.\n") );
-                tty_printf(_("key marked as ultimately trusted.\n") );
-               tty_printf("\n");
-               list_keyblock(pub_root,0,1,NULL);
-            }
-            
 
-           if( !opt.batch
-               && ( get_parameter_algo( para, pKEYTYPE ) == PUBKEY_ALGO_DSA
-                     || no_enc_rsa )
-               && !get_parameter( para, pSUBKEYTYPE ) )
+         if (!opt.batch
+             && (get_parameter_algo (para, pKEYTYPE) == PUBKEY_ALGO_DSA
+                 || no_enc_rsa) && !get_parameter (para, pSUBKEYTYPE))
            {
-               tty_printf(_("Note that this key cannot be used for "
-                            "encryption.  You may want to use\n"
-                            "the command \"--edit-key\" to generate a "
-                            "secondary key for this purpose.\n") );
+             tty_printf (_("Note that this key cannot be used for "
+                           "encryption.  You may want to use\n"
+                           "the command \"--edit-key\" to generate a "
+                           "secondary key for this purpose.\n"));
            }
        }
     }
 
-    if( rc ) {
-       if( opt.batch )
-           log_error("key generation failed: %s\n", gpg_strerror (rc) );
-       else
-           tty_printf(_("Key generation failed: %s\n"), gpg_strerror (rc) );
+  if (rc)
+    {
+      if (opt.batch)
+       log_error ("key generation failed: %s\n", gpg_strerror (rc));
+      else
+       tty_printf (_("Key generation failed: %s\n"), gpg_strerror (rc));
     }
-    else {
-        PKT_public_key *pk = find_kbnode (pub_root, 
-                                    PKT_PUBLIC_KEY)->pkt->pkt.public_key;
-        print_status_key_created (did_sub? 'B':'P', pk);
+  else
+    {
+      PKT_public_key *pk = find_kbnode (pub_root,
+                                       PKT_PUBLIC_KEY)->pkt->pkt.public_key;
+      print_status_key_created (did_sub ? 'B' : 'P', pk);
     }
-    release_kbnode( pub_root );
-    release_kbnode( sec_root );
-    if( sk ) /* the unprotected  secret key */
-       free_secret_key(sk);
+
+  release_kbnode (pub_root);
+  release_kbnode (sec_root);
+  if (sk && !card)         /* The unprotected secret key unless we have */
+    free_secret_key (sk);  /* shallow copy in card mode. */
 }
 
 
@@ -2534,3 +2611,268 @@ write_keyblock( iobuf_t out, KBNODE node )
     }
     return 0;
 }
+
+\f
+static void
+show_sha1_fpr (const unsigned char *fpr)
+{
+  int i;
+
+  if (fpr)
+    {
+      for (i=0; i < 20 ; i+=2, fpr += 2 )
+        {
+          if (i == 10 )
+            tty_printf (" ");
+          tty_printf (" %02X%02X", *fpr, fpr[1]);
+        }
+    }
+  else
+    tty_printf ("[none]");
+  tty_printf ("\n");
+}
+
+static void
+show_smartcard (struct agent_card_info_s *info)
+{
+  /* FIXME: Sanitize what we show. */
+  tty_printf ("Name of cardholder: %s\n",
+              info->disp_name && *info->disp_name? info->disp_name 
+                                                 : "[not set]");
+  tty_printf ("URL of public key : %s\n",
+              info->pubkey_url && *info->pubkey_url? info->pubkey_url 
+                                                 : "[not set]");
+  tty_printf ("Signature key ....: ");
+  show_sha1_fpr (info->fpr1valid? info->fpr1:NULL);
+  tty_printf ("Encryption key....: ");
+  show_sha1_fpr (info->fpr2valid? info->fpr2:NULL);
+  tty_printf ("Authentication key: ");
+  show_sha1_fpr (info->fpr3valid? info->fpr3:NULL);
+}
+
+
+static char *
+smartcard_get_one_name (const char *prompt1, const char *prompt2)
+{
+  char *name;
+  int i;
+
+  for (;;)
+    {
+      name = cpr_get (prompt1, prompt2);
+      if (!name)
+        return NULL;
+      trim_spaces (name);
+      cpr_kill_prompt ();
+      for (i=0; name[i] && name[i] >= ' ' && name[i] <= 126; i++)
+        ;
+
+      /* The name must be in Latin-1 and not UTF-8 - lacking the code
+         to ensure this we restrict it to ASCII. */
+      if (name[i])
+        tty_printf (_("Error: Only plain ASCII is currently allowed.\n"));
+      else if (strchr (name, '<'))
+        tty_printf (_("Error: The \"<\" character may not be used.\n"));
+      else if (strstr (name, "  "))
+        tty_printf (_("Error: Double spaces are not allowed.\n"));    
+      else
+        return name;
+      xfree (name);
+    }
+}
+
+static int
+smartcard_change_name (const char *current_name)
+{
+  char *surname = NULL, *givenname = NULL;
+  char *isoname, *p;
+  int rc;
+
+  surname = smartcard_get_one_name ("keygen.smartcard.surname",
+                                    _("Cardholder's surname: "));
+  givenname = smartcard_get_one_name ("keygen.smartcard.givenname",
+                                       _("Cardholder's given name: "));
+  if (!surname || !givenname || (!*surname && !*givenname))
+    {
+      xfree (surname);
+      xfree (givenname);
+      return -1; /*canceled*/
+    }
+
+  isoname = xmalloc ( strlen (surname) + 2 + strlen (givenname) + 1);
+  strcpy (stpcpy (stpcpy (isoname, surname), "<<"), givenname);
+  xfree (surname);
+  xfree (givenname);
+  for (p=isoname; *p; p++)
+    if (*p == ' ')
+      *p = '<';
+
+  log_debug ("setting Name to `%s'\n", isoname);
+  rc = agent_scd_setattr ("DISP-NAME", isoname, strlen (isoname) );
+  if (rc)
+    log_error ("error setting Name: %s\n", gpg_strerror (rc));
+
+  return rc;
+}
+
+
+/* Check whether a smartcatrd is available and alow to select it as
+   the target for key generation. 
+   
+   Return values: -1 = Quit generation
+                   0 = No smartcard
+                   1 = Generate primary key
+                   2 = generate subkey
+*/
+static int
+check_smartcard (void)
+{
+  struct agent_card_info_s info;
+  int rc;
+
+  rc = agent_learn (&info);
+  if (rc)
+    {
+      tty_printf (_("OpenPGP card not available: %s\n"),
+                  gpg_strerror (rc));
+      return 0;
+    }
+  
+  tty_printf (_("OpenPGP card with serial number %s detected\n"), "xxx");
+
+
+  for (;;)
+    {
+      char *answer;
+      int reread = 0;
+
+      tty_printf ("\n");
+      show_smartcard (&info);
+
+      tty_printf ("\n"
+                  "N - change cardholder name\n"
+                  "U - change public key URL\n"
+                  "1 - generate signature key\n"
+                  "2 - generate encryption key\n"
+                  "3 - generate authentication key\n"
+                  "Q - quit\n"
+                  "\n");
+
+      answer = cpr_get("keygen.smartcard.menu",_("Your selection? "));
+      cpr_kill_prompt();
+      if (strlen (answer) != 1)
+        continue;
+
+      rc = 0;
+      if ( *answer == 'N' || *answer == 'n')
+        {
+          if (!smartcard_change_name (info.disp_name))
+            reread = 1;
+        }
+      else if ( *answer == 'U' || *answer == 'u')
+        {
+        }
+      else if ( *answer == '1' || *answer == '2')
+        {
+          rc = *answer - '0';
+          break;
+        }
+      else if ( *answer == '3' )
+        {
+          tty_printf (_("Generation of authentication key"
+                        " not yet implemented\n"));
+        }
+      else if ( *answer == 'q' || *answer == 'Q')
+        {
+          rc = -1;
+          break;
+        }
+
+      if (reread)
+        {
+          xfree (info.disp_name); info.disp_name = NULL;
+          xfree (info.pubkey_url); info.pubkey_url = NULL;
+          rc = agent_learn (&info);
+          if (rc)
+            {
+              tty_printf (_("OpenPGP card not anymore available: %s\n"),
+                          gpg_strerror (rc));
+              g10_exit (1);
+            }
+          reread = 0;
+        }
+    }
+
+  xfree (info.disp_name); 
+  xfree (info.pubkey_url);
+
+  return rc;
+}
+
+
+
+static int
+gen_card_key (int algo, int keyno, KBNODE pub_root, KBNODE sec_root,
+              u32 expireval)
+{
+  int rc;
+  struct agent_card_genkey_s info;
+  PACKET *pkt;
+  PKT_secret_key *sk;
+  PKT_public_key *pk;
+
+  assert (algo == PUBKEY_ALGO_RSA);
+
+  rc = agent_scd_genkey (&info, keyno, 0);
+  if (gpg_err_code (rc) == GPG_ERR_EEXIST)
+    {
+      tty_printf ("\n");
+      log_error ("WARNING: key does already exists!\n");
+      tty_printf ("\n");
+      if ( cpr_get_answer_is_yes( "keygen.card.replace_key",
+                                  _("Replace existing key? ")))
+        rc = agent_scd_genkey (&info, keyno, 1);
+    }
+
+  if (rc)
+    {
+      log_error ("key generation failed: %s\n", gpg_strerror (rc));
+      return rc;
+    }
+  if ( !info.n || !info.e )
+    {
+      log_error ("communication error with SCD\n");
+      gcry_mpi_release (info.n);
+      gcry_mpi_release (info.e);
+      return gpg_error (GPG_ERR_GENERAL);
+    }
+  
+
+  pk = xcalloc (1, sizeof *pk );
+  sk = xcalloc (1, sizeof *sk );
+  sk->timestamp = pk->timestamp = make_timestamp();
+  sk->version = pk->version = 4;
+  if (expireval)
+      sk->expiredate = pk->expiredate = pk->timestamp + expireval;
+  sk->pubkey_algo = pk->pubkey_algo = algo;
+  pk->pkey[0] = info.n;
+  pk->pkey[1] = info.e; 
+  sk->skey[0] = mpi_copy (pk->pkey[0]);
+  sk->skey[1] = mpi_copy (pk->pkey[1]);
+  sk->skey[2] = mpi_set_opaque (NULL, xstrdup ("dummydata"), 10);
+  sk->is_protected = 1;
+  sk->protect.s2k.mode = 1002;
+
+  pkt = xcalloc (1,sizeof *pkt);
+  pkt->pkttype = keyno == 1 ? 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 = keyno == 1 ? PKT_SECRET_KEY : PKT_SECRET_SUBKEY;
+  pkt->pkt.secret_key = sk;
+  add_kbnode(sec_root, new_kbnode( pkt ));
+
+  return 0;
+}
+
index c1a716b..fc9e255 100644 (file)
@@ -1634,6 +1634,8 @@ parse_key( iobuf_t inp, int pkttype, unsigned long pktlen,
                    break;
                  case 1001: if( list_mode ) printf(  "\tgnu-dummy S2K" );
                    break;
+                 case 1002: if (list_mode) printf("\tgnu-divert-to-card S2K");
+                   break;
                  default:
                    if( list_mode )
                        printf(  "\tunknown %sS2K %d\n",
@@ -1669,6 +1671,31 @@ parse_key( iobuf_t inp, int pkttype, unsigned long pktlen,
                        printf("\tprotect count: %lu\n",
                                            (ulong)sk->protect.s2k.count);
                }
+               else if( sk->protect.s2k.mode == 1002 ) {
+                    size_t snlen;
+                    /* Read the serial number. */
+                    if (pktlen < 1) {
+                       rc = GPG_ERR_INV_PACKET;
+                       goto leave;
+                   }
+                   snlen = iobuf_get (inp);
+                   pktlen--;
+                    if (pktlen < snlen || snlen == -1) {
+                       rc = GPG_ERR_INV_PACKET;
+                       goto leave;
+                    }
+
+                   if( list_mode ) {
+                      printf("\tserial-number: ");
+                      for (;snlen; snlen--)
+                        printf ("%02X", (unsigned int)iobuf_get_noeof (inp));
+                      putchar ('\n');
+                    }
+                    else {
+                      for (;snlen; snlen--)
+                        iobuf_get_noeof (inp);
+                    }
+               }
            }
            /* Note that a sk->protect.algo > 110 is illegal, but I'm
               not erroring on it here as otherwise there would be no
@@ -1698,6 +1725,8 @@ parse_key( iobuf_t inp, int pkttype, unsigned long pktlen,
            }
            if( sk->protect.s2k.mode == 1001 )
                sk->protect.ivlen = 0;
+           else if( sk->protect.s2k.mode == 1002 )
+               sk->protect.ivlen = 0;
 
            if( pktlen < sk->protect.ivlen ) {
                rc = GPG_ERR_INV_PACKET;
@@ -1719,7 +1748,8 @@ parse_key( iobuf_t inp, int pkttype, unsigned long pktlen,
         * If the user is so careless, not to protect his secret key,
         * we can assume, that he operates an open system :=(.
         * So we put the key into secure memory when we unprotect it. */
-       if( sk->protect.s2k.mode == 1001 ) {
+       if( sk->protect.s2k.mode == 1001
+            || sk->protect.s2k.mode == 1002 ) {
            /* better set some dummy stuff here */
            sk->skey[npkey] = mpi_set_opaque(NULL, xstrdup ("dummydata"), 10);
            pktlen = 0;
index 41cd31f..d003401 100644 (file)
@@ -794,7 +794,7 @@ agent_get_passphrase ( u32 *keyid, int mode, const char *tryagain_text,
         tryagain_text = _(tryagain_text);
 
       /* We allocate 2 time the needed space for atext so that there
-         is nenough space for escaping */
+         is enough space for escaping */
       line = xmalloc (15 + 46 
                       +  3*strlen (tryagain_text) + 3*strlen (atext) + 2);
       strcpy (line, "GET_PASSPHRASE ");
index 573c78f..dff463c 100644 (file)
@@ -253,6 +253,9 @@ check_secret_key( PKT_secret_key *sk, int n )
     int rc = gpg_error (GPG_ERR_BAD_PASSPHRASE);
     int i,mode;
 
+    if (sk && sk->is_protected && sk->protect.s2k.mode == 1002)
+      return 0; /* Let the scdaemon handle it. */
+
     if(n<0)
       {
        n=abs(n);
index 9eeed2c..5d0490c 100644 (file)
@@ -231,3 +231,8 @@ encode_md_value (int pubkey_algo, gcry_md_hd_t md, int hash_algo,
   return frame;
 }
 
+
+
+
+
+
index fa9e9ea..86159a4 100644 (file)
@@ -249,74 +249,94 @@ static int
 do_sign( PKT_secret_key *sk, PKT_signature *sig,
         MD_HANDLE md, int digest_algo )
 {
-    gcry_mpi_t frame;
-    byte *dp;
-    int rc;
-
-    if( sk->timestamp > sig->timestamp ) {
-       ulong d = sk->timestamp - sig->timestamp;
-       log_info( d==1 ? _("key has been created %lu second "
-                          "in future (time warp or clock problem)\n")
-                      : _("key has been created %lu seconds "
-                          "in future (time warp or clock problem)\n"), d );
-       if( !opt.ignore_time_conflict )
-           return GPG_ERR_TIME_CONFLICT;
+  gcry_mpi_t frame;
+  byte *dp;
+  int rc;
+
+  if( sk->timestamp > sig->timestamp ) {
+    ulong d = sk->timestamp - sig->timestamp;
+    log_info( d==1 ? _("key has been created %lu second "
+                       "in future (time warp or clock problem)\n")
+              : _("key has been created %lu seconds "
+                  "in future (time warp or clock problem)\n"), d );
+    if( !opt.ignore_time_conflict )
+      return GPG_ERR_TIME_CONFLICT;
+  }
+
+  print_pubkey_algo_note(sk->pubkey_algo);
+
+  if( !digest_algo )
+    digest_algo = gcry_md_get_algo(md);
+
+  print_digest_algo_note( digest_algo );
+  dp = gcry_md_read ( md, digest_algo );
+  sig->digest_algo = digest_algo;
+  sig->digest_start[0] = dp[0];
+  sig->digest_start[1] = dp[1];
+  if (sk->is_protected && sk->protect.s2k.mode == 1002)
+    { /* FIXME: Note that we do only support RSA for now. */
+      char *rbuf;
+      size_t rbuflen;
+
+      /* FIXME: We need to pass the correct keyid or better the
+         fingerprint to the scdaemon. */
+      rc = agent_scd_pksign ("nokeyid", digest_algo,
+                             gcry_md_read (md, digest_algo),
+                             gcry_md_get_algo_dlen (digest_algo),
+                             &rbuf, &rbuflen);
+      if (!rc)
+        {
+          unsigned int nbytes = rbuflen;
+          if (gcry_mpi_scan (&sig->data[0], GCRYMPI_FMT_USG, rbuf, &nbytes ))
+            BUG ();
+        }
     }
-
-
-    print_pubkey_algo_note(sk->pubkey_algo);
-
-    if( !digest_algo )
-       digest_algo = gcry_md_get_algo(md);
-
-    print_digest_algo_note( digest_algo );
-    dp = gcry_md_read ( md, digest_algo );
-    sig->digest_algo = digest_algo;
-    sig->digest_start[0] = dp[0];
-    sig->digest_start[1] = dp[1];
-    frame = encode_md_value( sk->pubkey_algo, md,
-                            digest_algo, mpi_get_nbits(sk->skey[0]), 0 );
-    if (!frame)
+  else
+    {
+      frame = encode_md_value( sk->pubkey_algo, md,
+                               digest_algo, mpi_get_nbits(sk->skey[0]), 0 );
+      if (!frame)
         return GPG_ERR_GENERAL;
-    rc = pk_sign( sk->pubkey_algo, sig->data, frame, sk->skey );
-    gcry_mpi_release (frame);
-    if (!rc && !opt.no_sig_create_check) {
-        /* check that the signature verification worked and nothing is
-         * fooling us e.g. by a bug in the signature create
-         * code or by deliberately introduced faults. */
-        PKT_public_key *pk = xcalloc (1,sizeof *pk);
-
-        if( get_pubkey( pk, sig->keyid ) )
-            rc = GPG_ERR_NO_PUBKEY;
-        else {
-            frame = encode_md_value (pk->pubkey_algo, md,
-                                     sig->digest_algo,
-                                     mpi_get_nbits(pk->pkey[0]), 0);
-            if (!frame)
-                rc = GPG_ERR_GENERAL;
-            else
-                rc = pk_verify (pk->pubkey_algo, frame,
-                                sig->data, pk->pkey);
-            gcry_mpi_release (frame);
-        }
-        if (rc)
-            log_error (_("checking created signature failed: %s\n"),
-                         gpg_strerror (rc));
-        free_public_key (pk);
+      rc = pk_sign( sk->pubkey_algo, sig->data, frame, sk->skey );
+      gcry_mpi_release (frame);
     }
-    if( rc )
-       log_error(_("signing failed: %s\n"), gpg_strerror (rc) );
+  if (!rc && !opt.no_sig_create_check) {
+    /* check that the signature verification worked and nothing is
+     * fooling us e.g. by a bug in the signature create
+     * code or by deliberately introduced faults. */
+    PKT_public_key *pk = xcalloc (1,sizeof *pk);
+
+    if( get_pubkey( pk, sig->keyid ) )
+      rc = GPG_ERR_NO_PUBKEY;
     else {
-       if( opt.verbose ) {
-           char *ustr = get_user_id_string_printable (sig->keyid);
-           log_info(_("%s/%s signature from: \"%s\"\n"),
-                    gcry_pk_algo_name (sk->pubkey_algo),
-                    gcry_md_algo_name (sig->digest_algo),
-                    ustr );
-           xfree (ustr);
-       }
+      frame = encode_md_value (pk->pubkey_algo, md,
+                               sig->digest_algo,
+                               mpi_get_nbits(pk->pkey[0]), 0);
+      if (!frame)
+        rc = GPG_ERR_GENERAL;
+      else
+        rc = pk_verify (pk->pubkey_algo, frame,
+                        sig->data, pk->pkey);
+      gcry_mpi_release (frame);
     }
-    return rc;
+    if (rc)
+      log_error (_("checking created signature failed: %s\n"),
+                 gpg_strerror (rc));
+    free_public_key (pk);
+  }
+  if( rc )
+    log_error(_("signing failed: %s\n"), gpg_strerror (rc) );
+  else {
+    if( opt.verbose ) {
+      char *ustr = get_user_id_string_printable (sig->keyid);
+      log_info(_("%s/%s signature from: \"%s\"\n"),
+               gcry_pk_algo_name (sk->pubkey_algo),
+               gcry_md_algo_name (sig->digest_algo),
+               ustr );
+      xfree (ustr);
+    }
+  }
+  return rc;
 }
 
 
@@ -1170,7 +1190,7 @@ sign_symencrypt_file (const char *fname, STRLIST locusr)
  * SIGVERSION gives the minimal required signature packet version;
  * this is needed so that special properties like local sign are not
  * applied (actually: dropped) when a v3 key is used.  TIMESTAMP is
- * the timestamp to use for the signature. 0 means "now" */
+ * the timestamp to use for the signature. 0 means "now". */
 int
 make_keysig_packet( PKT_signature **ret_sig, PKT_public_key *pk,
                    PKT_user_id *uid, PKT_public_key *subpk,
index d8af2ef..39239be 100644 (file)
@@ -372,10 +372,11 @@ tdbio_end_transaction()
        else
            is_locked = 1;
     }
-    block_all_signals();
+#warning block_all_signals is not yet available in ../common/signals.c
+    /*    block_all_signals(); */
     in_transaction = 0;
     rc = tdbio_sync();
-    unblock_all_signals();
+/*      unblock_all_signals(); */
     if( !opt.lock_once ) {
        if( !release_dotlock( lockhandle ) )
            is_locked = 0;