Signing using Netkey 3 cards does now work.
authorWerner Koch <wk@gnupg.org>
Thu, 26 Mar 2009 19:27:04 +0000 (19:27 +0000)
committerWerner Koch <wk@gnupg.org>
Thu, 26 Mar 2009 19:27:04 +0000 (19:27 +0000)
14 files changed:
NEWS
agent/ChangeLog
agent/agent.h
agent/command.c
doc/gpgsm.texi
scd/ChangeLog
scd/apdu.c
scd/app-nks.c
scd/command.c
scd/scdaemon.h
sm/ChangeLog
sm/gpgsm.c
sm/gpgsm.h
sm/sign.c

diff --git a/NEWS b/NEWS
index f9d6979..015ca16 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -17,6 +17,8 @@ Noteworthy changes in version 2.0.12 (not released)
 
  * Better synchronization of several smartcard sessions.
 
+ * Support for the Telesec Netkey 3 cards.
+
 
 Noteworthy changes in version 2.0.11 (2009-03-03)
 -------------------------------------------------
index e91731d..c551991 100644 (file)
@@ -1,5 +1,9 @@
 2009-03-26  Werner Koch  <wk@g10code.com>
 
+       * agent.h (MAX_DIGEST_LEN): Change to 64.
+       * command.c (cmd_sethash): Allow digest length of 48 and 64.
+       (cmd_sethash): Allow more hash algos.
+
        * trustlist.c (reformat_name): New.
        (agent_marktrusted): Use a reformatted name.  Reload the table
        before the update and always reload it at the end.
index 48b199b..ba31d93 100644 (file)
@@ -42,7 +42,7 @@
 #define MD_USER_TLS_MD5SHA1 (GCRY_MODULE_ID_USER+1)
 
 /* Maximum length of a digest.  */
-#define MAX_DIGEST_LEN 36
+#define MAX_DIGEST_LEN 64
 
 /* A large struct name "opt" to keep global flags */
 struct
index b31e405..56d390b 100644 (file)
@@ -637,8 +637,14 @@ cmd_sethash (assuan_context_t ctx, char *line)
     {
       if (has_option (line, "--hash=sha1"))
         algo = GCRY_MD_SHA1;
+      else if (has_option (line, "--hash=sha224"))
+        algo = GCRY_MD_SHA224;
       else if (has_option (line, "--hash=sha256"))
         algo = GCRY_MD_SHA256;
+      else if (has_option (line, "--hash=sha384"))
+        algo = GCRY_MD_SHA384;
+      else if (has_option (line, "--hash=sha512"))
+        algo = GCRY_MD_SHA512;
       else if (has_option (line, "--hash=rmd160"))
         algo = GCRY_MD_RMD160;
       else if (has_option (line, "--hash=md5"))
@@ -671,7 +677,8 @@ cmd_sethash (assuan_context_t ctx, char *line)
   n /= 2;
   if (algo == MD_USER_TLS_MD5SHA1 && n == 36)
     ;
-  else if (n != 16 && n != 20 && n != 24 && n != 32)
+  else if (n != 16 && n != 20 && n != 24 
+           && n != 28 && n != 32 && n != 48 && n != 64)
     return set_error (GPG_ERR_ASS_PARAMETER, "unsupported length of hash");
 
   if (n > MAX_DIGEST_LEN)
index 659d546..08b1c45 100644 (file)
@@ -568,6 +568,12 @@ encryption.  For convenience the strings @code{3DES}, @code{AES} and
 @code{AES256} may be used instead of their OIDs.  The default is
 @code{3DES} (1.2.840.113549.3.7).
   
+@item --digest-algo @code{name}
+Use @code{name} as the message digest algorithm.  Usually this
+algorithm is deduced from the respective signing certificate.  This
+option forces the use of the given algorithm and may lead to severe
+interoperability problems.
+
 @end table
 
 
index c12603a..f6de259 100644 (file)
@@ -1,3 +1,16 @@
+2009-03-26  Werner Koch  <wk@g10code.com>
+
+       * command.c (cmd_pksign): Allow more hash algorithms.
+
+       * scdaemon.h (MAX_DIGEST_LEN): Change to 64.
+
+       * apdu.c (open_ccid_reader): Clear the is_to flag.
+
+       * app-nks.c (filelist): Add field KID.
+       (do_getattr): Change standard authentication key.
+       (do_sign): Setup a security environment for TCOS 3 cards and support
+       all SHA-2 algorithms.
+
 2009-03-24  Werner Koch  <wk@g10code.com>
 
        * command.c (struct server_local_s): Add flag
index dfddd3f..b12b7e9 100644 (file)
@@ -1951,6 +1951,9 @@ open_ccid_reader (const char *portstr)
   reader_table[slot].send_apdu_reader = send_apdu_ccid;
   reader_table[slot].check_keypad = check_ccid_keypad;
   reader_table[slot].dump_status_reader = dump_ccid_reader_status;
+  /* Our CCID reader code does not support T=0 at all, thus reset the
+     flag.  */
+  reader_table[slot].is_t0 = 0;
 
   dump_reader_status (slot);
   return slot;
@@ -2839,10 +2842,10 @@ send_le (int slot, int class, int ins, int p0, int p1,
 
   if (lc != -1 && (lc > 255 || lc < 0))
     {
-      /* Data does not fit into an APDU.  What we do now dependes on
+      /* Data does not fit into an APDU.  What we do now depends on
          the EXTENDED_MODE parameter.  */
       if (!extended_mode)
-        return SW_WRONG_LENGTH; /* No way. to send such an APDU. */
+        return SW_WRONG_LENGTH; /* No way to send such an APDU.  */
       else if (extended_mode > 0)
         return SW_HOST_NOT_SUPPORTED; /* FIXME.  */
       else if (extended_mode < 0)
index 3113d34..d2ba978 100644 (file)
 
 /* Notes:
 
-  - This is still work in progress.  We are now targeting TCOS 3 cards
-    but try to keep compatibility to TCOS 2.  Both are not fully
-    working as of now.  TCOS 3 PIN management seems to work.  Use GPA
-    from SVN trunk to test it.
+  - We are now targeting TCOS 3 cards and it may happen that there is
+    a regression towards TCOS 2 cards.  Please report.
+
+  - The TKS3 AUT key is not used by our authentication command but
+    accessible via the decrypt command.
 
   - If required, we automagically switch between the NKS application
     and the SigG application.  This avoids to use the DINSIG
@@ -63,25 +64,26 @@ static struct
   int fid;       /* File ID. */
   int nks_ver;   /* 0 for NKS version 2, 3 for version 3. */
   int certtype;  /* Type of certificate or 0 if it is not a certificate. */
-  int iskeypair; /* If true has the FID of the correspoding certificate. */
+  int iskeypair; /* If true has the FID of the corresponding certificate. */
   int issignkey; /* True if file is a key usable for signing. */
   int isenckey;  /* True if file is a key usable for decryption. */
+  unsigned char kid;  /* Corresponding key references.  */
 } filelist[] = {
-  { 0, 0x4531, 0, 0,  0xC000, 1, 0 }, /* EF_PK.NKS.SIG */
-  { 1, 0x4531, 3, 0,  0x0000, 1, 1 }, /* EF_PK.CH.SIG  */
-  { 0, 0xC000, 0, 101 },              /* EF_C.NKS.SIG  */
-  { 1, 0xC000, 0, 101 },              /* EF_C.CH.SIG  */
+  { 0, 0x4531, 0, 0,  0xC000, 1, 0, 0x80 }, /* EF_PK.NKS.SIG */
+  { 1, 0x4531, 3, 0,  0x0000, 1, 1, 0x84 }, /* EF_PK.CH.SIG  */
+  { 0, 0xC000, 0, 101 },                    /* EF_C.NKS.SIG  */
+  { 1, 0xC000, 0, 101 },                    /* EF_C.CH.SIG  */
   { 0, 0x4331, 0, 100 },
   { 0, 0x4332, 0, 100 },
-  { 0, 0xB000, 0, 110 },              /* EF_PK.RCA.NKS */
-  { 0, 0x45B1, 0, 0,  0xC200, 0, 1 }, /* EF_PK.NKS.ENC */
-  { 0, 0xC200, 0, 101 },              /* EF_C.NKS.ENC  */
+  { 0, 0xB000, 0, 110 },                    /* EF_PK.RCA.NKS */
+  { 0, 0x45B1, 0, 0,  0xC200, 0, 1, 0x81 }, /* EF_PK.NKS.ENC */
+  { 0, 0xC200, 0, 101 },                    /* EF_C.NKS.ENC  */
   { 0, 0x43B1, 0, 100 },
   { 0, 0x43B2, 0, 100 },
-  { 0, 0x4571, 3, 0,  0xc500, 0, 0 }, /* EF_PK.NKS.AUT */
-  { 0, 0xC500, 3, 101 },              /* EF_C.NKS.AUT  */
-  { 0, 0x45B2, 3, 0,  0xC201, 0, 1 }, /* EF_PK.NKS.ENC1024 */
-  { 0, 0xC201, 3, 101 },              /* EF_C.NKS.ENC1024  */
+  { 0, 0x4571, 3, 0,  0xc500, 0, 0, 0x82 }, /* EF_PK.NKS.AUT */
+  { 0, 0xC500, 3, 101 },                    /* EF_C.NKS.AUT  */
+  { 0, 0x45B2, 3, 0,  0xC201, 0, 1, 0x83 }, /* EF_PK.NKS.ENC1024 */
+  { 0, 0xC201, 3, 101 },                    /* EF_C.NKS.ENC1024  */
 /*   { 1, 0xB000, 3, ...  */
   { 0, 0 }
 };
@@ -303,10 +305,12 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name)
     {
     case 1: /* $AUTHKEYID */
       {
-        /* NetKey 3.0 cards define this key for authentication.
-           FIXME: We don't have the readkey command, so this
-           information is pretty useless.  */
-        char const tmp[] = "NKS-NKS3.4571";
+        /* NetKey 3.0 cards define an authentication key but according
+           to the specs this key is only usable for encryption and not
+           signing.  it might work anyway but it has not yet been
+           tested - fixme.  Thus for now we use the NKS signature key
+           for authentication.  */
+        char const tmp[] = "NKS-NKS3.4531";
         send_status_info (ctrl, table[idx].name, tmp, strlen (tmp), NULL, 0);
       }
       break;
@@ -685,13 +689,18 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
   int rc, i;
   int is_sigg = 0;
   int fid;
-  unsigned char data[35];   /* Must be large enough for a SHA-1 digest
-                               + the largest OID _prefix above. */
+  unsigned char kid;
+  unsigned char data[83];   /* Must be large enough for a SHA-1 digest
+                               + the largest OID prefix. */
+  size_t datalen;
 
   if (!keyidstr || !*keyidstr)
     return gpg_error (GPG_ERR_INV_VALUE);
-  if (indatalen != 20 && indatalen != 16 && indatalen != 35)
-    return gpg_error (GPG_ERR_INV_VALUE);
+  switch (indatalen)
+    {
+    case 16: case 20: case 35: case 47: case 51: case 67: case 83: break;
+    default: return gpg_error (GPG_ERR_INV_VALUE);
+    }
 
   /* Check that the provided ID is valid.  This is not really needed
      but we do it to enforce correct usage by the caller. */
@@ -721,22 +730,35 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
     return gpg_error (GPG_ERR_NOT_FOUND);
   if (!filelist[i].issignkey)
     return gpg_error (GPG_ERR_INV_ID);
-
-  /* Prepare the DER object from INDATA. */
-  if (indatalen == 35)
+  kid = filelist[i].kid;
+
+  /* Prepare the DER object from INDATA.  */
+  if (app->app_local->nks_version > 2 && (indatalen == 35
+                                          || indatalen == 47
+                                          || indatalen == 51
+                                          || indatalen == 67 
+                                          || indatalen == 83))
+    {
+      /* The caller send data matching the length of the ASN.1 encoded
+         hash for SHA-{1,224,256,384,512}.  Assume that is okay.  */
+      assert (indatalen <= sizeof data);
+      memcpy (data, indata, indatalen);
+      datalen = indatalen;
+    }
+  else if (indatalen == 35)
     {
       /* Alright, the caller was so kind to send us an already
-         prepared DER object.  Check that it is waht we want and that
-         it matches the hash algorithm. */
+         prepared DER object.  This is for TCOS 2. */
       if (hashalgo == GCRY_MD_SHA1 && !memcmp (indata, sha1_prefix, 15))
         ;
-      else if (hashalgo == GCRY_MD_RMD160 && !memcmp (indata, rmd160_prefix,15))
+      else if (hashalgo == GCRY_MD_RMD160 && !memcmp (indata,rmd160_prefix,15))
         ;
       else 
         return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
       memcpy (data, indata, indatalen);
+      datalen = 35;
     }
-  else
+  else if (indatalen == 20)
     {
       if (hashalgo == GCRY_MD_SHA1)
         memcpy (data, sha1_prefix, 15);
@@ -745,11 +767,32 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
       else 
         return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
       memcpy (data+15, indata, indatalen);
+      datalen = 35;
     }
+  else
+    return gpg_error (GPG_ERR_INV_VALUE);
+
 
-  rc = verify_pin (app, 0, NULL, pincb, pincb_arg);
+  /* Send an MSE for PSO:Computer_Signature.  */
+  if (app->app_local->nks_version > 2)
+    {
+      unsigned char mse[6];
+      
+      mse[0] = 0x80; /* Algorithm reference.  */
+      mse[1] = 1;
+      mse[2] = 2;    /* RSA, card does pkcs#1 v1.5 padding, no ASN.1 check.  */
+      mse[3] = 0x84; /* Private key reference.  */
+      mse[4] = 1;
+      mse[5] = kid;
+      rc = iso7816_manage_security_env (app->slot, 0x41, 0xB6,
+                                        mse, sizeof mse);
+    }
+  /* Verify using PW1.CH.  */
+  if (!rc)
+    rc = verify_pin (app, 0, NULL, pincb, pincb_arg);
+  /* Compute the signature.  */
   if (!rc)
-    rc = iso7816_compute_ds (app->slot, data, 35, outdata, outdatalen);
+    rc = iso7816_compute_ds (app->slot, data, datalen, outdata, outdatalen);
   return rc;
 }
 
index aa0e635..07aed17 100644 (file)
@@ -905,7 +905,7 @@ pin_cb (void *opaque, const char *info, char **retstr)
 }
 
 
-/* PKSIGN [--hash=[rmd160|sha1|md5]] <hexified_id>
+/* PKSIGN [--hash=[rmd160|sha{1,224,256,384,512}|md5]] <hexified_id>
 
    The --hash option is optional; the default is SHA1.
 
@@ -924,6 +924,14 @@ cmd_pksign (assuan_context_t ctx, char *line)
     hash_algo = GCRY_MD_RMD160;
   else if (has_option (line, "--hash=sha1"))
     hash_algo = GCRY_MD_SHA1;
+  else if (has_option (line, "--hash=sha224"))
+    hash_algo = GCRY_MD_SHA224;
+  else if (has_option (line, "--hash=sha256"))
+    hash_algo = GCRY_MD_SHA256;
+  else if (has_option (line, "--hash=sha384"))
+    hash_algo = GCRY_MD_SHA384;
+  else if (has_option (line, "--hash=sha512"))
+    hash_algo = GCRY_MD_SHA512;
   else if (has_option (line, "--hash=md5"))
     hash_algo = GCRY_MD_MD5;
   else if (!strstr (line, "--"))
index 7c63692..c429396 100644 (file)
@@ -39,7 +39,7 @@
 #define MD_USER_TLS_MD5SHA1 (GCRY_MODULE_ID_USER+1)
 
 /* Maximum length of a digest.  */
-#define MAX_DIGEST_LEN 36
+#define MAX_DIGEST_LEN 64
 
 
 
index 0905159..9f926cf 100644 (file)
@@ -1,5 +1,12 @@
 2009-03-26  Werner Koch  <wk@g10code.com>
 
+       * gpgsm.c (main): s/def_digest_string/forced_digest_algo/ and
+       activate the --digest-algo option.
+       * gpgsm.h (struct opt): s/def_digest_algo/forced_digest_algo/.
+       * sign.c (gpgsm_sign): Implement --digest-algo.
+
+       * sign.c (MAX_DIGEST_LEN): Change to 64.
+
        * call-agent.c (gpgsm_agent_marktrusted): Format the issuer name.
 
 2009-03-25  Werner Koch  <wk@g10code.com>
index f49b742..8a2a1c0 100644 (file)
@@ -843,8 +843,8 @@ main ( int argc, char **argv)
   int use_random_seed = 1;
   int no_common_certs_import = 0;
   int with_fpr = 0;
-  char *def_digest_string = NULL;
-  char *extra_digest_algo = NULL;
+  const char *forced_digest_algo = NULL;
+  const char *extra_digest_algo = NULL;
   enum cmd_and_opt_values cmd = 0;
   struct server_control_s ctrl;
   certlist_t recplist = NULL;
@@ -1301,7 +1301,7 @@ main ( int argc, char **argv)
           break;
 
         case oDigestAlgo:
-          /* Dummy for now. */
+          forced_digest_algo = pargs.r.ret_str;
           break;
 
         case oExtraDigestAlgo: 
@@ -1460,12 +1460,10 @@ main ( int argc, char **argv)
            || !gcry_cipher_mode_from_oid (opt.def_cipher_algoid))
         log_error (_("selected cipher algorithm is invalid\n"));
 
-      if (def_digest_string)
+      if (forced_digest_algo)
         {
-          opt.def_digest_algo = gcry_md_map_name (def_digest_string);
-          xfree (def_digest_string);
-          def_digest_string = NULL;
-          if (our_md_test_algo(opt.def_digest_algo) )
+          opt.forced_digest_algo = gcry_md_map_name (forced_digest_algo);
+          if (our_md_test_algo(opt.forced_digest_algo) )
             log_error (_("selected digest algorithm is invalid\n"));
         }
       if (extra_digest_algo)
index e9327d2..781561a 100644 (file)
@@ -33,7 +33,7 @@
 #include "../common/estream.h"
 #include "../common/audit.h"
 
-#define MAX_DIGEST_LEN 24 
+#define MAX_DIGEST_LEN 64
 
 struct keyserver_spec
 {
@@ -92,9 +92,10 @@ struct
   const char *def_cipher_algoid;  /* cipher algorithm to use if
                                      nothing else is specified */
 
-  int def_digest_algo;    /* Ditto for hash algorithm */
   int def_compress_algo;  /* Ditto for compress algorithm */
 
+  int forced_digest_algo; /* User forced hash algorithm. */
+
   char *def_recipient;    /* userID of the default recipient */
   int def_recipient_self; /* The default recipient is the default key */
 
index fadd664..446cd37 100644 (file)
--- a/sm/sign.c
+++ b/sm/sign.c
@@ -399,11 +399,22 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist,
   /* Figure out the hash algorithm to use. We do not want to use the
      one for the certificate but if possible an OID for the plain
      algorithm.  */
+  if (opt.forced_digest_algo && opt.verbose)
+    log_info ("user requested hash algorithm %d\n", opt.forced_digest_algo);
   for (i=0, cl=signerlist; cl; cl = cl->next, i++)
     {
       const char *oid = ksba_cert_get_digest_algo (cl->cert);
 
-      cl->hash_algo = oid ? gcry_md_map_name (oid) : 0;
+      if (opt.forced_digest_algo)
+        {
+          oid = NULL;
+          cl->hash_algo = opt.forced_digest_algo;
+        }
+      else
+        {
+          oid = ksba_cert_get_digest_algo (cl->cert);
+          cl->hash_algo = oid ? gcry_md_map_name (oid) : 0;
+        }
       switch (cl->hash_algo)
         {
         case GCRY_MD_SHA1:   oid = "1.3.14.3.2.26"; break;
@@ -427,6 +438,7 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist,
         }
       cl->hash_algo_oid = oid;
     }
+
   if (opt.verbose)
     {
       for (i=0, cl=signerlist; cl; cl = cl->next, i++)