]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
Improved TLS 1.2 support. Added support for the SignatureAlgorithm extension
authorNikos Mavrogiannopoulos <nmav@gnutls.org>
Sun, 1 Nov 2009 00:54:08 +0000 (02:54 +0200)
committerNikos Mavrogiannopoulos <nmav@gnutls.org>
Sun, 1 Nov 2009 00:54:08 +0000 (02:54 +0200)
as well for the SignatureAlgorithm in certificate request.

Limitation for TLS 1.2 clients:
 Only SHA1 or SHA256 are supported for generating signatures in
certificate verify message. That is to avoid storing all handshake
messages in memory. To be reconsidered in the future.

21 files changed:
lib/Makefile.am
lib/auth_cert.c
lib/auth_cert.h
lib/ext_signature.c [new file with mode: 0644]
lib/ext_signature.h [new file with mode: 0644]
lib/gnutls_algorithms.c
lib/gnutls_algorithms.h
lib/gnutls_cert.c
lib/gnutls_cert.h
lib/gnutls_errors.c
lib/gnutls_extensions.c
lib/gnutls_handshake.c
lib/gnutls_int.h
lib/gnutls_priority.c
lib/gnutls_sig.c
lib/gnutls_sig.h
lib/gnutls_state.c
lib/gnutls_state.h
lib/gnutls_x509.c
lib/includes/gnutls/gnutls.h.in
lib/openpgp/gnutls_openpgp.c

index 350a0875c0eae85bcfccd1ccda675c459ccf1efb..07d3a97823e1b7abad4be2be15d64ceb51be7ea3 100644 (file)
@@ -80,7 +80,7 @@ COBJECTS = gnutls_record.c gnutls_compress.c debug.c gnutls_cipher.c  \
        gnutls_rsa_export.c auth_rsa_export.c ext_server_name.c         \
        auth_dh_common.c gnutls_helper.c gnutls_supplemental.c          \
        crypto.c random.c pk-libgcrypt.c mpi-libgcrypt.c                \
-       rnd-libgcrypt.c cipher-libgcrypt.c mac-libgcrypt.c
+       rnd-libgcrypt.c cipher-libgcrypt.c mac-libgcrypt.c ext_signature.c
 
 if ENABLE_OPRFI
 COBJECTS += $(OPRFI_COBJECTS)
@@ -100,7 +100,7 @@ HFILES = debug.h gnutls_compress.h gnutls_cipher.h gnutls_buffers.h \
        ext_srp.h gnutls_srp.h auth_srp.h auth_srp_passwd.h             \
        gnutls_helper.h auth_psk.h auth_psk_passwd.h                    \
        gnutls_supplemental.h ext_oprfi.h crypto.h random.h             \
-       ext_session_ticket.h
+       ext_session_ticket.h ext_signature.h
 
 # Separate so we can create the documentation
 
index f44731356a1dd0dbdb8cec2447b719adf183dfe2..3affa3271614856a6977a4a6ac99ecb1fd88fa2a 100644 (file)
@@ -43,6 +43,7 @@
 #include <gnutls_state.h>
 #include <gnutls_pk.h>
 #include <gnutls_x509.h>
+#include <ext_signature.h>
 #include "debug.h"
 
 #ifdef ENABLE_OPENPGP
@@ -112,6 +113,8 @@ _gnutls_copy_certificate_auth_info (cert_auth_info_t info,
   info->ncerts = ncerts;
 
   info->cert_type = cert[0].cert_type;
+  info->sign_algo = cert[0].sign_algo;
+
 #ifdef ENABLE_OPENPGP
   if (cert[0].cert_type == GNUTLS_CRT_OPENPGP)
     {
@@ -1023,6 +1026,14 @@ _gnutls_proc_x509_server_certificate (gnutls_session_t session,
          gnutls_assert ();
          goto cleanup;
        }
+      
+      /* check if signature algorithm is supported */
+      ret = _gnutls_session_sign_algo_supported(session, peer_certificate_list[j].sign_algo, 0);
+      if (ret < 0)
+        {
+          gnutls_assert();
+          goto cleanup;
+        }
 
       p += len;
     }
@@ -1356,8 +1367,15 @@ _gnutls_proc_cert_cert_req (gnutls_session_t session, opaque * data,
       DECR_LEN (dsize, 2);
       hash_num = _gnutls_read_uint16 (p);
       p += 2;
-
       DECR_LEN (dsize, hash_num);
+
+      ret = _gnutls_sign_algo_parse_data( session, p, hash_num);
+      if (ret < 0)
+        {
+          gnutls_assert();
+          return ret;
+        }
+
       p += hash_num;
     }
 
@@ -1401,6 +1419,10 @@ _gnutls_gen_cert_client_cert_vrfy (gnutls_session_t session, opaque ** data)
   gnutls_privkey *apr_pkey;
   int apr_cert_list_length, size;
   gnutls_datum_t signature;
+  int total_data;
+  opaque* p;
+  gnutls_sign_algorithm_t sign_algo;
+  gnutls_protocol_t ver = gnutls_protocol_get_version (session);
 
   *data = NULL;
 
@@ -1423,26 +1445,46 @@ _gnutls_gen_cert_client_cert_vrfy (gnutls_session_t session, opaque ** data)
          gnutls_assert ();
          return ret;
        }
+       sign_algo = ret;
     }
   else
     {
       return 0;
     }
 
-  *data = gnutls_malloc (signature.size + 2);
+  total_data = signature.size + 2;
+
+  /* add hash and signature algorithms */
+  if (_gnutls_version_has_selectable_sighash(ver))
+    {
+      total_data+=2;
+    }
+
+  *data = gnutls_malloc (total_data);
   if (*data == NULL)
     {
       _gnutls_free_datum (&signature);
       return GNUTLS_E_MEMORY_ERROR;
     }
+
+  p = *data;
+  if (_gnutls_version_has_selectable_sighash(ver))
+    {
+      /* error checking is not needed here since we have used those algorithms */
+      p[0] = _gnutls_sign_algo_hash2num(_gnutls_sign_get_hash_algorithm(sign_algo));
+      p[1] = _gnutls_sign_algo_pk2num(_gnutls_sign_get_pk_algorithm(sign_algo));
+      p+=2;
+    }
+
   size = signature.size;
-  _gnutls_write_uint16 (size, *data);
+  _gnutls_write_uint16 (size, p);
 
-  memcpy (&(*data)[2], signature.data, size);
+  p+=2;
+  memcpy (p, signature.data, size);
 
   _gnutls_free_datum (&signature);
 
-  return size + 2;
+  return total_data;
 }
 
 int
@@ -1455,6 +1497,8 @@ _gnutls_proc_cert_client_cert_vrfy (gnutls_session_t session,
   gnutls_datum_t sig;
   cert_auth_info_t info = _gnutls_get_auth_info (session);
   gnutls_cert peer_cert;
+  gnutls_sign_algorithm_t sign_algo = GNUTLS_SIGN_UNKNOWN;
+  gnutls_protocol_t ver = gnutls_protocol_get_version (session);
 
   if (info == NULL || info->ncerts == 0)
     {
@@ -1463,6 +1507,18 @@ _gnutls_proc_cert_client_cert_vrfy (gnutls_session_t session,
       return GNUTLS_E_INTERNAL_ERROR;
     }
 
+  if (_gnutls_version_has_selectable_sighash(ver))
+    {
+      DECR_LEN (dsize, 2);
+      sign_algo = _gnutls_sign_algo_num2sig (pdata[0], pdata[1]);
+      if (sign_algo == GNUTLS_PK_UNKNOWN)
+        {
+          gnutls_assert();
+          return GNUTLS_E_UNSUPPORTED_SIGNATURE_ALGORITHM;
+        }
+      pdata+=2;
+    }
+
   DECR_LEN (dsize, 2);
   size = _gnutls_read_uint16 (pdata);
   pdata += 2;
@@ -1482,7 +1538,7 @@ _gnutls_proc_cert_client_cert_vrfy (gnutls_session_t session,
       return ret;
     }
 
-  if ((ret = _gnutls_verify_sig_hdata (session, &peer_cert, &sig)) < 0)
+  if ((ret = _gnutls_verify_sig_hdata (session, &peer_cert, &sig, sign_algo)) < 0)
     {
       gnutls_assert ();
       _gnutls_gcert_deinit (&peer_cert);
@@ -1498,9 +1554,10 @@ int
 _gnutls_gen_cert_server_cert_req (gnutls_session_t session, opaque ** data)
 {
   gnutls_certificate_credentials_t cred;
-  int size;
+  int size, ret;
   opaque *pdata;
   gnutls_protocol_t ver = gnutls_protocol_get_version (session);
+  const int signalgosize = 2+MAX_SIGNATURE_ALGORITHMS*2;
 
   /* Now we need to generate the RDN sequence. This is
    * already in the CERTIFICATE_CRED structure, to improve
@@ -1525,7 +1582,7 @@ _gnutls_gen_cert_server_cert_req (gnutls_session_t session, opaque ** data)
   if (_gnutls_version_has_selectable_sighash(ver))
     /* Need two bytes to announce the number of supported hash
        functions (see below).  */
-    size += 2;
+    size += signalgosize;
 
   (*data) = gnutls_malloc (size);
   pdata = (*data);
@@ -1544,9 +1601,16 @@ _gnutls_gen_cert_server_cert_req (gnutls_session_t session, opaque ** data)
 
   if (_gnutls_version_has_selectable_sighash(ver))
     {
-      /* Supported hashes (nothing for now -- FIXME). */
-      _gnutls_write_uint16 (0, pdata);
-      pdata += 2;
+      ret = _gnutls_sign_algo_write_params(session, pdata, signalgosize);
+      if (ret < 0)
+        {
+          gnutls_assert();
+          return ret;
+        }
+      
+      /* recalculate size */
+      size=size-signalgosize+ret;
+      pdata += ret;
     }
 
   if (session->security_parameters.cert_type == GNUTLS_CRT_X509 &&
@@ -1842,10 +1906,10 @@ _gnutls_server_select_cert (gnutls_session_t session,
       if (requested_algo == GNUTLS_PK_ANY ||
          requested_algo == cred->cert_list[i][0].subject_pk_algorithm)
        {
-         /* if cert type matches 
+         /* if cert type and signature algorithm matches 
           */
          if (session->security_parameters.cert_type ==
-             cred->cert_list[i][0].cert_type)
+             cred->cert_list[i][0].cert_type && _gnutls_session_sign_algo_requested(session, cred->cert_list[i][0].sign_algo) == 0)
            {
              idx = i;
              break;
index 1b808dcfb68350f4d13bede7755855f3db839592..f982bc7a18b859baf4294a35a55de8ae35a9b628 100644 (file)
@@ -117,6 +117,7 @@ typedef struct cert_auth_info_st
   unsigned int ncerts;         /* holds the size of the list above */
 
   gnutls_certificate_type_t cert_type;
+  gnutls_sign_algorithm_t sign_algo;
 #ifdef ENABLE_OPENPGP
   int use_subkey;
   gnutls_openpgp_keyid_t subkey_id;
diff --git a/lib/ext_signature.c b/lib/ext_signature.c
new file mode 100644 (file)
index 0000000..5231174
--- /dev/null
@@ -0,0 +1,332 @@
+/*
+ * Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GNUTLS.
+ *
+ * The GNUTLS library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA
+ *
+ */
+
+/* This file contains the code the Certificate Type TLS extension.
+ * This extension is currently gnutls specific.
+ */
+
+#include "gnutls_int.h"
+#include "gnutls_errors.h"
+#include "gnutls_num.h"
+#include <ext_signature.h>
+#include <gnutls_state.h>
+#include <gnutls_num.h>
+#include <gnutls_algorithms.h>
+
+int _gnutls_sign_algo_pk2num (gnutls_pk_algorithm_t pk)
+{
+  switch (pk)
+    {
+    case GNUTLS_PK_RSA:
+      return 1;
+    case GNUTLS_PK_DSA:
+      return 2;
+    default:
+      gnutls_assert ();
+      return GNUTLS_E_INTERNAL_ERROR;
+    }
+}
+
+int _gnutls_sign_algo_hash2num (gnutls_digest_algorithm_t hash)
+{
+  switch (hash)
+    {
+    case GNUTLS_DIG_MD5:
+      return 1;
+    case GNUTLS_DIG_SHA1:
+      return 2;
+    case GNUTLS_DIG_SHA224:
+      return 3;
+    case GNUTLS_DIG_SHA256:
+      return 4;
+    case GNUTLS_DIG_SHA384:
+      return 5;
+    case GNUTLS_DIG_SHA512:
+      return 6;
+    default:
+      gnutls_assert ();
+      return GNUTLS_E_INTERNAL_ERROR;
+    }
+}
+
+gnutls_sign_algorithm_t
+_gnutls_sign_algo_num2sig (int hash, int sig)
+{
+  if (sig == 1)                 /* rsa */
+    {
+      switch (hash)
+        {
+        case 2:                /* sha1 */
+          return GNUTLS_SIGN_RSA_SHA1;
+        case 3:
+          return GNUTLS_SIGN_RSA_SHA224;
+        case 4:
+          return GNUTLS_SIGN_RSA_SHA256;
+        case 5:
+          return GNUTLS_SIGN_RSA_SHA384;
+        case 6:
+          return GNUTLS_SIGN_RSA_SHA512;
+        default:
+          return GNUTLS_SIGN_UNKNOWN;
+        }
+    }
+
+  if (sig == 2)                 /* DSA */
+    {
+      switch (hash)
+        {
+        case 2:                /* sha1 */
+          return GNUTLS_SIGN_DSA_SHA1;
+        default:
+          return GNUTLS_SIGN_UNKNOWN;
+        }
+    }
+
+  return GNUTLS_SIGN_UNKNOWN;
+}
+
+/* generates a SignatureAndHashAlgorithm structure with length as prefix
+ * by using the setup priorities.
+ */
+int _gnutls_sign_algo_write_params(gnutls_session_t session, opaque *data, size_t max_data_size)
+{
+opaque* p = data;
+int len, i ,j;
+int ret, hash, pk;
+
+          len = session->internals.priorities.sign_algo.algorithms * 2;
+          if (max_data_size < len + 2)
+            {
+              gnutls_assert ();
+              return GNUTLS_E_SHORT_MEMORY_BUFFER;
+            }
+
+          _gnutls_write_uint16 (len, p);
+          p += 2;
+
+          for (i = j = 0; i < len; i += 2, j++)
+            {
+              hash =
+                _gnutls_sign_get_hash_algorithm (session->
+                                                 internals.priorities.
+                                                 sign_algo.priority[j]);
+              if (hash == GNUTLS_DIG_UNKNOWN)
+                {
+                  gnutls_assert ();
+                  return GNUTLS_E_INTERNAL_ERROR;
+                }
+              pk =
+                _gnutls_sign_get_pk_algorithm (session->internals.priorities.
+                                               sign_algo.priority[j]);
+              if (pk == GNUTLS_PK_UNKNOWN)
+                {
+                  gnutls_assert ();
+                  return GNUTLS_E_INTERNAL_ERROR;
+                }
+              ret = _gnutls_sign_algo_hash2num (hash);
+              if (ret < 0)
+                {
+                  gnutls_assert ();
+                  return ret;
+                }
+              *p = ret;
+              p++;
+
+              ret = _gnutls_sign_algo_pk2num (pk);
+              if (ret < 0)
+                {
+                  gnutls_assert ();
+                  return ret;
+                }
+
+              *p = ret;
+              p++;
+
+            }
+          return len + 2;
+}
+
+/* Parses the Signature Algorithm structure and stores data into
+ * session->security_parameters.extensions. 
+ */
+int
+_gnutls_sign_algo_parse_data (gnutls_session_t session, const opaque * data,
+                              size_t data_size)
+{
+  int sig, i;
+
+  session->security_parameters.extensions.sign_algorithms_size = 0;
+
+  for (i = 0; i < data_size; i += 2)
+    {
+      sig = _gnutls_sign_algo_num2sig (data[i], data[i + 1]);
+      if (sig != GNUTLS_SIGN_UNKNOWN)
+        {
+          session->security_parameters.extensions.sign_algorithms[session->
+                                                                  security_parameters.extensions.
+                                                                  sign_algorithms_size++]
+            = sig;
+          if (session->security_parameters.extensions.sign_algorithms_size ==
+              MAX_SIGNATURE_ALGORITHMS)
+            break;
+        }
+    }
+
+  return 0;
+}
+
+/* 
+ * In case of a server: if a SIGNATURE_ALGORITHMS extension type is received then it stores
+ * into the session security parameters the new value. 
+ *
+ * In case of a client: If a signature_algorithms have been specified then it is an error;
+ *
+ */
+
+int
+_gnutls_signature_algorithm_recv_params (gnutls_session_t session,
+                                         const opaque * data,
+                                         size_t _data_size)
+{
+  ssize_t data_size = _data_size;
+
+  if (session->security_parameters.entity == GNUTLS_CLIENT)
+    {
+      /* nothing for now */
+      gnutls_assert ();
+      return GNUTLS_E_UNEXPECTED_PACKET;
+    }
+  else
+    {
+      /* SERVER SIDE - we must check if the sent cert type is the right one 
+       */
+      if (data_size > 2)
+        {
+          uint16_t len;
+
+
+          DECR_LEN (data_size, 2);
+          len = _gnutls_read_uint16 (data);
+          DECR_LEN (data_size, len);
+
+          _gnutls_sign_algo_parse_data (session, data + 2, len);
+
+        }
+    }
+
+  return 0;
+}
+
+/* returns data_size or a negative number on failure
+ */
+int
+_gnutls_signature_algorithm_send_params (gnutls_session_t session,
+                                         opaque * data, size_t data_size)
+{
+  int ret;
+  gnutls_protocol_t ver = gnutls_protocol_get_version (session);
+
+  /* this function sends the client extension data */
+  if (session->security_parameters.entity == GNUTLS_CLIENT
+      && _gnutls_version_has_selectable_sighash (ver))
+    {
+
+      if (session->internals.priorities.sign_algo.algorithms > 0)
+        {
+          ret = _gnutls_sign_algo_write_params(session, data, data_size);
+          if (ret < 0)
+            {
+              gnutls_assert();
+              return ret;
+            }
+        }
+    }
+
+  /* if we are here it means we don't send the extension */
+  return 0;
+}
+
+/* Returns a requested by the peer signature algorithm that
+ * matches the given public key algorithm. Index can be increased
+ * to return the second choice etc.
+ */
+gnutls_sign_algorithm_t
+_gnutls_session_get_sign_algo (gnutls_session_t session,
+                               gnutls_pk_algorithm_t pk,
+                               gnutls_digest_algorithm_t * hash)
+{
+  unsigned i;
+  gnutls_protocol_t ver = gnutls_protocol_get_version (session);
+
+
+  if (!_gnutls_version_has_selectable_sighash (ver) || session->security_parameters.extensions.sign_algorithms_size == 0)       /* none set, allow all */
+    {
+      *hash = GNUTLS_DIG_SHA1;
+      return _gnutls_x509_pk_to_sign (pk, *hash);
+    }
+
+  for (i = 0;
+       i < session->security_parameters.extensions.sign_algorithms_size; i++)
+    {
+      if (_gnutls_sign_get_pk_algorithm
+          (session->security_parameters.extensions.sign_algorithms[i]) == pk)
+        {
+          *hash =
+            _gnutls_sign_get_hash_algorithm (session->security_parameters.
+                                             extensions.sign_algorithms[i]);
+          return session->security_parameters.extensions.sign_algorithms[i];
+        }
+    }
+
+  return GNUTLS_SIGN_UNKNOWN;
+}
+
+
+/* Check if the given signature algorithm is accepted by
+ * the peer. Returns 0 on success or a negative value
+ * on error.
+ */
+int
+_gnutls_session_sign_algo_requested (gnutls_session_t session,
+                                     gnutls_sign_algorithm_t sig)
+{
+  unsigned i;
+  gnutls_protocol_t ver = gnutls_protocol_get_version (session);
+
+  if (!_gnutls_version_has_selectable_sighash (ver) || session->security_parameters.extensions.sign_algorithms_size == 0)       /* none set, allow all */
+    {
+      return 0;
+    }
+
+  for (i = 0;
+       i < session->security_parameters.extensions.sign_algorithms_size; i++)
+    {
+      if (session->security_parameters.extensions.sign_algorithms[i] == sig)
+        {
+          return 0;             /* ok */
+        }
+    }
+
+  return GNUTLS_E_UNSUPPORTED_SIGNATURE_ALGORITHM;
+}
diff --git a/lib/ext_signature.h b/lib/ext_signature.h
new file mode 100644 (file)
index 0000000..46b6154
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GNUTLS.
+ *
+ * The GNUTLS library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA
+ *
+ */
+
+/* signature algorithms extension
+ */
+int _gnutls_signature_algorithm_recv_params (gnutls_session_t session,
+                                  const opaque * data, size_t data_size);
+int _gnutls_signature_algorithm_send_params (gnutls_session_t session, opaque * data,
+                                  size_t);
+int _gnutls_session_sign_algo_requested (gnutls_session_t session,
+                                     gnutls_sign_algorithm_t sig);
+gnutls_sign_algorithm_t _gnutls_session_get_sign_algo (gnutls_session_t session, gnutls_pk_algorithm_t pk, gnutls_digest_algorithm_t *hash);
+int _gnutls_sign_algo_parse_data(gnutls_session_t session, const opaque* data, size_t data_size);
+int _gnutls_sign_algo_write_params(gnutls_session_t session, opaque *data, size_t max_data_size);
+int _gnutls_sign_algo_pk2num (gnutls_pk_algorithm_t pk);
+int _gnutls_sign_algo_hash2num (gnutls_digest_algorithm_t hash);
+gnutls_sign_algorithm_t _gnutls_sign_algo_num2sig (int hash, int sig);
index 8f1eb9fc05759a18061f850346a007eafad59c2b..873648e54c42fd8a74d09fea71ada3a7e3b7a8fc 100644 (file)
@@ -1833,7 +1833,7 @@ typedef struct gnutls_sign_entry gnutls_sign_entry;
 #define TLS_SIGN_AID_UNKNOWN {255, 255}
 
 static const gnutls_sign_entry sign_algorithms[] = {
-  {"RSA-SHA", SIG_RSA_SHA1_OID, GNUTLS_SIGN_RSA_SHA1, GNUTLS_PK_RSA,
+  {"RSA-SHA1", SIG_RSA_SHA1_OID, GNUTLS_SIGN_RSA_SHA1, GNUTLS_PK_RSA,
    GNUTLS_MAC_SHA1, {2, 1}},
   {"RSA-SHA256", SIG_RSA_SHA256_OID, GNUTLS_SIGN_RSA_SHA256, GNUTLS_PK_RSA,
    GNUTLS_MAC_SHA256, {4, 1}},
@@ -1843,7 +1843,7 @@ static const gnutls_sign_entry sign_algorithms[] = {
    GNUTLS_MAC_SHA512, {6, 1}},
   {"RSA-RMD160", SIG_RSA_RMD160_OID, GNUTLS_SIGN_RSA_RMD160, GNUTLS_PK_RSA,
    GNUTLS_MAC_RMD160, TLS_SIGN_AID_UNKNOWN},
-  {"DSA-SHA", SIG_DSA_SHA1_OID, GNUTLS_SIGN_DSA_SHA1, GNUTLS_PK_DSA,
+  {"DSA-SHA1", SIG_DSA_SHA1_OID, GNUTLS_SIGN_DSA_SHA1, GNUTLS_PK_DSA,
    GNUTLS_MAC_SHA1, {2, 2}},
   {"RSA-MD5", SIG_RSA_MD5_OID, GNUTLS_SIGN_RSA_MD5, GNUTLS_PK_RSA,
    GNUTLS_MAC_MD5, {1, 1}},
@@ -2001,9 +2001,9 @@ _gnutls_x509_sign_to_oid (gnutls_pk_algorithm_t pk,
 }
 
 gnutls_mac_algorithm_t
-_gnutls_sign_get_mac_algorithm (gnutls_sign_algorithm_t sign)
+_gnutls_sign_get_hash_algorithm (gnutls_sign_algorithm_t sign)
 {
-  gnutls_mac_algorithm_t ret = GNUTLS_MAC_UNKNOWN;
+  gnutls_mac_algorithm_t ret = GNUTLS_DIG_UNKNOWN;
 
   GNUTLS_SIGN_ALG_LOOP (ret = p->mac);
 
index 0a2faac52fac6e19876fbccbcf716b0aeb3fccc7..8736dc8afcf6056f67db82a3d6e18fce8af5d62b 100644 (file)
@@ -103,11 +103,12 @@ enum encipher_type _gnutls_kx_encipher_type (gnutls_kx_algorithm_t algorithm);
 gnutls_sign_algorithm_t _gnutls_x509_oid2sign_algorithm (const char *oid);
 gnutls_sign_algorithm_t _gnutls_x509_pk_to_sign (gnutls_pk_algorithm_t pk,
                                                 gnutls_mac_algorithm_t mac);
+gnutls_pk_algorithm_t _gnutls_x509_sign_to_pk (gnutls_sign_algorithm_t sign);
 const char *_gnutls_x509_sign_to_oid (gnutls_pk_algorithm_t,
                                      gnutls_mac_algorithm_t mac);
 gnutls_sign_algorithm_t _gnutls_tls_aid_to_sign (sign_algorithm_st aid);
 sign_algorithm_st _gnutls_sign_to_tls_aid (gnutls_sign_algorithm_t sign);
-gnutls_mac_algorithm_t _gnutls_sign_get_mac_algorithm (gnutls_sign_algorithm_t);
+gnutls_mac_algorithm_t _gnutls_sign_get_hash_algorithm (gnutls_sign_algorithm_t);
 gnutls_pk_algorithm_t _gnutls_sign_get_pk_algorithm (gnutls_sign_algorithm_t);
 
 int _gnutls_mac_priority (gnutls_session_t session,
index 42db17cb793b4eb7b27104d657167d4b8618d2e1..5b110ad298bc2087b64a0093120bb08dd54388af 100644 (file)
@@ -817,6 +817,7 @@ _gnutls_x509_crt_to_gcert (gnutls_cert * gcert,
 
   memset (gcert, 0, sizeof (gnutls_cert));
   gcert->cert_type = GNUTLS_CRT_X509;
+  gcert->sign_algo = gnutls_x509_crt_get_signature_algorithm(cert);
 
   if (!(flags & CERT_NO_COPY))
     {
index 2a752270fe010871993e70dfeb439b0aa7562fbc..c153607d3f0136d4ef80ce0f6c3361387675178d 100644 (file)
@@ -71,6 +71,7 @@ typedef struct gnutls_cert
   /* holds the type (PGP, X509)
    */
   gnutls_certificate_type_t cert_type;
+  gnutls_sign_algorithm_t sign_algo;
 
   gnutls_datum_t raw;
 
index 94be5af487e7152123bdc86c72cba9b1e8ce1259..b5d8bb6cb1e5c6aa4cddafc9c9b513f1f7a8d0a4 100644 (file)
@@ -226,6 +226,8 @@ static const gnutls_error_entry error_algorithms[] = {
 
   ERROR_ENTRY (N_("The OpenPGP fingerprint is not supported."),
               GNUTLS_E_OPENPGP_FINGERPRINT_UNSUPPORTED, 1),
+  ERROR_ENTRY (N_("The signature algorithm is not supported."),
+              GNUTLS_E_UNSUPPORTED_SIGNATURE_ALGORITHM, 1),
   ERROR_ENTRY (N_("The certificate has unsupported attributes."),
               GNUTLS_E_X509_UNSUPPORTED_ATTRIBUTE, 1),
   ERROR_ENTRY (N_("The OID is not supported."), GNUTLS_E_X509_UNSUPPORTED_OID,
index b7baca28b9d58a388116fb4a6d49a16ee965b7c6..5f2c47cc6bc88c45c3e39d90d20c9c54d24a3487 100644 (file)
@@ -36,6 +36,7 @@
 #include <ext_oprfi.h>
 #include <ext_srp.h>
 #include <ext_session_ticket.h>
+#include <ext_signature.h>
 #include <gnutls_num.h>
 
 typedef struct
@@ -349,6 +350,14 @@ _gnutls_ext_init (void)
     return ret;
 #endif
 
+  ret = gnutls_ext_register (GNUTLS_EXTENSION_SIGNATURE_ALGORITHMS,
+                             "SIGNATURE_ALGORITHMS",
+                             GNUTLS_EXT_TLS,
+                             _gnutls_signature_algorithm_recv_params,
+                             _gnutls_signature_algorithm_send_params);
+  if (ret != GNUTLS_E_SUCCESS)
+    return ret;
+
   return GNUTLS_E_SUCCESS;
 }
 
index 7423f2737c478a3b8fe90502d79a794a77f2b709..b7d5af33104a417cf9e1d80798a0649d75d0779d 100644 (file)
@@ -90,7 +90,9 @@ _gnutls_handshake_hash_buffers_clear (gnutls_session_t session)
           handshake_mac_handle_type == HANDSHAKE_MAC_TYPE_12)
     {
       _gnutls_hash_deinit (&session->internals.
-                          handshake_mac_handle.tls12.mac, NULL);
+                          handshake_mac_handle.tls12.sha256, NULL);
+      _gnutls_hash_deinit (&session->internals.
+                          handshake_mac_handle.tls12.sha1, NULL);
     }
   session->security_parameters.handshake_mac_handle_type = 0;
   session->internals.handshake_mac_handle_init = 0;
@@ -261,7 +263,7 @@ _gnutls_finished (gnutls_session_t session, int type, void *ret)
           handshake_mac_handle_type == HANDSHAKE_MAC_TYPE_12)
     {
       rc = _gnutls_hash_copy (&td_sha, &session->internals.
-                             handshake_mac_handle.tls12.mac);
+                             handshake_mac_handle.tls12.sha256);
       if (rc < 0)
        {
          gnutls_assert ();
@@ -574,7 +576,8 @@ _gnutls_handshake_hash_pending (gnutls_session_t session)
         } 
       else if (session->security_parameters.handshake_mac_handle_type == HANDSHAKE_MAC_TYPE_12)
         {
-          _gnutls_hash (&session->internals.handshake_mac_handle.tls12.mac, data, siz);
+          _gnutls_hash (&session->internals.handshake_mac_handle.tls12.sha256, data, siz);
+          _gnutls_hash (&session->internals.handshake_mac_handle.tls12.sha1, data, siz);
         }
     }
 
@@ -984,7 +987,9 @@ _gnutls_handshake_hash_add_sent (gnutls_session_t session,
         }
       else if (session->security_parameters.handshake_mac_handle_type == HANDSHAKE_MAC_TYPE_12) 
         { 
-          _gnutls_hash (&session->internals.handshake_mac_handle.tls12.mac, dataptr,
+          _gnutls_hash (&session->internals.handshake_mac_handle.tls12.sha256, dataptr,
+                   datalen);
+          _gnutls_hash (&session->internals.handshake_mac_handle.tls12.sha1, dataptr,
                    datalen);
         }
     }
@@ -2301,14 +2306,22 @@ _gnutls_handshake_hash_init (gnutls_session_t session)
         /* The algorithm to compute hash over handshake messages must be
           same as the one used as the basis for PRF.  By now we use
           SHA256. */
-          gnutls_digest_algorithm_t hash_algo = GNUTLS_MAC_SHA256;
+          ret =
+           _gnutls_hash_init (&session->internals.handshake_mac_handle.tls12.sha256,
+                          GNUTLS_DIG_SHA256);
+           if (ret < 0)
+            {
+              gnutls_assert ();
+              return GNUTLS_E_MEMORY_ERROR;
+            }
 
           ret =
-           _gnutls_hash_init (&session->internals.handshake_mac_handle.tls12.mac,
-                          hash_algo);
+           _gnutls_hash_init (&session->internals.handshake_mac_handle.tls12.sha1,
+                          GNUTLS_DIG_SHA1);
            if (ret < 0)
             {
               gnutls_assert ();
+              _gnutls_hash_deinit(&session->internals.handshake_mac_handle.tls12.sha256, NULL);
               return GNUTLS_E_MEMORY_ERROR;
             }
         }
index ac1da9df3ef5f785d89b7037d51123301a937d5f..5f07164aac2090be3797618ccdc139769825b880 100644 (file)
@@ -175,6 +175,7 @@ typedef enum extensions_t
   GNUTLS_EXTENSION_OPAQUE_PRF_INPUT = ENABLE_OPRFI,
 #endif
   GNUTLS_EXTENSION_SRP = 12,
+  GNUTLS_EXTENSION_SIGNATURE_ALGORITHMS = 13,
   GNUTLS_EXTENSION_SESSION_TICKET = 35,
   GNUTLS_EXTENSION_INNER_APPLICATION = 37703
 } extensions_t;
@@ -297,6 +298,7 @@ typedef struct
 } server_name_st;
 
 #define MAX_SERVER_NAME_EXTENSIONS 3
+#define MAX_SIGNATURE_ALGORITHMS 16
 
 struct gnutls_session_ticket_key_st {
   opaque key_name[SESSION_TICKET_KEY_NAME_SIZE];
@@ -312,6 +314,10 @@ typedef struct
 
   opaque srp_username[MAX_SRP_USERNAME + 1];
 
+  /* TLS 1.2 signature algorithms */
+  gnutls_sign_algorithm_t sign_algorithms[MAX_SIGNATURE_ALGORITHMS];
+  uint16_t sign_algorithms_size;
+
   /* TLS/IA data. */
   int gnutls_ia_enable, gnutls_ia_peer_enable;
   int gnutls_ia_allowskip, gnutls_ia_peer_allowskip;
@@ -437,6 +443,7 @@ struct gnutls_priority_st
   priority_st compression;
   priority_st protocol;
   priority_st cert_type;
+  priority_st sign_algo;
 
   /* to disable record padding */
   int no_padding;
@@ -489,7 +496,8 @@ typedef struct
         } tls10;
       struct
         {
-          digest_hd_st mac;    /* hash of the handshake messages for TLS 1.2+ */
+          digest_hd_st sha1;   /* hash of the handshake messages for TLS 1.2+ */
+          digest_hd_st sha256; /* hash of the handshake messages for TLS 1.2+ */
         } tls12;
     } handshake_mac_handle;
   int handshake_mac_handle_init; /* 1 when the previous union and type were initialized */
index f50eb8cccd8cc833c643c8f4e3059714089111c2..5248b4f1e17cc5a0fe2888caf5527a1e2ee69d85 100644 (file)
@@ -338,6 +338,27 @@ static const int comp_priority[] = {
   0
 };
 
+static const int sign_priority_default[] = {
+  GNUTLS_SIGN_RSA_SHA1,
+  GNUTLS_SIGN_DSA_SHA1,
+  GNUTLS_SIGN_RSA_SHA256,
+  GNUTLS_SIGN_RSA_SHA384,
+  GNUTLS_SIGN_RSA_SHA512,
+  0
+};
+
+static const int sign_priority_secure128[] = {
+  GNUTLS_SIGN_RSA_SHA256,
+  GNUTLS_SIGN_RSA_SHA384,
+  GNUTLS_SIGN_RSA_SHA512,
+  GNUTLS_SIGN_DSA_SHA1,
+  0
+};
+
+static const int sign_priority_secure256[] = {
+  GNUTLS_SIGN_RSA_SHA512,
+  0
+};
 
 static const int mac_priority_performance[] = {
   GNUTLS_MAC_MD5,
@@ -345,6 +366,7 @@ static const int mac_priority_performance[] = {
   0
 };
 
+
 static const int mac_priority_secure[] = {
   GNUTLS_MAC_SHA256,
   GNUTLS_MAC_SHA1,
@@ -509,7 +531,7 @@ gnutls_priority_set (gnutls_session_t session, gnutls_priority_t priority)
  * Namespace concern:
  * To avoid collisions in order to specify a compression algorithm in
  * this string you have to prefix it with "COMP-", protocol versions
- * with "VERS-" and certificate types with "CTYPE-". All other
+ * with "VERS-", signature algorithms with "SIGN-" and certificate types with "CTYPE-". All other
  * algorithms don't need a prefix.
  *
  * Examples:
@@ -561,6 +583,7 @@ gnutls_priority_init (gnutls_priority_t * priority_cache,
       _set_priority (&(*priority_cache)->protocol, protocol_priority);
       _set_priority (&(*priority_cache)->compression, comp_priority);
       _set_priority (&(*priority_cache)->cert_type, cert_type_priority);
+      _set_priority (&(*priority_cache)->sign_algo, sign_priority_default);
       i = 0;
     }
   else
@@ -576,12 +599,14 @@ gnutls_priority_init (gnutls_priority_t * priority_cache,
                         cipher_priority_performance);
          _set_priority (&(*priority_cache)->kx, kx_priority_performance);
          _set_priority (&(*priority_cache)->mac, mac_priority_performance);
+          _set_priority (&(*priority_cache)->sign_algo, sign_priority_default);
        }
       else if (strcasecmp (broken_list[i], "NORMAL") == 0)
        {
          _set_priority (&(*priority_cache)->cipher, cipher_priority_normal);
          _set_priority (&(*priority_cache)->kx, kx_priority_secure);
          _set_priority (&(*priority_cache)->mac, mac_priority_secure);
+          _set_priority (&(*priority_cache)->sign_algo, sign_priority_default);
        }
       else if (strcasecmp (broken_list[i], "SECURE256") == 0
               || strcasecmp (broken_list[i], "SECURE") == 0)
@@ -590,6 +615,7 @@ gnutls_priority_init (gnutls_priority_t * priority_cache,
                         cipher_priority_secure256);
          _set_priority (&(*priority_cache)->kx, kx_priority_secure);
          _set_priority (&(*priority_cache)->mac, mac_priority_secure);
+          _set_priority (&(*priority_cache)->sign_algo, sign_priority_secure256);
        }
       else if (strcasecmp (broken_list[i], "SECURE128") == 0)
        {
@@ -597,12 +623,14 @@ gnutls_priority_init (gnutls_priority_t * priority_cache,
                         cipher_priority_secure128);
          _set_priority (&(*priority_cache)->kx, kx_priority_secure);
          _set_priority (&(*priority_cache)->mac, mac_priority_secure);
+          _set_priority (&(*priority_cache)->sign_algo, sign_priority_secure128);
        }
       else if (strcasecmp (broken_list[i], "EXPORT") == 0)
        {
          _set_priority (&(*priority_cache)->cipher, cipher_priority_export);
          _set_priority (&(*priority_cache)->kx, kx_priority_export);
          _set_priority (&(*priority_cache)->mac, mac_priority_secure);
+          _set_priority (&(*priority_cache)->sign_algo, sign_priority_default);
        }                       /* now check if the element is something like -ALGO */
       else if (broken_list[i][0] == '!' || broken_list[i][0] == '+'
               || broken_list[i][0] == '-')
@@ -627,6 +655,8 @@ gnutls_priority_init (gnutls_priority_t * priority_cache,
                   gnutls_protocol_get_id (&broken_list[i][6])) !=
                  GNUTLS_VERSION_UNKNOWN)
                fn (&(*priority_cache)->protocol, algo);
+              else
+                goto error;
            }                   /* now check if the element is something like -ALGO */
          else if (strncasecmp (&broken_list[i][1], "COMP-", 5) == 0)
            {
@@ -634,6 +664,8 @@ gnutls_priority_init (gnutls_priority_t * priority_cache,
                   gnutls_compression_get_id (&broken_list[i][6])) !=
                  GNUTLS_COMP_UNKNOWN)
                fn (&(*priority_cache)->compression, algo);
+              else
+                goto error;
            }                   /* now check if the element is something like -ALGO */
          else if (strncasecmp (&broken_list[i][1], "CTYPE-", 6) == 0)
            {
@@ -641,6 +673,17 @@ gnutls_priority_init (gnutls_priority_t * priority_cache,
                   gnutls_certificate_type_get_id (&broken_list[i][7])) !=
                  GNUTLS_CRT_UNKNOWN)
                fn (&(*priority_cache)->cert_type, algo);
+              else
+                goto error;
+           }                   /* now check if the element is something like -ALGO */
+         else if (strncasecmp (&broken_list[i][1], "SIGN-", 5) == 0)
+           {
+             if ((algo =
+                  gnutls_sign_get_id (&broken_list[i][6])) !=
+                 GNUTLS_SIGN_UNKNOWN)
+               fn (&(*priority_cache)->sign_algo, algo);
+              else
+                goto error;
            }                   /* now check if the element is something like -ALGO */
          else
            goto error;
@@ -651,8 +694,11 @@ gnutls_priority_init (gnutls_priority_t * priority_cache,
            (*priority_cache)->no_padding = 1;
          else if (strcasecmp (&broken_list[i][1],
                               "VERIFY_ALLOW_SIGN_RSA_MD5") == 0)
-           (*priority_cache)->additional_verify_flags |=
-             GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD5;
+            {
+             prio_add (&(*priority_cache)->sign_algo, GNUTLS_SIGN_RSA_MD5);
+             (*priority_cache)->additional_verify_flags |=
+               GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD5;
+            }
          else if (strcasecmp (&broken_list[i][1],
                               "SSL3_RECORD_VERSION") == 0)
            (*priority_cache)->ssl3_record_version = 1;
index c49f2eda2ae775be89fda274eb0b4f4ad3f59773..76e1f6da893a49350c72a938bf7de572187d0300 100644 (file)
@@ -37,6 +37,8 @@
 #include <gnutls_sig.h>
 #include <gnutls_kx.h>
 #include <libtasn1.h>
+#include <ext_signature.h>
+#include <gnutls_state.h>
 
 static int
 _gnutls_tls_sign (gnutls_session_t session,
@@ -117,93 +119,6 @@ _gnutls_rsa_encode_sig (gnutls_mac_algorithm_t algo,
   return 0;
 }
 
-/* Generates a signature of all the previous sent packets in the 
- * handshake procedure. (20040227: now it works for SSL 3.0 as well)
- */
-int
-_gnutls_tls_sign_hdata (gnutls_session_t session,
-                       gnutls_cert * cert, gnutls_privkey * pkey,
-                       gnutls_datum_t * signature)
-{
-  gnutls_datum_t dconcat;
-  int ret;
-  opaque concat[MAX_SIG_SIZE];
-  digest_hd_st td_md5;
-  digest_hd_st td_sha;
-  gnutls_protocol_t ver = gnutls_protocol_get_version (session);
-
-  /* FIXME: This is not compliant to TLS 1.2. We should use an algorithm from the
-   * SignatureAndHashAlgorithm field of Certificate Request.
-   */
-  if (session->security_parameters.handshake_mac_handle_type != HANDSHAKE_MAC_TYPE_10)
-    {
-      gnutls_assert();
-      return GNUTLS_E_UNIMPLEMENTED_FEATURE;
-    }
-  
-  ret =
-    _gnutls_hash_copy (&td_sha, &session->internals.handshake_mac_handle.tls10.sha);
-  if (ret < 0)
-    {
-      gnutls_assert ();
-      return ret;
-    }
-
-  if (ver == GNUTLS_SSL3)
-    {
-      ret = _gnutls_generate_master (session, 1);
-      if (ret < 0)
-       {
-         gnutls_assert ();
-         return ret;
-       }
-
-      _gnutls_mac_deinit_ssl3_handshake (&td_sha, &concat[16],
-                                        session->security_parameters.
-                                        master_secret, GNUTLS_MASTER_SIZE);
-    }
-  else
-    _gnutls_hash_deinit (&td_sha, &concat[16]);
-
-  switch (cert->subject_pk_algorithm)
-    {
-    case GNUTLS_PK_RSA:
-      ret =
-       _gnutls_hash_copy (&td_md5,
-                          &session->internals.handshake_mac_handle.tls10.md5);
-      if (ret < 0)
-       {
-         gnutls_assert ();
-         return ret;
-       }
-
-      if (ver == GNUTLS_SSL3)
-       _gnutls_mac_deinit_ssl3_handshake (&td_md5, concat,
-                                          session->security_parameters.
-                                          master_secret, GNUTLS_MASTER_SIZE);
-      else
-       _gnutls_hash_deinit (&td_md5, concat);
-
-      dconcat.data = concat;
-      dconcat.size = 36;
-      break;
-    case GNUTLS_PK_DSA:
-      dconcat.data = &concat[16];
-      dconcat.size = 20;
-      break;
-
-    default:
-      gnutls_assert ();
-      return GNUTLS_E_INTERNAL_ERROR;
-    }
-  ret = _gnutls_tls_sign (session, cert, pkey, &dconcat, signature);
-  if (ret < 0)
-    {
-      gnutls_assert ();
-    }
-
-  return ret;
-}
 
 
 /* Generates a signature of all the random data and the parameters.
@@ -220,22 +135,17 @@ _gnutls_tls_sign_params (gnutls_session_t session, gnutls_cert * cert,
   digest_hd_st td_sha;
   opaque concat[MAX_SIG_SIZE];
   gnutls_protocol_t ver = gnutls_protocol_get_version (session);
-  gnutls_mac_algorithm_t mac_algo = GNUTLS_MAC_SHA1;
-  gnutls_sign_algorithm_t _sign_algo = GNUTLS_SIGN_UNKNOWN;
+  gnutls_digest_algorithm_t hash_algo;
 
-  if (_gnutls_version_has_selectable_prf (ver))
+  *sign_algo = _gnutls_session_get_sign_algo(session, cert->subject_pk_algorithm,
+                                           &hash_algo);
+  if (*sign_algo == GNUTLS_SIGN_UNKNOWN)
     {
-      _sign_algo = _gnutls_x509_pk_to_sign (cert->subject_pk_algorithm,
-                                           mac_algo);
-      if (_sign_algo == GNUTLS_SIGN_UNKNOWN)
-       {
-         gnutls_assert ();
-         return GNUTLS_E_UNKNOWN_PK_ALGORITHM;
-       }
+      gnutls_assert ();
+      return GNUTLS_E_UNKNOWN_PK_ALGORITHM;
     }
-  *sign_algo = _sign_algo;
 
-  ret = _gnutls_hash_init (&td_sha, mac_algo);
+  ret = _gnutls_hash_init (&td_sha, hash_algo);
   if (ret < 0)
     {
       gnutls_assert ();
@@ -275,21 +185,27 @@ _gnutls_tls_sign_params (gnutls_session_t session, gnutls_cert * cert,
          dconcat.size = 36;
        }
       else
-       {
+       { /* TLS 1.2 way */
          gnutls_datum_t hash;
 
          _gnutls_hash_deinit (&td_sha, concat);
 
          hash.data = concat;
-         hash.size = _gnutls_hash_get_algo_len (mac_algo);
+         hash.size = _gnutls_hash_get_algo_len (hash_algo);
          dconcat.data = concat;
          dconcat.size = sizeof concat;
 
-         _gnutls_rsa_encode_sig (mac_algo, &hash, &dconcat);
+         _gnutls_rsa_encode_sig (hash_algo, &hash, &dconcat);
        }
       break;
     case GNUTLS_PK_DSA:
       _gnutls_hash_deinit (&td_sha, concat);
+
+      if (hash_algo != GNUTLS_DIG_SHA1)
+        {
+          gnutls_assert();
+          return GNUTLS_E_INTERNAL_ERROR;
+        }
       dconcat.data = concat;
       dconcat.size = 20;
       break;
@@ -459,12 +375,160 @@ _gnutls_verify_sig (gnutls_cert * cert,
 }
 
 
+/* Generates a signature of all the random data and the parameters.
+ * Used in DHE_* ciphersuites.
+ */
+int
+_gnutls_verify_sig_params (gnutls_session_t session, gnutls_cert * cert,
+                          const gnutls_datum_t * params,
+                          gnutls_datum_t * signature,
+                          gnutls_sign_algorithm_t algo)
+{
+  gnutls_datum_t dconcat;
+  int ret;
+  digest_hd_st td_md5;
+  digest_hd_st td_sha;
+  opaque concat[MAX_SIG_SIZE];
+  gnutls_protocol_t ver = gnutls_protocol_get_version (session);
+  gnutls_digest_algorithm_t hash_algo = GNUTLS_DIG_SHA1;
+
+  ret = _gnutls_session_sign_algo_supported(session, algo, 0);
+  if (ret < 0)
+    {
+      gnutls_assert();
+      return ret;
+    }
+
+  if (!_gnutls_version_has_selectable_prf (ver))
+    {
+      ret = _gnutls_hash_init (&td_md5, GNUTLS_MAC_MD5);
+      if (ret < 0)
+       {
+         gnutls_assert ();
+         return ret;
+       }
+
+      _gnutls_hash (&td_md5, session->security_parameters.client_random,
+                   GNUTLS_RANDOM_SIZE);
+      _gnutls_hash (&td_md5, session->security_parameters.server_random,
+                   GNUTLS_RANDOM_SIZE);
+      _gnutls_hash (&td_md5, params->data, params->size);
+    }
+
+  if (algo != GNUTLS_SIGN_UNKNOWN)
+    hash_algo = _gnutls_sign_get_hash_algorithm (algo);
+
+  ret = _gnutls_hash_init (&td_sha, hash_algo);
+  if (ret < 0)
+    {
+      gnutls_assert ();
+      if (!_gnutls_version_has_selectable_prf (ver))
+       _gnutls_hash_deinit (&td_md5, NULL);
+      return ret;
+    }
+
+  _gnutls_hash (&td_sha, session->security_parameters.client_random,
+               GNUTLS_RANDOM_SIZE);
+  _gnutls_hash (&td_sha, session->security_parameters.server_random,
+               GNUTLS_RANDOM_SIZE);
+  _gnutls_hash (&td_sha, params->data, params->size);
+
+  if (!_gnutls_version_has_selectable_prf (ver))
+    {
+      _gnutls_hash_deinit (&td_md5, concat);
+      _gnutls_hash_deinit (&td_sha, &concat[16]);
+      dconcat.data = concat;
+      dconcat.size = 36;
+    }
+  else
+    {
+      gnutls_datum_t hash;
+
+      _gnutls_hash_deinit (&td_sha, concat);
+
+      hash.data = concat;
+      hash.size = _gnutls_hash_get_algo_len (hash_algo);
+      dconcat.data = concat;
+      dconcat.size = sizeof concat;
+
+      _gnutls_rsa_encode_sig (hash_algo, &hash, &dconcat);
+    }
+
+  ret = _gnutls_verify_sig (cert, &dconcat, signature,
+                           dconcat.size - _gnutls_hash_get_algo_len (hash_algo),
+                           _gnutls_sign_get_pk_algorithm (algo));
+  if (ret < 0)
+    {
+      gnutls_assert ();
+      return ret;
+    }
+
+  return ret;
+
+}
+
+/* Client certificate verify calculations
+ */
+
+/* this is _gnutls_verify_sig_hdata for TLS 1.2
+ */
+static int _gnutls_tls12_verify_sig_hdata (gnutls_session_t session, gnutls_cert * cert,
+                         gnutls_datum_t * signature, gnutls_sign_algorithm_t sign_algo)
+{
+  int ret;
+  opaque concat[MAX_SIG_SIZE];
+  digest_hd_st td;
+  gnutls_datum_t dconcat;
+  gnutls_sign_algorithm_t _sign_algo;
+  gnutls_digest_algorithm_t hash_algo;
+  digest_hd_st* handshake_td;
+
+  handshake_td = &session->internals.handshake_mac_handle.tls12.sha1;
+  hash_algo = handshake_td->algorithm;
+  _sign_algo = _gnutls_x509_pk_to_sign( cert->subject_pk_algorithm, hash_algo);
+
+  if (_sign_algo != sign_algo)
+    {
+      handshake_td = &session->internals.handshake_mac_handle.tls12.sha256;
+      hash_algo = handshake_td->algorithm;
+      _sign_algo = _gnutls_x509_pk_to_sign( cert->subject_pk_algorithm, hash_algo);
+      if (sign_algo != _sign_algo)
+        {
+          gnutls_assert();
+          return GNUTLS_E_UNSUPPORTED_SIGNATURE_ALGORITHM;
+        }
+    }
+
+  ret =
+    _gnutls_hash_copy (&td, handshake_td);
+  if (ret < 0)
+    {
+      gnutls_assert ();
+      return GNUTLS_E_HASH_FAILED;
+    }
+
+  _gnutls_hash_deinit (&td, concat);
+
+  dconcat.data = concat;
+  dconcat.size = _gnutls_hash_get_algo_len (hash_algo);
+
+  ret = _gnutls_verify_sig (cert, &dconcat, signature, 0, cert->subject_pk_algorithm);
+  if (ret < 0)
+    {
+      gnutls_assert ();
+      return ret;
+    }
+
+  return ret;
+
+}
+
 /* Verifies a TLS signature (like the one in the client certificate
  * verify message). 
  */
 int
 _gnutls_verify_sig_hdata (gnutls_session_t session, gnutls_cert * cert,
-                         gnutls_datum_t * signature)
+                         gnutls_datum_t * signature, gnutls_sign_algorithm_t sign_algo)
 {
   int ret;
   opaque concat[MAX_SIG_SIZE];
@@ -473,10 +537,14 @@ _gnutls_verify_sig_hdata (gnutls_session_t session, gnutls_cert * cert,
   gnutls_datum_t dconcat;
   gnutls_protocol_t ver = gnutls_protocol_get_version (session);
 
-  if (session->security_parameters.handshake_mac_handle_type != HANDSHAKE_MAC_TYPE_10)
+  if (session->security_parameters.handshake_mac_handle_type == HANDSHAKE_MAC_TYPE_12)
+    {
+      return _gnutls_tls12_verify_sig_hdata(session, cert, signature, sign_algo);
+    }
+  else if (session->security_parameters.handshake_mac_handle_type != HANDSHAKE_MAC_TYPE_10)
     {
       gnutls_assert();
-      return GNUTLS_E_UNIMPLEMENTED_FEATURE;
+      return GNUTLS_E_INTERNAL_ERROR;
     }
 
   ret =
@@ -521,7 +589,7 @@ _gnutls_verify_sig_hdata (gnutls_session_t session, gnutls_cert * cert,
   dconcat.data = concat;
   dconcat.size = 20 + 16;      /* md5+ sha */
 
-  ret = _gnutls_verify_sig (cert, &dconcat, signature, 16, GNUTLS_SIGN_UNKNOWN);
+  ret = _gnutls_verify_sig (cert, &dconcat, signature, 16, cert->subject_pk_algorithm);
   if (ret < 0)
     {
       gnutls_assert ();
@@ -532,86 +600,164 @@ _gnutls_verify_sig_hdata (gnutls_session_t session, gnutls_cert * cert,
 
 }
 
-/* Generates a signature of all the random data and the parameters.
- * Used in DHE_* ciphersuites.
+/* the same as _gnutls_tls_sign_hdata except that it is made for TLS 1.2
  */
-int
-_gnutls_verify_sig_params (gnutls_session_t session, gnutls_cert * cert,
-                          const gnutls_datum_t * params,
-                          gnutls_datum_t * signature,
-                          gnutls_sign_algorithm_t algo)
+static int _gnutls_tls12_sign_hdata (gnutls_session_t session,
+                       gnutls_cert * cert, gnutls_privkey * pkey,
+                       gnutls_datum_t * signature)
 {
   gnutls_datum_t dconcat;
   int ret;
-  digest_hd_st td_md5;
-  digest_hd_st td_sha;
   opaque concat[MAX_SIG_SIZE];
-  gnutls_protocol_t ver = gnutls_protocol_get_version (session);
-  gnutls_mac_algorithm_t mac_algo = GNUTLS_MAC_SHA1;
-
-  if (!_gnutls_version_has_selectable_prf (ver))
+  digest_hd_st td;
+  gnutls_sign_algorithm_t sign_algo;
+  gnutls_digest_algorithm_t hash_algo;
+  digest_hd_st* handshake_td;
+
+  handshake_td = &session->internals.handshake_mac_handle.tls12.sha1;
+  hash_algo = handshake_td->algorithm;
+  sign_algo = _gnutls_x509_pk_to_sign( cert->subject_pk_algorithm, hash_algo);
+
+  /* The idea here is to try signing with the one of the algorithms
+   * that have been initiated at handshake (SHA1, SHA256). If they
+   * are not requested by peer... tough luck
+   */
+  ret = _gnutls_session_sign_algo_requested(session, sign_algo);
+  if (sign_algo == GNUTLS_SIGN_UNKNOWN || ret < 0)
     {
-      ret = _gnutls_hash_init (&td_md5, GNUTLS_MAC_MD5);
+      handshake_td = &session->internals.handshake_mac_handle.tls12.sha256;
+      hash_algo = handshake_td->algorithm;
+      sign_algo = _gnutls_x509_pk_to_sign( cert->subject_pk_algorithm, hash_algo);
+      if (sign_algo == GNUTLS_SIGN_UNKNOWN)
+        {
+          gnutls_assert();
+          return GNUTLS_E_UNSUPPORTED_SIGNATURE_ALGORITHM;
+        }
+      
+      ret = _gnutls_session_sign_algo_requested(session, sign_algo);  
       if (ret < 0)
-       {
-         gnutls_assert ();
-         return ret;
-       }
+        {
+          gnutls_assert();
+          _gnutls_x509_log("Server did not allow either '%s' or '%s' for signing\n", gnutls_mac_get_name(hash_algo), gnutls_mac_get_name(session->internals.handshake_mac_handle.tls12.sha1.algorithm));
+          return ret;
+        }
+    }
+  
+  _gnutls_x509_log("sign hash data: picked %s with %s\n", gnutls_sign_algorithm_get_name(sign_algo), gnutls_mac_get_name(hash_algo));
 
-      _gnutls_hash (&td_md5, session->security_parameters.client_random,
-                   GNUTLS_RANDOM_SIZE);
-      _gnutls_hash (&td_md5, session->security_parameters.server_random,
-                   GNUTLS_RANDOM_SIZE);
-      _gnutls_hash (&td_md5, params->data, params->size);
+  ret =
+    _gnutls_hash_copy (&td, handshake_td);
+  if (ret < 0)
+    {
+      gnutls_assert ();
+      return ret;
     }
 
-  if (algo != GNUTLS_SIGN_UNKNOWN)
-    mac_algo = _gnutls_sign_get_mac_algorithm (algo);
-  ret = _gnutls_hash_init (&td_sha, mac_algo);
+  _gnutls_hash_deinit (&td, concat);
+
+  dconcat.data = concat;
+  dconcat.size = _gnutls_hash_get_algo_len (hash_algo);
+
+  ret = _gnutls_tls_sign (session, cert, pkey, &dconcat, signature);
   if (ret < 0)
     {
       gnutls_assert ();
-      if (!_gnutls_version_has_selectable_prf (ver))
-       _gnutls_hash_deinit (&td_md5, NULL);
       return ret;
     }
 
-  _gnutls_hash (&td_sha, session->security_parameters.client_random,
-               GNUTLS_RANDOM_SIZE);
-  _gnutls_hash (&td_sha, session->security_parameters.server_random,
-               GNUTLS_RANDOM_SIZE);
-  _gnutls_hash (&td_sha, params->data, params->size);
+  return sign_algo;
+}
 
-  if (!_gnutls_version_has_selectable_prf (ver))
+/* Generates a signature of all the previous sent packets in the 
+ * handshake procedure. 
+ * 20040227: now it works for SSL 3.0 as well
+ * 20091031: works for TLS 1.2 too!
+ *
+ * For TLS1.x, x<2 returns negative for failure and zero or unspecified for success.
+ * For TLS1.2 returns the signature algorithm used on success, or a negative value;
+ */
+int
+_gnutls_tls_sign_hdata (gnutls_session_t session,
+                       gnutls_cert * cert, gnutls_privkey * pkey,
+                       gnutls_datum_t * signature)
+{
+  gnutls_datum_t dconcat;
+  int ret;
+  opaque concat[MAX_SIG_SIZE];
+  digest_hd_st td_md5;
+  digest_hd_st td_sha;
+  gnutls_protocol_t ver = gnutls_protocol_get_version (session);
+
+  if (session->security_parameters.handshake_mac_handle_type == HANDSHAKE_MAC_TYPE_12)
     {
-      _gnutls_hash_deinit (&td_md5, concat);
-      _gnutls_hash_deinit (&td_sha, &concat[16]);
-      dconcat.data = concat;
-      dconcat.size = 36;
+      return _gnutls_tls12_sign_hdata(session, cert, pkey, signature);
+    }
+  else if (session->security_parameters.handshake_mac_handle_type != HANDSHAKE_MAC_TYPE_10)
+    {
+      gnutls_assert();
+      return GNUTLS_E_INTERNAL_ERROR;
+    }
+  
+  ret =
+    _gnutls_hash_copy (&td_sha, &session->internals.handshake_mac_handle.tls10.sha);
+  if (ret < 0)
+    {
+      gnutls_assert ();
+      return ret;
+    }
+
+  if (ver == GNUTLS_SSL3)
+    {
+      ret = _gnutls_generate_master (session, 1);
+      if (ret < 0)
+       {
+         gnutls_assert ();
+         return ret;
+       }
+
+      _gnutls_mac_deinit_ssl3_handshake (&td_sha, &concat[16],
+                                        session->security_parameters.
+                                        master_secret, GNUTLS_MASTER_SIZE);
     }
   else
+    _gnutls_hash_deinit (&td_sha, &concat[16]);
+
+  switch (cert->subject_pk_algorithm)
     {
-      gnutls_datum_t hash;
+    case GNUTLS_PK_RSA:
+      ret =
+       _gnutls_hash_copy (&td_md5,
+                          &session->internals.handshake_mac_handle.tls10.md5);
+      if (ret < 0)
+       {
+         gnutls_assert ();
+         return ret;
+       }
 
-      _gnutls_hash_deinit (&td_sha, concat);
+      if (ver == GNUTLS_SSL3)
+       _gnutls_mac_deinit_ssl3_handshake (&td_md5, concat,
+                                          session->security_parameters.
+                                          master_secret, GNUTLS_MASTER_SIZE);
+      else
+       _gnutls_hash_deinit (&td_md5, concat);
 
-      hash.data = concat;
-      hash.size = _gnutls_hash_get_algo_len (mac_algo);
       dconcat.data = concat;
-      dconcat.size = sizeof concat;
+      dconcat.size = 36;
+      break;
+    case GNUTLS_PK_DSA:
+      dconcat.data = &concat[16];
+      dconcat.size = 20;
+      break;
 
-      _gnutls_rsa_encode_sig (mac_algo, &hash, &dconcat);
+    default:
+      gnutls_assert ();
+      return GNUTLS_E_INTERNAL_ERROR;
     }
-
-  ret = _gnutls_verify_sig (cert, &dconcat, signature,
-                           dconcat.size - _gnutls_hash_get_algo_len (mac_algo),
-                           _gnutls_sign_get_pk_algorithm (algo));
+  ret = _gnutls_tls_sign (session, cert, pkey, &dconcat, signature);
   if (ret < 0)
     {
       gnutls_assert ();
-      return ret;
     }
 
   return ret;
-
 }
index c338869d4a86466245aebb8edba13fd7cd368b77..4cc0df20c159b79fe36f001184c87a474b1016a6 100644 (file)
@@ -38,7 +38,8 @@ int _gnutls_tls_sign_params (gnutls_session_t session,
                             gnutls_sign_algorithm_t * algo);
 
 int _gnutls_verify_sig_hdata (gnutls_session_t session,
-                             gnutls_cert * cert, gnutls_datum_t * signature);
+                             gnutls_cert * cert, gnutls_datum_t * signature,
+                             gnutls_sign_algorithm_t);
 
 int _gnutls_verify_sig_params (gnutls_session_t session,
                               gnutls_cert * cert,
index cd08f44a88c4f184b8343e28276c88638decdfc2..912cc608399b9e826bc5c9890ab6969f9d3a7ec5 100644 (file)
@@ -189,6 +189,61 @@ _gnutls_session_cert_type_supported (gnutls_session_t session,
   return GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE;
 }
 
+/* Check if the given signature algorithm is supported.
+ * This means that it is enabled by the priority functions,
+ * and in case of a server a matching certificate exists.
+ */
+int
+_gnutls_session_sign_algo_supported (gnutls_session_t session,
+                                    gnutls_sign_algorithm_t sig, int check_certs)
+{
+  unsigned i;
+  unsigned cert_found = 0;
+  gnutls_certificate_credentials_t cred;
+
+  if (check_certs != 0 && session->security_parameters.entity == GNUTLS_SERVER)
+    {
+      cred = (gnutls_certificate_credentials_t)
+       _gnutls_get_cred (session->key, GNUTLS_CRD_CERTIFICATE, NULL);
+
+      if (cred == NULL)
+       return GNUTLS_E_UNSUPPORTED_SIGNATURE_ALGORITHM;
+
+      if (cred->server_get_cert_callback == NULL)
+       {
+         for (i = 0; i < cred->ncerts; i++)
+           {
+             if (cred->cert_list[i][0].sign_algo == sig)
+               {
+                 cert_found = 1;
+                 break;
+               }
+           }
+
+         if (cert_found == 0)
+           /* no certificate is of that type.
+            */
+           return GNUTLS_E_UNSUPPORTED_SIGNATURE_ALGORITHM;
+       }
+    }
+
+  if (session->internals.priorities.sign_algo.algorithms == 0) /* none set, allow all */
+    {
+      gnutls_assert();
+      return 0;
+    }
+
+  for (i = 0; i < session->internals.priorities.sign_algo.algorithms; i++)
+    {
+      if (session->internals.priorities.sign_algo.priority[i] == sig)
+       {
+         return 0;             /* ok */
+       }
+    }
+
+  return GNUTLS_E_UNSUPPORTED_SIGNATURE_ALGORITHM;
+}
+
 /* this function deinitializes all the internal parameters stored
  * in a session struct.
  */
@@ -1360,3 +1415,4 @@ gnutls_session_enable_compatibility_mode (gnutls_session_t session)
 {
   gnutls_record_disable_padding (session);
 }
+
index dbb2a51a09a6ecdf54b1bf18c5edb788ee598ae9..bff002282055ff7aba7ebad77fafa98dd859ea2d 100644 (file)
@@ -41,7 +41,8 @@ void _gnutls_session_cert_type_set (gnutls_session_t session,
 
 int _gnutls_session_cert_type_supported (gnutls_session_t,
                                         gnutls_certificate_type_t);
-
+int _gnutls_session_sign_algo_supported (gnutls_session_t session,
+                                     gnutls_sign_algorithm_t sig, int check_certs);
 int _gnutls_dh_set_secret_bits (gnutls_session_t session, unsigned bits);
 
 int _gnutls_dh_set_peer_public (gnutls_session_t session, bigint_t public);
index 4ee09841a58d34cda6e191580ac8a6d3eb433dc5..ab7e44709f5cbe37902675e5b402ec1dca1583f1 100644 (file)
@@ -159,6 +159,14 @@ _gnutls_x509_cert_verify_peers (gnutls_session_t session,
          return ret;
        }
 
+      
+      if (ret < 0)
+        {
+          gnutls_assert();
+          CLEAR_CERTS;
+          return ret;
+        }
+
       ret = check_bits (peer_certificate_list[i], cred->verify_bits);
       if (ret < 0)
        {
@@ -171,6 +179,7 @@ _gnutls_x509_cert_verify_peers (gnutls_session_t session,
 
   /* Verify certificate 
    */
+  
   ret = gnutls_x509_crt_list_verify (peer_certificate_list,
                                     peer_certificate_list_size,
                                     cred->x509_ca_list, cred->x509_ncas,
index a4fe3fc67fb966d151f2693f33989555e3ced85e..26e6d86a8ac99f64892365156c64ca7ce86f3b3e 100644 (file)
@@ -145,6 +145,7 @@ extern "C" {
    */
   typedef enum
   {
+    GNUTLS_DIG_UNKNOWN = GNUTLS_MAC_UNKNOWN,
     GNUTLS_DIG_NULL = GNUTLS_MAC_NULL,
     GNUTLS_DIG_MD5 = GNUTLS_MAC_MD5,
     GNUTLS_DIG_SHA1 = GNUTLS_MAC_SHA1,
@@ -1374,8 +1375,8 @@ extern "C" {
 #define GNUTLS_E_WARNING_IA_FPHF_RECEIVED -103
 
 #define GNUTLS_E_IA_VERIFY_FAILED -104
-
 #define GNUTLS_E_UNKNOWN_ALGORITHM -105
+#define GNUTLS_E_UNSUPPORTED_SIGNATURE_ALGORITHM -106
 
 #define GNUTLS_E_BASE64_ENCODING_ERROR -201
 #define GNUTLS_E_INCOMPATIBLE_GCRYPT_LIBRARY -202      /* obsolete */
index 16d18d8946077b9a1a7f0105234ec15f6a20f6fe..fb500753905dc137ec7d664508e662b1c83f0746 100644 (file)
@@ -800,6 +800,8 @@ _gnutls_openpgp_crt_to_gcert (gnutls_cert * gcert, gnutls_openpgp_crt_t cert)
 
   memset (gcert, 0, sizeof (gnutls_cert));
   gcert->cert_type = GNUTLS_CRT_OPENPGP;
+  gcert->sign_algo = GNUTLS_SIGN_UNKNOWN; /* N/A here */
+
   gcert->version = gnutls_openpgp_crt_get_version (cert);
   gcert->params_size = MAX_PUBLIC_PARAMS_SIZE;