Wait for CertificateStatus message to verify certificate.

Applications may require the stapled OCSP response in order to verify
the certificate within the verification callback.

Change-Id: I8002e527f90c3ce7b6a66e3203c0a68371aac5ec
Reviewed-on: https://boringssl-review.googlesource.com/5730
Reviewed-by: David Benjamin <davidben@chromium.org>
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/ssl/test/bssl_shim.cc b/ssl/test/bssl_shim.cc
index 97044ab..eb5bdae 100644
--- a/ssl/test/bssl_shim.cc
+++ b/ssl/test/bssl_shim.cc
@@ -301,10 +301,29 @@
   return 1;
 }
 
-static int SkipVerify(int preverify_ok, X509_STORE_CTX *store_ctx) {
+static int VerifySucceed(X509_STORE_CTX *store_ctx, void *arg) {
+  SSL* ssl = (SSL*)X509_STORE_CTX_get_ex_data(store_ctx,
+      SSL_get_ex_data_X509_STORE_CTX_idx());
+  const TestConfig *config = GetConfigPtr(ssl);
+
+  if (!config->expected_ocsp_response.empty()) {
+    const uint8_t *data;
+    size_t len;
+    SSL_get0_ocsp_response(ssl, &data, &len);
+    if (len == 0) {
+      fprintf(stderr, "OCSP response not available in verify callback\n");
+      return 0;
+    }
+  }
+
   return 1;
 }
 
+static int VerifyFail(X509_STORE_CTX *store_ctx, void *arg) {
+  store_ctx->error = X509_V_ERR_APPLICATION_VERIFICATION;
+  return 0;
+}
+
 static int NextProtosAdvertisedCallback(SSL *ssl, const uint8_t **out,
                                         unsigned int *out_len, void *arg) {
   const TestConfig *config = GetConfigPtr(ssl);
@@ -675,6 +694,12 @@
     return nullptr;
   }
 
+  if (config->verify_fail) {
+    SSL_CTX_set_cert_verify_callback(ssl_ctx.get(), VerifyFail, NULL);
+  } else {
+    SSL_CTX_set_cert_verify_callback(ssl_ctx.get(), VerifySucceed, NULL);
+  }
+
   return ssl_ctx;
 }
 
@@ -908,6 +933,17 @@
     }
   }
 
+  if (config->expect_verify_result) {
+    int expected_verify_result = config->verify_fail ?
+      X509_V_ERR_APPLICATION_VERIFICATION :
+      X509_V_OK;
+
+    if (SSL_get_verify_result(ssl) != expected_verify_result) {
+      fprintf(stderr, "Wrong certificate verification result\n");
+      return false;
+    }
+  }
+
   if (!config->is_server) {
     /* Clients should expect a peer certificate chain iff this was not a PSK
      * cipher suite. */
@@ -955,7 +991,10 @@
   }
   if (config->require_any_client_certificate) {
     SSL_set_verify(ssl.get(), SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
-                   SkipVerify);
+                   NULL);
+  }
+  if (config->verify_peer) {
+    SSL_set_verify(ssl.get(), SSL_VERIFY_PEER, NULL);
   }
   if (config->false_start) {
     SSL_set_mode(ssl.get(), SSL_MODE_ENABLE_FALSE_START);
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go
index 8124382..3c077bf 100644
--- a/ssl/test/runner/runner.go
+++ b/ssl/test/runner/runner.go
@@ -2624,6 +2624,7 @@
 			"-enable-ocsp-stapling",
 			"-expect-ocsp-response",
 			base64.StdEncoding.EncodeToString(testOCSPResponse),
+			"-verify-peer",
 		},
 	})
 
@@ -2637,6 +2638,34 @@
 		},
 	})
 
+	tests = append(tests, testCase{
+		testType: clientTest,
+		name:     "CertificateVerificationSucceed",
+		flags: []string{
+			"-verify-peer",
+		},
+	})
+
+	tests = append(tests, testCase{
+		testType: clientTest,
+		name:     "CertificateVerificationFail",
+		flags: []string{
+			"-verify-fail",
+			"-verify-peer",
+		},
+		shouldFail:    true,
+		expectedError: ":CERTIFICATE_VERIFY_FAILED:",
+	})
+
+	tests = append(tests, testCase{
+		testType: clientTest,
+		name:     "CertificateVerificationSoftFail",
+		flags: []string{
+			"-verify-fail",
+			"-expect-verify-result",
+		},
+	})
+
 	if protocol == tls {
 		tests = append(tests, testCase{
 			name:        "Renegotiate-Client",
diff --git a/ssl/test/test_config.cc b/ssl/test/test_config.cc
index 7a87fd5..ad14ed3 100644
--- a/ssl/test/test_config.cc
+++ b/ssl/test/test_config.cc
@@ -93,6 +93,9 @@
   { "-check-close-notify", &TestConfig::check_close_notify },
   { "-shim-shuts-down", &TestConfig::shim_shuts_down },
   { "-microsoft-big-sslv3-buffer", &TestConfig::microsoft_big_sslv3_buffer },
+  { "-verify-fail", &TestConfig::verify_fail },
+  { "-verify-peer", &TestConfig::verify_peer },
+  { "-expect-verify-result", &TestConfig::expect_verify_result }
 };
 
 const Flag<std::string> kStringFlags[] = {
diff --git a/ssl/test/test_config.h b/ssl/test/test_config.h
index a41af49..5776095 100644
--- a/ssl/test/test_config.h
+++ b/ssl/test/test_config.h
@@ -92,6 +92,9 @@
   bool check_close_notify = false;
   bool shim_shuts_down = false;
   bool microsoft_big_sslv3_buffer = false;
+  bool verify_fail = false;
+  bool verify_peer = false;
+  bool expect_verify_result = false;
 };
 
 bool ParseConfig(int argc, char **argv, TestConfig *out_config);