Allow pkcs#10 creation directkly from a smart card
authorWerner Koch <wk@gnupg.org>
Wed, 11 Oct 2006 17:52:15 +0000 (17:52 +0000)
committerWerner Koch <wk@gnupg.org>
Wed, 11 Oct 2006 17:52:15 +0000 (17:52 +0000)
NEWS
agent/command.c
configure.ac
scd/ChangeLog
scd/app-openpgp.c
sm/ChangeLog
sm/call-agent.c
sm/certreqgen.c
sm/gpgsm.h
tools/ChangeLog
tools/gpgsm-gencert.sh

diff --git a/NEWS b/NEWS
index e1798d6..aaca936 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,7 @@
+Noteworthy changes in version 1.9.93
+-------------------------------------------------
+
+
 Noteworthy changes in version 1.9.92 (2006-10-11)
 -------------------------------------------------
 
index a2634f7..cebbf9a 100644 (file)
@@ -236,7 +236,7 @@ parse_hexstring (assuan_context_t ctx, const char *string, size_t *len)
 }
 
 /* Parse the keygrip in STRING into the provided buffer BUF.  BUF must
-   provide space for 20 bytes. BUF is not changed if the fucntions
+   provide space for 20 bytes. BUF is not changed if the function
    returns an error. */
 static int
 parse_keygrip (assuan_context_t ctx, const char *string, unsigned char *buf)
index 7c2bf75..f93ab72 100644 (file)
@@ -26,8 +26,8 @@ min_automake_version="1.9.3"
 # Remember to change the version number immediately *after* a release.
 # Set my_issvn to "yes" for non-released code.  Remember to run an
 # "svn up" and "autogen.sh" right before creating a distribution.
-m4_define([my_version], [1.9.92])
-m4_define([my_issvn], [no])
+m4_define([my_version], [1.9.93])
+m4_define([my_issvn], [yes])
 
 
 m4_define([svn_revision], m4_esyscmd([echo -n $((svn info 2>/dev/null \
index 9f6a0f5..41a89b8 100644 (file)
@@ -1,3 +1,7 @@
+2006-10-11  Werner Koch  <wk@g10code.com>
+
+       * app-openpgp.c (do_sign): Redirect to do_auth for OpenPGP.3.
+
 2006-10-06  Werner Koch  <wk@g10code.com>
 
        * Makefile.am (AM_CFLAGS): Use PTH version of libassuan.
index 4de465e..466f37c 100644 (file)
@@ -142,6 +142,11 @@ struct app_local_s {
 static unsigned long convert_sig_counter_value (const unsigned char *value,
                                                 size_t valuelen);
 static unsigned long get_sig_counter (app_t app);
+static gpg_error_t do_auth (app_t app, const char *keyidstr,
+                            gpg_error_t (*pincb)(void*, const char *, char **),
+                            void *pincb_arg,
+                            const void *indata, size_t indatalen,
+                            unsigned char **outdata, size_t *outdatalen);
 
 
 
@@ -2088,7 +2093,11 @@ check_against_given_fingerprint (app_t app, const char *fpr, int keyno)
    Note that this function may return the error code
    GPG_ERR_WRONG_CARD to indicate that the card currently present does
    not match the one required for the requested action (e.g. the
-   serial number does not match). */
+   serial number does not match). 
+   
+   As a special feature a KEYIDSTR of "OPENPGP.3" redirects the
+   operation to the auth command.
+*/
 static gpg_error_t 
 do_sign (app_t app, const char *keyidstr, int hashalgo,
          gpg_error_t (*pincb)(void*, const char *, char **),
@@ -2109,6 +2118,7 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
   int n;
   const char *fpr = NULL;
   unsigned long sigcount;
+  int use_auth = 0;
 
   if (!keyidstr || !*keyidstr)
     return gpg_error (GPG_ERR_INV_VALUE);
@@ -2136,6 +2146,8 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
   /* Check whether an OpenPGP card of any version has been requested. */
   if (!strcmp (keyidstr, "OPENPGP.1"))
     ;
+  else if (!strcmp (keyidstr, "OPENPGP.3"))
+    use_auth = 1;
   else if (strlen (keyidstr) < 32 || strncmp (keyidstr, "D27600012401", 12))
     return gpg_error (GPG_ERR_INV_ID);
   else
@@ -2178,6 +2190,14 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
     return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
   memcpy (data+15, indata, indatalen);
 
+  if (use_auth)
+    {
+      /* This is a hack to redirect to the internal authenticate command.  */
+      return do_auth (app, "OPENPGP.3", pincb, pincb_arg,
+                      data, 35,
+                      outdata, outdatalen);
+    }
+
   sigcount = get_sig_counter (app);
   log_info (_("signatures created so far: %lu\n"), sigcount);
 
index 74c5c43..8bf306e 100644 (file)
@@ -1,3 +1,10 @@
+2006-10-11  Werner Koch  <wk@g10code.com>
+
+       * certreqgen.c (proc_parameters, create_request): Allow for
+       creation directly from a card.
+       * call-agent.c (gpgsm_agent_readkey): New arg FROMCARD.
+       (gpgsm_scd_pksign): New.
+
 2006-10-06  Werner Koch  <wk@g10code.com>
 
        * Makefile.am (AM_CFLAGS): Use PTH version of libassuan.
index 35ad1b8..47f5304 100644 (file)
@@ -271,6 +271,84 @@ gpgsm_agent_pksign (ctrl_t ctrl, const char *keygrip, const char *desc,
 }
 
 
+/* Call the scdaemon to do a sign operation using the key identified by
+   the hex string KEYID. */
+int
+gpgsm_scd_pksign (ctrl_t ctrl, const char *keyid, const char *desc,
+                  unsigned char *digest, size_t digestlen, int digestalgo,
+                  unsigned char **r_buf, size_t *r_buflen )
+{
+  int rc, i;
+  char *p, line[ASSUAN_LINELENGTH];
+  membuf_t data;
+  size_t len;
+  const char *hashopt;
+  unsigned char *sigbuf;
+  size_t sigbuflen;
+
+  *r_buf = NULL;
+
+  switch(digestalgo)
+    {
+    case GCRY_MD_SHA1:  hashopt = "--hash=sha1"; break;
+    case GCRY_MD_RMD160:hashopt = "--hash=rmd160"; break;
+    case GCRY_MD_MD5:   hashopt = "--hash=md5"; break;
+    case GCRY_MD_SHA256:hashopt = "--hash=sha256"; break;
+    default: 
+      return gpg_error (GPG_ERR_DIGEST_ALGO);
+    }
+
+  rc = start_agent (ctrl);
+  if (rc)
+    return rc;
+
+  if (digestlen*2 + 50 > DIM(line))
+    return gpg_error (GPG_ERR_GENERAL);
+
+  p = stpcpy (line, "SCD SETDATA " );
+  for (i=0; i < digestlen ; i++, p += 2 )
+    sprintf (p, "%02X", digest[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 %s", hashopt, 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;
+    }
+  sigbuf = get_membuf (&data, &sigbuflen);
+
+  /* Create an S-expression from it which is formatted like this:
+     "(7:sig-val(3:rsa(1:sSIGBUFLEN:SIGBUF)))" Fixme: If a card ever
+     creates non-RSA keys we need to change things. */
+  *r_buflen = 21 + 11 + sigbuflen + 4;
+  p = xtrymalloc (*r_buflen);
+  *r_buf = (unsigned char*)p;
+  if (!p)
+    {
+      xfree (sigbuf);
+      return 0;
+    }
+  p = stpcpy (p, "(7:sig-val(3:rsa(1:s" );
+  sprintf (p, "%u:", (unsigned int)sigbuflen);
+  p += strlen (p);
+  memcpy (p, sigbuf, sigbuflen);
+  p += sigbuflen;
+  strcpy (p, ")))");
+  xfree (sigbuf);
+
+  assert (gcry_sexp_canon_len (*r_buf, *r_buflen, NULL, NULL));
+  return  0;
+}
+
+
 
 \f
 /* Handle a CIPHERTEXT inquiry.  Note, we only send the data,
@@ -449,9 +527,12 @@ gpgsm_agent_genkey (ctrl_t ctrl,
 }
 
 \f
-/* Call the agent to read the public key part for a given keygrip.  */
+/* 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
-gpgsm_agent_readkey (ctrl_t ctrl, const char *hexkeygrip,
+gpgsm_agent_readkey (ctrl_t ctrl, int fromcard, const char *hexkeygrip,
                      ksba_sexp_t *r_pubkey)
 {
   int rc;
@@ -469,7 +550,8 @@ gpgsm_agent_readkey (ctrl_t ctrl, const char *hexkeygrip,
   if (rc)
     return rc;
 
-  snprintf (line, DIM(line)-1, "READKEY %s", hexkeygrip);
+  snprintf (line, DIM(line)-1, "%sREADKEY %s",
+            fromcard? "SCD ":"", hexkeygrip);
   line[DIM(line)-1] = 0;
 
   init_membuf (&data, 1024);
index e100675..f0221d3 100644 (file)
@@ -148,6 +148,7 @@ static int proc_parameters (ctrl_t ctrl,
                             struct reqgen_ctrl_s *outctrl);
 static int create_request (ctrl_t ctrl,
                            struct para_data_s *para,
+                           const char *carddirect,
                            ksba_const_sexp_t public,
                            struct reqgen_ctrl_s *outctrl);
 
@@ -452,15 +453,24 @@ proc_parameters (ctrl_t ctrl,
   ksba_sexp_t public;
   int seq;
   size_t erroff, errlen;
+  char *cardkeyid = NULL;
 
   /* Check that we have all required parameters; */
   assert (get_parameter (para, pKEYTYPE, 0));
 
-  /* We can only use RSA for now.  There is a with pkcs-10 on how to
-     use ElGamal because it is expected that a PK algorithm can always
-     be used for signing. */
+  /* We can only use RSA for now.  There is a problem with pkcs-10 on
+     how to use ElGamal because it is expected that a PK algorithm can
+     always be used for signing. Another problem is that on-card
+     generated encryption keys may not be used for signing.  */
   i = get_parameter_algo (para, pKEYTYPE);
-  if (i < 1 || i != GCRY_PK_RSA )
+  if (!i && (s = get_parameter_value (para, pKEYTYPE, 0)) && *s)
+    {
+      /* Hack to allow creation of certificates directly from a smart
+         card.  For example: "Key-Type: card:OPENPGP.3".  */
+      if (!strncmp (s, "card:", 5) && s[5])
+        cardkeyid = xtrystrdup (s+5);
+    }
+  if ( (i < 1 || i != GCRY_PK_RSA) && !cardkeyid )
     {
       r = get_parameter (para, pKEYTYPE, 0);
       log_error (_("line %d: invalid algorithm\n"), r->lnr);
@@ -472,18 +482,22 @@ proc_parameters (ctrl_t ctrl,
     nbits = 1024;
   else
     nbits = get_parameter_uint (para, pKEYLENGTH);
-  if (nbits < 1024 || nbits > 4096)
+  if ((nbits < 1024 || nbits > 4096) && !cardkeyid)
     {
       /* The BSI specs dated 2002-11-25 don't allow lengths below 1024. */
       r = get_parameter (para, pKEYLENGTH, 0);
       log_error (_("line %d: invalid key length %u (valid are %d to %d)\n"),
                  r->lnr, nbits, 1024, 4096);
+      xfree (cardkeyid);
       return gpg_error (GPG_ERR_INV_PARAMETER);
     }
     
   /* Check the usage. */
   if (parse_parameter_usage (para, pKEYUSAGE))
-    return gpg_error (GPG_ERR_INV_PARAMETER);
+    {
+      xfree (cardkeyid);
+      return gpg_error (GPG_ERR_INV_PARAMETER);
+    }
 
   /* Check that there is a subject name and that this DN fits our
      requirements. */
@@ -491,6 +505,7 @@ proc_parameters (ctrl_t ctrl,
     {
       r = get_parameter (para, pNAMEDN, 0);
       log_error (_("line %d: no subject name given\n"), r->lnr);
+      xfree (cardkeyid);
       return gpg_error (GPG_ERR_INV_PARAMETER);
     }
   err = ksba_dn_teststr (s, 0, &erroff, &errlen);
@@ -504,6 +519,7 @@ proc_parameters (ctrl_t ctrl,
         log_error (_("line %d: invalid subject name `%s' at pos %d\n"),
                    r->lnr, s, erroff);
 
+      xfree (cardkeyid);
       return gpg_error (GPG_ERR_INV_PARAMETER);
     }
 
@@ -518,19 +534,32 @@ proc_parameters (ctrl_t ctrl,
         {
           r = get_parameter (para, pNAMEEMAIL, seq);
           log_error (_("line %d: not a valid email address\n"), r->lnr);
+          xfree (cardkeyid);
           return gpg_error (GPG_ERR_INV_PARAMETER);
         }
     }
 
-  s = get_parameter_value (para, pKEYGRIP, 0);
-  if (s) /* Use existing key.  */
+  if (cardkeyid) /* Take the key from the current smart card. */
     {
-      rc = gpgsm_agent_readkey (ctrl, s, &public);
+      rc = gpgsm_agent_readkey (ctrl, 1, cardkeyid, &public);
+      if (rc)
+        {
+          r = get_parameter (para, pKEYTYPE, 0);
+          log_error (_("line %d: error reading key `%s' from card: %s\n"),
+                     r->lnr, cardkeyid, gpg_strerror (rc));
+          xfree (cardkeyid);
+          return rc;
+        }
+    }
+  else if ((s=get_parameter_value (para, pKEYGRIP, 0))) /* Use existing key.*/
+    {
+      rc = gpgsm_agent_readkey (ctrl, 0, s, &public);
       if (rc)
         {
           r = get_parameter (para, pKEYTYPE, 0);
           log_error (_("line %d: error getting key by keygrip `%s': %s\n"),
                      r->lnr, s, gpg_strerror (rc));
+          xfree (cardkeyid);
           return rc;
         }
     }
@@ -546,12 +575,14 @@ proc_parameters (ctrl_t ctrl,
           r = get_parameter (para, pKEYTYPE, 0);
           log_error (_("line %d: key generation failed: %s\n"),
                      r->lnr, gpg_strerror (rc));
+          xfree (cardkeyid);
           return rc;
         }
     }
 
-  rc = create_request (ctrl, para, public, outctrl);
+  rc = create_request (ctrl, para, cardkeyid, public, outctrl);
   xfree (public);
+  xfree (cardkeyid);
 
   return rc;
 }
@@ -560,8 +591,10 @@ proc_parameters (ctrl_t ctrl,
 /* Parameters are checked, the key pair has been created.  Now
    generate the request and write it out */
 static int
-create_request (ctrl_t ctrl,
-                struct para_data_s *para, ksba_const_sexp_t public,
+create_request (ctrl_t ctrl, 
+                struct para_data_s *para, 
+                const char *carddirect,
+                ksba_const_sexp_t public,
                 struct reqgen_ctrl_s *outctrl)
 {
   ksba_certreq_t cr;
@@ -758,11 +791,18 @@ create_request (ctrl_t ctrl,
           for (n=0; n < 20; n++)
             sprintf (hexgrip+n*2, "%02X", grip[n]);
 
-          rc = gpgsm_agent_pksign (ctrl, hexgrip, NULL,
-                                   gcry_md_read(md, GCRY_MD_SHA1), 
-                                   gcry_md_get_algo_dlen (GCRY_MD_SHA1),
-                                   GCRY_MD_SHA1,
-                                   &sigval, &siglen);
+          if (carddirect)
+            rc = gpgsm_scd_pksign (ctrl, carddirect, NULL,
+                                     gcry_md_read(md, GCRY_MD_SHA1), 
+                                     gcry_md_get_algo_dlen (GCRY_MD_SHA1),
+                                     GCRY_MD_SHA1,
+                                     &sigval, &siglen);
+          else
+            rc = gpgsm_agent_pksign (ctrl, hexgrip, NULL,
+                                     gcry_md_read(md, GCRY_MD_SHA1), 
+                                     gcry_md_get_algo_dlen (GCRY_MD_SHA1),
+                                     GCRY_MD_SHA1,
+                                     &sigval, &siglen);
           if (rc)
             {
               log_error ("signing failed: %s\n", gpg_strerror (rc));
index 541783d..d92bf59 100644 (file)
@@ -322,12 +322,15 @@ int gpgsm_agent_pksign (ctrl_t ctrl, const char *keygrip, const char *desc,
                         size_t digestlen,
                         int digestalgo,
                         unsigned char **r_buf, size_t *r_buflen);
+int gpgsm_scd_pksign (ctrl_t ctrl, const char *keyid, const char *desc,
+                      unsigned char *digest, size_t digestlen, int digestalgo,
+                      unsigned char **r_buf, size_t *r_buflen);
 int gpgsm_agent_pkdecrypt (ctrl_t ctrl, const char *keygrip, const char *desc,
                            ksba_const_sexp_t ciphertext, 
                            char **r_buf, size_t *r_buflen);
 int gpgsm_agent_genkey (ctrl_t ctrl,
                         ksba_const_sexp_t keyparms, ksba_sexp_t *r_pubkey);
-int gpgsm_agent_readkey (ctrl_t ctrl, const char *hexkeygrip,
+int gpgsm_agent_readkey (ctrl_t ctrl, int fromcard, const char *hexkeygrip,
                          ksba_sexp_t *r_pubkey);
 int gpgsm_agent_istrusted (ctrl_t ctrl, ksba_cert_t cert,
                            struct rootca_flags_s *rootca_flags);
index de22a2e..5ed3810 100644 (file)
@@ -1,3 +1,7 @@
+2006-10-11  Werner Koch  <wk@g10code.com>
+
+       * gpgsm-gencert.sh: Allow generation of card keys.
+
 2006-10-08  Werner Koch  <wk@g10code.com>
 
        * Makefile.am (gpgkey2ssh_LDADD): Add LIBINTL. Suggested by
index 3949f23..19e961f 100755 (executable)
@@ -84,19 +84,27 @@ query_user_menu()
     echo "You selected: $ANSWER" >&2
 }
 
-query_user_menu "Key type" "RSA" "existing key"
-if [ "$ANSWER" = "existing key" ]; then
-  # User requested to use an existing key; need to set some dummy defaults
-  KEY_TYPE=RSA 
-  KEY_LENGTH=1024
-  query_user "Keygrip "
-  KEY_GRIP=$ANSWER
-else
-  KEY_TYPE=$ANSWER
-  query_user_menu "Key length" "1024" "2048"
-  KEY_LENGTH=$ANSWER
-  KEY_GRIP=
-fi
+query_user_menu "Key type" "RSA" "existing key" "OPENPGP.1" "OPENPGP.3"
+case "$ANSWER" in
+  RSA)
+    KEY_TYPE=$ANSWER
+    query_user_menu "Key length" "1024" "2048"
+    KEY_LENGTH=$ANSWER
+    KEY_GRIP=
+    ;;
+  existing*)
+    # User requested to use an existing key; need to set some dummy defaults
+    KEY_TYPE=RSA 
+    KEY_LENGTH=1024
+    query_user "Keygrip "
+    KEY_GRIP=$ANSWER
+    ;;
+  *) 
+    KEY_TYPE="card:$ANSWER"
+    KEY_LENGTH=
+    KEY_GRIP=
+    ;;
+esac
 
 
 query_user_menu "Key usage" "sign, encrypt" "sign" "encrypt"
@@ -162,7 +170,7 @@ query_user_menu "Really create such a CSR?" "yes" "no"
     
 
 echo -e "$ASSUAN_COMMANDS" | \
-    gpgsm --no-log-file --debug-level none --debug-none \
+     gpgsm --no-log-file --debug-level none --debug-none \
            --server 4< "$file_parameter" 5>"$outfile" >/dev/null
 
 cat "$outfile"