Start support of TCOS 3 cards.
authorWerner Koch <wk@gnupg.org>
Thu, 26 Jun 2008 19:09:07 +0000 (19:09 +0000)
committerWerner Koch <wk@gnupg.org>
Thu, 26 Jun 2008 19:09:07 +0000 (19:09 +0000)
Support restriction attribute.
Fix utf-8 printing problems.
Use AES by default.

14 files changed:
NEWS
TODO
common/ChangeLog
common/estream.c
jnlib/ChangeLog
jnlib/stringhelp.c
scd/ChangeLog
scd/app-dinsig.c
sm/ChangeLog
sm/gpgsm.c
sm/gpgsm.h
sm/keylist.c
sm/qualified.c
sm/sign.c

diff --git a/NEWS b/NEWS
index ec7e235..8108d5f 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -25,6 +25,8 @@ Noteworthy changes in version 2.0.10 (unreleased)
  * New control statement %ask-passphrase for the unattended key
    generation of gpg2.
 
+ * gpgsm now uses AES by default.
+
 
 Noteworthy changes in version 2.0.9 (2008-03-26)
 ------------------------------------------------
diff --git a/TODO b/TODO
index f10297f..139b90d 100644 (file)
--- a/TODO
+++ b/TODO
@@ -66,7 +66,8 @@
   We should check the card status in open-card to make this smoother.
   Needs to be integrated with the status file update, though.  It is
   not a real problem because application will get a card removed
-  status and should the send a reset to try solving the problem.
+  status and should then send a reset to try solving the problem.
+** Resolve fixme in do_sign of app-dinsig.
 
 ** Add a regression test to check the extkeyusage.
 
index 3840eaa..dcee95b 100644 (file)
@@ -1,3 +1,9 @@
+2008-06-26  Werner Koch  <wk@g10code.com>
+
+       * estream.c (es_write_sanitized): Loose check for control
+       characters to better cope with utf-8.  The range 0x80..0x9f is
+       nowadays not anymore accidently used for control charaters.
+
 2008-06-25  Marcus Brinkmann  <marcus@g10code.de>
 
        Revert last three changes related to handle translation.
index e712e85..254fab9 100644 (file)
@@ -3074,7 +3074,7 @@ es_write_sanitized (estream_t ES__RESTRICT stream,
   for (; length; length--, p++, count++)
     {
       if (*p < 0x20 
-          || (*p >= 0x7f && *p < 0xa0)
+          || *p == 0x7f
           || (delimiters 
               && (strchr (delimiters, *p) || *p == '\\')))
         {
index d098344..c7bef60 100644 (file)
@@ -1,3 +1,9 @@
+2008-06-26  Werner Koch  <wk@g10code.com>
+
+       * stringhelp.c (print_sanitized_buffer2): Loose check for control
+       characters to better cope with utf-8.  The range 0x80..0x9f is
+       nowadays not anymore accidently used for control charaters.
+
 2008-06-13  Werner Koch  <wk@g10code.com>
 
        * dotlock.c: Reformat code and implement locking for W32.
index 0287505..50e869c 100644 (file)
@@ -406,9 +406,8 @@ print_sanitized_buffer2 (FILE *fp, const void *buffer, size_t length,
 
   for (; length; length--, p++, count++)
     {
-      /* Fixme: Check whether *p < 0xa0 is correct for utf8 encoding. */
       if (*p < 0x20 
-          || (*p >= 0x7f && *p < 0xa0)
+          || *p == 0x7f
           || *p == delim 
           || *p == delim2
           || ((delim || delim2) && *p=='\\'))
index 60c139c..3cd6eb0 100644 (file)
@@ -1,3 +1,7 @@
+2008-06-25  Werner Koch  <wk@g10code.com>
+
+       * app-dinsig.c (do_sign): Allow for SHA256.
+
 2008-06-24  Werner Koch  <wk@g10code.com>
 
        * app-common.h (app_ctx_s): Renamed reset_mode parameter of
index 54b54b1..cd6c3d0 100644 (file)
@@ -1,5 +1,5 @@
 /* app-dinsig.c - The DINSIG (DIN V 66291-1) card application.
- * Copyright (C) 2002, 2004, 2005, 2007 Free Software Foundation, Inc.
+ * Copyright (C) 2002, 2004, 2005, 2007, 2008 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -397,14 +397,20 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
   static unsigned char rmd160_prefix[15] = /* Object ID is 1.3.36.3.2.1 */
     { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x24, 0x03,
       0x02, 0x01, 0x05, 0x00, 0x04, 0x14 };
+  static unsigned char sha256_prefix[19] = /* OID is 2.16.840.1.101.3.4.2.1 */
+    { 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
+      0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
+      0x00, 0x04, 0x20 };
   int rc;
   int fid;
-  unsigned char data[35];   /* Must be large enough for a SHA-1 digest
-                               + the largest OID _prefix above. */
+  unsigned char data[19+32]; /* Must be large enough for a SHA-256 digest
+                                + the largest OID _prefix above. */
+  int datalen;
 
   if (!keyidstr || !*keyidstr)
     return gpg_error (GPG_ERR_INV_VALUE);
-  if (indatalen != 20 && indatalen != 16 && indatalen != 35)
+  if (indatalen != 20 && indatalen != 16 && indatalen != 32
+      && indatalen != (15+20) && indatalen != (19+32))
     return gpg_error (GPG_ERR_INV_VALUE);
 
   /* Check that the provided ID is vaid.  This is not really needed
@@ -421,7 +427,8 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
     return gpg_error (GPG_ERR_NOT_FOUND);
 
   /* Prepare the DER object from INDATA. */
-  if (indatalen == 35)
+  datalen = 35;
+  if (indatalen == 15+20)
     {
       /* Alright, the caller was so kind to send us an already
          prepared DER object.  Check that it is what we want and that
@@ -434,24 +441,103 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
         return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
       memcpy (data, indata, indatalen);
     }
+  else if (indatalen == 19+32)
+    {
+      /* Alright, the caller was so kind to send us an already
+         prepared DER object.  Check that it is what we want and that
+         it matches the hash algorithm. */
+      datalen = indatalen;
+      if (hashalgo == GCRY_MD_SHA256 && !memcmp (indata, sha256_prefix, 19))
+        ;
+      else if (hashalgo == GCRY_MD_SHA1 && !memcmp (indata, sha256_prefix, 19))
+        {
+          /* Fixme: This is a kludge.  A better solution is not to use
+             SHA1 as default but use an autodetection.  However this
+             needs changes in all app-*.c */
+          hashalgo = GCRY_MD_SHA256;
+          datalen  = indatalen;
+        }
+      else 
+        return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
+      memcpy (data, indata, indatalen);
+    }
   else
     {
+      int len = 15;
       if (hashalgo == GCRY_MD_SHA1)
-        memcpy (data, sha1_prefix, 15);
+        memcpy (data, sha1_prefix, len);
       else if (hashalgo == GCRY_MD_RMD160)
-        memcpy (data, rmd160_prefix, 15);
+        memcpy (data, rmd160_prefix, len);
+      else if (hashalgo == GCRY_MD_SHA256)
+        {
+          len = 19;
+          datalen = len + indatalen;
+          memcpy (data, sha256_prefix, len);
+        }
       else 
         return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
-      memcpy (data+15, indata, indatalen);
+      memcpy (data+len, indata, indatalen);
     }
 
   rc = verify_pin (app, pincb, pincb_arg);
   if (!rc)
-    rc = iso7816_compute_ds (app->slot, data, 35, outdata, outdatalen);
+    rc = iso7816_compute_ds (app->slot, data, datalen, outdata, outdatalen);
   return rc;
 }
 
 
+#if 0
+#warning test function - works but may brick your card
+/* Handle the PASSWD command.  CHVNOSTR is currently ignored; we
+   always use VHV0.  RESET_MODE is not yet implemented.  */
+static gpg_error_t 
+do_change_pin (app_t app, ctrl_t ctrl,  const char *chvnostr, 
+               unsigned int flags,
+               gpg_error_t (*pincb)(void*, const char *, char **),
+               void *pincb_arg)
+{
+  gpg_error_t err;
+  char *pinvalue;
+  const char *oldpin;
+  size_t oldpinlen;
+
+  if ((flags & APP_CHANGE_FLAG_RESET))
+    return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+
+  if ((flags & APP_CHANGE_FLAG_NULLPIN))
+    {
+      /* With the nullpin flag, we do not verify the PIN - it would fail
+         if the Nullpin is still set.  */
+      oldpin = "\0\0\0\0\0";
+      oldpinlen = 6;
+    }
+  else
+    {
+      err = verify_pin (app, pincb, pincb_arg);
+      if (err)
+        return err;
+      oldpin = NULL;
+      oldpinlen = 0;
+    }
+
+  /* TRANSLATORS: Do not translate the "|*|" prefixes but
+     keep it at the start of the string.  We need this elsewhere
+     to get some infos on the string. */
+  err = pincb (pincb_arg, _("|N|Initial New PIN"), &pinvalue); 
+  if (err)
+    {
+      log_error (_("error getting new PIN: %s\n"), gpg_strerror (err));
+      return err;
+    }
+
+  err = iso7816_change_reference_data (app->slot, 0x81, 
+                                       oldpin, oldpinlen,
+                                       pinvalue, strlen (pinvalue));
+  xfree (pinvalue);
+  return err;
+}
+
+
 
 /* Select the DINSIG application on the card in SLOT.  This function
    must be used before any other DINSIG application functions. */
@@ -475,7 +561,7 @@ app_select_dinsig (app_t app)
       app->fnc.sign = do_sign;
       app->fnc.auth = NULL;
       app->fnc.decipher = NULL;
-      app->fnc.change_pin = NULL;
+      app->fnc.change_pin = NULL /*do_change_pin*/;
       app->fnc.check_pin = NULL;
 
       app->force_chv1 = 1;
index 20ab483..a6a584b 100644 (file)
@@ -1,3 +1,16 @@
+2008-06-25  Werner Koch  <wk@g10code.com>
+
+       * sign.c (gpgsm_sign): Revamp the hash algorithm selection.
+       * gpgsm.h (struct certlist_s): Add field HASH_ALGO and HASH_ALGO_OID.
+
+       * qualified.c (gpgsm_qualified_consent): Fix double free.
+
+       * gpgsm.c (main): Change default cipher algo to AES.
+
+       * keylist.c (print_utf8_extn_raw, print_utf8_extn): New.
+       (list_cert_raw, list_cert_std): Print the TeleSec restriction
+       extension.
+
 2008-06-23  Werner Koch  <wk@g10code.com>
 
        * encrypt.c (encode_session_key): Replace xmalloc by xtrymalloc.
index 50ffb84..dfbe826 100644 (file)
@@ -987,7 +987,7 @@ main ( int argc, char **argv)
   create_dotlock (NULL); /* register locking cleanup */
   i18n_init();
 
-  opt.def_cipher_algoid = "3DES";  /*des-EDE3-CBC*/
+  opt.def_cipher_algoid = "AES";  /*des-EDE3-CBC*/
 
   opt.homedir = default_homedir ();
 
index acc53b5..2cd1775 100644 (file)
@@ -207,6 +207,8 @@ struct certlist_s
   ksba_cert_t cert;
   int is_encrypt_to; /* True if the certificate has been set through
                         the --encrypto-to option. */
+  int hash_algo;     /* Used to track the hash algorithm to use.  */
+  const char *hash_algo_oid;  /* And the corresponding OID.  */
 };
 typedef struct certlist_s *certlist_t;
 
index 870ddf4..6dcdc8d 100644 (file)
@@ -1,6 +1,6 @@
 /* keylist.c - Print certificates in various formats.
  * Copyright (C) 1998, 1999, 2000, 2001, 2003,
- *               2004, 2005 Free Software Foundation, Inc.
+ *               2004, 2005, 2008 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -35,6 +35,7 @@
 #include "keydb.h"
 #include "../kbx/keybox.h" /* for KEYBOX_FLAG_* */
 #include "i18n.h"
+#include "tlv.h"
 
 struct list_external_parm_s 
 {
@@ -77,12 +78,18 @@ struct
 };
 
 
+/* Do not print this extension in the list of extensions.  This is set
+   for oids which are already available via ksba fucntions. */
+#define OID_FLAG_SKIP 1
+/* The extension is a simple UTF8String and should be printed.  */
+#define OID_FLAG_UTF8 2 
+
 /* A table mapping OIDs to a descriptive string. */
 static struct 
 {
   char *oid;
   char *name;
-  unsigned int flag;
+  unsigned int flag; /* A flag as described above.  */
 } oidtranstbl[] = {
 
   /* Algorithms. */
@@ -115,6 +122,10 @@ static struct
   { "0.2.262.1.10.12.4", "telesecCRLFilteredExt" },
   { "0.2.262.1.10.12.5", "telesecCRLFilterExt"},
   { "0.2.262.1.10.12.6", "telesecNamingAuthorityExt" },
+#define OIDSTR_restriction \
+    "1.3.36.8.3.8"
+  { OIDSTR_restriction,      "restriction", OID_FLAG_UTF8 },
+
 
   /* PKIX private extensions. */
   { "1.3.6.1.5.5.7.1.1", "authorityInfoAccess" },
@@ -135,12 +146,12 @@ static struct
   { "1.3.6.1.5.5.7.48.5", "caRepository" },
 
   /* X.509 id-ce */
-  { "2.5.29.14", "subjectKeyIdentifier", 1},
-  { "2.5.29.15", "keyUsage", },
+  { "2.5.29.14", "subjectKeyIdentifier", OID_FLAG_SKIP},
+  { "2.5.29.15", "keyUsage", OID_FLAG_SKIP},
   { "2.5.29.16", "privateKeyUsagePeriod" },
-  { "2.5.29.17", "subjectAltName", },
-  { "2.5.29.18", "issuerAltName", },
-  { "2.5.29.19", "basicConstraints", 1},
+  { "2.5.29.17", "subjectAltName", OID_FLAG_SKIP},
+  { "2.5.29.18", "issuerAltName", OID_FLAG_SKIP},
+  { "2.5.29.19", "basicConstraints", OID_FLAG_SKIP},
   { "2.5.29.20", "cRLNumber" },
   { "2.5.29.21", "cRLReason" },
   { "2.5.29.22", "expirationDate" },
@@ -150,13 +161,13 @@ static struct
   { "2.5.29.28", "issuingDistributionPoint" },
   { "2.5.29.29", "certificateIssuer" },
   { "2.5.29.30", "nameConstraints" },
-  { "2.5.29.31", "cRLDistributionPoints", },
-  { "2.5.29.32", "certificatePolicies", },
+  { "2.5.29.31", "cRLDistributionPoints", OID_FLAG_SKIP},
+  { "2.5.29.32", "certificatePolicies", OID_FLAG_SKIP},
   { "2.5.29.32.0", "anyPolicy" },
   { "2.5.29.33", "policyMappings" },
-  { "2.5.29.35", "authorityKeyIdentifier", },
+  { "2.5.29.35", "authorityKeyIdentifier", OID_FLAG_SKIP},
   { "2.5.29.36", "policyConstraints" },
-  { "2.5.29.37", "extKeyUsage", },
+  { "2.5.29.37", "extKeyUsage", OID_FLAG_SKIP},
   { "2.5.29.46", "freshestCRL" },
   { "2.5.29.54", "inhibitAnyPolicy" },
 
@@ -561,6 +572,59 @@ print_names_raw (estream_t fp, int indent, ksba_name_t name)
 }
 
 
+static void
+print_utf8_extn_raw (estream_t fp, int indent, 
+                     const unsigned char *der, size_t derlen)
+{
+  gpg_error_t err;
+  int class, tag, constructed, ndef;
+  size_t objlen, hdrlen;
+
+  if (indent < 0)
+    indent = - indent;
+
+  err = parse_ber_header (&der, &derlen, &class, &tag, &constructed,
+                          &ndef, &objlen, &hdrlen);
+  if (!err && (objlen > derlen || tag != TAG_UTF8_STRING))
+    err = gpg_error (GPG_ERR_INV_OBJ);
+  if (err)
+    {
+      es_fprintf (fp, "%*s[%s]\n", indent, "", gpg_strerror (err));
+      return;
+    }
+  es_fprintf (fp, "%*s(%.*s)\n", indent, "", objlen, der);
+}
+
+
+static void
+print_utf8_extn (estream_t fp, int indent, 
+                 const unsigned char *der, size_t derlen)
+{
+  gpg_error_t err;
+  int class, tag, constructed, ndef;
+  size_t objlen, hdrlen;
+  int indent_all;
+
+  if ((indent_all = (indent < 0)))
+    indent = - indent;
+
+  err = parse_ber_header (&der, &derlen, &class, &tag, &constructed,
+                          &ndef, &objlen, &hdrlen);
+  if (!err && (objlen > derlen || tag != TAG_UTF8_STRING))
+    err = gpg_error (GPG_ERR_INV_OBJ);
+  if (err)
+    {
+      es_fprintf (fp, "%*s[Error - %s]\n",
+                  indent_all? indent:0, "", gpg_strerror (err));
+      return;
+    }
+  es_fprintf (fp, "%*s\"", indent_all? indent:0, "");
+  /* Fixme: we should implement word wrapping */
+  es_write_sanitized (fp, der, objlen, "\"", NULL);
+  es_fputs ("\"\n", fp);
+}
+
+
 /* List one certificate in raw mode useful to have a closer look at
    the certificate.  This one does no beautification and only minimal
    output sanitation.  It is mainly useful for debugging. */
@@ -581,6 +645,7 @@ list_cert_raw (ctrl_t ctrl, KEYDB_HANDLE hd,
   const char *oid, *s;
   ksba_name_t name, name2;
   unsigned int reason;
+  const unsigned char *cert_der = NULL;
 
   es_fprintf (fp, "           ID: 0x%08lX\n",
               gpgsm_get_short_fingerprint (cert));
@@ -892,11 +957,19 @@ list_cert_raw (ctrl_t ctrl, KEYDB_HANDLE hd,
       unsigned int flag;
 
       s = get_oid_desc (oid, &flag);
+      if ((flag & OID_FLAG_SKIP))
+        continue;
 
-      if (!(flag & 1))
-        es_fprintf (fp, "     %s: %s%s%s%s  [%d octets]\n",
-                 i? "critExtn":"    extn",
-                 oid, s?" (":"", s?s:"", s?")":"", (int)len);
+      es_fprintf (fp, "     %s: %s%s%s%s  [%d octets]\n",
+                  i? "critExtn":"    extn",
+                  oid, s?" (":"", s?s:"", s?")":"", (int)len);
+      if ((flag & OID_FLAG_UTF8))
+        {
+          if (!cert_der)
+            cert_der = ksba_cert_get_image (cert, NULL);
+          assert (cert_der);
+          print_utf8_extn_raw (fp, -15, cert_der+off, len);
+        }
     }
 
 
@@ -938,6 +1011,10 @@ list_cert_std (ctrl_t ctrl, ksba_cert_t cert, estream_t fp, int have_secret,
   int is_ca, chainlen;
   unsigned int kusage;
   char *string, *p, *pend;
+  size_t off, len;
+  const char *oid;
+  const unsigned char *cert_der = NULL;
+
 
   es_fprintf (fp, "           ID: 0x%08lX\n",
               gpgsm_get_short_fingerprint (cert));
@@ -1053,6 +1130,21 @@ list_cert_std (ctrl_t ctrl, ksba_cert_t cert, estream_t fp, int have_secret,
       es_putc ('\n', fp);
     }
 
+  /* Print restrictions.  */
+  for (idx=0; !(err=ksba_cert_get_extension (cert, idx,
+                                             &oid, NULL, &off, &len));idx++)
+    {
+      if (!strcmp (oid, OIDSTR_restriction) )
+        {
+          if (!cert_der)
+            cert_der = ksba_cert_get_image (cert, NULL);
+          assert (cert_der);
+          es_fputs ("  restriction: ", fp);
+          print_utf8_extn (fp, 15, cert_der+off, len);
+        }
+    }
+
+  /* Print policies.  */
   err = ksba_cert_get_cert_policies (cert, &string);
   if (gpg_err_code (err) != GPG_ERR_NO_DATA)
     {
index 507c151..d902728 100644 (file)
@@ -215,7 +215,6 @@ gpgsm_qualified_consent (ctrl_t ctrl, ksba_cert_t cert)
     err = 0;
 
   i18n_switchback (orig_codeset);
-  xfree (orig_codeset);
   xfree (subject);
 
   if (err)
index a6d02e9..fadd664 100644 (file)
--- a/sm/sign.c
+++ b/sm/sign.c
@@ -1,5 +1,5 @@
 /* sign.c - Sign a message
- *     Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc.
+ *     Copyright (C) 2001, 2002, 2003, 2008 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -396,6 +396,44 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist,
       release_signerlist = 1;
     }
 
+  /* 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.  */
+  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;
+      switch (cl->hash_algo)
+        {
+        case GCRY_MD_SHA1:   oid = "1.3.14.3.2.26"; break;
+        case GCRY_MD_RMD160: oid = "1.3.36.3.2.1"; break;
+        case GCRY_MD_SHA224: oid = "2.16.840.1.101.3.4.2.4"; break;
+        case GCRY_MD_SHA256: oid = "2.16.840.1.101.3.4.2.1"; break;
+        case GCRY_MD_SHA384: oid = "2.16.840.1.101.3.4.2.2"; break;
+        case GCRY_MD_SHA512: oid = "2.16.840.1.101.3.4.2.3"; break;
+/*         case GCRY_MD_WHIRLPOOL: oid = "No OID yet"; break; */
+              
+        case GCRY_MD_MD5:  /* We don't want to use MD5.  */
+        case 0:            /* No algorithm found in cert.  */
+        default:           /* Other algorithms.  */
+          log_info (_("hash algorithm %d (%s) for signer %d not supported;"
+                      " using %s\n"),
+                    cl->hash_algo, oid? oid: "?", i, 
+                    gcry_md_algo_name (GCRY_MD_SHA1));
+          cl->hash_algo = GCRY_MD_SHA1;
+          oid = "1.3.14.3.2.26";
+          break;
+        }
+      cl->hash_algo_oid = oid;
+    }
+  if (opt.verbose)
+    {
+      for (i=0, cl=signerlist; cl; cl = cl->next, i++)
+        log_info (_("hash algorithm used for signer %d: %s (%s)\n"), 
+                  i, gcry_md_algo_name (cl->hash_algo), cl->hash_algo_oid);
+    }
+
 
   /* Gather certificates of signers and store them in the CMS object. */
   for (cl=signerlist; cl; cl = cl->next)
@@ -419,7 +457,7 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist,
           goto leave;
         }
       /* Set the hash algorithm we are going to use */
-      err = ksba_cms_add_digest_algo (cms, "1.3.14.3.2.26" /*SHA-1*/);
+      err = ksba_cms_add_digest_algo (cms, cl->hash_algo_oid);
       if (err)
         {
           log_debug ("ksba_cms_add_digest_algo failed: %s\n",
@@ -458,7 +496,8 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist,
         }
     }
   
-  /* Prepare hashing (actually we are figuring out what we have set above)*/
+  /* Prepare hashing (actually we are figuring out what we have set
+     above). */
   rc = gcry_md_open (&data_md, 0, 0);
   if (rc)
     {
@@ -474,10 +513,6 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist,
       if (!algo)
         {
           log_error ("unknown hash algorithm `%s'\n", algoid? algoid:"?");
-          if (algoid
-              && (  !strcmp (algoid, "1.2.840.113549.1.1.2")
-                    ||!strcmp (algoid, "1.2.840.113549.2.2")))
-            log_info (_("(this is the MD2 algorithm)\n"));
           rc = gpg_error (GPG_ERR_BUG);
           goto leave;
         }
@@ -485,26 +520,23 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist,
     }
 
   if (detached)
-    { /* we hash the data right now so that we can store the message
+    { /* We hash the data right now so that we can store the message
          digest.  ksba_cms_build() takes this as an flag that detached
          data is expected. */
       unsigned char *digest;
       size_t digest_len;
-      /* Fixme do this for all signers and get the algo to use from
-         the signer's certificate - does not make much sense, but we
-         should do this consistent as we have already done it above. */
-      algo = GCRY_MD_SHA1; 
+
       hash_data (data_fd, data_md);
-      digest = gcry_md_read (data_md, algo);
-      digest_len = gcry_md_get_algo_dlen (algo);
-      if ( !digest || !digest_len)
-        {
-          log_error ("problem getting the hash of the data\n");
-          rc = gpg_error (GPG_ERR_BUG);
-          goto leave;
-        }
       for (cl=signerlist,signer=0; cl; cl = cl->next, signer++)
         {
+          digest = gcry_md_read (data_md, cl->hash_algo);
+          digest_len = gcry_md_get_algo_dlen (cl->hash_algo);
+          if ( !digest || !digest_len )
+            {
+              log_error ("problem getting the hash of the data\n");
+              rc = gpg_error (GPG_ERR_BUG);
+              goto leave;
+            }
           err = ksba_cms_set_message_digest (cms, signer, digest, digest_len);
           if (err)
             {
@@ -559,30 +591,26 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist,
         }
 
       if (stopreason == KSBA_SR_BEGIN_DATA)
-        { /* hash the data and store the message digest */
+        { 
+          /* Hash the data and store the message digest. */
           unsigned char *digest;
           size_t digest_len;
 
           assert (!detached);
-          /* Fixme: get the algo to use from the signer's certificate
-             - does not make much sense, but we should do this
-             consistent as we have already done it above.  Code is
-             mostly duplicated above. */
 
-          algo = GCRY_MD_SHA1; 
           rc = hash_and_copy_data (data_fd, data_md, writer);
           if (rc)
             goto leave;
-          digest = gcry_md_read (data_md, algo);
-          digest_len = gcry_md_get_algo_dlen (algo);
-          if ( !digest || !digest_len)
-            {
-              log_error ("problem getting the hash of the data\n");
-              rc = gpg_error (GPG_ERR_BUG);
-              goto leave;
-            }
           for (cl=signerlist,signer=0; cl; cl = cl->next, signer++)
             {
+              digest = gcry_md_read (data_md, cl->hash_algo);
+              digest_len = gcry_md_get_algo_dlen (cl->hash_algo);
+              if ( !digest || !digest_len )
+                {
+                  log_error ("problem getting the hash of the data\n");
+                  rc = gpg_error (GPG_ERR_BUG);
+                  goto leave;
+                }
               err = ksba_cms_set_message_digest (cms, signer,
                                                  digest, digest_len);
               if (err)
@@ -595,11 +623,11 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist,
             }
         }
       else if (stopreason == KSBA_SR_NEED_SIG)
-        { /* calculate the signature for all signers */
+        { 
+          /* Compute the signature for all signers.  */
           gcry_md_hd_t md;
 
-          algo = GCRY_MD_SHA1;
-          rc = gcry_md_open (&md, algo, 0);
+          rc = gcry_md_open (&md, 0, 0);
           if (rc)
             {
               log_error ("md_open failed: %s\n", gpg_strerror (rc));
@@ -615,6 +643,13 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist,
 
               if (signer)
                 gcry_md_reset (md);
+              {
+                certlist_t cl_tmp;
+
+                for (cl_tmp=signerlist; cl_tmp; cl_tmp = cl_tmp->next)
+                  gcry_md_enable (md, cl_tmp->hash_algo);
+              }
+
               rc = ksba_cms_hash_signed_attrs (cms, signer);
               if (rc)
                 {
@@ -625,7 +660,7 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist,
                 }
             
               rc = gpgsm_create_cms_signature (ctrl, cl->cert,
-                                               md, algo, &sigval);
+                                               md, cl->hash_algo, &sigval);
               if (rc)
                 {
                   gcry_md_close (md);
@@ -656,7 +691,7 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist,
                 rc = asprintf (&buf, "%c %d %d 00 %s %s",
                                detached? 'D':'S',
                                pkalgo, 
-                               algo, 
+                               cl->hash_algo, 
                                signed_at,
                                fpr);
               }