/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This code is made available to you under your choice of the following sets * of licensing terms:
*/ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/ /* Copyright 2014 Mozilla Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License.
*/ #include"pkixgtest.h"
// We allow underscores for compatibility with existing practices.
DNS_ID_MATCH("a_b", "a_b"),
DNS_ID_MATCH("*.example.com", "uses_underscore.example.com"),
DNS_ID_MATCH("*.uses_underscore.example.com", "a.uses_underscore.example.com"),
// We allow reference ID labels to start and end with hyphens for // compatibility.
DNS_ID_MATCH("*.example.com", "-.example.com"),
DNS_ID_MATCH("*.example.com", "-hyphenstart.example.com"),
DNS_ID_MATCH("*.example.com", "hyphenend-.example.com"), // Presented ID labels may not start or end with hyphens.
DNS_ID_BAD_DER("-.example.com", "-.example.com"),
DNS_ID_BAD_DER("-hyphenstart.example.com", "-hyphenstart.example.com"),
DNS_ID_BAD_DER("hyphenend-.example.com", "hyphenend-.example.com"),
// See bug 1139039 // A DNS-ID must not end in an all-numeric label. We don't consider // underscores to be numeric.
DNS_ID_MATCH("_1", "_1"),
DNS_ID_MATCH("example._1", "example._1"),
DNS_ID_MATCH("example.1_", "example.1_"),
// Wildcard not in leftmost label
DNS_ID_MATCH("d.c.b.a", "d.c.b.a"),
DNS_ID_BAD_DER("d.*.b.a", "d.c.b.a"),
DNS_ID_BAD_DER("d.c*.b.a", "d.c.b.a"),
DNS_ID_BAD_DER("d.c*.b.a", "d.cc.b.a"),
// case sensitivity
DNS_ID_MATCH("abcdefghijklmnopqrstuvwxyz", "ABCDEFGHIJKLMNOPQRSTUVWXYZ"),
DNS_ID_MATCH("ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz"),
DNS_ID_MATCH("aBc", "Abc"),
// digits
DNS_ID_MATCH("a1", "a1"),
// A trailing dot indicates an absolute name. Absolute presented names are // not allowed, but absolute reference names are allowed.
DNS_ID_MATCH("example", "example"),
DNS_ID_BAD_DER("example.", "example."),
DNS_ID_MATCH("example", "example."),
DNS_ID_BAD_DER("example.", "example"),
DNS_ID_MATCH("example.com", "example.com"),
DNS_ID_BAD_DER("example.com.", "example.com."),
DNS_ID_MATCH("example.com", "example.com."),
DNS_ID_BAD_DER("example.com.", "example.com"),
DNS_ID_BAD_DER("example.com..", "example.com."),
DNS_ID_BAD_DER("example.com..", "example.com"),
DNS_ID_BAD_DER("example.com...", "example.com."),
// "*" cannot expand to nothing.
DNS_ID_BAD_DER("c*.b.a", "c.b.a"),
///////////////////////////////////////////////////////////////////////////// // These are test cases adapted from Chromium's x509_certificate_unittest.cc. // The parameter order is the opposite in Chromium's tests. Also, some tests // were modified to fit into this framework or due to intentional differences // between mozilla::pkix and Chromium.
// '*' must be the only character in the wildcard label
DNS_ID_BAD_DER("wa*.bar.foo.com", "WALLY.bar.foo.com"),
// We require "*" to be the last character in a wildcard label, but // Chromium does not.
DNS_ID_BAD_DER("*Ly.bar.foo.com", "wally.bar.foo.com"),
// Chromium does URL decoding of the reference ID, but we don't, and we also // require that the reference ID is valid, so we can't test these two. // DNS_ID_MATCH("www.foo.com", "ww%57.foo.com"), // DNS_ID_MATCH("www&.foo.com", "www%26.foo.com"),
// Our matcher requires the reference ID to be a valid DNS name, so we cannot // test this case. //DNS_ID_BAD_DER("*.*.bar.foo.com", "*..bar.foo.com"),
DNS_ID_MATCH("www.bath.org", "www.bath.org"),
// Our matcher requires the reference ID to be a valid DNS name, so we cannot // test these cases. // DNS_ID_BAD_DER("www.bath.org", ""), // DNS_ID_BAD_DER("www.bath.org", "20.30.40.50"), // DNS_ID_BAD_DER("www.bath.org", "66.77.88.99"),
// The following are adapted from the examples quoted from // http://tools.ietf.org/html/rfc6125#section-6.4.3 // (e.g., *.example.com would match foo.example.com but // not bar.foo.example.com or example.com).
DNS_ID_MATCH("*.example.com", "foo.example.com"),
DNS_ID_MISMATCH("*.example.com", "bar.foo.example.com"),
DNS_ID_MISMATCH("*.example.com", "example.com"), // (e.g., baz*.example.net and *baz.example.net and b*z.example.net would // be taken to match baz1.example.net and foobaz.example.net and // buzz.example.net, respectively. However, we don't allow any characters // other than '*' in the wildcard label.
DNS_ID_BAD_DER("baz*.example.net", "baz1.example.net"),
// Both of these are different from Chromium, but match NSS, becaues the // wildcard character "*" is not the last character of the label.
DNS_ID_BAD_DER("*baz.example.net", "foobaz.example.net"),
DNS_ID_BAD_DER("b*z.example.net", "buzz.example.net"),
// Wildcards should not be valid for public registry controlled domains, // and unknown/unrecognized domains, at least three domain components must // be present. For mozilla::pkix and NSS, there must always be at least two // labels after the wildcard label.
DNS_ID_MATCH("*.test.example", "www.test.example"),
DNS_ID_MATCH("*.example.co.uk", "test.example.co.uk"),
DNS_ID_BAD_DER("*.exmaple", "test.example"),
// The result is different than Chromium, because Chromium takes into account // the additional knowledge it has that "co.uk" is a TLD. mozilla::pkix does // not know that.
DNS_ID_MATCH("*.co.uk", "example.co.uk"),
// IDN variants of wildcards and registry controlled domains.
DNS_ID_MATCH("*.xn--poema-9qae5a.com.br", "www.xn--poema-9qae5a.com.br"),
DNS_ID_MATCH("*.example.xn--mgbaam7a8h", "test.example.xn--mgbaam7a8h"),
// RFC6126 allows this, and NSS accepts it, but Chromium disallows it. // TODO: File bug against Chromium.
DNS_ID_MATCH("*.com.br", "xn--poema-9qae5a.com.br"),
DNS_ID_BAD_DER("*.xn--mgbaam7a8h", "example.xn--mgbaam7a8h"), // Wildcards should be permissible for 'private' registry-controlled // domains. (In mozilla::pkix, we do not know if it is a private registry- // controlled domain or not.)
DNS_ID_MATCH("*.appspot.com", "www.appspot.com"),
DNS_ID_MATCH("*.s3.amazonaws.com", "foo.s3.amazonaws.com"),
// Multiple wildcards are not valid.
DNS_ID_BAD_DER("*.*.com", "foo.example.com"),
DNS_ID_BAD_DER("*.bar.*.com", "foo.bar.example.com"),
// Absolute vs relative DNS name tests. Although not explicitly specified // in RFC 6125, absolute reference names (those ending in a .) should // match either absolute or relative presented names. We don't allow // absolute presented names. // TODO: File errata against RFC 6125 about this.
DNS_ID_BAD_DER("foo.com.", "foo.com"),
DNS_ID_MATCH("foo.com", "foo.com."),
DNS_ID_BAD_DER("foo.com.", "foo.com."),
DNS_ID_BAD_DER("f.", "f"),
DNS_ID_MATCH("f", "f."),
DNS_ID_BAD_DER("f.", "f."),
DNS_ID_BAD_DER("*.bar.foo.com.", "www-3.bar.foo.com"),
DNS_ID_MATCH("*.bar.foo.com", "www-3.bar.foo.com."),
DNS_ID_BAD_DER("*.bar.foo.com.", "www-3.bar.foo.com."),
// We require the reference ID to be a valid DNS name, so we cannot test this // case. // DNS_ID_MISMATCH(".", "."),
// The result is different than Chromium because we don't know that co.uk is // a TLD.
DNS_ID_MATCH("*.co.uk", "foo.co.uk"),
DNS_ID_MATCH("*.co.uk", "foo.co.uk."),
DNS_ID_BAD_DER("*.co.uk.", "foo.co.uk"),
DNS_ID_BAD_DER("*.co.uk.", "foo.co.uk."),
DNS_ID_MISMATCH("*.example.com", "localhost"),
DNS_ID_MISMATCH("*.example.com", "localhost."), // Note that we already have the testcase DNS_ID_BAD_DER("*", "foo") above
};
// Allowed character set
I("a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z", true, true),
I("A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z", true, true),
I("0.1.2.3.4.5.6.7.8.9.a", true, true), // "a" needed to avoid numeric last label
I("a-b", true, true), // hyphen (presented ID labels cannot start or end with a hyphen)
// Wildcard specifications are not valid reference names, but are valid // presented names if there are enough labels and if '*' is the only // character in the wildcard label.
I("*.a", false, false),
I("a*", false, false),
I("a*.", false, false),
I("a*.a", false, false),
I("a*.a.", false, false),
I("*.a.b", false, true),
I("*.a.b.", false, false),
I("a*.b.c", false, false),
I("*.a.b.c", false, true),
I("a*.b.c.d", false, false),
// Multiple wildcards are not allowed.
I("a**.b.c", false, false),
I("a*b*.c.d", false, false),
I("a*.b*.c", false, false),
// Wildcards are only allowed in the first label.
I("a.*", false, false),
I("a.*.b", false, false),
I("a.b.*", false, false),
I("a.b*.c", false, false),
I("*.b*.c", false, false),
I(".*.a.b", false, false),
I(".a*.b.c", false, false),
// Wildcards must be at the *end* of the first label.
I("*a.b.c", false, false),
I("a*b.c.d", false, false),
// maximum label length is 63 characters
I("1234567890""1234567890""1234567890" "1234567890""1234567890""1234567890""abc", true, true),
I("1234567890""1234567890""1234567890" "1234567890""1234567890""1234567890""abcd", false, false),
// maximum total length is 253 characters
I("1234567890""1234567890""1234567890""1234567890""1234567890""." "1234567890""1234567890""1234567890""1234567890""1234567890""." "1234567890""1234567890""1234567890""1234567890""1234567890""." "1234567890""1234567890""1234567890""1234567890""1234567890""." "1234567890""1234567890""1234567890""1234567890""12345678""a", true, true),
I("1234567890""1234567890""1234567890""1234567890""1234567890""." "1234567890""1234567890""1234567890""1234567890""1234567890""." "1234567890""1234567890""1234567890""1234567890""1234567890""." "1234567890""1234567890""1234567890""1234567890""1234567890""." "1234567890""1234567890""1234567890""1234567890""123456789""a", false, false),
};
staticconst InputValidity DNSNAMES_VALIDITY_TURKISH_I[] =
{ // http://en.wikipedia.org/wiki/Dotted_and_dotless_I#In_computing // IDN registration rules disallow "latin capital letter i with dot above," // but our checks aren't intended to enforce those rules.
I("I", true, true), // ASCII capital I
I("i", true, true), // ASCII lowercase i
I("\xC4\xB0", false, false), // latin capital letter i with dot above
I("\xC4\xB1", false, false), // latin small letter dotless i
I("xn--i-9bb", true, true), // latin capital letter i with dot above, in punycode
I("xn--cfa", true, true), // latin small letter dotless i, in punycode
I("xn--\xC4\xB0", false, false), // latin capital letter i with dot above, mashup
I("xn--\xC4\xB1", false, false), // latin small letter dotless i, mashup
};
#define IPV4_VALID(str, a, b, c, d) \
{ \
ByteString(reinterpret_cast<const uint8_t*>(str), sizeof(str) - 1), \ true, \
{ a, b, c, d } \
}
// The value of expectedValueIfValid must be ignored for invalid IP addresses. // The value { 73, 73, 73, 73 } is used because it is unlikely to result in an // accidental match, unlike { 0, 0, 0, 0 }, which is a value we actually test. #define IPV4_INVALID(str) \
{ \
ByteString(reinterpret_cast<const uint8_t*>(str), sizeof(str) - 1), \ false, \
{ 73, 73, 73, 73 } \
}
#define IPV6_VALID(str, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) \
{ \
ByteString(reinterpret_cast<const uint8_t*>(str), sizeof(str) - 1), \ true, \
{ a, b, c, d, \
e, f, g, h, \
i, j, k, l, \
m, n, o, p } \
}
// Contraction in full IPv6 addresses not allowed
IPV6_INVALID("::1234:5678:9abc:def0:1234:5678:9abc:def0"), // start
IPV6_INVALID("1234:5678:9abc:def0:1234:5678:9abc:def0::"), // end
IPV6_INVALID("1234:5678::9abc:def0:1234:5678:9abc:def0"), // interior
// Multiple contractions not allowed
IPV6_INVALID("::1::"),
IPV6_INVALID("::1::2"),
IPV6_INVALID("1::2::"),
class pkixnames_MatchPresentedDNSIDWithReferenceDNSID
: public ::testing::Test
, public ::testing::WithParamInterface<PresentedMatchesReference>
{ public:
DefaultNameMatchingPolicy mNameMatchingPolicy;
};
class pkixnames_Turkish_I_Comparison
: public ::testing::Test
, public ::testing::WithParamInterface<InputValidity>
{ public:
DefaultNameMatchingPolicy mNameMatchingPolicy;
};
TEST_P(pkixnames_Turkish_I_Comparison, MatchPresentedDNSIDWithReferenceDNSID)
{ // Make sure we don't have the similar problems that strcasecmp and others // have with the other kinds of "i" and "I" commonly used in Turkish locales.
class pkixnames_IsValidReferenceDNSID
: public ::testing::Test
, public ::testing::WithParamInterface<InputValidity>
{ public:
DefaultNameMatchingPolicy mNameMatchingPolicy;
};
class pkixnames_ParseIPv4Address
: public ::testing::Test
, public ::testing::WithParamInterface<IPAddressParams<4>>
{ public:
DefaultNameMatchingPolicy mNameMatchingPolicy;
};
TEST_P(pkixnames_ParseIPv4Address, ParseIPv4Address)
{ const IPAddressParams<4>& param(GetParam());
SCOPED_TRACE(param.input.c_str());
Input input;
ASSERT_EQ(Success, input.Init(param.input.data(),
param.input.length()));
uint8_t ipAddress[4];
ASSERT_EQ(param.isValid, ParseIPv4Address(input, ipAddress)); if (param.isValid) { for (size_t i = 0; i < sizeof(ipAddress); ++i) {
ASSERT_EQ(param.expectedValueIfValid[i], ipAddress[i]);
}
}
}
class pkixnames_ParseIPv6Address
: public ::testing::Test
, public ::testing::WithParamInterface<IPAddressParams<16>>
{ public:
DefaultNameMatchingPolicy mNameMatchingPolicy;
};
TEST_P(pkixnames_ParseIPv6Address, ParseIPv6Address)
{ const IPAddressParams<16>& param(GetParam());
SCOPED_TRACE(param.input.c_str());
Input input;
ASSERT_EQ(Success, input.Init(param.input.data(),
param.input.length()));
uint8_t ipAddress[16];
ASSERT_EQ(param.isValid, ParseIPv6Address(input, ipAddress)); if (param.isValid) { for (size_t i = 0; i < sizeof(ipAddress); ++i) {
ASSERT_EQ(param.expectedValueIfValid[i], ipAddress[i]);
}
}
}
// This is an arbitrary string that is used to indicate that no SAN extension // should be put into the generated certificate. It needs to be different from // "" or any other subjectAltName value that we actually want to test, but its // actual value does not matter. Note that this isn't a correctly-encoded SAN // extension value! staticconst ByteString
NO_SAN(reinterpret_cast<const uint8_t*>("I'm a bad, bad, certificate"));
class pkixnames_CheckCertHostname
: public ::testing::Test
, public ::testing::WithParamInterface<CheckCertHostnameParams>
{ public:
DefaultNameMatchingPolicy mNameMatchingPolicy;
};
// The subnet is 1.2.0.0/16 but it is specified as 1.2.3.0/16 staticconst uint8_t ipv4_constraint_CIDR_16_bad_addr_bytes[] = {
1, 2, 3, 0, 0xff, 0xff, 0, 0
};
// Masks are supposed to be of the form <ones><zeros>, but this one is of the // form <ones><zeros><ones><zeros>. staticconst uint8_t ipv4_constraint_bad_mask_bytes[] = {
1, 2, 3, 0, 0xff, 0, 0xff, 0
};
// The subnet is 1122::/16 but it is specified as 1122:3344::/16 staticconst uint8_t ipv6_constraint_CIDR_16_bad_addr_bytes[] = {
0x11, 0x22, 0x33, 0x44, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0xff, 0xff, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0
};
// Masks are supposed to be of the form <ones><zeros>, but this one is of the // form <ones><zeros><ones><zeros>. staticconst uint8_t ipv6_constraint_bad_mask_bytes[] = {
0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0xff, 0xff, 0, 0, 0xff, 0xff, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
};
// Note that, for DNSNames, these test cases in CHECK_CERT_HOSTNAME_PARAMS are // mostly about testing different scenerios regarding the structure of entries // in the subjectAltName and subject of the certificate, than about the how // specific presented identifier values are matched against the reference // identifier values. This is because we also use the test cases in // DNSNAMES_VALIDITY to test CheckCertHostname. Consequently, tests about // whether specific presented DNSNames (including wildcards, in particular) are // matched against a reference DNSName only need to be added to // DNSNAMES_VALIDITY, and not here. staticconst CheckCertHostnameParams CHECK_CERT_HOSTNAME_PARAMS[] =
{ // This is technically illegal. PrintableString is defined in such a way that // '*' is not an allowed character, but there are many real-world certificates // that are encoded this way.
WITHOUT_SAN("foo.example.com", RDN(CN("*.example.com", der::PrintableString)),
Success),
WITHOUT_SAN("foo.example.com", RDN(CN("*.example.com", der::UTF8String)),
Success),
// Many certificates use TeletexString when encoding wildcards in CN-IDs // because PrintableString is defined as not allowing '*' and UTF8String was, // at one point in history, considered too new to depend on for compatibility. // We accept TeletexString-encoded CN-IDs when they don't contain any escape // sequences. The reference I used for the escape codes was // https://tools.ietf.org/html/rfc1468. The escaping mechanism is actually // pretty complex and these tests don't even come close to testing all the // possibilities.
WITHOUT_SAN("foo.example.com", RDN(CN("*.example.com", der::TeletexString)),
Success), // "ESC ( B" ({0x1B,0x50,0x42}) is the escape code to switch to ASCII, which // is redundant because it already the default.
WITHOUT_SAN("foo.example.com",
RDN(CN("\x1B(B*.example.com", der::TeletexString)),
Result::ERROR_BAD_CERT_DOMAIN),
WITHOUT_SAN("foo.example.com",
RDN(CN("*.example\x1B(B.com", der::TeletexString)),
Result::ERROR_BAD_CERT_DOMAIN),
WITHOUT_SAN("foo.example.com",
RDN(CN("*.example.com\x1B(B", der::TeletexString)),
Result::ERROR_BAD_CERT_DOMAIN), // "ESC $ B" ({0x1B,0x24,0x42}) is the escape code to switch to // JIS X 0208-1983 (a Japanese character set).
WITHOUT_SAN("foo.example.com",
RDN(CN("\x1B$B*.example.com", der::TeletexString)),
Result::ERROR_BAD_CERT_DOMAIN),
WITHOUT_SAN("foo.example.com",
RDN(CN("*.example.com\x1B$B", der::TeletexString)),
Result::ERROR_BAD_CERT_DOMAIN),
// Match a DNSName SAN entry with a redundant (ignored) matching CN-ID.
WITH_SAN("a", RDN(CN("a")), DNSName("a"), Success), // Match a DNSName SAN entry when there is an CN-ID that doesn't match.
WITH_SAN("b", RDN(CN("a")), DNSName("b"), Success), // Do not match a CN-ID when there is a valid DNSName SAN Entry.
WITH_SAN("a", RDN(CN("a")), DNSName("b"), Result::ERROR_BAD_CERT_DOMAIN), // Do not match a CN-ID when there is a malformed DNSName SAN Entry.
WITH_SAN("a", RDN(CN("a")), DNSName("!"), Result::ERROR_BAD_DER), // Do not match a matching CN-ID when there is a valid IPAddress SAN entry.
WITH_SAN("a", RDN(CN("a")), IPAddress(ipv4_addr_bytes),
Result::ERROR_BAD_CERT_DOMAIN), // Do not match a matching CN-ID when there is a malformed IPAddress SAN entry.
WITH_SAN("a", RDN(CN("a")), IPAddress(example_com),
Result::ERROR_BAD_CERT_DOMAIN), // Match a DNSName against a matching CN-ID when there is a SAN, but the SAN // does not contain an DNSName or IPAddress entry.
WITH_SAN("a", RDN(CN("a")), RFC822Name("foo@example.com"), Success), // Match a matching CN-ID when there is no SAN.
WITHOUT_SAN("a", RDN(CN("a")), Success), // Do not match a mismatching CN-ID when there is no SAN.
WITHOUT_SAN("a", RDN(CN("b")), Result::ERROR_BAD_CERT_DOMAIN),
// The first DNSName matches.
WITH_SAN("a", RDN(CN("foo")), DNSName("a") + DNSName("b"), Success), // The last DNSName matches.
WITH_SAN("b", RDN(CN("foo")), DNSName("a") + DNSName("b"), Success), // The middle DNSName matches.
WITH_SAN("b", RDN(CN("foo")),
DNSName("a") + DNSName("b") + DNSName("c"), Success), // After an IP address.
WITH_SAN("b", RDN(CN("foo")),
IPAddress(ipv4_addr_bytes) + DNSName("b"), Success), // Before an IP address.
WITH_SAN("a", RDN(CN("foo")),
DNSName("a") + IPAddress(ipv4_addr_bytes), Success), // Between an RFC822Name and an IP address.
WITH_SAN("b", RDN(CN("foo")),
RFC822Name("foo@example.com") + DNSName("b") +
IPAddress(ipv4_addr_bytes),
Success), // Duplicate DNSName.
WITH_SAN("a", RDN(CN("foo")), DNSName("a") + DNSName("a"), Success), // After an invalid DNSName.
WITH_SAN("b", RDN(CN("foo")), DNSName("!") + DNSName("b"),
Result::ERROR_BAD_DER),
// http://tools.ietf.org/html/rfc5280#section-4.2.1.6: "If the subjectAltName // extension is present, the sequence MUST contain at least one entry." // However, for compatibility reasons, this is not enforced. See bug 1143085. // This case is treated as if the extension is not present (i.e. name // matching falls back to the subject CN).
WITH_SAN("a", RDN(CN("a")), ByteString(), Success),
WITH_SAN("a", RDN(CN("b")), ByteString(), Result::ERROR_BAD_CERT_DOMAIN),
// http://tools.ietf.org/html/rfc5280#section-4.1.2.6 says "If subject naming // information is present only in the subjectAltName extension (e.g., a key // bound only to an email address or URI), then the subject name MUST be an // empty sequence and the subjectAltName extension MUST be critical." So, we // have to support an empty subject. We don't enforce that the SAN must be // critical or even that there is a SAN when the subject is empty, though.
WITH_SAN("a", ByteString(), DNSName("a"), Success), // Make sure we return ERROR_BAD_CERT_DOMAIN and not ERROR_BAD_DER.
WITHOUT_SAN("a", ByteString(), Result::ERROR_BAD_CERT_DOMAIN),
// Two CNs in the same RDN, both match.
WITHOUT_SAN("a", RDN(CN("a") + CN("a")), Success), // Two CNs in the same RDN, both DNSNames, first one matches.
WITHOUT_SAN("a", RDN(CN("a") + CN("b")),
Result::ERROR_BAD_CERT_DOMAIN), // Two CNs in the same RDN, both DNSNames, last one matches.
WITHOUT_SAN("b", RDN(CN("a") + CN("b")), Success), // Two CNs in the same RDN, first one matches, second isn't a DNSName.
WITHOUT_SAN("a", RDN(CN("a") + CN("Not a DNSName")),
Result::ERROR_BAD_CERT_DOMAIN), // Two CNs in the same RDN, first one not a DNSName, second matches.
WITHOUT_SAN("b", RDN(CN("Not a DNSName") + CN("b")), Success),
// Two CNs in separate RDNs, both match.
WITHOUT_SAN("a", RDN(CN("a")) + RDN(CN("a")), Success), // Two CNs in separate RDNs, both DNSNames, first one matches.
WITHOUT_SAN("a", RDN(CN("a")) + RDN(CN("b")),
Result::ERROR_BAD_CERT_DOMAIN), // Two CNs in separate RDNs, both DNSNames, last one matches.
WITHOUT_SAN("b", RDN(CN("a")) + RDN(CN("b")), Success), // Two CNs in separate RDNs, first one matches, second isn't a DNSName.
WITHOUT_SAN("a", RDN(CN("a")) + RDN(CN("Not a DNSName")),
Result::ERROR_BAD_CERT_DOMAIN), // Two CNs in separate RDNs, first one not a DNSName, second matches.
WITHOUT_SAN("b", RDN(CN("Not a DNSName")) + RDN(CN("b")), Success),
// One CN, one RDN, CN is the first AVA in the RDN, CN matches.
WITHOUT_SAN("a", RDN(CN("a") + OU("b")), Success), // One CN, one RDN, CN is the first AVA in the RDN, CN does not match.
WITHOUT_SAN("b", RDN(CN("a") + OU("b")),
Result::ERROR_BAD_CERT_DOMAIN), // One CN, one RDN, CN is not the first AVA in the RDN, CN matches.
WITHOUT_SAN("b", RDN(OU("a") + CN("b")), Success), // One CN, one RDN, CN is not the first AVA in the RDN, CN does not match.
WITHOUT_SAN("a", RDN(OU("a") + CN("b")),
Result::ERROR_BAD_CERT_DOMAIN),
// One CN, multiple RDNs, CN is in the first RDN, CN matches.
WITHOUT_SAN("a", RDN(CN("a")) + RDN(OU("b")), Success), // One CN, multiple RDNs, CN is in the first RDN, CN does not match.
WITHOUT_SAN("b", RDN(CN("a")) + RDN(OU("b")), Result::ERROR_BAD_CERT_DOMAIN), // One CN, multiple RDNs, CN is not in the first RDN, CN matches.
WITHOUT_SAN("b", RDN(OU("a")) + RDN(CN("b")), Success), // One CN, multiple RDNs, CN is not in the first RDN, CN does not match.
WITHOUT_SAN("a", RDN(OU("a")) + RDN(CN("b")), Result::ERROR_BAD_CERT_DOMAIN),
// One CN, one RDN, CN is not in the first or last AVA, CN matches.
WITHOUT_SAN("b", RDN(OU("a") + CN("b") + OU("c")), Success), // One CN, multiple RDNs, CN is not in the first or last RDN, CN matches.
WITHOUT_SAN("b", RDN(OU("a")) + RDN(CN("b")) + RDN(OU("c")), Success),
// Empty CN does not match.
WITHOUT_SAN("example.com", RDN(CN("")), Result::ERROR_BAD_CERT_DOMAIN),
// Do not match a DNSName that is encoded in a malformed IPAddress.
WITH_SAN("example.com", RDN(CN("foo")), IPAddress(example_com),
Result::ERROR_BAD_CERT_DOMAIN),
// We skip over the malformed IPAddress and match the DNSName entry because // we've heard reports of real-world certificates that have malformed // IPAddress SANs.
WITH_SAN("example.org", RDN(CN("foo")),
IPAddress(example_com) + DNSName("example.org"), Success),
// Match a matching IPv4 address SAN entry.
WITH_SAN(ipv4_addr_str, RDN(CN("foo")), IPAddress(ipv4_addr_bytes),
Success), // Match a matching IPv4 addresses in the CN when there is no SAN
WITHOUT_SAN(ipv4_addr_str, RDN(CN(ipv4_addr_str)), Success), // Do not match a matching IPv4 address in the CN when there is a SAN with // a DNSName entry.
WITH_SAN(ipv4_addr_str, RDN(CN(ipv4_addr_str)),
DNSName("example.com"), Result::ERROR_BAD_CERT_DOMAIN), // Do not match a matching IPv4 address in the CN when there is a SAN with // a non-matching IPAddress entry.
WITH_SAN(ipv4_addr_str, RDN(CN(ipv4_addr_str)),
IPAddress(ipv6_addr_bytes), Result::ERROR_BAD_CERT_DOMAIN), // Match a matching IPv4 address in the CN when there is a SAN with a // non-IPAddress, non-DNSName entry.
WITH_SAN(ipv4_addr_str, RDN(CN(ipv4_addr_str)),
RFC822Name("foo@example.com"), Success), // Do not match a matching IPv4 address in the CN when there is a SAN with a // malformed IPAddress entry.
WITH_SAN(ipv4_addr_str, RDN(CN(ipv4_addr_str)),
IPAddress(example_com), Result::ERROR_BAD_CERT_DOMAIN), // Do not match a matching IPv4 address in the CN when there is a SAN with a // malformed DNSName entry.
WITH_SAN(ipv4_addr_str, RDN(CN(ipv4_addr_str)),
DNSName("!"), Result::ERROR_BAD_CERT_DOMAIN),
// We don't match IPv6 addresses in the CN, regardless of whether there is // a SAN.
WITHOUT_SAN(ipv6_addr_str, RDN(CN(ipv6_addr_str)),
Result::ERROR_BAD_CERT_DOMAIN),
WITH_SAN(ipv6_addr_str, RDN(CN(ipv6_addr_str)),
DNSName("example.com"), Result::ERROR_BAD_CERT_DOMAIN),
WITH_SAN(ipv6_addr_str, RDN(CN(ipv6_addr_str)),
IPAddress(ipv6_addr_bytes), Success),
WITH_SAN(ipv6_addr_str, RDN(CN("foo")), IPAddress(ipv6_addr_bytes),
Success),
// We don't match the binary encoding of the bytes of IP addresses in the // CN.
WITHOUT_SAN(ipv4_addr_str, RDN(CN(ipv4_addr_bytes_as_str)),
Result::ERROR_BAD_CERT_DOMAIN),
WITHOUT_SAN(ipv6_addr_str, RDN(CN(ipv6_addr_bytes_as_str)),
Result::ERROR_BAD_CERT_DOMAIN),
// We don't match IP addresses with DNSName SANs.
WITH_SAN(ipv4_addr_str, RDN(CN("foo")),
DNSName(ipv4_addr_bytes_as_str), Result::ERROR_BAD_CERT_DOMAIN),
WITH_SAN(ipv4_addr_str, RDN(CN("foo")), DNSName(ipv4_addr_str),
Result::ERROR_BAD_CERT_DOMAIN),
WITH_SAN(ipv6_addr_str, RDN(CN("foo")),
DNSName(ipv6_addr_bytes_as_str), Result::ERROR_BAD_CERT_DOMAIN),
WITH_SAN(ipv6_addr_str, RDN(CN("foo")), DNSName(ipv6_addr_str),
Result::ERROR_BAD_CERT_DOMAIN),
// Do not match an IPv4 reference ID against the equivalent IPv4-compatible // IPv6 SAN entry.
WITH_SAN(ipv4_addr_str, RDN(CN("foo")),
IPAddress(ipv4_compatible_ipv6_addr_bytes),
Result::ERROR_BAD_CERT_DOMAIN), // Do not match an IPv4 reference ID against the equivalent IPv4-mapped IPv6 // SAN entry.
WITH_SAN(ipv4_addr_str, RDN(CN("foo")),
IPAddress(ipv4_mapped_ipv6_addr_bytes),
Result::ERROR_BAD_CERT_DOMAIN), // Do not match an IPv4-compatible IPv6 reference ID against the equivalent // IPv4 SAN entry.
WITH_SAN(ipv4_compatible_ipv6_addr_str, RDN(CN("foo")),
IPAddress(ipv4_addr_bytes), Result::ERROR_BAD_CERT_DOMAIN), // Do not match an IPv4 reference ID against the equivalent IPv4-mapped IPv6 // SAN entry.
WITH_SAN(ipv4_mapped_ipv6_addr_str, RDN(CN("foo")),
IPAddress(ipv4_addr_bytes),
Result::ERROR_BAD_CERT_DOMAIN),
// Test that the presence of an otherName entry is handled appropriately. // (The actual value of the otherName entry isn't important - that's not what // we're testing here.)
WITH_SAN("example.com", ByteString(), // The tag for otherName is CONTEXT_SPECIFIC | CONSTRUCTED | 0
TLV((2 << 6) | (1 << 5) | 0, ByteString()) + DNSName("example.com"),
Success),
WITH_SAN("example.com", ByteString(),
TLV((2 << 6) | (1 << 5) | 0, ByteString()),
Result::ERROR_BAD_CERT_DOMAIN),
};
ByteString extensions[2]; if (subjectAltName != NO_SAN) {
extensions[0] = CreateEncodedSubjectAltName(subjectAltName);
EXPECT_FALSE(ENCODING_FAILED(extensions[0]));
} if (endEntityOrCA == EndEntityOrCA::MustBeCA) { // Currently, these tests assume that if we're creating a CA certificate, it // will not have a subjectAlternativeName extension. If that assumption // changes, this code will have to be updated. Ideally this would be // ASSERT_EQ, but that inserts a 'return;', which doesn't match this // function's return type.
EXPECT_EQ(subjectAltName, NO_SAN);
extensions[0] = CreateEncodedBasicConstraints(true, nullptr,
Critical::Yes);
EXPECT_FALSE(ENCODING_FAILED(extensions[0]));
}
TEST_F(pkixnames_CheckCertHostname, SANWithoutSequence)
{ // A certificate with a truly empty SAN extension (one that doesn't even // contain a SEQUENCE at all) is malformed. If we didn't treat this as // malformed then we'd have to treat it like the CN_EmptySAN cases.
// The default name matching policy halts on invalid SAN entries.
ASSERT_EQ(Result::ERROR_BAD_DER,
CheckCertHostname(certInput, hostnameInput, mNameMatchingPolicy));
SkipInvalidSubjectAlternativeNamesNameMatchingPolicy nameMatchingPolicy; // A policy that skips invalid SAN entries should result in a domain mismatch // error.
ASSERT_EQ(Result::ERROR_BAD_CERT_DOMAIN,
CheckCertHostname(certInput, hostnameInput, nameMatchingPolicy));
}
class pkixnames_CheckCertHostname_PresentedMatchesReference
: public ::testing::Test
, public ::testing::WithParamInterface<PresentedMatchesReference>
{ public:
DefaultNameMatchingPolicy mNameMatchingPolicy;
};
TEST_P(pkixnames_CheckCertHostname_PresentedMatchesReference, CN_NoSAN)
{ // Since there is no SAN, a valid presented DNS ID in the subject CN field // should result in a match.
TEST_P(pkixnames_CheckCertHostname_PresentedMatchesReference,
SubjectAltName_CNNotDNSName)
{ // A DNSName SAN entry should match, regardless of the contents of the // subject CN.
TEST_P(pkixnames_Turkish_I_Comparison, CheckCertHostname_CN_NoSAN)
{ // Make sure we don't have the similar problems that strcasecmp and others // have with the other kinds of "i" and "I" commonly used in Turkish locales, // when we're matching a CN due to lack of subjectAltName.
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.