Add client certificate parse and certificate verify

Change-Id: I638db78922a03db6f8bd70c6c5f56fb60365547d
Signed-off-by: XiaokangQian <xiaokang.qian@arm.com>
diff --git a/library/ssl_tls13_generic.c b/library/ssl_tls13_generic.c
index e69fd7b..a260717 100644
--- a/library/ssl_tls13_generic.c
+++ b/library/ssl_tls13_generic.c
@@ -153,6 +153,30 @@
     *verify_buffer_len = idx;
 }
 
+/* Coordinate: Check whether a certificate verify message is expected.
+ * Returns a negative value on failure, and otherwise
+ * - SSL_CERTIFICATE_VERIFY_SKIP
+ * - SSL_CERTIFICATE_VERIFY_READ
+ * to indicate if the CertificateVerify message should be present or not.
+ */
+#define SSL_CERTIFICATE_VERIFY_SKIP 0
+#define SSL_CERTIFICATE_VERIFY_READ 1
+static int ssl_tls13_read_certificate_verify_coordinate( mbedtls_ssl_context *ssl )
+{
+    if( mbedtls_ssl_tls13_some_psk_enabled( ssl ) )
+        return( SSL_CERTIFICATE_VERIFY_SKIP );
+
+#if !defined(MBEDTLS_KEY_EXCHANGE_WITH_CERT_ENABLED)
+    MBEDTLS_SSL_DEBUG_MSG( 1, ( "should never happen" ) );
+    return( MBEDTLS_ERR_SSL_INTERNAL_ERROR );
+#else
+    if( ssl->session_negotiate->peer_cert == NULL )
+        return( SSL_CERTIFICATE_VERIFY_SKIP );
+
+    return( SSL_CERTIFICATE_VERIFY_READ );
+#endif /* MBEDTLS_KEY_EXCHANGE_WITH_CERT_ENABLED */
+}
+
 static int ssl_tls13_parse_certificate_verify( mbedtls_ssl_context *ssl,
                                                const unsigned char *buf,
                                                const unsigned char *end,
@@ -315,6 +339,20 @@
 
     MBEDTLS_SSL_DEBUG_MSG( 2, ( "=> parse certificate verify" ) );
 
+    MBEDTLS_SSL_PROC_CHK_NEG( ssl_tls13_read_certificate_verify_coordinate( ssl ) );
+    if( ret == SSL_CERTIFICATE_VERIFY_SKIP )
+    {
+        MBEDTLS_SSL_DEBUG_MSG( 2, ( "<= skip parse certificate verify" ) );
+        ret = 0;
+        goto cleanup;
+    }
+    else if( ret != SSL_CERTIFICATE_VERIFY_READ )
+    {
+        MBEDTLS_SSL_DEBUG_MSG( 1, ( "should never happen" ) );
+        ret = MBEDTLS_ERR_SSL_INTERNAL_ERROR;
+        goto cleanup;
+    }
+
     MBEDTLS_SSL_PROC_CHK(
         mbedtls_ssl_tls13_fetch_handshake_msg( ssl,
                 MBEDTLS_SSL_HS_CERTIFICATE_VERIFY, &buf, &buf_len ) );
@@ -367,13 +405,66 @@
 
 /*
  *
- * STATE HANDLING: Incoming Certificate, client-side only currently.
+ * STATE HANDLING: Incoming Certificate.
  *
  */
 
-/*
- * Implementation
+/* Coordination: Check if a certificate is expected.
+ * Returns a negative error code on failure, and otherwise
+ * SSL_CERTIFICATE_EXPECTED or
+ * SSL_CERTIFICATE_SKIP
+ * indicating whether a Certificate message is expected or not.
  */
+#define SSL_CERTIFICATE_EXPECTED   0
+#define SSL_CERTIFICATE_SKIP       1
+
+static int ssl_tls13_read_certificate_coordinate( mbedtls_ssl_context *ssl )
+{
+#if defined(MBEDTLS_SSL_SRV_C)
+    int authmode = ssl->conf->authmode;
+#endif /* MBEDTLS_SSL_SRV_C */
+
+#if defined(MBEDTLS_SSL_SRV_C)
+    if( ssl->conf->endpoint == MBEDTLS_SSL_IS_SERVER )
+    {
+        MBEDTLS_SSL_DEBUG_MSG( 1, ( "Switch to handshake keys for inbound traffic" ) );
+
+        mbedtls_ssl_set_inbound_transform( ssl, ssl->handshake->transform_handshake );
+    }
+#endif /* MBEDTLS_SSL_SRV_C */
+
+    if( mbedtls_ssl_tls13_some_psk_enabled( ssl ) )
+        return( SSL_CERTIFICATE_SKIP );
+
+#if !defined(MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED)
+    ( ( void )authmode );
+    MBEDTLS_SSL_DEBUG_MSG( 1, ( "should never happen" ) );
+    return( MBEDTLS_ERR_SSL_INTERNAL_ERROR );
+#else
+#if defined(MBEDTLS_SSL_SRV_C)
+    if( ssl->conf->endpoint == MBEDTLS_SSL_IS_SERVER )
+    {
+        /* If SNI was used, overwrite authentication mode
+         * from the configuration. */
+#if defined(MBEDTLS_SSL_SERVER_NAME_INDICATION)
+        if( ssl->handshake->sni_authmode != MBEDTLS_SSL_VERIFY_UNSET )
+            authmode = ssl->handshake->sni_authmode;
+#endif /* MBEDTLS_SSL_SERVER_NAME_INDICATION */
+
+        if( authmode == MBEDTLS_SSL_VERIFY_NONE )
+        {
+            /* NOTE: Is it intentional that we set verify_result
+             * to SKIP_VERIFY on server-side only? */
+            ssl->session_negotiate->verify_result =
+                MBEDTLS_X509_BADCERT_SKIP_VERIFY;
+            return( SSL_CERTIFICATE_SKIP );
+        }
+    }
+#endif /* MBEDTLS_SSL_SRV_C */
+
+    return( SSL_CERTIFICATE_EXPECTED );
+#endif /* !MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED */
+}
 
 #if defined(MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED)
 #if defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
@@ -415,10 +506,39 @@
     const unsigned char *p = buf;
     const unsigned char *certificate_list_end;
 
-    MBEDTLS_SSL_CHK_BUF_READ_PTR( p, end, 4 );
+    MBEDTLS_SSL_CHK_BUF_READ_PTR( p, end, 1 );
     certificate_request_context_len = p[0];
-    certificate_list_len = MBEDTLS_GET_UINT24_BE( p, 1 );
-    p += 4;
+    p++;
+
+#if defined(MBEDTLS_SSL_SRV_C)
+    if( ssl->conf->endpoint == MBEDTLS_SSL_IS_SERVER )
+    {
+        MBEDTLS_SSL_CHK_BUF_READ_PTR( p, end,
+                                      certificate_request_context_len + 3 );
+
+        /* check whether we got an empty certificate message */
+        if( memcmp( p + certificate_request_context_len , "\0\0\0", 3 ) == 0 )
+        {
+            MBEDTLS_SSL_DEBUG_MSG( 1,
+                ( "client has no certificate - empty certificate message received" ) );
+
+            ssl->session_negotiate->verify_result = MBEDTLS_X509_BADCERT_MISSING;
+            if( ssl->conf->authmode == MBEDTLS_SSL_VERIFY_OPTIONAL )
+                return( 0 );
+            else
+            {
+                MBEDTLS_SSL_DEBUG_MSG( 1, ( "client certificate required" ) );
+                MBEDTLS_SSL_PEND_FATAL_ALERT( MBEDTLS_SSL_ALERT_MSG_CERT_REQUIRED,
+                                              MBEDTLS_ERR_SSL_NO_CLIENT_CERTIFICATE );
+                return( MBEDTLS_ERR_SSL_NO_CLIENT_CERTIFICATE );
+            }
+        }
+    }
+#endif /* MBEDTLS_SSL_SRV_C */
+
+    MBEDTLS_SSL_CHK_BUF_READ_PTR( p, end, 3 );
+    certificate_list_len = MBEDTLS_GET_UINT24_BE( p, 0 );
+    p += 3;
 
     /* In theory, the certificate list can be up to 2^24 Bytes, but we don't
      * support anything beyond 2^16 = 64K.
@@ -547,10 +667,56 @@
 static int ssl_tls13_validate_certificate( mbedtls_ssl_context *ssl )
 {
     int ret = 0;
+    int authmode = ssl->conf->authmode;
     mbedtls_x509_crt *ca_chain;
     mbedtls_x509_crl *ca_crl;
     uint32_t verify_result = 0;
 
+    /* If SNI was used, overwrite authentication mode
+     * from the configuration. */
+#if defined(MBEDTLS_SSL_SERVER_NAME_INDICATION)
+    if( ssl->handshake->sni_authmode != MBEDTLS_SSL_VERIFY_UNSET )
+        authmode = ssl->handshake->sni_authmode;
+#endif
+
+    /*
+     * If the client hasn't sent a certificate ( i.e. it sent
+     * an empty certificate chain ), this is reflected in the peer CRT
+     * structure being unset.
+     * Check for that and handle it depending on the
+     * server's authentication mode.
+     */
+#if defined(MBEDTLS_SSL_SRV_C)
+    if( ssl->conf->endpoint == MBEDTLS_SSL_IS_SERVER &&
+        ssl->session_negotiate->peer_cert == NULL )
+    {
+        MBEDTLS_SSL_DEBUG_MSG( 1, ( "client has no certificate" ) );
+
+        /* The client was asked for a certificate but didn't send
+           one. The client should know what's going on, so we
+           don't send an alert. */
+
+        /* Note that for authmode == VERIFY_NONE we don't end up in this
+         * routine in the first place, because ssl_tls13_read_certificate_coordinate
+         * will return CERTIFICATE_SKIP. */
+        ssl->session_negotiate->verify_result = MBEDTLS_X509_BADCERT_MISSING;
+        if( authmode == MBEDTLS_SSL_VERIFY_OPTIONAL )
+            return( 0 );
+        else
+            return( MBEDTLS_ERR_SSL_NO_CLIENT_CERTIFICATE );
+    }
+#endif /* MBEDTLS_SSL_SRV_C */
+
+    if( authmode == MBEDTLS_SSL_VERIFY_NONE )
+    {
+        /* NOTE: This happens on client-side only, with the
+         * server-side case of VERIFY_NONE being handled earlier
+         * and leading to `ssl->verify_result` being set to
+         * MBEDTLS_X509_BADCERT_SKIP_VERIFY --
+         * is this difference intentional? */
+        return( 0 );
+    }
+
 #if defined(MBEDTLS_SSL_SERVER_NAME_INDICATION)
     if( ssl->handshake->sni_ca_chain != NULL )
     {
@@ -593,8 +759,21 @@
             ret = MBEDTLS_ERR_SSL_BAD_CERTIFICATE;
     }
 
+    /* mbedtls_x509_crt_verify_with_profile is supposed to report a
+     * verification failure through MBEDTLS_ERR_X509_CERT_VERIFY_FAILED,
+     * with details encoded in the verification flags. All other kinds
+     * of error codes, including those from the user provided f_vrfy
+     * functions, are treated as fatal and lead to a failure of
+     * ssl_tls13_parse_certificate even if verification was optional. */
+    if( authmode == MBEDTLS_SSL_VERIFY_OPTIONAL &&
+        ( ret == MBEDTLS_ERR_X509_CERT_VERIFY_FAILED ||
+          ret == MBEDTLS_ERR_SSL_BAD_CERTIFICATE ) )
+    {
+        ret = 0;
+    }
 
-    if( ca_chain == NULL )
+
+    if( ca_chain == NULL && authmode == MBEDTLS_SSL_VERIFY_REQUIRED )
     {
         MBEDTLS_SSL_DEBUG_MSG( 1, ( "got no CA chain" ) );
         ret = MBEDTLS_ERR_SSL_CA_CHAIN_REQUIRED;
@@ -654,29 +833,47 @@
     int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
     MBEDTLS_SSL_DEBUG_MSG( 2, ( "=> parse certificate" ) );
 
+    /* Coordination:
+     * Check if we expect a certificate, and if yes,
+     * check if a non-empty certificate has been sent.
+     */
+    MBEDTLS_SSL_PROC_CHK_NEG( ssl_tls13_read_certificate_coordinate( ssl ) );
 #if defined(MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED)
-    unsigned char *buf;
-    size_t buf_len;
+    if( ret == SSL_CERTIFICATE_EXPECTED )
+    {
+        unsigned char *buf;
+        size_t buf_len;
 
-    MBEDTLS_SSL_PROC_CHK( mbedtls_ssl_tls13_fetch_handshake_msg(
-                          ssl, MBEDTLS_SSL_HS_CERTIFICATE,
-                          &buf, &buf_len ) );
+        MBEDTLS_SSL_PROC_CHK( mbedtls_ssl_tls13_fetch_handshake_msg(
+                              ssl, MBEDTLS_SSL_HS_CERTIFICATE,
+                              &buf, &buf_len ) );
 
-    /* Parse the certificate chain sent by the peer. */
-    MBEDTLS_SSL_PROC_CHK( ssl_tls13_parse_certificate( ssl, buf, buf + buf_len ) );
-    /* Validate the certificate chain and set the verification results. */
-    MBEDTLS_SSL_PROC_CHK( ssl_tls13_validate_certificate( ssl ) );
+        /* Parse the certificate chain sent by the peer. */
+        MBEDTLS_SSL_PROC_CHK( ssl_tls13_parse_certificate( ssl, buf,
+                                                           buf + buf_len ) );
+        /* Validate the certificate chain and set the verification results. */
+        MBEDTLS_SSL_PROC_CHK( ssl_tls13_validate_certificate( ssl ) );
 
-    mbedtls_ssl_add_hs_msg_to_checksum( ssl, MBEDTLS_SSL_HS_CERTIFICATE,
-                                        buf, buf_len );
+        mbedtls_ssl_add_hs_msg_to_checksum( ssl, MBEDTLS_SSL_HS_CERTIFICATE,
+                                            buf, buf_len );
+    }
+    else
+#endif /* MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED */
+    if( ret == SSL_CERTIFICATE_SKIP )
+    {
+        MBEDTLS_SSL_DEBUG_MSG( 2, ( "<= skip parse certificate" ) );
+        ret = 0;
+    }
+    else
+    {
+        MBEDTLS_SSL_DEBUG_MSG( 1, ( "should never happen" ) );
+        ret = MBEDTLS_ERR_SSL_INTERNAL_ERROR;
+    }
+
 
 cleanup:
 
     MBEDTLS_SSL_DEBUG_MSG( 2, ( "<= parse certificate" ) );
-#else
-    MBEDTLS_SSL_DEBUG_MSG( 1, ( "should never happen" ) );
-    ret = MBEDTLS_ERR_SSL_INTERNAL_ERROR;
-#endif /* MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED */
     return( ret );
 }
 #if defined(MBEDTLS_KEY_EXCHANGE_WITH_CERT_ENABLED)
diff --git a/library/ssl_tls13_server.c b/library/ssl_tls13_server.c
index 8c8eb2a..1288e49 100644
--- a/library/ssl_tls13_server.c
+++ b/library/ssl_tls13_server.c
@@ -1613,6 +1613,24 @@
             ret = ssl_tls13_handshake_wrapup( ssl );
             break;
 
+        case MBEDTLS_SSL_CLIENT_CERTIFICATE:
+            ret = mbedtls_ssl_tls13_process_certificate( ssl );
+            if( ret == 0 )
+            {
+                mbedtls_ssl_handshake_set_state(
+                    ssl, MBEDTLS_SSL_CLIENT_CERTIFICATE_VERIFY );
+            }
+            break;
+
+        case MBEDTLS_SSL_CLIENT_CERTIFICATE_VERIFY:
+            ret = mbedtls_ssl_tls13_process_certificate_verify( ssl );
+            if( ret == 0 )
+            {
+                mbedtls_ssl_handshake_set_state(
+                    ssl, MBEDTLS_SSL_CLIENT_FINISHED );
+            }
+            break;
+
         default:
             MBEDTLS_SSL_DEBUG_MSG( 1, ( "invalid state %d", ssl->state ) );
             return( MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE );