Add TLS hash mode
authorWerner Koch <wk@gnupg.org>
Fri, 6 Oct 2006 10:58:18 +0000 (10:58 +0000)
committerWerner Koch <wk@gnupg.org>
Fri, 6 Oct 2006 10:58:18 +0000 (10:58 +0000)
agent/ChangeLog
agent/agent.h
agent/command.c
agent/divert-scd.c
agent/pksign.c

index d95b040..fd24855 100644 (file)
@@ -1,5 +1,12 @@
 2006-10-05  Werner Koch  <wk@g10code.com>
 
+       * command.c (has_option_name): New.
+       (cmd_sethash): New --hash option.
+       * pksign.c (do_encode_raw_pkcs1): New.
+       (agent_pksign_do): Use it here for the TLS algo.
+       * agent.h (GCRY_MD_USER_TLS_MD5SHA1): New.
+       * divert-scd.c (pksign): Add case for tls-md5sha1.
+       
        * divert-scd.c (encode_md_for_card): Check that the algo is valid.
 
 2006-10-04  Werner Koch  <wk@g10code.com>
index 3667e81..cd51207 100644 (file)
 #include "../common/errors.h"
 #include "membuf.h"
 
+/* To convey some special hash algorithms we use algorithm numbers
+   reserved for application use. */
+#ifndef GCRY_MD_USER
+#define GCRY_MD_USER 1024
+#endif
+#define GCRY_MD_USER_TLS_MD5SHA1 (GCRY_MD_USER+1)
 
-#define MAX_DIGEST_LEN 24 
+/* Maximum length of a digest.  */
+#define MAX_DIGEST_LEN 36
 
 /* A large struct name "opt" to keep global flags */
 struct
index a8a701f..a2634f7 100644 (file)
@@ -136,6 +136,21 @@ has_option (const char *line, const char *name)
   return (s && (s == line || spacep (s-1)) && (!s[n] || spacep (s+n)));
 }
 
+/* Same as has_option but does only test for the name of the option
+   and ignores an argument, i.e. with NAME being "--hash" it would
+   return true for "--hash" as well as for "--hash=foo". */
+static int
+has_option_name (const char *line, const char *name)
+{
+  const char *s;
+  int n = strlen (name);
+
+  s = strstr (line, name);
+  return (s && (s == line || spacep (s-1))
+          && (!s[n] || spacep (s+n) || s[n] == '='));
+}
+
+
 /* Skip over options.  It is assumed that leading spaces have been
    removed (this is the case for lines passed to a handler from
    assuan).  Bkanls after the options are also removed. */
@@ -455,7 +470,7 @@ cmd_setkeydesc (assuan_context_t ctx, char *line)
 }
 
 
-/* SETHASH <algonumber> <hexstring> 
+/* SETHASH --hash=<name>|<algonumber> <hexstring> 
 
   The client can use this command to tell the server about the data
   (which usually is a hash) to be signed. */
@@ -470,12 +485,37 @@ cmd_sethash (assuan_context_t ctx, char *line)
   char *endp;
   int algo;
 
-  /* Parse the algo number and check it. */
-  algo = (int)strtoul (line, &endp, 10);
-  for (line = endp; *line == ' ' || *line == '\t'; line++)
-    ;
-  if (!algo || gcry_md_test_algo (algo))
-    return set_error (GPG_ERR_UNSUPPORTED_ALGORITHM, NULL);
+  /* Parse the alternative hash options which may be used instead of
+     the algo number.  */
+  if (has_option_name (line, "--hash"))
+    {
+      if (has_option (line, "--hash=sha1"))
+        algo = GCRY_MD_SHA1;
+      else if (has_option (line, "--hash=sha256"))
+        algo = GCRY_MD_SHA256;
+      else if (has_option (line, "--hash=rmd160"))
+        algo = GCRY_MD_RMD160;
+      else if (has_option (line, "--hash=md5"))
+        algo = GCRY_MD_MD5;
+      else if (has_option (line, "--hash=tls-md5sha1"))
+        algo = GCRY_MD_USER_TLS_MD5SHA1;
+      else
+        return set_error (GPG_ERR_ASS_PARAMETER, "invalid hash algorithm");
+    }
+  else
+    algo = 0;
+
+  line = skip_options (line);
+  
+  if (!algo)
+    {
+      /* No hash option has been given: require an algo number instead  */
+      algo = (int)strtoul (line, &endp, 10);
+      for (line = endp; *line == ' ' || *line == '\t'; line++)
+        ;
+      if (!algo || gcry_md_test_algo (algo))
+        return set_error (GPG_ERR_UNSUPPORTED_ALGORITHM, NULL);
+    }
   ctrl->digest.algo = algo;
 
   /* Parse the hash value. */
@@ -483,8 +523,11 @@ cmd_sethash (assuan_context_t ctx, char *line)
   if (rc)
     return rc;
   n /= 2;
-  if (n != 16 && n != 20 && n != 24 && n != 32)
+  if (algo == GCRY_MD_USER_TLS_MD5SHA1 && n == 36)
+    ;
+  else if (n != 16 && n != 20 && n != 24 && n != 32)
     return set_error (GPG_ERR_ASS_PARAMETER, "unsupported length of hash");
+
   if (n > MAX_DIGEST_LEN)
     return set_error (GPG_ERR_ASS_PARAMETER, "hash value to long");
 
index 89f177e..67c9f46 100644 (file)
@@ -318,26 +318,36 @@ divert_pksign (ctrl_t ctrl,
   int rc;
   char *kid;
   size_t siglen;
-  unsigned char *sigval;
-  unsigned char *data;
-  size_t ndata;
+  unsigned char *sigval = NULL;
 
   rc = ask_for_card (ctrl, shadow_info, &kid);
   if (rc)
     return rc;
 
-  rc = encode_md_for_card (digest, digestlen, algo, 
-                           &data, &ndata);
-  if (rc)
-    return rc;
+  if (algo == GCRY_MD_USER_TLS_MD5SHA1)
+    {
+      rc = agent_card_pksign (ctrl, kid, getpin_cb, ctrl,
+                              digest, digestlen, &sigval, &siglen);
+    }
+  else
+    {
+      unsigned char *data;
+      size_t ndata;
+
+      rc = encode_md_for_card (digest, digestlen, algo, &data, &ndata);
+      if (!rc)
+        {
+          rc = agent_card_pksign (ctrl, kid, getpin_cb, ctrl,
+                                  data, ndata, &sigval, &siglen);
+          xfree (data);
+        }
+    }
 
-  rc = agent_card_pksign (ctrl, kid, getpin_cb, ctrl,
-                          data, ndata, &sigval, &siglen);
   if (!rc)
     *r_sig = sigval;
-  xfree (data);
+
   xfree (kid);
-  
+
   return rc;
 }
 
index 9863f9d..c187ecc 100644 (file)
@@ -39,7 +39,7 @@ do_encode_md (const byte * md, size_t mdlen, int algo, gcry_sexp_t * r_hash,
   gcry_sexp_t hash;
   int rc;
 
-  if (! raw_value)
+  if (!raw_value)
     {
       const char *s;
       char tmp[16+1];
@@ -55,7 +55,7 @@ do_encode_md (const byte * md, size_t mdlen, int algo, gcry_sexp_t * r_hash,
 
       rc = gcry_sexp_build (&hash, NULL,
                            "(data (flags pkcs1) (hash %s %b))",
-                           tmp, mdlen, md);
+                           tmp, (int)mdlen, md);
     }
   else
     {
@@ -77,6 +77,55 @@ do_encode_md (const byte * md, size_t mdlen, int algo, gcry_sexp_t * r_hash,
 }
 
 
+/* Special version of do_encode_md to take care of pckcs#1 padding.
+   For TLS-MD5SHA1 we need to do the padding ourself as Libgrypt does
+   not know about this special scheme.  Fixme: We should have a
+   pkcs1-only-padding flag for Libgcrypt. */
+static int
+do_encode_raw_pkcs1 (const byte *md, size_t mdlen, unsigned int nbits,
+                     gcry_sexp_t *r_hash)
+{
+  int rc;
+  gcry_sexp_t hash;
+  unsigned char *frame;
+  size_t i, n, nframe;
+            
+  nframe = (nbits+7) / 8;
+  if ( !mdlen || mdlen + 8 + 4 > nframe )
+    {
+      /* Can't encode this hash into a frame of size NFRAME. */
+      return gpg_error (GPG_ERR_TOO_SHORT);
+    }
+
+  frame = xtrymalloc (nframe);
+  if (!frame)
+    return gpg_error_from_syserror ();
+  
+  /* Assemble the pkcs#1 block type 1. */
+  n = 0;
+  frame[n++] = 0;
+  frame[n++] = 1; /* Block type. */
+  i = nframe - mdlen - 3 ;
+  assert (i >= 8); /* At least 8 bytes of padding.  */
+  memset (frame+n, 0xff, i );
+  n += i;
+  frame[n++] = 0;
+  memcpy (frame+n, md, mdlen );
+  n += mdlen;
+  assert (n == nframe);
+  
+  /* Create the S-expression.  */
+  rc = gcry_sexp_build (&hash, NULL,
+                        "(data (flags raw) (value %b))",
+                        (int)nframe, frame);
+  xfree (frame);
+
+  *r_hash = hash;
+  return rc;   
+}
+
+
+
 /* SIGN whatever information we have accumulated in CTRL and return
    the signature S-Expression. */
 int
@@ -133,12 +182,18 @@ agent_pksign_do (ctrl_t ctrl, const char *desc_text,
 
       gcry_sexp_t s_hash = NULL;
 
-      /* put the hash into a sexp */
-      rc = do_encode_md (ctrl->digest.value,
-                         ctrl->digest.valuelen,
-                         ctrl->digest.algo,
-                         &s_hash,
-                        ctrl->digest.raw_value);
+      /* Put the hash into a sexp */
+      if (ctrl->digest.algo == GCRY_MD_USER_TLS_MD5SHA1)
+        rc = do_encode_raw_pkcs1 (ctrl->digest.value,
+                                  ctrl->digest.valuelen,
+                                  gcry_pk_get_nbits (s_skey),
+                                  &s_hash);
+      else
+        rc = do_encode_md (ctrl->digest.value,
+                           ctrl->digest.valuelen,
+                           ctrl->digest.algo,
+                           &s_hash,
+                           ctrl->digest.raw_value);
       if (rc)
         goto leave;