OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "net/cert/internal/verify_name_match.h" | 5 #include "net/cert/internal/verify_name_match.h" |
6 | 6 |
| 7 #include "base/base_paths.h" |
| 8 #include "base/files/file_path.h" |
| 9 #include "base/files/file_util.h" |
| 10 #include "base/path_service.h" |
| 11 #include "base/strings/string_number_conversions.h" |
| 12 #include "base/strings/string_util.h" |
7 #include "net/der/input.h" | 13 #include "net/der/input.h" |
8 #include "testing/gtest/include/gtest/gtest.h" | 14 #include "testing/gtest/include/gtest/gtest.h" |
9 | 15 |
10 namespace net { | 16 namespace net { |
11 | 17 namespace { |
12 TEST(VerifyNameMatchTest, Simple) { | 18 |
13 // TODO(mattm): Use valid Names. | 19 der::Input InputFromString(const std::string& s) { |
14 const uint8_t hello[] = {'h', 'e', 'l', 'l', 'o'}; | 20 return der::Input(reinterpret_cast<const uint8_t*>(s.data()), s.size()); |
15 const uint8_t aello[] = {'a', 'e', 'l', 'l', 'o'}; | 21 } |
16 const uint8_t hello1[] = {'h', 'e', 'l', 'l', 'o', '1'}; | 22 |
17 EXPECT_TRUE(VerifyNameMatch(der::Input(hello), der::Input(hello))); | 23 std::string LoadTestData(const std::string& prefix, |
18 EXPECT_FALSE(VerifyNameMatch(der::Input(aello), der::Input(hello))); | 24 const std::string& value_type, |
19 EXPECT_FALSE(VerifyNameMatch(der::Input(hello), der::Input(hello1))); | 25 const std::string& suffix) { |
20 EXPECT_FALSE(VerifyNameMatch(der::Input(hello1), der::Input(hello))); | 26 base::FilePath src_root; |
| 27 PathService::Get(base::DIR_SOURCE_ROOT, &src_root); |
| 28 std::string filename = prefix + "-" + value_type + "-" + suffix + ".der"; |
| 29 base::FilePath filepath = |
| 30 src_root.Append("net/data/verify_name_match_unittest/names") |
| 31 .Append(filename); |
| 32 std::string result; |
| 33 bool success = base::ReadFileToString(filepath, &result); |
| 34 EXPECT_TRUE(success); |
| 35 return result; |
| 36 } |
| 37 |
| 38 // All string types. |
| 39 static const char* kValueTypes[] = {"PRINTABLESTRING", |
| 40 "T61STRING", |
| 41 "UTF8", |
| 42 "BMPSTRING", |
| 43 "UNIVERSALSTRING"}; |
| 44 // String types that can encode the Unicode Basic Multilingual Plane. |
| 45 static const char* kUnicodeBMPValueTypes[] = {"UTF8", |
| 46 "BMPSTRING", |
| 47 "UNIVERSALSTRING"}; |
| 48 // String types that can encode the Unicode Supplementary Planes. |
| 49 static const char* kUnicodeSupplementaryValueTypes[] = {"UTF8", |
| 50 "UNIVERSALSTRING"}; |
| 51 |
| 52 static const char* kMangleTypes[] = {"unmangled", |
| 53 "case_swap", |
| 54 "extra_whitespace"}; |
| 55 |
| 56 } // namespace |
| 57 |
| 58 class VerifyNameMatchSimpleTest |
| 59 : public ::testing::TestWithParam< |
| 60 ::testing::tuple<const char*, const char*>> { |
| 61 public: |
| 62 std::string value_type() const { return ::testing::get<0>(GetParam()); } |
| 63 std::string suffix() const { return ::testing::get<1>(GetParam()); } |
| 64 }; |
| 65 |
| 66 // Compare each input against itself, verifies that all input data is parsed |
| 67 // successfully. |
| 68 TEST_P(VerifyNameMatchSimpleTest, ExactEquality) { |
| 69 std::string der = LoadTestData("ascii", value_type(), suffix()); |
| 70 EXPECT_TRUE(VerifyNameMatch(InputFromString(der), InputFromString(der))); |
| 71 |
| 72 std::string der_extra_attr = |
| 73 LoadTestData("ascii", value_type(), suffix() + "-extra_attr"); |
| 74 EXPECT_TRUE(VerifyNameMatch(InputFromString(der_extra_attr), |
| 75 InputFromString(der_extra_attr))); |
| 76 |
| 77 std::string der_extra_rdn = |
| 78 LoadTestData("ascii", value_type(), suffix() + "-extra_rdn"); |
| 79 EXPECT_TRUE(VerifyNameMatch(InputFromString(der_extra_rdn), |
| 80 InputFromString(der_extra_rdn))); |
| 81 } |
| 82 |
| 83 // Ensure that a Name does not match another Name which is exactly the same but |
| 84 // with an extra attribute in one Relative Distinguished Name. |
| 85 TEST_P(VerifyNameMatchSimpleTest, ExtraAttrDoesNotMatch) { |
| 86 std::string der = LoadTestData("ascii", value_type(), suffix()); |
| 87 std::string der_extra_attr = |
| 88 LoadTestData("ascii", value_type(), suffix() + "-extra_attr"); |
| 89 EXPECT_FALSE( |
| 90 VerifyNameMatch(InputFromString(der), InputFromString(der_extra_attr))); |
| 91 EXPECT_FALSE( |
| 92 VerifyNameMatch(InputFromString(der_extra_attr), InputFromString(der))); |
| 93 } |
| 94 |
| 95 // Ensure that a Name does not match another Name which is exactly the same but |
| 96 // with an extra Relative Distinguished Name. |
| 97 TEST_P(VerifyNameMatchSimpleTest, ExtraRdnDoesNotMatch) { |
| 98 std::string der = LoadTestData("ascii", value_type(), suffix()); |
| 99 std::string der_extra_rdn = |
| 100 LoadTestData("ascii", value_type(), suffix() + "-extra_rdn"); |
| 101 EXPECT_FALSE( |
| 102 VerifyNameMatch(InputFromString(der), InputFromString(der_extra_rdn))); |
| 103 EXPECT_FALSE( |
| 104 VerifyNameMatch(InputFromString(der_extra_rdn), InputFromString(der))); |
| 105 } |
| 106 |
| 107 INSTANTIATE_TEST_CASE_P(InstantiationName, |
| 108 VerifyNameMatchSimpleTest, |
| 109 ::testing::Combine(::testing::ValuesIn(kValueTypes), |
| 110 ::testing::ValuesIn(kMangleTypes))); |
| 111 |
| 112 class VerifyNameMatchNormalizationTest |
| 113 : public ::testing::TestWithParam<::testing::tuple<bool, const char*>> { |
| 114 public: |
| 115 bool expected_result() const { return ::testing::get<0>(GetParam()); } |
| 116 std::string value_type() const { return ::testing::get<1>(GetParam()); } |
| 117 }; |
| 118 |
| 119 // Verify matching is case insensitive (for the types which currently support |
| 120 // normalization). |
| 121 TEST_P(VerifyNameMatchNormalizationTest, CaseInsensitivity) { |
| 122 std::string normal = LoadTestData("ascii", value_type(), "unmangled"); |
| 123 std::string case_swap = LoadTestData("ascii", value_type(), "case_swap"); |
| 124 EXPECT_EQ(expected_result(), VerifyNameMatch(InputFromString(normal), |
| 125 InputFromString(case_swap))); |
| 126 EXPECT_EQ(expected_result(), VerifyNameMatch(InputFromString(case_swap), |
| 127 InputFromString(normal))); |
| 128 } |
| 129 |
| 130 // Verify matching folds whitespace (for the types which currently support |
| 131 // normalization). |
| 132 TEST_P(VerifyNameMatchNormalizationTest, CollapseWhitespace) { |
| 133 std::string normal = LoadTestData("ascii", value_type(), "unmangled"); |
| 134 std::string whitespace = |
| 135 LoadTestData("ascii", value_type(), "extra_whitespace"); |
| 136 EXPECT_EQ(expected_result(), VerifyNameMatch(InputFromString(normal), |
| 137 InputFromString(whitespace))); |
| 138 EXPECT_EQ(expected_result(), VerifyNameMatch(InputFromString(whitespace), |
| 139 InputFromString(normal))); |
| 140 } |
| 141 |
| 142 INSTANTIATE_TEST_CASE_P( |
| 143 InstantiationName, |
| 144 VerifyNameMatchNormalizationTest, |
| 145 ::testing::Values(::testing::make_tuple(true, "PRINTABLESTRING"), |
| 146 ::testing::make_tuple(false, "T61STRING"), |
| 147 ::testing::make_tuple(true, "UTF8"), |
| 148 ::testing::make_tuple(true, "BMPSTRING"), |
| 149 ::testing::make_tuple(true, "UNIVERSALSTRING"))); |
| 150 |
| 151 class VerifyNameMatchDifferingTypesTest |
| 152 : public ::testing::TestWithParam< |
| 153 ::testing::tuple<const char*, const char*>> { |
| 154 public: |
| 155 std::string value_type_1() const { return ::testing::get<0>(GetParam()); } |
| 156 std::string value_type_2() const { return ::testing::get<1>(GetParam()); } |
| 157 }; |
| 158 |
| 159 TEST_P(VerifyNameMatchDifferingTypesTest, NormalizableTypesAreEqual) { |
| 160 std::string der_1 = LoadTestData("ascii", value_type_1(), "unmangled"); |
| 161 std::string der_2 = LoadTestData("ascii", value_type_2(), "unmangled"); |
| 162 if (value_type_1() == value_type_2()) { |
| 163 EXPECT_TRUE( |
| 164 VerifyNameMatch(InputFromString(der_1), InputFromString(der_2))); |
| 165 } else if ((value_type_1() == "PRINTABLESTRING" || value_type_1() == "UTF8" || |
| 166 value_type_1() == "BMPSTRING" || |
| 167 value_type_1() == "UNIVERSALSTRING") && |
| 168 (value_type_2() == "PRINTABLESTRING" || value_type_2() == "UTF8" || |
| 169 value_type_2() == "BMPSTRING" || |
| 170 value_type_2() == "UNIVERSALSTRING")) { |
| 171 EXPECT_TRUE( |
| 172 VerifyNameMatch(InputFromString(der_1), InputFromString(der_2))); |
| 173 } else { |
| 174 EXPECT_FALSE( |
| 175 VerifyNameMatch(InputFromString(der_1), InputFromString(der_2))); |
| 176 } |
| 177 } |
| 178 |
| 179 INSTANTIATE_TEST_CASE_P(InstantiationName, |
| 180 VerifyNameMatchDifferingTypesTest, |
| 181 ::testing::Combine(::testing::ValuesIn(kValueTypes), |
| 182 ::testing::ValuesIn(kValueTypes))); |
| 183 |
| 184 class VerifyNameMatchUnicodeConversionTest |
| 185 : public ::testing::TestWithParam< |
| 186 ::testing::tuple<const char*, |
| 187 ::testing::tuple<const char*, const char*>>> { |
| 188 public: |
| 189 std::string prefix() const { return ::testing::get<0>(GetParam()); } |
| 190 std::string value_type_1() const { |
| 191 return ::testing::get<0>(::testing::get<1>(GetParam())); |
| 192 } |
| 193 std::string value_type_2() const { |
| 194 return ::testing::get<1>(::testing::get<1>(GetParam())); |
| 195 } |
| 196 }; |
| 197 |
| 198 TEST_P(VerifyNameMatchUnicodeConversionTest, UnicodeConversionsAreEqual) { |
| 199 std::string der_1 = LoadTestData(prefix(), value_type_1(), "unmangled"); |
| 200 std::string der_2 = LoadTestData(prefix(), value_type_2(), "unmangled"); |
| 201 EXPECT_TRUE(VerifyNameMatch(InputFromString(der_1), InputFromString(der_2))); |
| 202 } |
| 203 |
| 204 INSTANTIATE_TEST_CASE_P( |
| 205 BMPConversion, |
| 206 VerifyNameMatchUnicodeConversionTest, |
| 207 ::testing::Combine( |
| 208 ::testing::Values("unicode_bmp"), |
| 209 ::testing::Combine(::testing::ValuesIn(kUnicodeBMPValueTypes), |
| 210 ::testing::ValuesIn(kUnicodeBMPValueTypes)))); |
| 211 |
| 212 INSTANTIATE_TEST_CASE_P( |
| 213 SMPConversion, |
| 214 VerifyNameMatchUnicodeConversionTest, |
| 215 ::testing::Combine( |
| 216 ::testing::Values("unicode_supplementary"), |
| 217 ::testing::Combine( |
| 218 ::testing::ValuesIn(kUnicodeSupplementaryValueTypes), |
| 219 ::testing::ValuesIn(kUnicodeSupplementaryValueTypes)))); |
| 220 |
| 221 // Matching should fail if a PrintableString contains invalid characters. |
| 222 TEST(VerifyNameMatchInvalidDataTest, FailOnInvalidPrintableStringChars) { |
| 223 std::string normal = LoadTestData("ascii", "PRINTABLESTRING", "unmangled"); |
| 224 // Find a known location inside a PrintableString in the DER-encoded data. |
| 225 size_t replace_location = normal.find("0123456789"); |
| 226 ASSERT_NE(std::string::npos, replace_location); |
| 227 for (int c = 0; c < 256; ++c) { |
| 228 SCOPED_TRACE(base::IntToString(c)); |
| 229 if (IsAsciiAlpha(c) || IsAsciiDigit(c)) |
| 230 continue; |
| 231 switch (c) { |
| 232 case ' ': |
| 233 case '\'': |
| 234 case '(': |
| 235 case ')': |
| 236 case '*': |
| 237 case '+': |
| 238 case ',': |
| 239 case '-': |
| 240 case '.': |
| 241 case '/': |
| 242 case ':': |
| 243 case '=': |
| 244 case '?': |
| 245 continue; |
| 246 } |
| 247 std::string invalid = normal.replace(replace_location, 1, 1, c); |
| 248 // Verification should fail due to the invalid character. |
| 249 EXPECT_FALSE( |
| 250 VerifyNameMatch(InputFromString(invalid), InputFromString(invalid))); |
| 251 } |
| 252 } |
| 253 |
| 254 TEST(VerifyNameMatchInvalidDataTest, FailOnAttributeTypeAndValueExtraData) { |
| 255 std::string invalid = |
| 256 LoadTestData("invalid", "AttributeTypeAndValue", "extradata"); |
| 257 // Verification should fail due to extra element in AttributeTypeAndValue |
| 258 // sequence. |
| 259 EXPECT_FALSE( |
| 260 VerifyNameMatch(InputFromString(invalid), InputFromString(invalid))); |
| 261 } |
| 262 |
| 263 TEST(VerifyNameMatchInvalidDataTest, FailOnAttributeTypeAndValueShort) { |
| 264 std::string invalid = |
| 265 LoadTestData("invalid", "AttributeTypeAndValue", "onlyOneElement"); |
| 266 // Verification should fail due to AttributeTypeAndValue sequence having only |
| 267 // one element. |
| 268 EXPECT_FALSE( |
| 269 VerifyNameMatch(InputFromString(invalid), InputFromString(invalid))); |
| 270 } |
| 271 |
| 272 TEST(VerifyNameMatchInvalidDataTest, FailOnAttributeTypeAndValueEmpty) { |
| 273 std::string invalid = |
| 274 LoadTestData("invalid", "AttributeTypeAndValue", "empty"); |
| 275 // Verification should fail due to empty AttributeTypeAndValue sequence. |
| 276 EXPECT_FALSE( |
| 277 VerifyNameMatch(InputFromString(invalid), InputFromString(invalid))); |
| 278 } |
| 279 |
| 280 TEST(VerifyNameMatchInvalidDataTest, FailOnBadAttributeType) { |
| 281 std::string invalid = |
| 282 LoadTestData("invalid", "AttributeTypeAndValue", "badAttributeType"); |
| 283 // Verification should fail due to Attribute Type not being an OID. |
| 284 EXPECT_FALSE( |
| 285 VerifyNameMatch(InputFromString(invalid), InputFromString(invalid))); |
| 286 } |
| 287 |
| 288 TEST(VerifyNameMatchInvalidDataTest, FailOnAttributeTypeAndValueNotSequence) { |
| 289 std::string invalid = |
| 290 LoadTestData("invalid", "AttributeTypeAndValue", "setNotSequence"); |
| 291 // Verification should fail due to AttributeTypeAndValue being a Set instead |
| 292 // of a Sequence. |
| 293 EXPECT_FALSE( |
| 294 VerifyNameMatch(InputFromString(invalid), InputFromString(invalid))); |
| 295 } |
| 296 |
| 297 TEST(VerifyNameMatchInvalidDataTest, FailOnRdnNotSet) { |
| 298 std::string invalid = LoadTestData("invalid", "RDN", "sequenceInsteadOfSet"); |
| 299 // Verification should fail due to RDN being a Sequence instead of a Set. |
| 300 EXPECT_FALSE( |
| 301 VerifyNameMatch(InputFromString(invalid), InputFromString(invalid))); |
| 302 } |
| 303 |
| 304 TEST(VerifyNameMatchInvalidDataTest, FailOnEmptyRdn) { |
| 305 std::string invalid = LoadTestData("invalid", "RDN", "empty"); |
| 306 // Verification should fail due to RDN having zero AttributeTypeAndValues. |
| 307 EXPECT_FALSE( |
| 308 VerifyNameMatch(InputFromString(invalid), InputFromString(invalid))); |
| 309 } |
| 310 |
| 311 TEST(VerifyNameMatchInvalidDataTest, FailOnNameNotSequence) { |
| 312 std::string invalid = LoadTestData("invalid", "Name", "setInsteadOfSequence"); |
| 313 // Verification should fail due to Name being a Set instead of a Sequence. |
| 314 EXPECT_FALSE( |
| 315 VerifyNameMatch(InputFromString(invalid), InputFromString(invalid))); |
| 316 } |
| 317 |
| 318 TEST(VerifyNameMatchInvalidDataTest, FailOnEmptyName) { |
| 319 std::string invalid = LoadTestData("invalid", "Name", "empty"); |
| 320 // Verification should fail due to Name having zero RDNs. |
| 321 EXPECT_FALSE( |
| 322 VerifyNameMatch(InputFromString(invalid), InputFromString(invalid))); |
| 323 } |
| 324 |
| 325 TEST(VerifyNameMatchInvalidDataTest, FailOnTrailingData) { |
| 326 std::string invalid = |
| 327 LoadTestData("ascii", "PRINTABLESTRING", "unmangled") + "X"; |
| 328 // Verification should fail due to the appended character. |
| 329 EXPECT_FALSE( |
| 330 VerifyNameMatch(InputFromString(invalid), InputFromString(invalid))); |
| 331 } |
| 332 |
| 333 // Matching should fail if a BMPString contains surrogates. |
| 334 TEST(VerifyNameMatchInvalidDataTest, FailOnBmpStringSurrogates) { |
| 335 std::string normal = LoadTestData("unicode_bmp", "BMPSTRING", "unmangled"); |
| 336 // Find a known location inside a BMPSTRING in the DER-encoded data. |
| 337 size_t replace_location = normal.find("\x67\x71\x4e\xac"); |
| 338 ASSERT_NE(std::string::npos, replace_location); |
| 339 // Replace with U+1D400 MATHEMATICAL BOLD CAPITAL A, which requires surrogates |
| 340 // to represent. |
| 341 std::string invalid = normal.replace(replace_location, 4, "\xd8\x35\xdc\x00"); |
| 342 // Verification should fail due to the invalid codepoints. |
| 343 EXPECT_FALSE( |
| 344 VerifyNameMatch(InputFromString(invalid), InputFromString(invalid))); |
21 } | 345 } |
22 | 346 |
23 } // namespace net | 347 } // namespace net |
OLD | NEW |