]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
handshake: introduced server side handshake [2/2]
authorNikos Mavrogiannopoulos <nmav@redhat.com>
Thu, 21 Sep 2017 10:58:51 +0000 (12:58 +0200)
committerNikos Mavrogiannopoulos <nmav@redhat.com>
Mon, 19 Feb 2018 14:29:34 +0000 (15:29 +0100)
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>
lib/gnutls_int.h
lib/handshake-tls13.c
lib/handshake.c
lib/tls13-sig.c
lib/tls13-sig.h
lib/tls13/certificate.c
lib/tls13/certificate_request.c
lib/tls13/certificate_verify.c
lib/tls13/certificate_verify.h

index 998274e264da29d67273f2bc950b1cbedc35511f..62dd6cb6c75e92a997343ca1a255346bafdae0f4 100644 (file)
@@ -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
index 77cf9ffbed18c7311948d830dcfc9a8c84c858c9..9b45d6f729ae05649f5385f1e39f33ac4cbf01b8 100644 (file)
@@ -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;
 }
 
index 408d7ca23c198343e05368621155e48abe892ba5..97e45d60abd3e7b3a915ea61523e89560603849a 100644 (file)
@@ -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)
 {
index 953f5bf845d88f3df4a4ebe4d20f45b12c823148..8995fa456c89e88f838b462c584188a03a106a79 100644 (file)
@@ -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;
+
+}
index b82dbc91b814206ac5cda0927f7f475354a26330..777dd0f48b25e9c0c63a67042792e748c8597db4 100644 (file)
@@ -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
index 29c7de4590d61ae2cf1df3bb21c19c236b1884b8..e53f116213baa8c8bc3531e69c1e2484dc5a52ef 100644 (file)
@@ -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);
index 0afe8b155aca8786562e4c6e8b6ac93ae8a15e6b..a8f2c03b3cfec3ef5f4385f3d690a6d3b7679211 100644 (file)
@@ -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;
 }
 
index e702236797fc5f22516efce349c0daad6a193ca7..8f92d4074b8a975a45e594db1edb15469e249bae 100644 (file)
@@ -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;
 }
index c0641ebffcf0b7d3b41dbe0660afad0e5fb579dd..03373e70cc0449f2a87b87ce367d27b77ac064e2 100644 (file)
@@ -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);