From be33e24b8104e405b061f950e492bec3804511ed Mon Sep 17 00:00:00 2001 From: Nikos Mavrogiannopoulos Date: Thu, 21 Sep 2017 12:58:51 +0200 Subject: handshake: introduced server side handshake [2/2] That is, send server certificate verify and receive certificate and certificate verify messages. In addition introduced flags to mark the expected, or sent messages. Signed-off-by: Nikos Mavrogiannopoulos --- lib/gnutls_int.h | 5 +++ lib/handshake-tls13.c | 23 +++++++---- lib/handshake.c | 3 +- lib/tls13-sig.c | 70 +++++++++++++++++++++++++++++++ lib/tls13-sig.h | 7 ++++ lib/tls13/certificate.c | 26 +++++++++++- lib/tls13/certificate_request.c | 2 + lib/tls13/certificate_verify.c | 91 ++++++++++++++++++++++++++++++++++++++++- lib/tls13/certificate_verify.h | 2 +- 9 files changed, 217 insertions(+), 12 deletions(-) diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h index 3cc35e2f2b..33a0018cd5 100644 --- a/lib/gnutls_int.h +++ b/lib/gnutls_int.h @@ -1106,6 +1106,11 @@ typedef struct { unsigned int handshake_timeout_ms; /* timeout in milliseconds */ unsigned int record_timeout_ms; /* timeout in milliseconds */ +#define HSK_CRT_VRFY_EXPECTED 1 +#define HSK_CRT_SENT (1<<1) +#define HSK_CRT_ASKED (1<<2) + unsigned hsk_flags; /* TLS1.3 only */ + unsigned crt_requested; /* 1 if client auth was requested (i.e., client cert). * In case of a server this holds 1 if we should wait * for a client certificate verify diff --git a/lib/handshake-tls13.c b/lib/handshake-tls13.c index 77cf9ffbed..9b45d6f729 100644 --- a/lib/handshake-tls13.c +++ b/lib/handshake-tls13.c @@ -109,7 +109,7 @@ int _gnutls13_handshake_client(gnutls_session_t session) IMED_RET("send certificate", ret, 0); /* fall through */ case STATE108: - ret = _gnutls13_send_certificate_verify(session); + ret = _gnutls13_send_certificate_verify(session, AGAIN(STATE108)); STATE = STATE108; IMED_RET("send certificate verify", ret, 0); /* fall through */ @@ -217,36 +217,41 @@ int _gnutls13_handshake_server(gnutls_session_t session) IMED_RET("send certificate", ret, 0); /* fall through */ case STATE104: - abort(); + ret = _gnutls13_send_certificate_verify(session, AGAIN(STATE104)); STATE = STATE104; IMED_RET("send certificate verify", ret, 0); /* fall through */ case STATE105: - abort(); + ret = _gnutls13_send_finished(session, AGAIN(STATE105)); STATE = STATE105; IMED_RET("send finished", ret, 0); /* fall through */ case STATE106: - abort(); + ret = _gnutls13_recv_certificate(session); STATE = STATE106; IMED_RET("recv certificate", ret, 0); /* fall through */ case STATE107: - abort(); + ret = _gnutls13_recv_certificate_verify(session); STATE = STATE107; IMED_RET("recv certificate verify", ret, 0); /* fall through */ case STATE108: - ret = _gnutls_run_verify_callback(session, GNUTLS_SERVER); + ret = _gnutls_run_verify_callback(session, GNUTLS_CLIENT); STATE = STATE108; if (ret < 0) return gnutls_assert_val(ret); /* fall through */ case STATE109: - abort(); + ret = _gnutls13_recv_finished(session); STATE = STATE109; IMED_RET("recv finished", ret, 0); /* fall through */ + case STATE110: + ret = + generate_ap_traffic_keys(session); + STATE = STATE110; + IMED_RET("generate app keys", ret, 0); STATE = STATE0; break; @@ -254,6 +259,10 @@ int _gnutls13_handshake_server(gnutls_session_t session) return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); } + /* explicitly reset any false start flags */ + session->internals.recv_state = RECV_STATE_0; + session->internals.initial_negotiation_completed = 1; + return 0; } diff --git a/lib/handshake.c b/lib/handshake.c index 408d7ca23c..97e45d60ab 100644 --- a/lib/handshake.c +++ b/lib/handshake.c @@ -2374,7 +2374,8 @@ gnutls_handshake_set_timeout(gnutls_session_t session, unsigned int ms) } /* Runs the certificate verification callback. - * side is either GNUTLS_CLIENT or GNUTLS_SERVER. + * side is the side that we verify the certificate + * from (either GNUTLS_CLIENT or GNUTLS_SERVER). */ int _gnutls_run_verify_callback(gnutls_session_t session, unsigned int side) { diff --git a/lib/tls13-sig.c b/lib/tls13-sig.c index 953f5bf845..8995fa456c 100644 --- a/lib/tls13-sig.c +++ b/lib/tls13-sig.c @@ -127,3 +127,73 @@ _gnutls13_handshake_verify_data(gnutls_session_t session, return ret; } + +int +_gnutls13_handshake_sign_data(gnutls_session_t session, + gnutls_pcert_st * cert, gnutls_privkey_t pkey, + const gnutls_datum_t *context, + gnutls_datum_t * signature, + const gnutls_sign_entry_st *se) +{ + gnutls_datum_t p; + int ret; + gnutls_buffer_st buf; + uint8_t prefix[PREFIX_SIZE]; + + if (unlikely(se == NULL || se->hash == GNUTLS_DIG_SHA1 || se->pk == GNUTLS_PK_RSA)) + return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); + + _gnutls_handshake_log + ("HSK[%p]: signing TLS 1.3 handshake data: using %s\n", session, se->name); + + _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.length, + 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; + + ret = gnutls_privkey_sign_data2(pkey, se->id, 0, &p, signature); + if (ret < 0) { + gnutls_assert(); + } + + ret = 0; + cleanup: + _gnutls_buffer_clear(&buf); + + return ret; + +} diff --git a/lib/tls13-sig.h b/lib/tls13-sig.h index b82dbc91b8..777dd0f48b 100644 --- a/lib/tls13-sig.h +++ b/lib/tls13-sig.h @@ -33,4 +33,11 @@ _gnutls13_handshake_verify_data(gnutls_session_t session, const gnutls_datum_t *signature, const gnutls_sign_entry_st *se); +int +_gnutls13_handshake_sign_data(gnutls_session_t session, + gnutls_pcert_st * cert, gnutls_privkey_t pkey, + const gnutls_datum_t *context, + gnutls_datum_t * signature, + const gnutls_sign_entry_st *se); + #endif diff --git a/lib/tls13/certificate.c b/lib/tls13/certificate.c index 29c7de4590..e53f116213 100644 --- a/lib/tls13/certificate.c +++ b/lib/tls13/certificate.c @@ -35,11 +35,25 @@ int _gnutls13_recv_certificate(gnutls_session_t session) { int ret; gnutls_buffer_st buf; + unsigned optional = 0; - ret = _gnutls_recv_handshake(session, GNUTLS_HANDSHAKE_CERTIFICATE_PKT, 0, &buf); + if (session->security_parameters.entity == GNUTLS_SERVER) { + /* if we didn't request a certificate, there will not be any */ + if (session->internals.send_cert_req == 0) + return 0; + + if (session->internals.send_cert_req != GNUTLS_CERT_REQUIRE) + optional = 1; + } + + ret = _gnutls_recv_handshake(session, GNUTLS_HANDSHAKE_CERTIFICATE_PKT, optional, &buf); if (ret < 0) return gnutls_assert_val(ret); + if (buf.length == 0 && optional) { + return 0; + } + if (buf.data[0] != 0) { /* The context field must be empty during handshake */ gnutls_assert(); @@ -59,6 +73,8 @@ int _gnutls13_recv_certificate(gnutls_session_t session) goto cleanup; } + session->internals.hsk_flags |= HSK_CRT_VRFY_EXPECTED; + ret = 0; cleanup: @@ -85,6 +101,14 @@ int _gnutls13_send_certificate(gnutls_session_t session, unsigned again) if (ret < 0) return gnutls_assert_val(ret); + if (session->security_parameters.entity == GNUTLS_CLIENT) { + /* if we didn't get a cert request there will not be any */ + if (apr_cert_list_length == 0 || + !(session->internals.hsk_flags & HSK_CRT_ASKED)) { + return 0; + } + } + ret = _gnutls_buffer_append_prefix(&buf, 8, 0); if (ret < 0) return gnutls_assert_val(ret); diff --git a/lib/tls13/certificate_request.c b/lib/tls13/certificate_request.c index 0afe8b155a..a8f2c03b3c 100644 --- a/lib/tls13/certificate_request.c +++ b/lib/tls13/certificate_request.c @@ -66,6 +66,8 @@ int _gnutls13_recv_certificate_request(gnutls_session_t session) if (ret < 0) return gnutls_assert_val(ret); + session->internals.hsk_flags |= HSK_CRT_ASKED; + return 0; } diff --git a/lib/tls13/certificate_verify.c b/lib/tls13/certificate_verify.c index e702236797..8f92d4074b 100644 --- a/lib/tls13/certificate_verify.c +++ b/lib/tls13/certificate_verify.c @@ -27,6 +27,7 @@ #include "ext/signature.h" #include "algorithms.h" #include "tls13-sig.h" +#include "mbuffers.h" #include "tls13/certificate_verify.h" #define SRV_CTX "TLS 1.3, server CertificateVerify" @@ -47,6 +48,11 @@ int _gnutls13_recv_certificate_verify(gnutls_session_t session) memset(&peer_cert, 0, sizeof(peer_cert)); + /* this message is only expected if we have received + * a certificate message */ + if (!(session->internals.hsk_flags & HSK_CRT_VRFY_EXPECTED)) + return 0; + cred = (gnutls_certificate_credentials_t) _gnutls_get_cred(session, GNUTLS_CRD_CERTIFICATE); if (unlikely(cred == NULL)) @@ -120,7 +126,88 @@ int _gnutls13_recv_certificate_verify(gnutls_session_t session) return ret; } -int _gnutls13_send_certificate_verify(gnutls_session_t session) +int _gnutls13_send_certificate_verify(gnutls_session_t session, unsigned again) { - return 0; + int ret; + gnutls_pcert_st *apr_cert_list; + gnutls_privkey_t apr_pkey; + int apr_cert_list_length; + mbuffer_st *bufel = NULL; + gnutls_buffer_st buf; + gnutls_datum_t sig = {NULL, 0}; + gnutls_sign_algorithm_t algo; + const gnutls_sign_entry_st *se; + + if (again == 0) { + _gnutls_buffer_init(&buf); + + ret = _gnutls_get_selected_cert(session, &apr_cert_list, + &apr_cert_list_length, &apr_pkey); + if (ret < 0) + return gnutls_assert_val(ret); + + if (apr_cert_list_length == 0) { + if (session->security_parameters.entity == GNUTLS_SERVER) { + return gnutls_assert_val(GNUTLS_E_INSUFFICIENT_CREDENTIALS); + } else { + /* if we didn't get a cert request there will not be any */ + if (!(session->internals.hsk_flags & HSK_CRT_SENT)) + return 0; + else + return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); + } + } + + algo = _gnutls_session_get_sign_algo(session, &apr_cert_list[0], apr_pkey, 0); + if (algo == GNUTLS_SIGN_UNKNOWN) + return gnutls_assert_val(GNUTLS_E_INCOMPATIBLE_SIG_WITH_KEY); + + if (session->security_parameters.entity == GNUTLS_SERVER) + gnutls_sign_algorithm_set_server(session, algo); + else + gnutls_sign_algorithm_set_client(session, algo); + + se = _gnutls_sign_to_entry(algo); + + ret = _gnutls13_handshake_sign_data(session, &apr_cert_list[0], apr_pkey, &srv_ctx, &sig, se); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = _gnutls_buffer_append_data(&buf, se->aid.id, 2); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = _gnutls_buffer_append_data_prefix(&buf, 16, sig.data, sig.size); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + bufel = _gnutls_handshake_alloc(session, buf.length); + if (bufel == NULL) { + gnutls_assert(); + ret = GNUTLS_E_MEMORY_ERROR; + goto cleanup; + } + + _mbuffer_set_udata_size(bufel, 0); + ret = _mbuffer_append_data(bufel, buf.data, buf.length); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + _gnutls_buffer_clear(&buf); + gnutls_free(sig.data); + } + + return _gnutls_send_handshake(session, bufel, GNUTLS_HANDSHAKE_CERTIFICATE_VERIFY); + + cleanup: + gnutls_free(sig.data); + _gnutls_buffer_clear(&buf); + _mbuffer_xfree(&bufel); + return ret; } diff --git a/lib/tls13/certificate_verify.h b/lib/tls13/certificate_verify.h index c0641ebffc..03373e70cc 100644 --- a/lib/tls13/certificate_verify.h +++ b/lib/tls13/certificate_verify.h @@ -21,4 +21,4 @@ */ int _gnutls13_recv_certificate_verify(gnutls_session_t session); -int _gnutls13_send_certificate_verify(gnutls_session_t session); +int _gnutls13_send_certificate_verify(gnutls_session_t session, unsigned again); -- cgit v1.2.1