Add tests for auto-chaining.
Alas, wpa_supplicant relies on the auto-chaining feature, so we can't
easily remove it. Write tests for it to ensure it stays working.
These test currently do not work for TLS 1.3 because the feature is
broken in 1.3. A follow-up change will fix this.
BUG=70
Change-Id: I2c04f55b712d66f5af1556254a5b017ebc3244f7
Reviewed-on: https://boringssl-review.googlesource.com/12965
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/ssl/ssl_test.cc b/ssl/ssl_test.cc
index 796c051..0c376b8 100644
--- a/ssl/ssl_test.cc
+++ b/ssl/ssl_test.cc
@@ -1142,7 +1142,8 @@
"j2VNHwsSrJwkD4QUGlUtH7vwnQmyCFxZMmWAJg==\n"
"-----END CERTIFICATE-----\n";
bssl::UniquePtr<BIO> bio(BIO_new_mem_buf(kCertPEM, strlen(kCertPEM)));
- return bssl::UniquePtr<X509>(PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr));
+ return bssl::UniquePtr<X509>(
+ PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr));
}
static bssl::UniquePtr<EVP_PKEY> GetTestKey() {
@@ -1197,6 +1198,90 @@
PEM_read_bio_PrivateKey(bio.get(), nullptr, nullptr, nullptr));
}
+static bssl::UniquePtr<X509> GetChainTestCertificate() {
+ static const char kCertPEM[] =
+ "-----BEGIN CERTIFICATE-----\n"
+ "MIIC0jCCAbqgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwDzENMAsGA1UEAwwEQiBD\n"
+ "QTAeFw0xNjAyMjgyMDI3MDNaFw0yNjAyMjUyMDI3MDNaMBgxFjAUBgNVBAMMDUNs\n"
+ "aWVudCBDZXJ0IEEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDRvaz8\n"
+ "CC/cshpCafJo4jLkHEoBqDLhdgFelJoAiQUyIqyWl2O7YHPnpJH+TgR7oelzNzt/\n"
+ "kLRcH89M/TszB6zqyLTC4aqmvzKL0peD/jL2LWBucR0WXIvjA3zoRuF/x86+rYH3\n"
+ "tHb+xs2PSs8EGL/Ev+ss+qTzTGEn26fuGNHkNw6tOwPpc+o8+wUtzf/kAthamo+c\n"
+ "IDs2rQ+lP7+aLZTLeU/q4gcLutlzcK5imex5xy2jPkweq48kijK0kIzl1cPlA5d1\n"
+ "z7C8jU50Pj9X9sQDJTN32j7UYRisJeeYQF8GaaN8SbrDI6zHgKzrRLyxDt/KQa9V\n"
+ "iLeXANgZi+Xx9KgfAgMBAAGjLzAtMAwGA1UdEwEB/wQCMAAwHQYDVR0lBBYwFAYI\n"
+ "KwYBBQUHAwEGCCsGAQUFBwMCMA0GCSqGSIb3DQEBCwUAA4IBAQBFEVbmYl+2RtNw\n"
+ "rDftRDF1v2QUbcN2ouSnQDHxeDQdSgasLzT3ui8iYu0Rw2WWcZ0DV5e0ztGPhWq7\n"
+ "AO0B120aFRMOY+4+bzu9Q2FFkQqc7/fKTvTDzIJI5wrMnFvUfzzvxh3OHWMYSs/w\n"
+ "giq33hTKeHEq6Jyk3btCny0Ycecyc3yGXH10sizUfiHlhviCkDuESk8mFDwDDzqW\n"
+ "ZF0IipzFbEDHoIxLlm3GQxpiLoEV4k8KYJp3R5KBLFyxM6UGPz8h72mIPCJp2RuK\n"
+ "MYgF91UDvVzvnYm6TfseM2+ewKirC00GOrZ7rEcFvtxnKSqYf4ckqfNdSU1Y+RRC\n"
+ "1ngWZ7Ih\n"
+ "-----END CERTIFICATE-----\n";
+ bssl::UniquePtr<BIO> bio(BIO_new_mem_buf(kCertPEM, strlen(kCertPEM)));
+ return bssl::UniquePtr<X509>(
+ PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr));
+}
+
+static bssl::UniquePtr<X509> GetChainTestIntermediate() {
+ static const char kCertPEM[] =
+ "-----BEGIN CERTIFICATE-----\n"
+ "MIICwjCCAaqgAwIBAgICEAEwDQYJKoZIhvcNAQELBQAwFDESMBAGA1UEAwwJQyBS\n"
+ "b290IENBMB4XDTE2MDIyODIwMjcwM1oXDTI2MDIyNTIwMjcwM1owDzENMAsGA1UE\n"
+ "AwwEQiBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALsSCYmDip2D\n"
+ "GkjFxw7ykz26JSjELkl6ArlYjFJ3aT/SCh8qbS4gln7RH8CPBd78oFdfhIKQrwtZ\n"
+ "3/q21ykD9BAS3qHe2YdcJfm8/kWAy5DvXk6NXU4qX334KofBAEpgdA/igEFq1P1l\n"
+ "HAuIfZCpMRfT+i5WohVsGi8f/NgpRvVaMONLNfgw57mz1lbtFeBEISmX0kbsuJxF\n"
+ "Qj/Bwhi5/0HAEXG8e7zN4cEx0yPRvmOATRdVb/8dW2pwOHRJq9R5M0NUkIsTSnL7\n"
+ "6N/z8hRAHMsV3IudC5Yd7GXW1AGu9a+iKU+Q4xcZCoj0DC99tL4VKujrV1kAeqsM\n"
+ "cz5/dKzi6+cCAwEAAaMjMCEwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC\n"
+ "AQYwDQYJKoZIhvcNAQELBQADggEBAIIeZiEeNhWWQ8Y4D+AGDwqUUeG8NjCbKrXQ\n"
+ "BlHg5wZ8xftFaiP1Dp/UAezmx2LNazdmuwrYB8lm3FVTyaPDTKEGIPS4wJKHgqH1\n"
+ "QPDhqNm85ey7TEtI9oYjsNim/Rb+iGkIAMXaxt58SzxbjvP0kMr1JfJIZbic9vye\n"
+ "NwIspMFIpP3FB8ywyu0T0hWtCQgL4J47nigCHpOu58deP88fS/Nyz/fyGVWOZ76b\n"
+ "WhWwgM3P3X95fQ3d7oFPR/bVh0YV+Cf861INwplokXgXQ3/TCQ+HNXeAMWn3JLWv\n"
+ "XFwk8owk9dq/kQGdndGgy3KTEW4ctPX5GNhf3LJ9Q7dLji4ReQ4=\n"
+ "-----END CERTIFICATE-----\n";
+ bssl::UniquePtr<BIO> bio(BIO_new_mem_buf(kCertPEM, strlen(kCertPEM)));
+ return bssl::UniquePtr<X509>(
+ PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr));
+}
+
+static bssl::UniquePtr<EVP_PKEY> GetChainTestKey() {
+ static const char kKeyPEM[] =
+ "-----BEGIN PRIVATE KEY-----\n"
+ "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDRvaz8CC/cshpC\n"
+ "afJo4jLkHEoBqDLhdgFelJoAiQUyIqyWl2O7YHPnpJH+TgR7oelzNzt/kLRcH89M\n"
+ "/TszB6zqyLTC4aqmvzKL0peD/jL2LWBucR0WXIvjA3zoRuF/x86+rYH3tHb+xs2P\n"
+ "Ss8EGL/Ev+ss+qTzTGEn26fuGNHkNw6tOwPpc+o8+wUtzf/kAthamo+cIDs2rQ+l\n"
+ "P7+aLZTLeU/q4gcLutlzcK5imex5xy2jPkweq48kijK0kIzl1cPlA5d1z7C8jU50\n"
+ "Pj9X9sQDJTN32j7UYRisJeeYQF8GaaN8SbrDI6zHgKzrRLyxDt/KQa9ViLeXANgZ\n"
+ "i+Xx9KgfAgMBAAECggEBAK0VjSJzkyPaamcyTVSWjo7GdaBGcK60lk657RjR+lK0\n"
+ "YJ7pkej4oM2hdsVZFsP8Cs4E33nXLa/0pDsRov/qrp0WQm2skwqGMC1I/bZ0WRPk\n"
+ "wHaDrBBfESWnJDX/AGpVtlyOjPmgmK6J2usMPihQUDkKdAYrVWJePrMIxt1q6BMe\n"
+ "iczs3qriMmtY3bUc4UyUwJ5fhDLjshHvfuIpYQyI6EXZM6dZksn9LylXJnigY6QJ\n"
+ "HxOYO0BDwOsZ8yQ8J8afLk88i0GizEkgE1z3REtQUwgWfxr1WV/ud+T6/ZhSAgH9\n"
+ "042mQvSFZnIUSEsmCvjhWuAunfxHKCTcAoYISWfzWpkCgYEA7gpf3HHU5Tn+CgUn\n"
+ "1X5uGpG3DmcMgfeGgs2r2f/IIg/5Ac1dfYILiybL1tN9zbyLCJfcbFpWBc9hJL6f\n"
+ "CPc5hUiwWFJqBJewxQkC1Ae/HakHbip+IZ+Jr0842O4BAArvixk4Lb7/N2Ct9sTE\n"
+ "NJO6RtK9lbEZ5uK61DglHy8CS2UCgYEA4ZC1o36kPAMQBggajgnucb2yuUEelk0f\n"
+ "AEr+GI32MGE+93xMr7rAhBoqLg4AITyIfEnOSQ5HwagnIHonBbv1LV/Gf9ursx8Z\n"
+ "YOGbvT8zzzC+SU1bkDzdjAYnFQVGIjMtKOBJ3K07++ypwX1fr4QsQ8uKL8WSOWwt\n"
+ "Z3Bym6XiZzMCgYADnhy+2OwHX85AkLt+PyGlPbmuelpyTzS4IDAQbBa6jcuW/2wA\n"
+ "UE2km75VUXmD+u2R/9zVuLm99NzhFhSMqlUxdV1YukfqMfP5yp1EY6m/5aW7QuIP\n"
+ "2MDa7TVL9rIFMiVZ09RKvbBbQxjhuzPQKL6X/PPspnhiTefQ+dl2k9xREQKBgHDS\n"
+ "fMfGNEeAEKezrfSVqxphE9/tXms3L+ZpnCaT+yu/uEr5dTIAawKoQ6i9f/sf1/Sy\n"
+ "xedsqR+IB+oKrzIDDWMgoJybN4pkZ8E5lzhVQIjFjKgFdWLzzqyW9z1gYfABQPlN\n"
+ "FiS20WX0vgP1vcKAjdNrHzc9zyHBpgQzDmAj3NZZAoGBAI8vKCKdH7w3aL5CNkZQ\n"
+ "2buIeWNA2HZazVwAGG5F2TU/LmXfRKnG6dX5bkU+AkBZh56jNZy//hfFSewJB4Kk\n"
+ "buB7ERSdaNbO21zXt9FEA3+z0RfMd/Zv2vlIWOSB5nzl/7UKti3sribK6s9ZVLfi\n"
+ "SxpiPQ8d/hmSGwn4ksrWUsJD\n"
+ "-----END PRIVATE KEY-----\n";
+ bssl::UniquePtr<BIO> bio(BIO_new_mem_buf(kKeyPEM, strlen(kKeyPEM)));
+ return bssl::UniquePtr<EVP_PKEY>(
+ PEM_read_bio_PrivateKey(bio.get(), nullptr, nullptr, nullptr));
+}
+
static bool CompleteHandshakes(SSL *client, SSL *server) {
// Drive both their handshakes to completion.
for (;;) {
@@ -2783,6 +2868,113 @@
return true;
}
+static bool ChainsEqual(STACK_OF(X509) *chain,
+ const std::vector<X509 *> &expected) {
+ if (sk_X509_num(chain) != expected.size()) {
+ return false;
+ }
+
+ for (size_t i = 0; i < expected.size(); i++) {
+ if (X509_cmp(sk_X509_value(chain, i), expected[i]) != 0) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static bool TestAutoChain(bool is_dtls, const SSL_METHOD *method,
+ uint16_t version) {
+ if (version == TLS1_3_VERSION) {
+ // TODO(davidben): Auto-chaining does not currently work in TLS 1.3.
+ return true;
+ }
+
+ bssl::UniquePtr<X509> cert = GetChainTestCertificate();
+ bssl::UniquePtr<X509> intermediate = GetChainTestIntermediate();
+ bssl::UniquePtr<EVP_PKEY> key = GetChainTestKey();
+ if (!cert || !intermediate || !key) {
+ return false;
+ }
+
+ // Configure both client and server to accept any certificate. Add
+ // |intermediate| to the cert store.
+ bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(method));
+ if (!ctx ||
+ !SSL_CTX_use_certificate(ctx.get(), cert.get()) ||
+ !SSL_CTX_use_PrivateKey(ctx.get(), key.get()) ||
+ !SSL_CTX_set_min_proto_version(ctx.get(), version) ||
+ !SSL_CTX_set_max_proto_version(ctx.get(), version) ||
+ !X509_STORE_add_cert(SSL_CTX_get_cert_store(ctx.get()),
+ intermediate.get())) {
+ return false;
+ }
+ SSL_CTX_set_verify(
+ ctx.get(), SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nullptr);
+ SSL_CTX_set_cert_verify_callback(ctx.get(), VerifySucceed, NULL);
+
+ // By default, the client and server should each only send the leaf.
+ bssl::UniquePtr<SSL> client, server;
+ if (!ConnectClientAndServer(&client, &server, ctx.get(), ctx.get(),
+ nullptr /* no session */)) {
+ return false;
+ }
+
+ if (!ChainsEqual(SSL_get_peer_full_cert_chain(client.get()), {cert.get()})) {
+ fprintf(stderr, "Client-received chain did not match.\n");
+ return false;
+ }
+
+ if (!ChainsEqual(SSL_get_peer_full_cert_chain(server.get()), {cert.get()})) {
+ fprintf(stderr, "Server-received chain did not match.\n");
+ return false;
+ }
+
+ // If auto-chaining is enabled, then the intermediate is sent.
+ SSL_CTX_clear_mode(ctx.get(), SSL_MODE_NO_AUTO_CHAIN);
+ if (!ConnectClientAndServer(&client, &server, ctx.get(), ctx.get(),
+ nullptr /* no session */)) {
+ return false;
+ }
+
+ if (!ChainsEqual(SSL_get_peer_full_cert_chain(client.get()),
+ {cert.get(), intermediate.get()})) {
+ fprintf(stderr, "Client-received chain did not match (auto-chaining).\n");
+ return false;
+ }
+
+ if (!ChainsEqual(SSL_get_peer_full_cert_chain(server.get()),
+ {cert.get(), intermediate.get()})) {
+ fprintf(stderr, "Server-received chain did not match (auto-chaining).\n");
+ return false;
+ }
+
+ // Auto-chaining does not override explicitly-configured intermediates.
+ if (!SSL_CTX_add1_chain_cert(ctx.get(), cert.get()) ||
+ !ConnectClientAndServer(&client, &server, ctx.get(), ctx.get(),
+ nullptr /* no session */)) {
+ return false;
+ }
+
+ if (!ChainsEqual(SSL_get_peer_full_cert_chain(client.get()),
+ {cert.get(), cert.get()})) {
+ fprintf(stderr,
+ "Client-received chain did not match (auto-chaining, explicit "
+ "intermediate).\n");
+ return false;
+ }
+
+ if (!ChainsEqual(SSL_get_peer_full_cert_chain(server.get()),
+ {cert.get(), cert.get()})) {
+ fprintf(stderr,
+ "Server-received chain did not match (auto-chaining, explicit "
+ "intermediate).\n");
+ return false;
+ }
+
+ return true;
+}
+
static bool ForEachVersion(bool (*test_func)(bool is_dtls,
const SSL_METHOD *method,
uint16_t version)) {
@@ -2859,7 +3051,8 @@
!TestSetVersion() ||
!ForEachVersion(TestVersion) ||
!ForEachVersion(TestALPNCipherAvailable) ||
- !ForEachVersion(TestSSLClearSessionResumption)) {
+ !ForEachVersion(TestSSLClearSessionResumption) ||
+ !ForEachVersion(TestAutoChain)) {
ERR_print_errors_fp(stderr);
return 1;
}