diff options
Diffstat (limited to 'src/crypto/x509/x509_test.cc')
-rw-r--r-- | src/crypto/x509/x509_test.cc | 248 |
1 files changed, 227 insertions, 21 deletions
diff --git a/src/crypto/x509/x509_test.cc b/src/crypto/x509/x509_test.cc index ed4978aa..0c5fc2d7 100644 --- a/src/crypto/x509/x509_test.cc +++ b/src/crypto/x509/x509_test.cc @@ -12,6 +12,7 @@ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include <functional> #include <string> #include <vector> @@ -422,6 +423,78 @@ static const char kEd25519CertNull[] = "recgVPpVS7B+d9g4EwtZXIh4lodTBDHBBw==\n" "-----END CERTIFICATE-----\n"; +// kSANTypesLeaf is a leaf certificate (signed by |kSANTypesRoot|) which +// contains SANS for example.com, test@example.com, 127.0.0.1, and +// https://example.com/. (The latter is useless for now since crypto/x509 +// doesn't deal with URI SANs directly.) +static const char kSANTypesLeaf[] = + "-----BEGIN CERTIFICATE-----\n" + "MIIClzCCAgCgAwIBAgIJAOjwnT/iW+qmMA0GCSqGSIb3DQEBCwUAMCsxFzAVBgNV\n" + "BAoTDkJvcmluZ1NTTCBUZXN0MRAwDgYDVQQDEwdSb290IENBMB4XDTE1MDEwMTAw\n" + "MDAwMFoXDTI1MDEwMTAwMDAwMFowLzEXMBUGA1UEChMOQm9yaW5nU1NMIFRlc3Qx\n" + "FDASBgNVBAMTC2V4YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB\n" + "gQDbRn2TLhInBki8Bighq37EtqJd/h5SRYh6NkelCA2SQlvCgcC+l3mYQPtPbRT9\n" + "KxOLwqUuZ9jUCZ7WIji3Sgt0cyvCNPHRk+WW2XR781ifbGE8wLBB1NkrKyQjd1sc\n" + "O711Xc4gVM+hY4cdHiTE8x0aUIuqthRD7ZendWL0FMhS1wIDAQABo4G+MIG7MA4G\n" + "A1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDAYD\n" + "VR0TAQH/BAIwADAZBgNVHQ4EEgQQn5EWH0NDPkmm3m22gNefYDAbBgNVHSMEFDAS\n" + "gBBAN9cB+0AvuBx+VAQnjFkBMEQGA1UdEQQ9MDuCC2V4YW1wbGUuY29tgRB0ZXN0\n" + "QGV4YW1wbGUuY29thwR/AAABhhRodHRwczovL2V4YW1wbGUuY29tLzANBgkqhkiG\n" + "9w0BAQsFAAOBgQBtwJvY6+Tk6D6DOtDVaNoJ5y8E25CCuE/Ga4OuIcYJas+yLckf\n" + "dZwUV3GUG2oBXl2MrpUFxXd4hKBO1CmlBY+hZEeIx0Yp6QWK9P/vnZeydOTP26mk\n" + "jusJ2PqSmtKNU1Zcaba4d29oFejmOAfeguhR8AHpsc/zHEaS5Q9cJsuJcw==\n" + "-----END CERTIFICATE-----\n"; + +// -----BEGIN RSA PRIVATE KEY----- +// MIICWwIBAAKBgQDbRn2TLhInBki8Bighq37EtqJd/h5SRYh6NkelCA2SQlvCgcC+ +// l3mYQPtPbRT9KxOLwqUuZ9jUCZ7WIji3Sgt0cyvCNPHRk+WW2XR781ifbGE8wLBB +// 1NkrKyQjd1scO711Xc4gVM+hY4cdHiTE8x0aUIuqthRD7ZendWL0FMhS1wIDAQAB +// AoGACwf7z0i1DxOI2zSwFimLghfyCSp8mgT3fbZ3Wj0SebYu6ZUffjceneM/AVrq +// gGYHYLOVHcWJqfkl7X3hPo9SDhzLx0mM545/q21ZWCwjhswH7WiCEqV2/zeDO9WU +// NIO1VU0VoLm0AQ7ZvwnyB+fpgF9kkkDtbBJW7XWrfNVtlnECQQD97YENpEJ3X1kj +// 3rrkrHWDkKAyoWWY1i8Fm7LnganC9Bv6AVwgn5ZlE/479aWHF8vbOFEA3pFPiNZJ +// t9FTCfpJAkEA3RCXjGI0Y6GALFLwEs+nL/XZAfJaIpJEZVLCVosYQOSaMS4SchfC +// GGYVquT7ZgKk9uvz89Fg87OtBMWS9lrkHwJADGkGLKeBhBoJ3kHtem2fVK3F1pOi +// xoR5SdnhNYVVyaxqjZ5xZTrHe+stOrr3uxGDqhQniVZXXb6/Ul0Egv1y2QJAVg/h +// kAujba4wIhFf2VLyOZ+yjil1ocPj0LZ5Zgvcs1bMGJ1hHP3W2HzVrqRaowoggui1 +// HpTC891dXGA2qKYV7QJAFDmT2A7OVvh3y4AEgzVwHrDmCMwMHKjCIntS7fjxrJnF +// YvJUG1zoHwUVrxxbR3DbpTODlktLcl/0b97D0IkH3w== +// -----END RSA PRIVATE KEY----- + +static const char kSANTypesRoot[] = + "-----BEGIN CERTIFICATE-----\n" + "MIICTTCCAbagAwIBAgIIAj5CwoHlWuYwDQYJKoZIhvcNAQELBQAwKzEXMBUGA1UE\n" + "ChMOQm9yaW5nU1NMIFRlc3QxEDAOBgNVBAMTB1Jvb3QgQ0EwHhcNMTUwMTAxMDAw\n" + "MDAwWhcNMjUwMTAxMDAwMDAwWjArMRcwFQYDVQQKEw5Cb3JpbmdTU0wgVGVzdDEQ\n" + "MA4GA1UEAxMHUm9vdCBDQTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA6Q5/\n" + "EQzmWuaGg3D2UQcuAngR9bIkkjjuJmICx5TxPqF3asCP1SJotl3iTNrghRE1wpJy\n" + "SY2BtIiXa7f8skRb2U0GcPkMxo/ps9+jaoRsQ1m+nbLQdpvD1/qZWcO45fNTA71J\n" + "1rPMokP+rcILuQG4VimUAySnDSghKamulFtK+Z8CAwEAAaN6MHgwDgYDVR0PAQH/\n" + "BAQDAgIEMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAPBgNVHRMBAf8E\n" + "BTADAQH/MBkGA1UdDgQSBBBAN9cB+0AvuBx+VAQnjFkBMBsGA1UdIwQUMBKAEEA3\n" + "1wH7QC+4HH5UBCeMWQEwDQYJKoZIhvcNAQELBQADgYEAc4N6hTE62/3gwg+kyc2f\n" + "c/Jj1mHrOt+0NRaBnmvbmNpsEjHS96Ef4Wt/ZlPXPkkv1C1VosJnOIMF3Q522wRH\n" + "bqaxARldS12VAa3gcWisDWD+SqSyDxjyojz0XDiJkTrFuCTCUiZO+1GLB7SO10Ms\n" + "d5YVX0c90VMnUhF/dlrqS9U=\n" + "-----END CERTIFICATE-----\n"; + +// -----BEGIN RSA PRIVATE KEY----- +// MIICXAIBAAKBgQDpDn8RDOZa5oaDcPZRBy4CeBH1siSSOO4mYgLHlPE+oXdqwI/V +// Imi2XeJM2uCFETXCknJJjYG0iJdrt/yyRFvZTQZw+QzGj+mz36NqhGxDWb6dstB2 +// m8PX+plZw7jl81MDvUnWs8yiQ/6twgu5AbhWKZQDJKcNKCEpqa6UW0r5nwIDAQAB +// AoGALEF5daZqc+aEsp8X1yky3nsoheyPL0kqSBWii33IFemZgKcSaRnAoqjPWWLS +// 8dHj0I/4rej2MW8iuezVSpDak9tK5boHORC3w4p/wifkizQkLt1DANxTVbzcKvrt +// aZ7LjVaKkhjRJbLddniowFHkkWVbUccjvzcUd7Y2VuLbAhECQQDq4FE88aHio8zg +// bxSd0PwjEFwLYQTR19u812SoR8PmR6ofIL+pDwOV+fVs+OGcAAOgkhIukOrksQ4A +// 1cKtnyhXAkEA/gRI+u3tZ7UE1twIkBfZ6IvCdRodkPqHAYIxMRLzL+MhyZt4MEGc +// Ngb/F6U9/WOBFnoR/PI7IwE3ejutzKcL+QJBAKh+6eilk7QKPETZi1m3/dmNt+p1 +// 3EZJ65pqjwxmB3Rg/vs7vCMk4TarTdSyKu+F1xRPFfoP/mK3Xctdjj6NyhsCQAYF +// 7/0TOzfkUPMPUJyqFB6xgbDpJ55ScnUUsznoqx+NkTWInDb4t02IqO/UmT2y6FKy +// Hk8TJ1fTJY+ebqaVp3ECQApx9gQ+n0zIhx97FMUuiRse73xkcW4+pZ8nF+8DmeQL +// /JKuuFGmzkG+rUbXFmo/Zg2ozVplw71NnQJ4znPsf7A= +// -----END RSA PRIVATE KEY----- + + // CertFromPEM parses the given, NUL-terminated pem block and returns an // |X509*|. static bssl::UniquePtr<X509> CertFromPEM(const char *pem) { @@ -484,12 +557,10 @@ static bssl::UniquePtr<STACK_OF(X509_CRL)> CRLsToStack( } static int Verify(X509 *leaf, const std::vector<X509 *> &roots, - const std::vector<X509 *> &intermediates, - const std::vector<X509_CRL *> &crls, - unsigned long flags, - bool use_additional_untrusted, - const char *hostname, - size_t hostname_len) { + const std::vector<X509 *> &intermediates, + const std::vector<X509_CRL *> &crls, unsigned long flags, + bool use_additional_untrusted, + std::function<void(X509_VERIFY_PARAM *)> configure_callback) { bssl::UniquePtr<STACK_OF(X509)> roots_stack(CertsToStack(roots)); bssl::UniquePtr<STACK_OF(X509)> intermediates_stack( CertsToStack(intermediates)); @@ -528,7 +599,9 @@ static int Verify(X509 *leaf, const std::vector<X509 *> &roots, } X509_VERIFY_PARAM_set_time(param, 1474934400 /* Sep 27th, 2016 */); X509_VERIFY_PARAM_set_depth(param, 16); - X509_VERIFY_PARAM_set1_host(param, hostname, hostname_len); + if (configure_callback) { + configure_callback(param); + } if (flags) { X509_VERIFY_PARAM_set_flags(param, flags); } @@ -547,9 +620,9 @@ static int Verify(X509 *leaf, const std::vector<X509 *> &roots, const std::vector<X509_CRL *> &crls, unsigned long flags = 0) { const int r1 = - Verify(leaf, roots, intermediates, crls, flags, false, nullptr, 0); + Verify(leaf, roots, intermediates, crls, flags, false, nullptr); const int r2 = - Verify(leaf, roots, intermediates, crls, flags, true, nullptr, 0); + Verify(leaf, roots, intermediates, crls, flags, true, nullptr); if (r1 != r2) { fprintf(stderr, @@ -617,19 +690,152 @@ TEST(X509Test, TestVerify) { Verify(forgery.get(), {intermediate_self_signed.get(), root_cross_signed.get()}, {leaf_no_key_usage.get(), intermediate.get()}, empty_crls)); +} - static const char kHostname[] = "example.com"; - static const char kWrongHostname[] = "example2.com"; - ASSERT_EQ(X509_V_OK, - Verify(leaf.get(), {root.get()}, {intermediate.get()}, empty_crls, - 0, false, kHostname, strlen(kHostname))); - // The wrong hostname should trigger a hostname error. - ASSERT_EQ(X509_V_ERR_HOSTNAME_MISMATCH, - Verify(leaf.get(), {root.get()}, {intermediate.get()}, empty_crls, - 0, false, kWrongHostname, strlen(kWrongHostname))); - // Passing zero, for this API, is supported for compatibility with OpenSSL. - ASSERT_EQ(X509_V_OK, Verify(leaf.get(), {root.get()}, {intermediate.get()}, - empty_crls, 0, false, kHostname, 0)); +static const char kHostname[] = "example.com"; +static const char kWrongHostname[] = "example2.com"; +static const char kEmail[] = "test@example.com"; +static const char kWrongEmail[] = "test2@example.com"; +static const uint8_t kIP[4] = {127, 0, 0, 1}; +static const uint8_t kWrongIP[4] = {127, 0, 0, 2}; +static const char kIPString[] = "127.0.0.1"; +static const char kWrongIPString[] = "127.0.0.2"; + +TEST(X509Test, ZeroLengthsWithX509PARAM) { + bssl::UniquePtr<X509> leaf(CertFromPEM(kSANTypesLeaf)); + bssl::UniquePtr<X509> root(CertFromPEM(kSANTypesRoot)); + ASSERT_TRUE(leaf); + ASSERT_TRUE(root); + + std::vector<X509_CRL *> empty_crls; + + struct Test { + const char *correct_value; + size_t correct_value_len; + const char *incorrect_value; + size_t incorrect_value_len; + int (*func)(X509_VERIFY_PARAM *, const char *, size_t); + int mismatch_error; + }; + const std::vector<Test> kTests = { + {kHostname, strlen(kHostname), kWrongHostname, strlen(kWrongHostname), + X509_VERIFY_PARAM_set1_host, X509_V_ERR_HOSTNAME_MISMATCH}, + {kEmail, strlen(kEmail), kWrongEmail, strlen(kWrongEmail), + X509_VERIFY_PARAM_set1_email, X509_V_ERR_EMAIL_MISMATCH}, + }; + + for (size_t i = 0; i < kTests.size(); i++) { + SCOPED_TRACE(i); + const Test &test = kTests[i]; + + // The correct value should work. + ASSERT_EQ(X509_V_OK, + Verify(leaf.get(), {root.get()}, {}, empty_crls, 0, false, + [&test](X509_VERIFY_PARAM *param) { + ASSERT_TRUE(test.func(param, test.correct_value, + test.correct_value_len)); + })); + + // The wrong value should trigger a verification error. + ASSERT_EQ(test.mismatch_error, + Verify(leaf.get(), {root.get()}, {}, empty_crls, 0, false, + [&test](X509_VERIFY_PARAM *param) { + ASSERT_TRUE(test.func(param, test.incorrect_value, + test.incorrect_value_len)); + })); + + // Passing zero as the length, unlike OpenSSL, should trigger an error and + // should cause verification to fail. + ASSERT_EQ(X509_V_ERR_INVALID_CALL, + Verify(leaf.get(), {root.get()}, {}, empty_crls, 0, false, + [&test](X509_VERIFY_PARAM *param) { + ASSERT_FALSE(test.func(param, test.correct_value, 0)); + })); + + // Passing an empty value should be an error when setting and should cause + // verification to fail. + ASSERT_EQ(X509_V_ERR_INVALID_CALL, + Verify(leaf.get(), {root.get()}, {}, empty_crls, 0, false, + [&test](X509_VERIFY_PARAM *param) { + ASSERT_FALSE(test.func(param, nullptr, 0)); + })); + + // Passing a value with embedded NULs should also be an error and should + // also cause verification to fail. + ASSERT_EQ(X509_V_ERR_INVALID_CALL, + Verify(leaf.get(), {root.get()}, {}, empty_crls, 0, false, + [&test](X509_VERIFY_PARAM *param) { + ASSERT_FALSE(test.func(param, "a", 2)); + })); + } + + // IP addresses work slightly differently: + + // The correct value should still work. + ASSERT_EQ(X509_V_OK, Verify(leaf.get(), {root.get()}, {}, empty_crls, 0, + false, [](X509_VERIFY_PARAM *param) { + ASSERT_TRUE(X509_VERIFY_PARAM_set1_ip( + param, kIP, sizeof(kIP))); + })); + + // Incorrect values should still fail. + ASSERT_EQ(X509_V_ERR_IP_ADDRESS_MISMATCH, + Verify(leaf.get(), {root.get()}, {}, empty_crls, 0, false, + [](X509_VERIFY_PARAM *param) { + ASSERT_TRUE(X509_VERIFY_PARAM_set1_ip(param, kWrongIP, + sizeof(kWrongIP))); + })); + + // Zero length values should trigger an error when setting and cause + // verification to always fail. + ASSERT_EQ(X509_V_ERR_INVALID_CALL, + Verify(leaf.get(), {root.get()}, {}, empty_crls, 0, false, + [](X509_VERIFY_PARAM *param) { + ASSERT_FALSE(X509_VERIFY_PARAM_set1_ip(param, kIP, 0)); + })); + + // ... and so should NULL values. + ASSERT_EQ(X509_V_ERR_INVALID_CALL, + Verify(leaf.get(), {root.get()}, {}, empty_crls, 0, false, + [](X509_VERIFY_PARAM *param) { + ASSERT_FALSE(X509_VERIFY_PARAM_set1_ip(param, nullptr, 0)); + })); + + // Zero bytes in an IP address are, of course, fine. This is tested above + // because |kIP| contains zeros. +} + +TEST(X509Test, ZeroLengthsWithCheckFunctions) { + bssl::UniquePtr<X509> leaf(CertFromPEM(kSANTypesLeaf)); + + EXPECT_EQ( + 1, X509_check_host(leaf.get(), kHostname, strlen(kHostname), 0, nullptr)); + EXPECT_NE(1, X509_check_host(leaf.get(), kWrongHostname, + strlen(kWrongHostname), 0, nullptr)); + + EXPECT_EQ(1, X509_check_email(leaf.get(), kEmail, strlen(kEmail), 0)); + EXPECT_NE(1, + X509_check_email(leaf.get(), kWrongEmail, strlen(kWrongEmail), 0)); + + EXPECT_EQ(1, X509_check_ip(leaf.get(), kIP, sizeof(kIP), 0)); + EXPECT_NE(1, X509_check_ip(leaf.get(), kWrongIP, sizeof(kWrongIP), 0)); + + EXPECT_EQ(1, X509_check_ip_asc(leaf.get(), kIPString, 0)); + EXPECT_NE(1, X509_check_ip_asc(leaf.get(), kWrongIPString, 0)); + + // OpenSSL supports passing zero as the length for host and email. We do not + // and it should always fail. + EXPECT_NE(1, X509_check_host(leaf.get(), kHostname, 0, 0, nullptr)); + EXPECT_NE(1, X509_check_host(leaf.get(), kWrongHostname, 0, 0, nullptr)); + + EXPECT_NE(1, X509_check_email(leaf.get(), kEmail, 0, 0)); + EXPECT_NE(1, X509_check_email(leaf.get(), kWrongEmail, 0, 0)); + + EXPECT_NE(1, X509_check_ip(leaf.get(), kIP, 0, 0)); + EXPECT_NE(1, X509_check_ip(leaf.get(), kWrongIP, 0, 0)); + + // Unlike all the other functions, |X509_check_ip_asc| doesn't take a length, + // so it cannot be zero. } TEST(X509Test, TestCRL) { |