Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(129)

Side by Side Diff: net/cert/internal/parse_certificate_unittest.cc

Issue 1279963003: Add a function for parsing RFC 5280's "TBSCertificate". (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@cert_mapper
Patch Set: Fully move expectations to test data Created 5 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "net/cert/internal/parse_certificate.h"
6
7 #include "base/base64.h"
8 #include "base/base_paths.h"
9 #include "base/files/file_util.h"
10 #include "base/path_service.h"
11 #include "net/base/test_data_directory.h"
12 #include "net/cert/pem_tokenizer.h"
13 #include "net/der/input.h"
14 #include "testing/gtest/include/gtest/gtest.h"
15
16 namespace net {
17
18 namespace der {
19
20 // These functions are used by GTEST to support EXPECT_EQ() for
davidben 2015/08/11 20:31:57 Nit: I think GTest is the more common capitalizati
eroman 2015/08/12 00:37:10 Done.
21 // der::Input.
22
23 void PrintTo(const Input& data, ::std::ostream* os) {
davidben 2015/08/11 20:31:57 #include <ostream>
eroman 2015/08/12 00:37:10 Done.
24 std::string b64;
25 base::Base64Encode(
26 base::StringPiece(reinterpret_cast<const char*>(data.UnsafeData()),
27 data.Length()),
28 &b64);
29
30 *os << "[" << b64 << "]";
31 }
32
33 bool operator==(const Input& a, const Input& b) {
34 return a.Equals(b);
35 }
36
37 } // namespace der
38
39 namespace {
40
41 // Returns a path to the file |file_name| within the unittest data directory.
42 base::FilePath GetTestFilePath(const std::string& file_name) {
43 base::FilePath src_root;
44 PathService::Get(base::DIR_SOURCE_ROOT, &src_root);
45 return src_root.Append(
46 FILE_PATH_LITERAL("net/data/parse_certificate_unittest"))
47 .AppendASCII(file_name);
48 }
49
50 // Contains the expected values in a ParsedCertificate.
51 struct ParsedCertificateExpectations {
52 std::string signature;
53 std::string signature_algorithm;
54 };
55
56 // Contains the expected values in a ParsedTbsCertificate.
57 struct ParsedTbsCertificateExpectations {
58 std::string serial_number;
59 std::string signature_algorithm;
60 std::string issuer;
61 std::string validity;
62 std::string subject;
63 std::string spki;
64 std::string extensions;
65 };
66
67 // Reads a test file |file_name| which contains both a certificate
68 // (CERTIFICATE), and all the expected values for the parsed files (like SPKI).
69 // On success fills |cert| with the cert DER data, and fills
70 // |cert_expectations| and |tbs_expectations| with the expected values for its
71 // consitituent fields.
72 ::testing::AssertionResult LoadTestCertificatePemData(
73 const std::string& file_name,
74 std::string* cert,
75 ParsedCertificateExpectations* cert_expectations,
76 ParsedTbsCertificateExpectations* tbs_expectations) {
77 base::FilePath path = GetTestFilePath(file_name);
78 std::string file_data;
79 if (!base::ReadFileToString(path, &file_data)) {
80 return ::testing::AssertionFailure() << "Couldn't read file: "
81 << path.value();
82 }
83
84 const char kCertificateBlock[] = "CERTIFICATE";
85 const char kSignatureBlock[] = "SIGNATURE";
86 const char kSignatureAlgorithmBlock[] = "SIGNATURE ALGORITHM";
87 const char kSerialNumber[] = "SERIAL NUMBER";
88 const char kIssuerBlock[] = "ISSUER";
89 const char kValidityBlock[] = "VALIDITY";
90 const char kSubjectBlock[] = "SUBJECT";
91 const char kSpkiBlock[] = "SPKI";
92 const char kExtensionsBlock[] = "EXTENSIONS";
93
94 std::vector<std::string> pem_headers;
95 pem_headers.push_back(kCertificateBlock);
96 pem_headers.push_back(kSignatureBlock);
97 pem_headers.push_back(kSignatureAlgorithmBlock);
98 pem_headers.push_back(kSerialNumber);
99 pem_headers.push_back(kIssuerBlock);
100 pem_headers.push_back(kValidityBlock);
101 pem_headers.push_back(kSubjectBlock);
102 pem_headers.push_back(kSpkiBlock);
103 pem_headers.push_back(kExtensionsBlock);
104
105 PEMTokenizer pem_tokenizer(file_data, pem_headers);
106 while (pem_tokenizer.GetNext()) {
107 const std::string& block_type = pem_tokenizer.block_type();
108 if (block_type == kCertificateBlock) {
109 cert->assign(pem_tokenizer.data());
110 } else if (block_type == kSignatureBlock) {
111 cert_expectations->signature.assign(pem_tokenizer.data());
112 } else if (block_type == kSignatureAlgorithmBlock) {
113 cert_expectations->signature_algorithm.assign(pem_tokenizer.data());
114 tbs_expectations->signature_algorithm.assign(pem_tokenizer.data());
115 } else if (block_type == kSerialNumber) {
116 tbs_expectations->serial_number.assign(pem_tokenizer.data());
117 } else if (block_type == kIssuerBlock) {
118 tbs_expectations->issuer.assign(pem_tokenizer.data());
119 } else if (block_type == kValidityBlock) {
120 tbs_expectations->validity.assign(pem_tokenizer.data());
121 } else if (block_type == kSubjectBlock) {
122 tbs_expectations->subject.assign(pem_tokenizer.data());
123 } else if (block_type == kSpkiBlock) {
124 tbs_expectations->spki.assign(pem_tokenizer.data());
125 } else if (block_type == kExtensionsBlock) {
126 tbs_expectations->extensions.assign(pem_tokenizer.data());
127 }
128 }
129
130 return ::testing::AssertionSuccess();
131 }
132
133 // TODO(eroman): This function was copied from verify_signed_data_unittest.cc
134 //
135 // Creates a der::Input from an std::string. The lifetimes are a bit subtle
136 // when using this function:
137 //
138 // The returned der::Input() is only valid so long as the input string is alive
139 // and is not mutated.
140 //
141 // Note that the input parameter has been made a pointer to prevent callers
142 // from accidentally passing an r-value.
143 der::Input InputFromString(const std::string* s) {
144 return der::Input(reinterpret_cast<const uint8_t*>(s->data()), s->size());
145 }
146
147 // Verifies that the given |cert| matches |expectations|.
148 void EnsureParsedCertificate(
149 const ParsedCertificate& cert,
150 const ParsedCertificateExpectations& expectations) {
151 EXPECT_EQ(0, cert.signature_value.unused_bits());
152 EXPECT_EQ(InputFromString(&expectations.signature),
153 cert.signature_value.bytes());
154 EXPECT_EQ(InputFromString(&expectations.signature_algorithm),
155 cert.signature_algorithm_tlv);
156 }
157
158 // Verifies that the given |tbs| matches |expectations|.
159 void EnsureParsedTbsCertificate(
160 const ParsedTbsCertificate& tbs,
161 const ParsedTbsCertificateExpectations& expectations,
162 CertificateVersion expected_version) {
163 EXPECT_EQ(expected_version, tbs.version);
164
165 EXPECT_EQ(InputFromString(&expectations.serial_number), tbs.serial_number);
166 EXPECT_EQ(InputFromString(&expectations.signature_algorithm),
167 tbs.signature_algorithm_tlv);
168
169 EXPECT_EQ(InputFromString(&expectations.issuer), tbs.issuer_tlv);
170 EXPECT_EQ(InputFromString(&expectations.validity), tbs.validity_tlv);
171 EXPECT_EQ(InputFromString(&expectations.subject), tbs.subject_tlv);
172 EXPECT_EQ(InputFromString(&expectations.spki), tbs.spki_tlv);
173
174 EXPECT_FALSE(tbs.has_issuer_unique_id);
175 EXPECT_FALSE(tbs.has_subject_unique_id);
176
177 EXPECT_EQ(InputFromString(&expectations.extensions), tbs.extensions_tlv);
178 EXPECT_EQ(!expectations.extensions.empty(), tbs.has_extensions);
179 }
180
181 // Loads certificate data from the PEM file |file_name| and verifies that both
182 // parsing the Certificate and TBSCertificate succeed. Upon return |cert| and
183 // |tbs| are filled accordingly.
184 void EnsureParsingTbsSucceds(const std::string& file_name,
185 CertificateVersion expected_version) {
186 std::string cert_data;
187 ParsedCertificateExpectations cert_expectations;
188 ParsedTbsCertificateExpectations tbs_expectations;
189
190 // Read the certificate data and test expectations from a single PEM file.
191 ASSERT_TRUE(LoadTestCertificatePemData(
192 file_name, &cert_data, &cert_expectations, &tbs_expectations));
193
194 // Parsing the certificate should succeed.
195 ParsedCertificate cert;
196 ASSERT_TRUE(ParseCertificate(InputFromString(&cert_data), &cert));
197
198 // Ensure that the ParsedCertificate matches expectations.
199 EnsureParsedCertificate(cert, cert_expectations);
200
201 ParsedTbsCertificate tbs;
202 ASSERT_TRUE(ParseTbsCertificate(cert.tbs_certificate_tlv, &tbs));
203
204 // Ensure that the ParsedTbdCertificate matches expectations.
205 EnsureParsedTbsCertificate(tbs, tbs_expectations, expected_version);
206 }
207
208 // Loads certificate data from the PEM file |file_name| and verifies that the
209 // Certificate parsing succeed, however the TBSCertificate parsing fails.
210 void EnsureParsingTbsFails(const std::string& file_name) {
211 std::string cert_data;
212 ParsedCertificateExpectations cert_expectations;
213 ParsedTbsCertificateExpectations tbs_expectations;
214
215 ASSERT_TRUE(LoadTestCertificatePemData(
216 file_name, &cert_data, &cert_expectations, &tbs_expectations));
217
218 ParsedCertificate cert;
219 ASSERT_TRUE(ParseCertificate(InputFromString(&cert_data), &cert));
220
221 EnsureParsedCertificate(cert, cert_expectations);
222
223 ParsedTbsCertificate tbs;
224 ASSERT_FALSE(ParseTbsCertificate(cert.tbs_certificate_tlv, &tbs));
225 }
226
227 // Tests parsing a TBSCertificate with a negative serial number.
228 //
229 // CAs are not supposed to include negative serial numbers, however RFC 5280
230 // says that consumers should be expected to deal with them (so they are allowed
231 // by ParseTbsCertificate()).
232 TEST(ParseCertificateTest, NegativeSerialNumber) {
233 EnsureParsingTbsSucceds("negative_serial.pem", CertificateVersion::V3);
234 }
235
236 // Tests parsing a TBSCertificate with a serial number that is 21 octets long
237 // (and the first byte is 0).
238 TEST(ParseCertificateTest, SerialNumber21OctetsLeading0) {
239 EnsureParsingTbsFails("serial_number_21_octets_leading_0.pem");
240 }
241
242 // Tests parsing a TBSCertificate with a serial number that is 26 octets long
243 // (and does not contain a leading 0).
244 TEST(ParseCertificateTest, SerialNumber26Octets) {
245 EnsureParsingTbsFails("serial_number_26_octets.pem");
246 }
247
248 // Tests parsing a TBSCertificate which lacks a version number (causing it to
249 // default to v1).
250 TEST(ParseCertificateTest, CertificateVersion1) {
251 EnsureParsingTbsSucceds("version1.pem", CertificateVersion::V1);
252 }
253
254 // Tests parsing a TBSCertificate with version 3.
255 TEST(ParseCertificateTest, CertificateVersion3) {
256 EnsureParsingTbsSucceds("version3.pem", CertificateVersion::V3);
257 }
258
259 } // namespace
260
261 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698