diff options
author | Nikos Mavrogiannopoulos <nmav@redhat.com> | 2017-09-14 10:22:36 +0200 |
---|---|---|
committer | Nikos Mavrogiannopoulos <nmav@redhat.com> | 2018-02-19 15:29:34 +0100 |
commit | 2f4cd8b95661ad5ad761bb03fcb7a4fba6d2289c (patch) | |
tree | d8ca5576919a4606f2d227d6a990e1a5e2c548f2 /lib | |
parent | 5a8eb0c3fe660c1db8497c58fb5cae22829bc5dd (diff) | |
download | gnutls-2f4cd8b95661ad5ad761bb03fcb7a4fba6d2289c.tar.gz |
handshake: added basic support for TLS 1.3 handshake in client side
That does not include support for client certificates as it
requires extension handling improvements in order for extensions
to be context sensitive (now they cannot distinguish whether the
parsing routine is called during client hello or certificate request
reading)
This does not include proper parsing of extensions present in
the certificate message.
Signed-off-by: Nikos Mavrogiannopoulos <nmav@redhat.com>
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Makefile.am | 7 | ||||
-rw-r--r-- | lib/algorithms.h | 3 | ||||
-rw-r--r-- | lib/algorithms/sign.c | 21 | ||||
-rw-r--r-- | lib/auth/cert.c | 11 | ||||
-rw-r--r-- | lib/auth/cert.h | 2 | ||||
-rw-r--r-- | lib/constate.c | 14 | ||||
-rw-r--r-- | lib/gnutls_int.h | 2 | ||||
-rw-r--r-- | lib/handshake-tls13.c | 18 | ||||
-rw-r--r-- | lib/pcert.c | 3 | ||||
-rw-r--r-- | lib/tls13-sig.c | 129 | ||||
-rw-r--r-- | lib/tls13-sig.h | 36 | ||||
-rw-r--r-- | lib/tls13/certificate.c | 223 | ||||
-rw-r--r-- | lib/tls13/certificate.h | 24 | ||||
-rw-r--r-- | lib/tls13/certificate_request.c | 70 | ||||
-rw-r--r-- | lib/tls13/certificate_request.h | 23 | ||||
-rw-r--r-- | lib/tls13/certificate_verify.c | 126 | ||||
-rw-r--r-- | lib/tls13/certificate_verify.h | 24 | ||||
-rw-r--r-- | lib/tls13/finished.c | 154 | ||||
-rw-r--r-- | lib/tls13/finished.h | 24 |
19 files changed, 889 insertions, 25 deletions
diff --git a/lib/Makefile.am b/lib/Makefile.am index 35633138ea..54bb8690f0 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -87,7 +87,12 @@ else COBJECTS += system/keys-dummy.c endif -COBJECTS += tls13/encrypted_extensions.c tls13/encrypted_extensions.h +COBJECTS += tls13/encrypted_extensions.c tls13/encrypted_extensions.h \ + tls13/certificate_request.c tls13/certificate_request.h \ + tls13/certificate_verify.c tls13/certificate_verify.h \ + tls13-sig.c tls13-sig.h \ + tls13/finished.c tls13/finished.h \ + tls13/certificate.c tls13/certificate.h if ENABLE_PKCS11 COBJECTS += pkcs11.c pkcs11x.c pkcs11_privkey.c pkcs11_write.c pkcs11_secret.c \ diff --git a/lib/algorithms.h b/lib/algorithms.h index da750d20ee..2ce4b92e24 100644 --- a/lib/algorithms.h +++ b/lib/algorithms.h @@ -343,6 +343,9 @@ gnutls_pk_algorithm_t _gnutls_x509_sign_to_pk(gnutls_sign_algorithm_t const char *_gnutls_x509_sign_to_oid(gnutls_pk_algorithm_t, gnutls_digest_algorithm_t mac); +const gnutls_sign_entry_st * +_gnutls_tls_aid_to_sign_entry(uint8_t id0, uint8_t id1, const version_entry_st *ver); + gnutls_sign_algorithm_t _gnutls_tls_aid_to_sign(uint8_t id0, uint8_t id1, const version_entry_st *ver); const sign_algorithm_st *_gnutls_sign_to_tls_aid(gnutls_sign_algorithm_t diff --git a/lib/algorithms/sign.c b/lib/algorithms/sign.c index 0b012d4639..33fd881892 100644 --- a/lib/algorithms/sign.c +++ b/lib/algorithms/sign.c @@ -639,7 +639,7 @@ _gnutls_tls_aid_to_sign(uint8_t id0, uint8_t id1, const version_entry_st *ver) return ret; GNUTLS_SIGN_LOOP( - if (p->aid.id[0] == id0 && + if (p->aid.id[0] == id0 && p->aid.id[1] == id1 && ((p->aid.tls_sem & ver->tls_sig_sem) != 0)) { @@ -648,7 +648,6 @@ _gnutls_tls_aid_to_sign(uint8_t id0, uint8_t id1, const version_entry_st *ver) } ); - return ret; } @@ -675,3 +674,21 @@ const gnutls_sign_entry_st *_gnutls_sign_to_entry(gnutls_sign_algorithm_t sign) return ret; } + +const gnutls_sign_entry_st * +_gnutls_tls_aid_to_sign_entry(uint8_t id0, uint8_t id1, const version_entry_st *ver) +{ + if (id0 == 255 && id1 == 255) + return NULL; + + GNUTLS_SIGN_LOOP( + if (p->aid.id[0] == id0 && + p->aid.id[1] == id1 && + ((p->aid.tls_sem & ver->tls_sig_sem) != 0)) { + + return p; + } + ); + + return NULL; +} diff --git a/lib/auth/cert.c b/lib/auth/cert.c index 86df597616..1025ac2f52 100644 --- a/lib/auth/cert.c +++ b/lib/auth/cert.c @@ -72,8 +72,7 @@ typedef enum CertificateSigType { RSA_SIGN = 1, DSA_SIGN = 2, ECDSA_SIGN = 64 /* Copies data from a internal certificate struct (gnutls_pcert_st) to * exported certificate struct (cert_auth_info_t) */ -static int copy_certificate_auth_info(cert_auth_info_t info, gnutls_pcert_st * certs, size_t ncerts, /* openpgp only */ - void *keyid) +int _gnutls_copy_certificate_auth_info(cert_auth_info_t info, gnutls_pcert_st * certs, size_t ncerts) { /* Copy peer's information to auth_info_t */ @@ -836,11 +835,11 @@ _gnutls_proc_x509_server_crt(gnutls_session_t session, goto cleanup; } - if ((ret = - copy_certificate_auth_info(info, + ret = + _gnutls_copy_certificate_auth_info(info, peer_certificate_list, - peer_certificate_list_size, - NULL)) < 0) { + peer_certificate_list_size); + if (ret < 0) { gnutls_assert(); goto cleanup; } diff --git a/lib/auth/cert.h b/lib/auth/cert.h index 9383c2e2cb..1c89ebe1a8 100644 --- a/lib/auth/cert.h +++ b/lib/auth/cert.h @@ -130,6 +130,8 @@ int _gnutls_get_selected_cert(gnutls_session_t session, int *apr_cert_list_length, gnutls_privkey_t * apr_pkey); +int _gnutls_copy_certificate_auth_info(cert_auth_info_t info, gnutls_pcert_st * certs, size_t ncerts); + int _gnutls_server_select_cert(gnutls_session_t session, const gnutls_cipher_suite_entry_st *cs); void _gnutls_selected_certs_deinit(gnutls_session_t session); diff --git a/lib/constate.c b/lib/constate.c index 1e6862e06a..e5968804a3 100644 --- a/lib/constate.c +++ b/lib/constate.c @@ -203,8 +203,6 @@ static int _tls13_set_keys(gnutls_session_t session, record_parameters_st * params, unsigned iv_size, unsigned key_size) { - uint8_t hs_ckey[MAX_HASH_SIZE]; - uint8_t hs_skey[MAX_HASH_SIZE]; uint8_t ckey_block[MAX_CIPHER_KEY_SIZE]; uint8_t civ_block[MAX_CIPHER_IV_SIZE]; uint8_t skey_block[MAX_CIPHER_KEY_SIZE]; @@ -215,33 +213,33 @@ _tls13_set_keys(gnutls_session_t session, record_parameters_st * params, ret = _tls13_derive_secret(session, HANDSHAKE_CLIENT_TRAFFIC_LABEL, sizeof(HANDSHAKE_CLIENT_TRAFFIC_LABEL)-1, session->internals.handshake_hash_buffer.data, - session->internals.handshake_hash_buffer.length, hs_ckey); + session->internals.handshake_hash_buffer.length, session->key.hs_ckey); if (ret < 0) return gnutls_assert_val(ret); /* client keys */ - ret = _tls13_expand_secret(session, "key", 3, NULL, 0, hs_ckey, key_size, ckey_block); + ret = _tls13_expand_secret(session, "key", 3, NULL, 0, session->key.hs_ckey, key_size, ckey_block); if (ret < 0) return gnutls_assert_val(ret); - ret = _tls13_expand_secret(session, "iv", 2, NULL, 0, hs_ckey, iv_size, civ_block); + ret = _tls13_expand_secret(session, "iv", 2, NULL, 0, session->key.hs_ckey, iv_size, civ_block); if (ret < 0) return gnutls_assert_val(ret); /* server keys */ ret = _tls13_derive_secret(session, HANDSHAKE_SERVER_TRAFFIC_LABEL, sizeof(HANDSHAKE_SERVER_TRAFFIC_LABEL)-1, session->internals.handshake_hash_buffer.data, - session->internals.handshake_hash_buffer.length, hs_skey); + session->internals.handshake_hash_buffer.length, session->key.hs_skey); if (ret < 0) return gnutls_assert_val(ret); - ret = _tls13_expand_secret(session, "key", 3, NULL, 0, hs_skey, key_size, skey_block); + ret = _tls13_expand_secret(session, "key", 3, NULL, 0, session->key.hs_skey, key_size, skey_block); if (ret < 0) return gnutls_assert_val(ret); - ret = _tls13_expand_secret(session, "iv", 2, NULL, 0, hs_skey, iv_size, siv_block); + ret = _tls13_expand_secret(session, "iv", 2, NULL, 0, session->key.hs_skey, iv_size, siv_block); if (ret < 0) return gnutls_assert_val(ret); diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h index c59cbdb0f6..af2e9ad766 100644 --- a/lib/gnutls_int.h +++ b/lib/gnutls_int.h @@ -427,6 +427,8 @@ struct gnutls_key_st { * early_secret, client_early_traffic_secret, ... */ uint8_t temp_secret[MAX_HASH_SIZE]; unsigned temp_secret_size; /* depends on negotiated PRF size */ + uint8_t hs_ckey[MAX_HASH_SIZE]; /* client_handshake_traffic_secret */ + uint8_t hs_skey[MAX_HASH_SIZE]; /* server_handshake_traffic_secret */ /* For ECDH KX */ gnutls_pk_params_st ecdh_params; /* private part */ diff --git a/lib/handshake-tls13.c b/lib/handshake-tls13.c index 5950c06d96..9502f1d69d 100644 --- a/lib/handshake-tls13.c +++ b/lib/handshake-tls13.c @@ -48,6 +48,10 @@ #include <dtls.h> #include "secrets.h" #include "tls13/encrypted_extensions.h" +#include "tls13/certificate_request.h" +#include "tls13/certificate_verify.h" +#include "tls13/certificate.h" +#include "tls13/finished.h" static int generate_hs_traffic_keys(gnutls_session_t session); @@ -72,17 +76,17 @@ int _gnutls13_handshake_client(gnutls_session_t session) IMED_RET("recv encrypted extensions", ret, 0); /* fall through */ case STATE102: - abort(); + ret = _gnutls13_recv_certificate_request(session); STATE = STATE102; IMED_RET("recv certificate request", ret, 0); /* fall through */ case STATE103: - abort(); + ret = _gnutls13_recv_certificate(session); STATE = STATE103; IMED_RET("recv certificate", ret, 0); /* fall through */ case STATE104: - abort(); + ret = _gnutls13_recv_certificate_verify(session); STATE = STATE104; IMED_RET("recv server certificate verify", ret, 0); /* fall through */ @@ -93,22 +97,22 @@ int _gnutls13_handshake_client(gnutls_session_t session) return gnutls_assert_val(ret); FALLTHROUGH; case STATE106: - abort(); + ret = _gnutls13_recv_finished(session); STATE = STATE106; IMED_RET("recv finished", ret, 0); /* fall through */ case STATE107: - abort(); + ret = _gnutls13_send_certificate(session); STATE = STATE107; IMED_RET("send certificate", ret, 0); /* fall through */ case STATE108: - abort(); + ret = _gnutls13_send_certificate_verify(session); STATE = STATE108; IMED_RET("send certificate verify", ret, 0); /* fall through */ case STATE109: - abort(); + ret = _gnutls13_send_finished(session, AGAIN(STATE109)); STATE = STATE109; IMED_RET("send finished", ret, 0); diff --git a/lib/pcert.c b/lib/pcert.c index 56322e124c..e88ddc3fba 100644 --- a/lib/pcert.c +++ b/lib/pcert.c @@ -317,7 +317,8 @@ int gnutls_pcert_export_x509(gnutls_pcert_st * pcert, **/ void gnutls_pcert_deinit(gnutls_pcert_st * pcert) { - gnutls_pubkey_deinit(pcert->pubkey); + if (pcert->pubkey) + gnutls_pubkey_deinit(pcert->pubkey); pcert->pubkey = NULL; _gnutls_free_datum(&pcert->cert); } diff --git a/lib/tls13-sig.c b/lib/tls13-sig.c new file mode 100644 index 0000000000..953f5bf845 --- /dev/null +++ b/lib/tls13-sig.c @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2017 Red Hat, Inc. + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GnuTLS. + * + * The GnuTLS 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 program. If not, see <http://www.gnu.org/licenses/> + * + */ + +#include "gnutls_int.h" +#include "errors.h" +#include <auth/cert.h> +#include <algorithms.h> +#include <ext/signature.h> +#include <abstract_int.h> +#include "tls13-sig.h" +#include "hash_int.h" + +#undef PREFIX_SIZE +#define PREFIX_SIZE 64 +#if PREFIX_SIZE < MAX_HASH_SIZE +/* we assume later that prefix is sufficient to store hash output */ +# error Need to modify code +#endif + +int +_gnutls13_handshake_verify_data(gnutls_session_t session, + unsigned verify_flags, + gnutls_pcert_st *cert, + const gnutls_datum_t *context, + const gnutls_datum_t *signature, + const gnutls_sign_entry_st *se) +{ + int ret; + const version_entry_st *ver = get_version(session); + gnutls_buffer_st buf; + uint8_t prefix[PREFIX_SIZE]; + gnutls_datum_t p; + + _gnutls_handshake_log + ("HSK[%p]: verifying TLS 1.3 handshake data using %s\n", session, + se->name); + + ret = + _gnutls_pubkey_compatible_with_sig(session, + cert->pubkey, ver, + se->id); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = + _gnutls_session_sign_algo_enabled(session, se->id); + if (ret < 0) + return gnutls_assert_val(ret); + + if (se->hash == GNUTLS_DIG_SHA1) /* explicitly prohibited */ + return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); + if (se->pk == GNUTLS_PK_RSA) /* explicitly prohibited */ + return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); + + _gnutls_buffer_init(&buf); + + memset(prefix, 0x20, sizeof(prefix)); + ret = _gnutls_buffer_append_data(&buf, prefix, sizeof(prefix)); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = _gnutls_buffer_append_data(&buf, context->data, context->size); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = _gnutls_buffer_append_data(&buf, "\x00", 1); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = gnutls_hash_fast(session->security_parameters.prf->id, + session->internals.handshake_hash_buffer.data, + session->internals.handshake_hash_buffer_prev_len, + prefix); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = _gnutls_buffer_append_data(&buf, prefix, session->security_parameters.prf->output_size); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + p.data = buf.data; + p.size = buf.length; + + /* Here we intentionally enable flag GNUTLS_VERIFY_ALLOW_BROKEN + * because we have checked whether the currently used signature + * algorithm is allowed in the session. */ + ret = gnutls_pubkey_verify_data2(cert->pubkey, se->id, + verify_flags|GNUTLS_VERIFY_ALLOW_BROKEN, + &p, signature); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = 0; + cleanup: + _gnutls_buffer_clear(&buf); + + return ret; +} diff --git a/lib/tls13-sig.h b/lib/tls13-sig.h new file mode 100644 index 0000000000..b82dbc91b8 --- /dev/null +++ b/lib/tls13-sig.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2017 Red Hat, Inc. + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GnuTLS. + * + * The GnuTLS 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 program. If not, see <http://www.gnu.org/licenses/> + * + */ + +#ifndef _TLS13_SIG_H +# define _TLS13_SIG_H + +#include "gnutls_int.h" + +int +_gnutls13_handshake_verify_data(gnutls_session_t session, + unsigned verify_flags, + gnutls_pcert_st *cert, + const gnutls_datum_t *context, + const gnutls_datum_t *signature, + const gnutls_sign_entry_st *se); + +#endif diff --git a/lib/tls13/certificate.c b/lib/tls13/certificate.c new file mode 100644 index 0000000000..8a01005301 --- /dev/null +++ b/lib/tls13/certificate.c @@ -0,0 +1,223 @@ +/* + * Copyright (C) 2017 Red Hat, Inc. + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GnuTLS. + * + * The GnuTLS 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 program. If not, see <http://www.gnu.org/licenses/> + * + */ + +#include "gnutls_int.h" +#include "errors.h" +#include "extv.h" +#include "handshake.h" +#include "tls13/certificate.h" +#include "auth/cert.h" + +static int parse_cert_extension(void *ctx, uint16_t tls_id, const uint8_t *data, int data_size); +static int parse_cert_list(gnutls_session_t session, uint8_t * data, size_t data_size); + +int _gnutls13_recv_certificate(gnutls_session_t session) +{ + int ret; + gnutls_buffer_st buf; + + ret = _gnutls_recv_handshake(session, GNUTLS_HANDSHAKE_CERTIFICATE_PKT, 0, &buf); + if (ret < 0) + return gnutls_assert_val(ret); + + if (buf.data[0] != 0) { + /* The context field must be empty during handshake */ + gnutls_assert(); + ret = GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER; + goto cleanup; + } + + /* buf.length is positive */ + buf.data++; + buf.length--; + + _gnutls_handshake_log("HSK[%p]: parsing certificate message\n", session); + + ret = parse_cert_list(session, buf.data, buf.length); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = 0; +cleanup: + + _gnutls_buffer_clear(&buf); + return ret; +} + +static int parse_cert_extension(void *ctx, uint16_t tls_id, const uint8_t *data, int data_size) +{ + /* ignore all extensions */ + return 0; +} + +static int +parse_cert_list(gnutls_session_t session, uint8_t * data, size_t data_size) +{ + int len, ret; + uint8_t *p = data; + cert_auth_info_t info; + gnutls_certificate_credentials_t cred; + ssize_t dsize = data_size, size; + int i; + gnutls_pcert_st *peer_certificate_list; + size_t peer_certificate_list_size = 0, j, x; + gnutls_datum_t tmp; + + cred = (gnutls_certificate_credentials_t) + _gnutls_get_cred(session, GNUTLS_CRD_CERTIFICATE); + if (cred == NULL) { + gnutls_assert(); + return GNUTLS_E_INSUFFICIENT_CREDENTIALS; + } + + if ((ret = + _gnutls_auth_info_set(session, GNUTLS_CRD_CERTIFICATE, + sizeof(cert_auth_info_st), 1)) < 0) { + gnutls_assert(); + return ret; + } + + info = _gnutls_get_auth_info(session, GNUTLS_CRD_CERTIFICATE); + + if (data == NULL || data_size == 0) { + gnutls_assert(); + /* no certificate was sent */ + return GNUTLS_E_NO_CERTIFICATE_FOUND; + } + + DECR_LEN(dsize, 3); + size = _gnutls_read_uint24(p); + p += 3; + + if (size != dsize) + return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); + + if (size == 0) { + gnutls_assert(); + /* no certificate was sent */ + return GNUTLS_E_NO_CERTIFICATE_FOUND; + } + + i = dsize; + while (i > 0) { + DECR_LEN(dsize, 3); + len = _gnutls_read_uint24(p); + p += 3; + DECR_LEN(dsize, len); + p += len; + i -= len + 3; + + DECR_LEN(dsize, 2); + len = _gnutls_read_uint16(p); + DECR_LEN(dsize, len); + + i -= len + 2; + + peer_certificate_list_size++; + } + + if (dsize != 0) + return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); + + if (peer_certificate_list_size == 0) { + gnutls_assert(); + return GNUTLS_E_NO_CERTIFICATE_FOUND; + } + + /* Ok we now allocate the memory to hold the + * certificate list + */ + + peer_certificate_list = + gnutls_calloc(1, + sizeof(gnutls_pcert_st) * + (peer_certificate_list_size)); + if (peer_certificate_list == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + p = data+3; + + /* Now we start parsing the list (again). + * We don't use DECR_LEN since the list has + * been parsed before. + */ + + for (j = 0; j < peer_certificate_list_size; j++) { + len = _gnutls_read_uint24(p); + p += 3; + + tmp.size = len; + tmp.data = p; + + ret = + gnutls_pcert_import_x509_raw(&peer_certificate_list + [j], &tmp, + GNUTLS_X509_FMT_DER, 0); + if (ret < 0) { + gnutls_assert(); + _gnutls_debug_log("error importing certificate[%d]: %s\n", (int)j, gnutls_strerror(ret)); + peer_certificate_list_size = j; + ret = GNUTLS_E_CERTIFICATE_ERROR; + goto cleanup; + } + + p += len; + + len = _gnutls_read_uint16(p); + p += 2; + + /* FIXME: properly parse extensions */ + ret = _gnutls_extv_parse(NULL, parse_cert_extension, p, len); + if (ret < 0) { + gnutls_assert(); + peer_certificate_list_size = j+1; + goto cleanup; + } + } + + ret = + _gnutls_copy_certificate_auth_info(info, + peer_certificate_list, + peer_certificate_list_size); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = 0; + + cleanup: + for(x=0;x<peer_certificate_list_size;x++) + gnutls_pcert_deinit(&peer_certificate_list[x]); + gnutls_free(peer_certificate_list); + return ret; + +} + +int _gnutls13_send_certificate(gnutls_session_t session) +{ + return 0; +} diff --git a/lib/tls13/certificate.h b/lib/tls13/certificate.h new file mode 100644 index 0000000000..686e4ea8ae --- /dev/null +++ b/lib/tls13/certificate.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2017 Red Hat, Inc. + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GnuTLS. + * + * The GnuTLS 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 program. If not, see <http://www.gnu.org/licenses/> + * + */ + +int _gnutls13_recv_certificate(gnutls_session_t session); +int _gnutls13_send_certificate(gnutls_session_t session); diff --git a/lib/tls13/certificate_request.c b/lib/tls13/certificate_request.c new file mode 100644 index 0000000000..9ec7bd6010 --- /dev/null +++ b/lib/tls13/certificate_request.c @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2017 Red Hat, Inc. + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GnuTLS. + * + * The GnuTLS 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 program. If not, see <http://www.gnu.org/licenses/> + * + */ + +#include "gnutls_int.h" +#include "errors.h" +#include "extv.h" +#include "handshake.h" +#include "tls13/certificate_request.h" + +static +int parse_cert_extension(void *ctx, uint16_t tls_id, const uint8_t *data, int data_size) +{ + /* ignore all exts */ + return 0; +} + +int _gnutls13_recv_certificate_request(gnutls_session_t session) +{ + int ret; + gnutls_buffer_st buf; + + ret = _gnutls_recv_handshake(session, GNUTLS_HANDSHAKE_CERTIFICATE_REQUEST, 1, &buf); + if (ret < 0) + return gnutls_assert_val(ret); + + /* if not received */ + if (buf.length == 0) { + _gnutls_buffer_clear(&buf); + return 0; + } + + _gnutls_handshake_log("HSK[%p]: parsing certificate request\n", session); + + if (buf.data[0] != 0) { + /* The context field must be empty during handshake */ + gnutls_assert(); + return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); + } + + /* buf.length is positive */ + buf.data++; + buf.length--; + + ret = _gnutls_extv_parse(NULL, parse_cert_extension, buf.data, buf.length); + _gnutls_buffer_clear(&buf); + + if (ret < 0) + return gnutls_assert_val(ret); + + return 0; +} diff --git a/lib/tls13/certificate_request.h b/lib/tls13/certificate_request.h new file mode 100644 index 0000000000..4a99221aad --- /dev/null +++ b/lib/tls13/certificate_request.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2017 Red Hat, Inc. + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GnuTLS. + * + * The GnuTLS 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 program. If not, see <http://www.gnu.org/licenses/> + * + */ + +int _gnutls13_recv_certificate_request(gnutls_session_t session); diff --git a/lib/tls13/certificate_verify.c b/lib/tls13/certificate_verify.c new file mode 100644 index 0000000000..e702236797 --- /dev/null +++ b/lib/tls13/certificate_verify.c @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2017 Red Hat, Inc. + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GnuTLS. + * + * The GnuTLS 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 program. If not, see <http://www.gnu.org/licenses/> + * + */ + +#include "gnutls_int.h" +#include "errors.h" +#include "handshake.h" +#include "auth/cert.h" +#include "ext/signature.h" +#include "algorithms.h" +#include "tls13-sig.h" +#include "tls13/certificate_verify.h" + +#define SRV_CTX "TLS 1.3, server CertificateVerify" +static const gnutls_datum_t srv_ctx = { + (void*)SRV_CTX, sizeof(SRV_CTX)-1 +}; + +int _gnutls13_recv_certificate_verify(gnutls_session_t session) +{ + int ret; + gnutls_buffer_st buf; + const gnutls_sign_entry_st *se; + gnutls_datum_t sig_data; + gnutls_certificate_credentials_t cred; + unsigned vflags; + gnutls_pcert_st peer_cert; + cert_auth_info_t info = _gnutls_get_auth_info(session, GNUTLS_CRD_CERTIFICATE); + + memset(&peer_cert, 0, sizeof(peer_cert)); + + cred = (gnutls_certificate_credentials_t) + _gnutls_get_cred(session, GNUTLS_CRD_CERTIFICATE); + if (unlikely(cred == NULL)) + return gnutls_assert_val(GNUTLS_E_INSUFFICIENT_CREDENTIALS); + if (unlikely(info == NULL)) + return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); + + ret = _gnutls_recv_handshake(session, GNUTLS_HANDSHAKE_CERTIFICATE_VERIFY, 0, &buf); + if (ret < 0) + return gnutls_assert_val(ret); + + _gnutls_handshake_log("HSK[%p]: parsing certificate verify\n", session); + + if (buf.length < 2) { + gnutls_assert(); + ret = GNUTLS_E_UNEXPECTED_PACKET_LENGTH; + goto cleanup; + } + + se = _gnutls_tls_aid_to_sign_entry(buf.data[0], buf.data[1], get_version(session)); + if (se == NULL) { + _gnutls_handshake_log("found unsupported signature (%d.%d)\n", (int)buf.data[0], (int)buf.data[1]); + ret = gnutls_assert_val(GNUTLS_E_UNSUPPORTED_SIGNATURE_ALGORITHM); + goto cleanup; + } + + gnutls_sign_algorithm_set_server(session, se->id); + + buf.data+=2; + buf.length-=2; + + /* we check during verification whether the algorithm is enabled */ + + ret = _gnutls_buffer_pop_datum_prefix16(&buf, &sig_data); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + if (sig_data.size == 0) { + gnutls_assert(); + ret = GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER; + goto cleanup; + } + + /* Verify the signature */ + ret = _gnutls_get_auth_info_pcert(&peer_cert, session->security_parameters.cert_type, info); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + vflags = cred->verify_flags | session->internals.additional_verify_flags; + + ret = _gnutls13_handshake_verify_data(session, vflags, &peer_cert, &srv_ctx, &sig_data, se); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + if (buf.length > 0) { + gnutls_assert(); + ret = GNUTLS_E_UNEXPECTED_PACKET_LENGTH; + goto cleanup; + } + + ret = 0; + cleanup: + gnutls_pcert_deinit(&peer_cert); + _gnutls_buffer_clear(&buf); + return ret; +} + +int _gnutls13_send_certificate_verify(gnutls_session_t session) +{ + return 0; +} diff --git a/lib/tls13/certificate_verify.h b/lib/tls13/certificate_verify.h new file mode 100644 index 0000000000..c0641ebffc --- /dev/null +++ b/lib/tls13/certificate_verify.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2017 Red Hat, Inc. + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GnuTLS. + * + * The GnuTLS 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 program. If not, see <http://www.gnu.org/licenses/> + * + */ + +int _gnutls13_recv_certificate_verify(gnutls_session_t session); +int _gnutls13_send_certificate_verify(gnutls_session_t session); diff --git a/lib/tls13/finished.c b/lib/tls13/finished.c new file mode 100644 index 0000000000..8d9975cefa --- /dev/null +++ b/lib/tls13/finished.c @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2017 Red Hat, Inc. + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GnuTLS. + * + * The GnuTLS 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 program. If not, see <http://www.gnu.org/licenses/> + * + */ + +#include "gnutls_int.h" +#include "errors.h" +#include "handshake.h" +#include "tls13/finished.h" +#include "mem.h" +#include "mbuffers.h" +#include "secrets.h" + +int _gnutls13_recv_finished(gnutls_session_t session) +{ + int ret; + gnutls_buffer_st buf; + uint8_t fkey[MAX_HASH_SIZE]; + uint8_t ts_hash[MAX_HASH_SIZE]; + uint8_t verifier[MAX_HASH_SIZE]; + const uint8_t *base_key; + unsigned hash_size = session->security_parameters.prf->output_size; + + if (session->security_parameters.entity == GNUTLS_CLIENT) + base_key = session->key.hs_skey; + else + base_key = session->key.hs_ckey; + + ret = _tls13_expand_secret(session, "finished", 8, NULL, 0, base_key, + hash_size, fkey); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = gnutls_hash_fast(session->security_parameters.prf->id, + session->internals.handshake_hash_buffer.data, + /* we haven't yet processed the finished message */ + session->internals.handshake_hash_buffer.length, + ts_hash); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = gnutls_hmac_fast(session->security_parameters.prf->id, + fkey, hash_size, + ts_hash, hash_size, + verifier); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = _gnutls_recv_handshake(session, GNUTLS_HANDSHAKE_FINISHED, 0, &buf); + if (ret < 0) + return gnutls_assert_val(ret); + + _gnutls_handshake_log("HSK[%p]: parsing finished\n", session); + + if (buf.length != hash_size) + return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); + + +#if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) +# warning This is unsafe for production builds +#else + if (safe_memcmp(verifier, buf.data, buf.length) != 0) { + gnutls_assert(); + return GNUTLS_E_ERROR_IN_FINISHED_PACKET; + } +#endif + + ret = 0; +cleanup: + + _gnutls_buffer_clear(&buf); + return ret; +} + +int _gnutls13_send_finished(gnutls_session_t session, unsigned again) +{ + int ret; + uint8_t fkey[MAX_HASH_SIZE]; + uint8_t ts_hash[MAX_HASH_SIZE]; + uint8_t verifier[MAX_HASH_SIZE]; + mbuffer_st *bufel = NULL; + const uint8_t *base_key; + unsigned hash_size = session->security_parameters.prf->output_size; + + if (again == 0) { + if (session->security_parameters.entity == GNUTLS_CLIENT) + base_key = session->key.hs_ckey; + else + base_key = session->key.hs_skey; + + ret = _tls13_expand_secret(session, "finished", 8, NULL, 0, base_key, + hash_size, fkey); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = gnutls_hash_fast(session->security_parameters.prf->id, + session->internals.handshake_hash_buffer.data, + session->internals.handshake_hash_buffer.length, + ts_hash); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = gnutls_hmac_fast(session->security_parameters.prf->id, + fkey, hash_size, + ts_hash, hash_size, + verifier); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + _gnutls_handshake_log("HSK[%p]: sending finished\n", session); + + bufel = _gnutls_handshake_alloc(session, hash_size); + if (bufel == NULL) + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + + _mbuffer_set_udata_size(bufel, 0); + ret = _mbuffer_append_data(bufel, verifier, hash_size); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + } + + return _gnutls_send_handshake(session, bufel, GNUTLS_HANDSHAKE_FINISHED); + +cleanup: + _mbuffer_xfree(&bufel); + return ret; +} diff --git a/lib/tls13/finished.h b/lib/tls13/finished.h new file mode 100644 index 0000000000..fc49c2e63f --- /dev/null +++ b/lib/tls13/finished.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2017 Red Hat, Inc. + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GnuTLS. + * + * The GnuTLS 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 program. If not, see <http://www.gnu.org/licenses/> + * + */ + +int _gnutls13_recv_finished(gnutls_session_t session); +int _gnutls13_send_finished(gnutls_session_t session, unsigned again); |