Add runner tests which send intermediate certificates.
Certificate chain with intermediate taken from Chromium's tests. Though
it doesn't really matter because the runner tests don't verify
certificates.
BUG=70
Change-Id: I46fd1d4be0f371b5bfd43370b97d2c8053cfad60
Reviewed-on: https://boringssl-review.googlesource.com/12261
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
Reviewed-by: Steven Valdez <svaldez@chromium.org>
Reviewed-by: David Benjamin <davidben@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
diff --git a/ssl/test/bssl_shim.cc b/ssl/test/bssl_shim.cc
index 1223146..87bdb86 100644
--- a/ssl/test/bssl_shim.cc
+++ b/ssl/test/bssl_shim.cc
@@ -138,12 +138,46 @@
return (TestState *)SSL_get_ex_data(ssl, g_state_index);
}
-static bssl::UniquePtr<X509> LoadCertificate(const std::string &file) {
+static bool LoadCertificate(bssl::UniquePtr<X509> *out_x509,
+ bssl::UniquePtr<STACK_OF(X509)> *out_chain,
+ const std::string &file) {
bssl::UniquePtr<BIO> bio(BIO_new(BIO_s_file()));
if (!bio || !BIO_read_filename(bio.get(), file.c_str())) {
- return nullptr;
+ return false;
}
- return bssl::UniquePtr<X509>(PEM_read_bio_X509(bio.get(), NULL, NULL, NULL));
+
+ out_x509->reset(PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr));
+ if (!*out_x509) {
+ return false;
+ }
+
+ out_chain->reset(sk_X509_new_null());
+ if (!*out_chain) {
+ return false;
+ }
+
+ // Keep reading the certificate chain.
+ for (;;) {
+ bssl::UniquePtr<X509> cert(
+ PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr));
+ if (!cert) {
+ break;
+ }
+
+ if (!sk_X509_push(out_chain->get(), cert.get())) {
+ return false;
+ }
+ cert.release(); // sk_X509_push takes ownership.
+ }
+
+ uint32_t err = ERR_peek_last_error();
+ if (ERR_GET_LIB(err) != ERR_LIB_PEM ||
+ ERR_GET_REASON(err) != PEM_R_NO_START_LINE) {
+ return false;
+}
+
+ ERR_clear_error();
+ return true;
}
static bssl::UniquePtr<EVP_PKEY> LoadPrivateKey(const std::string &file) {
@@ -321,6 +355,7 @@
};
static bool GetCertificate(SSL *ssl, bssl::UniquePtr<X509> *out_x509,
+ bssl::UniquePtr<STACK_OF(X509)> *out_chain,
bssl::UniquePtr<EVP_PKEY> *out_pkey) {
const TestConfig *config = GetTestConfig(ssl);
@@ -359,11 +394,9 @@
return false;
}
}
- if (!config->cert_file.empty()) {
- *out_x509 = LoadCertificate(config->cert_file.c_str());
- if (!*out_x509) {
- return false;
- }
+ if (!config->cert_file.empty() &&
+ !LoadCertificate(out_x509, out_chain, config->cert_file.c_str())) {
+ return false;
}
if (!config->ocsp_response.empty() &&
!SSL_CTX_set_ocsp_response(SSL_get_SSL_CTX(ssl),
@@ -376,8 +409,9 @@
static bool InstallCertificate(SSL *ssl) {
bssl::UniquePtr<X509> x509;
+ bssl::UniquePtr<STACK_OF(X509)> chain;
bssl::UniquePtr<EVP_PKEY> pkey;
- if (!GetCertificate(ssl, &x509, &pkey)) {
+ if (!GetCertificate(ssl, &x509, &chain, &pkey)) {
return false;
}
@@ -396,6 +430,11 @@
return false;
}
+ if (sk_X509_num(chain.get()) > 0 &&
+ !SSL_set1_chain(ssl, chain.get())) {
+ return false;
+ }
+
return true;
}
@@ -457,8 +496,9 @@
}
bssl::UniquePtr<X509> x509;
+ bssl::UniquePtr<STACK_OF(X509)> chain;
bssl::UniquePtr<EVP_PKEY> pkey;
- if (!GetCertificate(ssl, &x509, &pkey)) {
+ if (!GetCertificate(ssl, &x509, &chain, &pkey)) {
return -1;
}
@@ -467,7 +507,7 @@
return 0;
}
- // Asynchronous private keys are not supported with client_cert_cb.
+ // Chains and asynchronous private keys are not supported with client_cert_cb.
*out_x509 = x509.release();
*out_pkey = pkey.release();
return 1;
@@ -1352,6 +1392,46 @@
}
}
+ if (!config->expect_peer_cert_file.empty()) {
+ bssl::UniquePtr<X509> expect_leaf;
+ bssl::UniquePtr<STACK_OF(X509)> expect_chain;
+ if (!LoadCertificate(&expect_leaf, &expect_chain,
+ config->expect_peer_cert_file)) {
+ return false;
+ }
+
+ // For historical reasons, clients report a chain with a leaf and servers
+ // without.
+ if (!config->is_server) {
+ if (!sk_X509_insert(expect_chain.get(), expect_leaf.get(), 0)) {
+ return false;
+ }
+ X509_up_ref(expect_leaf.get()); // sk_X509_push takes ownership.
+ }
+
+ bssl::UniquePtr<X509> leaf(SSL_get_peer_certificate(ssl));
+ STACK_OF(X509) *chain = SSL_get_peer_cert_chain(ssl);
+ if (X509_cmp(leaf.get(), expect_leaf.get()) != 0) {
+ fprintf(stderr, "Received a different leaf certificate than expected.\n");
+ return false;
+ }
+
+ if (sk_X509_num(chain) != sk_X509_num(expect_chain.get())) {
+ fprintf(stderr, "Received a chain of length %zu instead of %zu.\n",
+ sk_X509_num(chain), sk_X509_num(expect_chain.get()));
+ return false;
+ }
+
+ for (size_t i = 0; i < sk_X509_num(chain); i++) {
+ if (X509_cmp(sk_X509_value(chain, i),
+ sk_X509_value(expect_chain.get(), i)) != 0) {
+ fprintf(stderr, "Chain certificate %zu did not match.\n",
+ i + 1);
+ return false;
+ }
+ }
+ }
+
return true;
}
diff --git a/ssl/test/runner/rsa_chain_cert.pem b/ssl/test/runner/rsa_chain_cert.pem
new file mode 100644
index 0000000..8eb6e60
--- /dev/null
+++ b/ssl/test/runner/rsa_chain_cert.pem
@@ -0,0 +1,35 @@
+-----BEGIN CERTIFICATE-----
+MIIC0jCCAbqgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwDzENMAsGA1UEAwwEQiBD
+QTAeFw0xNjAyMjgyMDI3MDNaFw0yNjAyMjUyMDI3MDNaMBgxFjAUBgNVBAMMDUNs
+aWVudCBDZXJ0IEEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDRvaz8
+CC/cshpCafJo4jLkHEoBqDLhdgFelJoAiQUyIqyWl2O7YHPnpJH+TgR7oelzNzt/
+kLRcH89M/TszB6zqyLTC4aqmvzKL0peD/jL2LWBucR0WXIvjA3zoRuF/x86+rYH3
+tHb+xs2PSs8EGL/Ev+ss+qTzTGEn26fuGNHkNw6tOwPpc+o8+wUtzf/kAthamo+c
+IDs2rQ+lP7+aLZTLeU/q4gcLutlzcK5imex5xy2jPkweq48kijK0kIzl1cPlA5d1
+z7C8jU50Pj9X9sQDJTN32j7UYRisJeeYQF8GaaN8SbrDI6zHgKzrRLyxDt/KQa9V
+iLeXANgZi+Xx9KgfAgMBAAGjLzAtMAwGA1UdEwEB/wQCMAAwHQYDVR0lBBYwFAYI
+KwYBBQUHAwEGCCsGAQUFBwMCMA0GCSqGSIb3DQEBCwUAA4IBAQBFEVbmYl+2RtNw
+rDftRDF1v2QUbcN2ouSnQDHxeDQdSgasLzT3ui8iYu0Rw2WWcZ0DV5e0ztGPhWq7
+AO0B120aFRMOY+4+bzu9Q2FFkQqc7/fKTvTDzIJI5wrMnFvUfzzvxh3OHWMYSs/w
+giq33hTKeHEq6Jyk3btCny0Ycecyc3yGXH10sizUfiHlhviCkDuESk8mFDwDDzqW
+ZF0IipzFbEDHoIxLlm3GQxpiLoEV4k8KYJp3R5KBLFyxM6UGPz8h72mIPCJp2RuK
+MYgF91UDvVzvnYm6TfseM2+ewKirC00GOrZ7rEcFvtxnKSqYf4ckqfNdSU1Y+RRC
+1ngWZ7Ih
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIICwjCCAaqgAwIBAgICEAEwDQYJKoZIhvcNAQELBQAwFDESMBAGA1UEAwwJQyBS
+b290IENBMB4XDTE2MDIyODIwMjcwM1oXDTI2MDIyNTIwMjcwM1owDzENMAsGA1UE
+AwwEQiBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALsSCYmDip2D
+GkjFxw7ykz26JSjELkl6ArlYjFJ3aT/SCh8qbS4gln7RH8CPBd78oFdfhIKQrwtZ
+3/q21ykD9BAS3qHe2YdcJfm8/kWAy5DvXk6NXU4qX334KofBAEpgdA/igEFq1P1l
+HAuIfZCpMRfT+i5WohVsGi8f/NgpRvVaMONLNfgw57mz1lbtFeBEISmX0kbsuJxF
+Qj/Bwhi5/0HAEXG8e7zN4cEx0yPRvmOATRdVb/8dW2pwOHRJq9R5M0NUkIsTSnL7
+6N/z8hRAHMsV3IudC5Yd7GXW1AGu9a+iKU+Q4xcZCoj0DC99tL4VKujrV1kAeqsM
+cz5/dKzi6+cCAwEAAaMjMCEwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC
+AQYwDQYJKoZIhvcNAQELBQADggEBAIIeZiEeNhWWQ8Y4D+AGDwqUUeG8NjCbKrXQ
+BlHg5wZ8xftFaiP1Dp/UAezmx2LNazdmuwrYB8lm3FVTyaPDTKEGIPS4wJKHgqH1
+QPDhqNm85ey7TEtI9oYjsNim/Rb+iGkIAMXaxt58SzxbjvP0kMr1JfJIZbic9vye
+NwIspMFIpP3FB8ywyu0T0hWtCQgL4J47nigCHpOu58deP88fS/Nyz/fyGVWOZ76b
+WhWwgM3P3X95fQ3d7oFPR/bVh0YV+Cf861INwplokXgXQ3/TCQ+HNXeAMWn3JLWv
+XFwk8owk9dq/kQGdndGgy3KTEW4ctPX5GNhf3LJ9Q7dLji4ReQ4=
+-----END CERTIFICATE-----
diff --git a/ssl/test/runner/rsa_chain_key.pem b/ssl/test/runner/rsa_chain_key.pem
new file mode 100644
index 0000000..d94d704
--- /dev/null
+++ b/ssl/test/runner/rsa_chain_key.pem
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDRvaz8CC/cshpC
+afJo4jLkHEoBqDLhdgFelJoAiQUyIqyWl2O7YHPnpJH+TgR7oelzNzt/kLRcH89M
+/TszB6zqyLTC4aqmvzKL0peD/jL2LWBucR0WXIvjA3zoRuF/x86+rYH3tHb+xs2P
+Ss8EGL/Ev+ss+qTzTGEn26fuGNHkNw6tOwPpc+o8+wUtzf/kAthamo+cIDs2rQ+l
+P7+aLZTLeU/q4gcLutlzcK5imex5xy2jPkweq48kijK0kIzl1cPlA5d1z7C8jU50
+Pj9X9sQDJTN32j7UYRisJeeYQF8GaaN8SbrDI6zHgKzrRLyxDt/KQa9ViLeXANgZ
+i+Xx9KgfAgMBAAECggEBAK0VjSJzkyPaamcyTVSWjo7GdaBGcK60lk657RjR+lK0
+YJ7pkej4oM2hdsVZFsP8Cs4E33nXLa/0pDsRov/qrp0WQm2skwqGMC1I/bZ0WRPk
+wHaDrBBfESWnJDX/AGpVtlyOjPmgmK6J2usMPihQUDkKdAYrVWJePrMIxt1q6BMe
+iczs3qriMmtY3bUc4UyUwJ5fhDLjshHvfuIpYQyI6EXZM6dZksn9LylXJnigY6QJ
+HxOYO0BDwOsZ8yQ8J8afLk88i0GizEkgE1z3REtQUwgWfxr1WV/ud+T6/ZhSAgH9
+042mQvSFZnIUSEsmCvjhWuAunfxHKCTcAoYISWfzWpkCgYEA7gpf3HHU5Tn+CgUn
+1X5uGpG3DmcMgfeGgs2r2f/IIg/5Ac1dfYILiybL1tN9zbyLCJfcbFpWBc9hJL6f
+CPc5hUiwWFJqBJewxQkC1Ae/HakHbip+IZ+Jr0842O4BAArvixk4Lb7/N2Ct9sTE
+NJO6RtK9lbEZ5uK61DglHy8CS2UCgYEA4ZC1o36kPAMQBggajgnucb2yuUEelk0f
+AEr+GI32MGE+93xMr7rAhBoqLg4AITyIfEnOSQ5HwagnIHonBbv1LV/Gf9ursx8Z
+YOGbvT8zzzC+SU1bkDzdjAYnFQVGIjMtKOBJ3K07++ypwX1fr4QsQ8uKL8WSOWwt
+Z3Bym6XiZzMCgYADnhy+2OwHX85AkLt+PyGlPbmuelpyTzS4IDAQbBa6jcuW/2wA
+UE2km75VUXmD+u2R/9zVuLm99NzhFhSMqlUxdV1YukfqMfP5yp1EY6m/5aW7QuIP
+2MDa7TVL9rIFMiVZ09RKvbBbQxjhuzPQKL6X/PPspnhiTefQ+dl2k9xREQKBgHDS
+fMfGNEeAEKezrfSVqxphE9/tXms3L+ZpnCaT+yu/uEr5dTIAawKoQ6i9f/sf1/Sy
+xedsqR+IB+oKrzIDDWMgoJybN4pkZ8E5lzhVQIjFjKgFdWLzzqyW9z1gYfABQPlN
+FiS20WX0vgP1vcKAjdNrHzc9zyHBpgQzDmAj3NZZAoGBAI8vKCKdH7w3aL5CNkZQ
+2buIeWNA2HZazVwAGG5F2TU/LmXfRKnG6dX5bkU+AkBZh56jNZy//hfFSewJB4Kk
+buB7ERSdaNbO21zXt9FEA3+z0RfMd/Zv2vlIWOSB5nzl/7UKti3sribK6s9ZVLfi
+SxpiPQ8d/hmSGwn4ksrWUsJD
+-----END PRIVATE KEY-----
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go
index 354df95..396d5f2 100644
--- a/ssl/test/runner/runner.go
+++ b/ssl/test/runner/runner.go
@@ -87,6 +87,7 @@
const (
testCertRSA testCert = iota
testCertRSA1024
+ testCertRSAChain
testCertECDSAP256
testCertECDSAP384
testCertECDSAP521
@@ -95,6 +96,7 @@
const (
rsaCertificateFile = "cert.pem"
rsa1024CertificateFile = "rsa_1024_cert.pem"
+ rsaChainCertificateFile = "rsa_chain_cert.pem"
ecdsaP256CertificateFile = "ecdsa_p256_cert.pem"
ecdsaP384CertificateFile = "ecdsa_p384_cert.pem"
ecdsaP521CertificateFile = "ecdsa_p521_cert.pem"
@@ -103,6 +105,7 @@
const (
rsaKeyFile = "key.pem"
rsa1024KeyFile = "rsa_1024_key.pem"
+ rsaChainKeyFile = "rsa_chain_key.pem"
ecdsaP256KeyFile = "ecdsa_p256_key.pem"
ecdsaP384KeyFile = "ecdsa_p384_key.pem"
ecdsaP521KeyFile = "ecdsa_p521_key.pem"
@@ -112,6 +115,7 @@
var (
rsaCertificate Certificate
rsa1024Certificate Certificate
+ rsaChainCertificate Certificate
ecdsaP256Certificate Certificate
ecdsaP384Certificate Certificate
ecdsaP521Certificate Certificate
@@ -135,6 +139,12 @@
cert: &rsa1024Certificate,
},
{
+ id: testCertRSAChain,
+ certFile: rsaChainCertificateFile,
+ keyFile: rsaChainKeyFile,
+ cert: &rsaChainCertificate,
+ },
+ {
id: testCertECDSAP256,
certFile: ecdsaP256CertificateFile,
keyFile: ecdsaP256KeyFile,
@@ -364,6 +374,9 @@
// expectMessageDropped, if true, means the test message is expected to
// be dropped by the client rather than echoed back.
expectMessageDropped bool
+ // expectPeerCertificate, if not nil, is the certificate chain the peer
+ // is expected to send.
+ expectPeerCertificate *Certificate
}
var testCases []testCase
@@ -581,6 +594,17 @@
return fmt.Errorf("expected peer to use curve %04x, but got %04x", expected, connState.CurveID)
}
+ if test.expectPeerCertificate != nil {
+ if len(connState.PeerCertificates) != len(test.expectPeerCertificate.Certificate) {
+ return fmt.Errorf("expected peer to send %d certificates, but got %d", len(connState.PeerCertificates), len(test.expectPeerCertificate.Certificate))
+ }
+ for i, cert := range connState.PeerCertificates {
+ if !bytes.Equal(cert.Raw, test.expectPeerCertificate.Certificate[i]) {
+ return fmt.Errorf("peer certificate %d did not match", i+1)
+ }
+ }
+ }
+
if test.exportKeyingMaterial > 0 {
actual := make([]byte, test.exportKeyingMaterial)
if _, err := io.ReadFull(tlsConn, actual); err != nil {
@@ -9152,6 +9176,42 @@
}
}
+func addCertificateTests() {
+ // Test that a certificate chain with intermediate may be sent and
+ // received as both client and server.
+ for _, ver := range tlsVersions {
+ testCases = append(testCases, testCase{
+ testType: clientTest,
+ name: "SendReceiveIntermediate-Client-" + ver.name,
+ config: Config{
+ Certificates: []Certificate{rsaChainCertificate},
+ ClientAuth: RequireAnyClientCert,
+ },
+ expectPeerCertificate: &rsaChainCertificate,
+ flags: []string{
+ "-cert-file", path.Join(*resourceDir, rsaChainCertificateFile),
+ "-key-file", path.Join(*resourceDir, rsaChainKeyFile),
+ "-expect-peer-cert-file", path.Join(*resourceDir, rsaChainCertificateFile),
+ },
+ })
+
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "SendReceiveIntermediate-Server-" + ver.name,
+ config: Config{
+ Certificates: []Certificate{rsaChainCertificate},
+ },
+ expectPeerCertificate: &rsaChainCertificate,
+ flags: []string{
+ "-cert-file", path.Join(*resourceDir, rsaChainCertificateFile),
+ "-key-file", path.Join(*resourceDir, rsaChainKeyFile),
+ "-require-any-client-certificate",
+ "-expect-peer-cert-file", path.Join(*resourceDir, rsaChainCertificateFile),
+ },
+ })
+ }
+}
+
func worker(statusChan chan statusMsg, c chan *testCase, shimPath string, wg *sync.WaitGroup) {
defer wg.Done()
@@ -9272,6 +9332,7 @@
addTLS13CipherPreferenceTests()
addPeekTests()
addRecordVersionTests()
+ addCertificateTests()
var wg sync.WaitGroup
diff --git a/ssl/test/test_config.cc b/ssl/test/test_config.cc
index 3d3ecd8..c0fe3c4 100644
--- a/ssl/test/test_config.cc
+++ b/ssl/test/test_config.cc
@@ -131,6 +131,7 @@
{ "-cipher-tls11", &TestConfig::cipher_tls11 },
{ "-export-label", &TestConfig::export_label },
{ "-export-context", &TestConfig::export_context },
+ { "-expect-peer-cert-file", &TestConfig::expect_peer_cert_file },
};
const Flag<std::string> kBase64Flags[] = {
diff --git a/ssl/test/test_config.h b/ssl/test/test_config.h
index 34c02fb..f4028cd 100644
--- a/ssl/test/test_config.h
+++ b/ssl/test/test_config.h
@@ -119,6 +119,7 @@
bool use_exporter_between_reads = false;
int expect_cipher_aes = 0;
int expect_cipher_no_aes = 0;
+ std::string expect_peer_cert_file;
};
bool ParseConfig(int argc, char **argv, TestConfig *out_config);