diff options
author | Nikos Mavrogiannopoulos <nmav@gnutls.org> | 2009-11-01 02:54:08 +0200 |
---|---|---|
committer | Nikos Mavrogiannopoulos <nmav@gnutls.org> | 2009-11-01 02:54:08 +0200 |
commit | 6773d2ddb01d86fa283ce154b419e989916ab3f7 (patch) | |
tree | c3e38318016acfd0c50c8a4f6d694f5aa6d6f08a /lib | |
parent | 9a262d093744f37b26f45c4e74d22f3a5a425211 (diff) | |
download | gnutls-6773d2ddb01d86fa283ce154b419e989916ab3f7.tar.gz |
Improved TLS 1.2 support. Added support for the SignatureAlgorithm extension
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.
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Makefile.am | 4 | ||||
-rw-r--r-- | lib/auth_cert.c | 90 | ||||
-rw-r--r-- | lib/auth_cert.h | 1 | ||||
-rw-r--r-- | lib/ext_signature.c | 332 | ||||
-rw-r--r-- | lib/ext_signature.h | 38 | ||||
-rw-r--r-- | lib/gnutls_algorithms.c | 8 | ||||
-rw-r--r-- | lib/gnutls_algorithms.h | 3 | ||||
-rw-r--r-- | lib/gnutls_cert.c | 1 | ||||
-rw-r--r-- | lib/gnutls_cert.h | 1 | ||||
-rw-r--r-- | lib/gnutls_errors.c | 2 | ||||
-rw-r--r-- | lib/gnutls_extensions.c | 9 | ||||
-rw-r--r-- | lib/gnutls_handshake.c | 27 | ||||
-rw-r--r-- | lib/gnutls_int.h | 10 | ||||
-rw-r--r-- | lib/gnutls_priority.c | 52 | ||||
-rw-r--r-- | lib/gnutls_sig.c | 458 | ||||
-rw-r--r-- | lib/gnutls_sig.h | 3 | ||||
-rw-r--r-- | lib/gnutls_state.c | 56 | ||||
-rw-r--r-- | lib/gnutls_state.h | 3 | ||||
-rw-r--r-- | lib/gnutls_x509.c | 9 | ||||
-rw-r--r-- | lib/includes/gnutls/gnutls.h.in | 3 | ||||
-rw-r--r-- | lib/openpgp/gnutls_openpgp.c | 2 |
21 files changed, 922 insertions, 190 deletions
diff --git a/lib/Makefile.am b/lib/Makefile.am index 350a0875c0..07d3a97823 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -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 diff --git a/lib/auth_cert.c b/lib/auth_cert.c index f44731356a..3affa32716 100644 --- a/lib/auth_cert.c +++ b/lib/auth_cert.c @@ -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; diff --git a/lib/auth_cert.h b/lib/auth_cert.h index 1b808dcfb6..f982bc7a18 100644 --- a/lib/auth_cert.h +++ b/lib/auth_cert.h @@ -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 index 0000000000..5231174a38 --- /dev/null +++ b/lib/ext_signature.c @@ -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 index 0000000000..46b6154f67 --- /dev/null +++ b/lib/ext_signature.h @@ -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); diff --git a/lib/gnutls_algorithms.c b/lib/gnutls_algorithms.c index 8f1eb9fc05..873648e54c 100644 --- a/lib/gnutls_algorithms.c +++ b/lib/gnutls_algorithms.c @@ -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); diff --git a/lib/gnutls_algorithms.h b/lib/gnutls_algorithms.h index 0a2faac52f..8736dc8afc 100644 --- a/lib/gnutls_algorithms.h +++ b/lib/gnutls_algorithms.h @@ -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, diff --git a/lib/gnutls_cert.c b/lib/gnutls_cert.c index 42db17cb79..5b110ad298 100644 --- a/lib/gnutls_cert.c +++ b/lib/gnutls_cert.c @@ -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)) { diff --git a/lib/gnutls_cert.h b/lib/gnutls_cert.h index 2a752270fe..c153607d3f 100644 --- a/lib/gnutls_cert.h +++ b/lib/gnutls_cert.h @@ -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; diff --git a/lib/gnutls_errors.c b/lib/gnutls_errors.c index 94be5af487..b5d8bb6cb1 100644 --- a/lib/gnutls_errors.c +++ b/lib/gnutls_errors.c @@ -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, diff --git a/lib/gnutls_extensions.c b/lib/gnutls_extensions.c index b7baca28b9..5f2c47cc6b 100644 --- a/lib/gnutls_extensions.c +++ b/lib/gnutls_extensions.c @@ -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; } diff --git a/lib/gnutls_handshake.c b/lib/gnutls_handshake.c index 7423f2737c..b7d5af3310 100644 --- a/lib/gnutls_handshake.c +++ b/lib/gnutls_handshake.c @@ -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; } } diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h index ac1da9df3e..5f07164aac 100644 --- a/lib/gnutls_int.h +++ b/lib/gnutls_int.h @@ -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 */ diff --git a/lib/gnutls_priority.c b/lib/gnutls_priority.c index f50eb8cccd..5248b4f1e1 100644 --- a/lib/gnutls_priority.c +++ b/lib/gnutls_priority.c @@ -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; diff --git a/lib/gnutls_sig.c b/lib/gnutls_sig.c index c49f2eda2a..76e1f6da89 100644 --- a/lib/gnutls_sig.c +++ b/lib/gnutls_sig.c @@ -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; - } diff --git a/lib/gnutls_sig.h b/lib/gnutls_sig.h index c338869d4a..4cc0df20c1 100644 --- a/lib/gnutls_sig.h +++ b/lib/gnutls_sig.h @@ -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, diff --git a/lib/gnutls_state.c b/lib/gnutls_state.c index cd08f44a88..912cc60839 100644 --- a/lib/gnutls_state.c +++ b/lib/gnutls_state.c @@ -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); } + diff --git a/lib/gnutls_state.h b/lib/gnutls_state.h index dbb2a51a09..bff0022820 100644 --- a/lib/gnutls_state.h +++ b/lib/gnutls_state.h @@ -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); diff --git a/lib/gnutls_x509.c b/lib/gnutls_x509.c index 4ee09841a5..ab7e44709f 100644 --- a/lib/gnutls_x509.c +++ b/lib/gnutls_x509.c @@ -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, diff --git a/lib/includes/gnutls/gnutls.h.in b/lib/includes/gnutls/gnutls.h.in index a4fe3fc67f..26e6d86a8a 100644 --- a/lib/includes/gnutls/gnutls.h.in +++ b/lib/includes/gnutls/gnutls.h.in @@ -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 */ diff --git a/lib/openpgp/gnutls_openpgp.c b/lib/openpgp/gnutls_openpgp.c index 16d18d8946..fb50075390 100644 --- a/lib/openpgp/gnutls_openpgp.c +++ b/lib/openpgp/gnutls_openpgp.c @@ -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; |