diff options
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; |