summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikos Mavrogiannopoulos <nmav@redhat.com>2017-09-21 12:58:51 +0200
committerNikos Mavrogiannopoulos <nmav@redhat.com>2017-10-10 18:10:33 +0200
commitbe33e24b8104e405b061f950e492bec3804511ed (patch)
tree5cfe95fca129b292e32ed5443f7fae7fd70245b9
parent4aed51d4cdff6db3d4d061e04d07d3251dd50f32 (diff)
downloadgnutls-be33e24b8104e405b061f950e492bec3804511ed.tar.gz
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 <nmav@redhat.com>
-rw-r--r--lib/gnutls_int.h5
-rw-r--r--lib/handshake-tls13.c23
-rw-r--r--lib/handshake.c3
-rw-r--r--lib/tls13-sig.c70
-rw-r--r--lib/tls13-sig.h7
-rw-r--r--lib/tls13/certificate.c26
-rw-r--r--lib/tls13/certificate_request.c2
-rw-r--r--lib/tls13/certificate_verify.c91
-rw-r--r--lib/tls13/certificate_verify.h2
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);