dirmngr: Add --policy-flags option to WKD_GET.
[gnupg.git] / sm / certcheck.c
index 93c0bec..904556f 100644 (file)
@@ -1,11 +1,11 @@
 /* certcheck.c - check one certificate
- *     Copyright (C) 2001, 2003 Free Software Foundation, Inc.
+ *     Copyright (C) 2001, 2003, 2004 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
  * GnuPG is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
+ * the Free Software Foundation; either version 3 of the License, or
  * (at your option) any later version.
  *
  * GnuPG is distributed in the hope that it will be useful,
@@ -14,8 +14,7 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
 #include <config.h>
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
-#include <unistd.h> 
+#include <unistd.h>
 #include <time.h>
 #include <assert.h>
 
+#include "gpgsm.h"
 #include <gcrypt.h>
 #include <ksba.h>
 
-#include "gpgsm.h"
 #include "keydb.h"
 #include "i18n.h"
 
 
+/* Return the number of bits of the Q parameter from the DSA key
+   KEY.  */
+static unsigned int
+get_dsa_qbits (gcry_sexp_t key)
+{
+  gcry_sexp_t l1, l2;
+  gcry_mpi_t q;
+  unsigned int nbits;
+
+  l1 = gcry_sexp_find_token (key, "public-key", 0);
+  if (!l1)
+    return 0; /* Does not contain a key object.  */
+  l2 = gcry_sexp_cadr (l1);
+  gcry_sexp_release  (l1);
+  l1 = gcry_sexp_find_token (l2, "q", 1);
+  gcry_sexp_release (l2);
+  if (!l1)
+    return 0; /* Invalid object.  */
+  q = gcry_sexp_nth_mpi (l1, 1, GCRYMPI_FMT_USG);
+  gcry_sexp_release (l1);
+  if (!q)
+    return 0; /* Missing value.  */
+  nbits = gcry_mpi_get_nbits (q);
+  gcry_mpi_release (q);
+
+  return nbits;
+}
+
+
 static int
-do_encode_md (GCRY_MD_HD md, int algo,  unsigned int nbits,
-              GCRY_MPI *r_val)
+do_encode_md (gcry_md_hd_t md, int algo, int pkalgo, unsigned int nbits,
+              gcry_sexp_t pkey, gcry_mpi_t *r_val)
 {
-  int nframe = (nbits+7) / 8;
-  byte *frame;
-  int i, n;
-  byte asn[100];
-  size_t asnlen;
-  size_t len;
-
-  asnlen = DIM(asn);
-  if (gcry_md_algo_info (algo, GCRYCTL_GET_ASNOID, asn, &asnlen))
+  int n;
+  size_t nframe;
+  unsigned char *frame;
+
+  if (pkalgo == GCRY_PK_DSA || pkalgo == GCRY_PK_ECDSA)
     {
-      log_error ("No object identifier for algo %d\n", algo);
-      return gpg_error (GPG_ERR_INTERNAL);
+      unsigned int qbits;
+
+      if ( pkalgo == GCRY_PK_ECDSA )
+        qbits = gcry_pk_get_nbits (pkey);
+      else
+        qbits = get_dsa_qbits (pkey);
+
+      if ( (qbits%8) )
+       {
+         log_error(_("DSA requires the hash length to be a"
+                     " multiple of 8 bits\n"));
+         return gpg_error (GPG_ERR_INTERNAL);
+       }
+
+      /* Don't allow any Q smaller than 160 bits.  We don't want
+        someone to issue signatures from a key with a 16-bit Q or
+        something like that, which would look correct but allow
+        trivial forgeries.  Yes, I know this rules out using MD5 with
+        DSA. ;) */
+      if (qbits < 160)
+       {
+         log_error (_("%s key uses an unsafe (%u bit) hash\n"),
+                     gcry_pk_algo_name (pkalgo), qbits);
+         return gpg_error (GPG_ERR_INTERNAL);
+       }
+
+      /* Check if we're too short.  Too long is safe as we'll
+        automatically left-truncate. */
+      nframe = gcry_md_get_algo_dlen (algo);
+      if (nframe < qbits/8)
+        {
+         log_error (_("a %u bit hash is not valid for a %u bit %s key\n"),
+                     (unsigned int)nframe*8,
+                     gcry_pk_get_nbits (pkey),
+                     gcry_pk_algo_name (pkalgo));
+          /* FIXME: we need to check the requirements for ECDSA.  */
+          if (nframe < 20 || pkalgo == GCRY_PK_DSA  )
+            return gpg_error (GPG_ERR_INTERNAL);
+        }
+
+      frame = xtrymalloc (nframe);
+      if (!frame)
+        return out_of_core ();
+      memcpy (frame, gcry_md_read (md, algo), nframe);
+      n = nframe;
+      /* Truncate.  */
+      if (n > qbits/8)
+        n = qbits/8;
     }
-
-  len = gcry_md_get_algo_dlen (algo);
-  
-  if ( len + asnlen + 4  > nframe )
+  else
     {
-      log_error ("can't encode a %d bit MD into a %d bits frame\n",
-                 (int)(len*8), (int)nbits);
-      return gpg_error (GPG_ERR_INTERNAL);
+      int i;
+      unsigned char asn[100];
+      size_t asnlen;
+      size_t len;
+
+      nframe = (nbits+7) / 8;
+
+      asnlen = DIM(asn);
+      if (!algo || gcry_md_test_algo (algo))
+        return gpg_error (GPG_ERR_DIGEST_ALGO);
+      if (gcry_md_algo_info (algo, GCRYCTL_GET_ASNOID, asn, &asnlen))
+        {
+          log_error ("no object identifier for algo %d\n", algo);
+          return gpg_error (GPG_ERR_INTERNAL);
+        }
+
+      len = gcry_md_get_algo_dlen (algo);
+
+      if ( len + asnlen + 4  > nframe )
+        {
+          log_error ("can't encode a %d bit MD into a %d bits frame\n",
+                     (int)(len*8), (int)nbits);
+          return gpg_error (GPG_ERR_INTERNAL);
+        }
+
+      /* We encode the MD in this way:
+       *
+       *          0  A PAD(n bytes)   0  ASN(asnlen bytes)  MD(len bytes)
+       *
+       * PAD consists of FF bytes.
+       */
+      frame = xtrymalloc (nframe);
+      if (!frame)
+        return out_of_core ();
+      n = 0;
+      frame[n++] = 0;
+      frame[n++] = 1; /* block type */
+      i = nframe - len - asnlen -3 ;
+      assert ( i > 1 );
+      memset ( frame+n, 0xff, i ); n += i;
+      frame[n++] = 0;
+      memcpy ( frame+n, asn, asnlen ); n += asnlen;
+      memcpy ( frame+n, gcry_md_read(md, algo), len ); n += len;
+      assert ( n == nframe );
     }
-  
-  /* We encode the MD in this way:
-   *
-   *      0  A PAD(n bytes)   0  ASN(asnlen bytes)  MD(len bytes)
-   *
-   * PAD consists of FF bytes.
-   */
-  frame = xtrymalloc (nframe);
-  if (!frame)
-    return OUT_OF_CORE (errno);
-  n = 0;
-  frame[n++] = 0;
-  frame[n++] = 1; /* block type */
-  i = nframe - len - asnlen -3 ;
-  assert ( i > 1 );
-  memset ( frame+n, 0xff, i ); n += i;
-  frame[n++] = 0;
-  memcpy ( frame+n, asn, asnlen ); n += asnlen;
-  memcpy ( frame+n, gcry_md_read(md, algo), len ); n += len;
-  assert ( n == nframe );
-  if (DBG_X509)
+  if (DBG_CRYPTO)
     {
       int j;
       log_debug ("encoded hash:");
@@ -89,49 +177,88 @@ do_encode_md (GCRY_MD_HD md, int algo,  unsigned int nbits,
         log_printf (" %02X", frame[j]);
       log_printf ("\n");
     }
-      
-  gcry_mpi_scan (r_val, GCRYMPI_FMT_USG, frame, &nframe);
+
+  gcry_mpi_scan (r_val, GCRYMPI_FMT_USG, frame, n, &nframe);
   xfree (frame);
   return 0;
 }
 
+/* Return the public key algorithm id from the S-expression PKEY.
+   FIXME: libgcrypt should provide such a function.  Note that this
+   implementation uses the names as used by libksba.  */
+static int
+pk_algo_from_sexp (gcry_sexp_t pkey)
+{
+  gcry_sexp_t l1, l2;
+  const char *name;
+  size_t n;
+  int algo;
+
+  l1 = gcry_sexp_find_token (pkey, "public-key", 0);
+  if (!l1)
+    return 0; /* Not found.  */
+  l2 = gcry_sexp_cadr (l1);
+  gcry_sexp_release (l1);
+
+  name = gcry_sexp_nth_data (l2, 0, &n);
+  if (!name)
+    algo = 0; /* Not found. */
+  else if (n==3 && !memcmp (name, "rsa", 3))
+    algo = GCRY_PK_RSA;
+  else if (n==3 && !memcmp (name, "dsa", 3))
+    algo = GCRY_PK_DSA;
+  /* Because this function is called only for verification we can
+     assume that ECC actually means ECDSA.  */
+  else if (n==3 && !memcmp (name, "ecc", 3))
+    algo = GCRY_PK_ECDSA;
+  else if (n==13 && !memcmp (name, "ambiguous-rsa", 13))
+    algo = GCRY_PK_RSA;
+  else
+    algo = 0;
+  gcry_sexp_release (l2);
+  return algo;
+}
+
 
-/*
-  Check the signature on CERT using the ISSUER-CERT.  This function
-  does only test the cryptographic signature and nothing else.  It is
-  assumed that the ISSUER_CERT is valid. */
+/* Check the signature on CERT using the ISSUER-CERT.  This function
+   does only test the cryptographic signature and nothing else.  It is
+   assumed that the ISSUER_CERT is valid. */
 int
-gpgsm_check_cert_sig (KsbaCert issuer_cert, KsbaCert cert)
+gpgsm_check_cert_sig (ksba_cert_t issuer_cert, ksba_cert_t cert)
 {
   const char *algoid;
-  GCRY_MD_HD md;
+  gcry_md_hd_t md;
   int rc, algo;
-  GCRY_MPI frame;
-  KsbaSexp p;
+  gcry_mpi_t frame;
+  ksba_sexp_t p;
   size_t n;
-  GCRY_SEXP s_sig, s_hash, s_pkey;
+  gcry_sexp_t s_sig, s_hash, s_pkey;
 
   algo = gcry_md_map_name ( (algoid=ksba_cert_get_digest_algo (cert)));
   if (!algo)
     {
-      log_error ("unknown hash algorithm `%s'\n", algoid? algoid:"?");
+      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"));
       return gpg_error (GPG_ERR_GENERAL);
     }
-  md = gcry_md_open (algo, 0);
-  if (!md)
+  rc = gcry_md_open (&md, algo, 0);
+  if (rc)
     {
-      log_error ("md_open failed: %s\n", gcry_strerror (-1));
-      return gpg_error (GPG_ERR_GENERAL);
+      log_error ("md_open failed: %s\n", gpg_strerror (rc));
+      return rc;
     }
   if (DBG_HASHING)
-    gcry_md_start_debug (md, "hash.cert");
+    gcry_md_debug (md, "hash.cert");
 
   rc = ksba_cert_hash (cert, 1, HASH_FNC, md);
   if (rc)
     {
-      log_error ("ksba_cert_hash failed: %s\n", ksba_strerror (rc));
+      log_error ("ksba_cert_hash failed: %s\n", gpg_strerror (rc));
       gcry_md_close (md);
-      return map_ksba_err (rc);
+      return rc;
     }
   gcry_md_final (md);
 
@@ -144,7 +271,7 @@ gpgsm_check_cert_sig (KsbaCert issuer_cert, KsbaCert cert)
       ksba_free (p);
       return gpg_error (GPG_ERR_BUG);
     }
-  if (DBG_X509)
+  if (DBG_CRYPTO)
     {
       int j;
       log_debug ("signature value:");
@@ -153,13 +280,13 @@ gpgsm_check_cert_sig (KsbaCert issuer_cert, KsbaCert cert)
       log_printf ("\n");
     }
 
-  rc = gcry_sexp_sscan ( &s_sig, NULL, p, n);
+  rc = gcry_sexp_sscan ( &s_sig, NULL, (char*)p, n);
   ksba_free (p);
   if (rc)
     {
-      log_error ("gcry_sexp_scan failed: %s\n", gcry_strerror (rc));
+      log_error ("gcry_sexp_scan failed: %s\n", gpg_strerror (rc));
       gcry_md_close (md);
-      return map_gcry_err (rc);
+      return rc;
     }
 
   p = ksba_cert_get_public_key (issuer_cert);
@@ -172,17 +299,18 @@ gpgsm_check_cert_sig (KsbaCert issuer_cert, KsbaCert cert)
       gcry_sexp_release (s_sig);
       return gpg_error (GPG_ERR_BUG);
     }
-  rc = gcry_sexp_sscan ( &s_pkey, NULL, p, n);
+  rc = gcry_sexp_sscan ( &s_pkey, NULL, (char*)p, n);
   ksba_free (p);
   if (rc)
     {
-      log_error ("gcry_sexp_scan failed: %s\n", gcry_strerror (rc));
+      log_error ("gcry_sexp_scan failed: %s\n", gpg_strerror (rc));
       gcry_md_close (md);
       gcry_sexp_release (s_sig);
-      return map_gcry_err (rc);
+      return rc;
     }
 
-  rc = do_encode_md (md, algo, gcry_pk_get_nbits (s_pkey), &frame);
+  rc = do_encode_md (md, algo, pk_algo_from_sexp (s_pkey),
+                     gcry_pk_get_nbits (s_pkey), s_pkey, &frame);
   if (rc)
     {
       gcry_md_close (md);
@@ -196,28 +324,32 @@ gpgsm_check_cert_sig (KsbaCert issuer_cert, KsbaCert cert)
     BUG ();
   gcry_mpi_release (frame);
 
-  
+
   rc = gcry_pk_verify (s_sig, s_hash, s_pkey);
-  if (DBG_CRYPTO)
-      log_debug ("gcry_pk_verify: %s\n", gcry_strerror (rc));
+  if (DBG_X509)
+      log_debug ("gcry_pk_verify: %s\n", gpg_strerror (rc));
   gcry_md_close (md);
   gcry_sexp_release (s_sig);
   gcry_sexp_release (s_hash);
   gcry_sexp_release (s_pkey);
-  return map_gcry_err (rc);
+  return rc;
 }
 
 
 
 int
-gpgsm_check_cms_signature (KsbaCert cert, KsbaConstSexp sigval,
-                           GCRY_MD_HD md, int algo)
+gpgsm_check_cms_signature (ksba_cert_t cert, ksba_const_sexp_t sigval,
+                           gcry_md_hd_t md, int mdalgo, int *r_pkalgo)
 {
   int rc;
-  KsbaSexp p;
-  GCRY_MPI frame;
-  GCRY_SEXP s_sig, s_hash, s_pkey;
+  ksba_sexp_t p;
+  gcry_mpi_t frame;
+  gcry_sexp_t s_sig, s_hash, s_pkey;
   size_t n;
+  int pkalgo;
+
+  if (r_pkalgo)
+    *r_pkalgo = 0;
 
   n = gcry_sexp_canon_len (sigval, 0, NULL, NULL);
   if (!n)
@@ -225,11 +357,11 @@ gpgsm_check_cms_signature (KsbaCert cert, KsbaConstSexp sigval,
       log_error ("libksba did not return a proper S-Exp\n");
       return gpg_error (GPG_ERR_BUG);
     }
-  rc = gcry_sexp_sscan (&s_sig, NULL, sigval, n);
+  rc = gcry_sexp_sscan (&s_sig, NULL, (char*)sigval, n);
   if (rc)
     {
-      log_error ("gcry_sexp_scan failed: %s\n", gcry_strerror (rc));
-      return map_gcry_err (rc);
+      log_error ("gcry_sexp_scan failed: %s\n", gpg_strerror (rc));
+      return rc;
     }
 
   p = ksba_cert_get_public_key (cert);
@@ -241,20 +373,23 @@ gpgsm_check_cms_signature (KsbaCert cert, KsbaConstSexp sigval,
       gcry_sexp_release (s_sig);
       return gpg_error (GPG_ERR_BUG);
     }
-  if (DBG_X509)
+  if (DBG_CRYPTO)
     log_printhex ("public key: ", p, n);
 
-  rc = gcry_sexp_sscan ( &s_pkey, NULL, p, n);
+  rc = gcry_sexp_sscan ( &s_pkey, NULL, (char*)p, n);
   ksba_free (p);
   if (rc)
     {
-      log_error ("gcry_sexp_scan failed: %s\n", gcry_strerror (rc));
+      log_error ("gcry_sexp_scan failed: %s\n", gpg_strerror (rc));
       gcry_sexp_release (s_sig);
-      return map_gcry_err (rc);
+      return rc;
     }
 
-
-  rc = do_encode_md (md, algo, gcry_pk_get_nbits (s_pkey), &frame);
+  pkalgo = pk_algo_from_sexp (s_pkey);
+  if (r_pkalgo)
+    *r_pkalgo = pkalgo;
+  rc = do_encode_md (md, mdalgo, pkalgo,
+                     gcry_pk_get_nbits (s_pkey), s_pkey, &frame);
   if (rc)
     {
       gcry_sexp_release (s_sig);
@@ -265,36 +400,37 @@ gpgsm_check_cms_signature (KsbaCert cert, KsbaConstSexp sigval,
   if ( gcry_sexp_build (&s_hash, NULL, "%m", frame) )
     BUG ();
   gcry_mpi_release (frame);
-  
+
   rc = gcry_pk_verify (s_sig, s_hash, s_pkey);
-  if (DBG_CRYPTO)
-      log_debug ("gcry_pk_verify: %s\n", gcry_strerror (rc));
+  if (DBG_X509)
+      log_debug ("gcry_pk_verify: %s\n", gpg_strerror (rc));
   gcry_sexp_release (s_sig);
   gcry_sexp_release (s_hash);
   gcry_sexp_release (s_pkey);
-  return map_gcry_err (rc);
+  return rc;
 }
 
 
 
 int
-gpgsm_create_cms_signature (KsbaCert cert, GCRY_MD_HD md, int mdalgo,
-                            char **r_sigval)
+gpgsm_create_cms_signature (ctrl_t ctrl, ksba_cert_t cert,
+                            gcry_md_hd_t md, int mdalgo,
+                            unsigned char **r_sigval)
 {
   int rc;
-  char *grip;
+  char *grip, *desc;
   size_t siglen;
 
   grip = gpgsm_get_keygrip_hexstring (cert);
   if (!grip)
     return gpg_error (GPG_ERR_BAD_CERT);
 
-  rc = gpgsm_agent_pksign (grip, gcry_md_read(md, mdalgo), 
+  desc = gpgsm_format_keydesc (cert);
+
+  rc = gpgsm_agent_pksign (ctrl, grip, desc, gcry_md_read(md, mdalgo),
                            gcry_md_get_algo_dlen (mdalgo), mdalgo,
                            r_sigval, &siglen);
+  xfree (desc);
   xfree (grip);
   return rc;
 }
-
-
-