* certcheck.c (do_encode_md): Add arg PKEY. Add support for DSA2
[gnupg.git] / sm / certcheck.c
index 5fb3767..6dbc72d 100644 (file)
@@ -15,7 +15,8 @@
  *
  * 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
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
  */
 
 #include <config.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_t md, int algo, int pkalgo, unsigned int nbits,
-              gcry_mpi_t *r_val)
+              gcry_sexp_t pkey, gcry_mpi_t *r_val)
 {
   int n;
   size_t nframe;
   unsigned char *frame;
 
-  if (pkalgo == GCRY_PK_DSA)
+  if (pkalgo == GCRY_PK_DSA || pkalgo == GCRY_PK_ECDSA)
     {
+      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 != 20)
+      if (nframe < qbits/8)
         {
-          log_error (_("DSA requires the use of a 160 bit hash algorithm\n"));
-          return gpg_error (GPG_ERR_INTERNAL);
+         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 (errno);
+        return out_of_core ();
       memcpy (frame, gcry_md_read (md, algo), nframe);
       n = nframe;
+      /* Truncate.  */
+      if (n > qbits/8)
+        n = qbits/8;
     }
   else
     {
@@ -67,6 +133,8 @@ do_encode_md (gcry_md_hd_t md, int algo, int pkalgo, unsigned int nbits,
       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);
@@ -90,7 +158,7 @@ do_encode_md (gcry_md_hd_t md, int algo, int pkalgo, unsigned int nbits,
        */
       frame = xtrymalloc (nframe);
       if (!frame)
-        return OUT_OF_CORE (errno);
+        return out_of_core ();
       n = 0;
       frame[n++] = 0;
       frame[n++] = 1; /* block type */
@@ -140,6 +208,10 @@ pk_algo_from_sexp (gcry_sexp_t pkey)
     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
@@ -149,10 +221,9 @@ pk_algo_from_sexp (gcry_sexp_t pkey)
 }
 
 
-/*
-  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 (ksba_cert_t issuer_cert, ksba_cert_t cert)
 {
@@ -240,7 +311,7 @@ gpgsm_check_cert_sig (ksba_cert_t issuer_cert, ksba_cert_t cert)
     }
 
   rc = do_encode_md (md, algo, pk_algo_from_sexp (s_pkey),
-                     gcry_pk_get_nbits (s_pkey), &frame);
+                     gcry_pk_get_nbits (s_pkey), s_pkey, &frame);
   if (rc)
     {
       gcry_md_close (md);
@@ -313,7 +384,7 @@ gpgsm_check_cms_signature (ksba_cert_t cert, ksba_const_sexp_t sigval,
 
 
   rc = do_encode_md (md, algo, pk_algo_from_sexp (s_pkey),
-                     gcry_pk_get_nbits (s_pkey), &frame);
+                     gcry_pk_get_nbits (s_pkey), s_pkey, &frame);
   if (rc)
     {
       gcry_sexp_release (s_sig);