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

Side by Side Diff: net/cert/internal/parse_certificate.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 "net/der/input.h"
8 #include "net/der/parse_values.h"
9 #include "net/der/parser.h"
10
11 namespace net {
12
13 namespace {
14
15 // Parses a Version according to RFC 5280:
16 //
17 // Version ::= INTEGER { v1(0), v2(1), v3(2) }
18 //
19 // No value other that v1, v2, or v3 is allowed (and if given will fail). RFC
20 // 5280 minimally requires the handling of v3 (and overwhelmingly these are the
21 // certificate versions in use today):
davidben 2015/08/11 20:31:56 Do we need to handle this unspecified v4 thing?
eroman 2015/08/11 21:13:33 From what I can tell v4 is not really a thing yet
davidben 2015/08/11 22:39:20 I know nothing about what v4 is. I think Ryan said
eroman 2015/08/12 00:37:10 Acknowledged. I will try and follow-up with v4 req
22 //
23 // Implementations SHOULD be prepared to accept any version certificate.
24 // At a minimum, conforming implementations MUST recognize version 3
25 // certificates.
26 //
27 WARN_UNUSED_RESULT bool ParseVersion(const der::Input& in,
28 CertificateVersion* version) {
29 der::Parser parser(in);
30 uint64_t version64;
31 if (!parser.ReadUint64(&version64))
32 return false;
33
34 switch (version64) {
35 case 0:
36 *version = CertificateVersion::V1;
37 break;
38 case 1:
39 *version = CertificateVersion::V2;
40 break;
41 case 2:
42 *version = CertificateVersion::V3;
43 break;
44 default:
45 // Don't allow any other version identifier.
46 return false;
47 }
48
49 // By definition the input to this function was a single INTEGER, so there
50 // shouldn't be anything else after it.
51 return !parser.HasMore();
52 }
53
54 // Returns true if the given serial number (CertificateSerialNumber in RFC 5280)
55 // is valid:
56 //
57 // CertificateSerialNumber ::= INTEGER
58 //
59 // The input to this function is the (unverified) value octets of the INTEGER.
60 // This function will verify that:
61 //
62 // * The octets are a valid DER-encoding of an INTEGER (for instance, minimal
63 // encoding length).
64 //
65 // * No more than 20 octets are used.
66 //
67 // Note that it DOES NOT reject non-positive values (zero or negative).
68 //
69 // For reference, here is what RFC 5280 section 4.1.2.2 says:
70 //
71 // Given the uniqueness requirements above, serial numbers can be
72 // expected to contain long integers. Certificate users MUST be able to
73 // handle serialNumber values up to 20 octets. Conforming CAs MUST NOT
74 // use serialNumber values longer than 20 octets.
75 //
76 // Note: Non-conforming CAs may issue certificates with serial numbers
77 // that are negative or zero. Certificate users SHOULD be prepared to
78 // gracefully handle such certificates.
79 WARN_UNUSED_RESULT bool VerifySerialNumber(const der::Input& value) {
80 der::ByteReader reader(value);
81
82 if (value.Length() == 0)
83 return false; // Not a valid DER-encoded INTEGER.
84
85 // Check if the serial number is too long per RFC 5280.
86 if (value.Length() > 20)
87 return false;
88
89 // Accept any single-byte serial number (including zero and negatives).
90 if (value.Length() == 1)
91 return true;
92
93 // INTEGER values in DER should be minimal. They should only contain a leading
94 // zero if the second octet has its most significant bit set to 1 (since
95 // without the leading zero the described number would be negative).
96 uint8_t first_byte;
97 if (!reader.ReadByte(&first_byte))
98 return false; // Unexpected
99
100 if (first_byte == 0) {
101 uint8_t second_byte;
102 if (!reader.ReadByte(&second_byte))
103 return false; // Unexpected
104
105 if ((second_byte & 0x80) == 0)
106 return false; // MSB must be 1.
107 }
108
109 return true;
110 }
111
112 } // namespace
113
114 ParsedTbsCertificate::ParsedTbsCertificate()
115 : version(CertificateVersion::V1),
116 has_issuer_unique_id(false),
117 has_subject_unique_id(false),
118 has_extensions(false) {}
119
120 ParsedTbsCertificate::~ParsedTbsCertificate() {}
121
122 bool ParseCertificate(const der::Input& certificate_tlv,
123 ParsedCertificate* out) {
davidben 2015/08/11 20:31:56 [I probably would have written this function in Bo
eroman 2015/08/11 21:13:33 This is a good point. I found it convenient in the
davidben 2015/08/11 22:39:20 Either's fine by me. I have noticed that Chromium
124 der::Parser parser(certificate_tlv);
125
126 // Certificate ::= SEQUENCE {
127 der::Parser certificate_parser;
128 if (!parser.ReadSequence(&certificate_parser))
129 return false;
130
131 // tbsCertificate TBSCertificate,
132 if (!certificate_parser.ReadRawTLV(&out->tbs_certificate_tlv))
133 return false;
134
135 // signatureAlgorithm AlgorithmIdentifier,
136 if (!certificate_parser.ReadRawTLV(&out->signature_algorithm_tlv))
137 return false;
138
139 // signatureValue BIT STRING }
140 if (!certificate_parser.ReadBitString(&out->signature_value))
141 return false;
142
143 // By definition the input was a single Certificate, so there shouldn't be
144 // unconsumed data.
145 if (parser.HasMore())
146 return false;
147
148 // There isn't an extension point at the end of Certificate.
149 if (certificate_parser.HasMore())
150 return false;
davidben 2015/08/11 20:31:56 Nit: It seems better to check certificate_parser b
eroman 2015/08/11 21:13:33 Will do.
eroman 2015/08/12 00:37:10 Done.
151
152 return true;
153 }
154
155 // From RFC 5280 section 4.1:
156 //
157 // Certificate ::= SEQUENCE {
158 // tbsCertificate TBSCertificate,
159 // signatureAlgorithm AlgorithmIdentifier,
160 // signatureValue BIT STRING }
161 //
162 // TBSCertificate ::= SEQUENCE {
163 // version [0] EXPLICIT Version DEFAULT v1,
164 // serialNumber CertificateSerialNumber,
165 // signature AlgorithmIdentifier,
166 // issuer Name,
167 // validity Validity,
168 // subject Name,
169 // subjectPublicKeyInfo SubjectPublicKeyInfo,
170 // issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL,
171 // -- If present, version MUST be v2 or v3
172 // subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL,
173 // -- If present, version MUST be v2 or v3
174 // extensions [3] EXPLICIT Extensions OPTIONAL
175 // -- If present, version MUST be v3
176 // }
177 bool ParseTbsCertificate(const der::Input& tbs_tlv, ParsedTbsCertificate* out) {
178 der::Parser parser(tbs_tlv);
179
180 // Certificate ::= SEQUENCE {
181 der::Parser tbs_parser;
182 if (!parser.ReadSequence(&tbs_parser))
183 return false;
184
185 // version [0] EXPLICIT Version DEFAULT v1,
186 der::Input version;
187 bool has_version;
188 if (!tbs_parser.ReadOptionalTag(der::ContextSpecificConstructed(0), &version,
189 &has_version)) {
190 return false;
191 }
192 if (has_version) {
193 if (!ParseVersion(version, &out->version))
194 return false;
davidben 2015/08/11 20:31:56 Although it seems we can't enforce it, there is a
eroman 2015/08/11 21:13:33 Good point! I should enforce that here, and will d
davidben 2015/08/11 22:39:20 I believe mozilla::pkix doesn't. Although, actuall
eroman 2015/08/12 00:37:10 Acknowledged thanks for the context! I have added
195 } else {
196 out->version = CertificateVersion::V1;
197 }
198
199 // serialNumber CertificateSerialNumber,
200 if (!tbs_parser.ReadTag(der::kInteger, &out->serial_number))
201 return false;
202 if (!VerifySerialNumber(out->serial_number))
203 return false;
204
205 // signature AlgorithmIdentifier,
206 if (!tbs_parser.ReadRawTLV(&out->signature_algorithm_tlv))
207 return false;
208
209 // issuer Name,
210 if (!tbs_parser.ReadRawTLV(&out->issuer_tlv))
211 return false;
212
213 // validity Validity,
214 if (!tbs_parser.ReadRawTLV(&out->validity_tlv))
215 return false;
216
217 // subject Name,
218 if (!tbs_parser.ReadRawTLV(&out->subject_tlv))
219 return false;
220
221 // subjectPublicKeyInfo SubjectPublicKeyInfo,
222 if (!tbs_parser.ReadRawTLV(&out->spki_tlv))
223 return false;
224
225 // issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL,
226 // -- If present, version MUST be v2 or v3
227 der::Input issuer_unique_id;
228 if (!tbs_parser.ReadOptionalTag(der::ContextSpecificPrimitive(1),
229 &issuer_unique_id,
230 &out->has_issuer_unique_id)) {
231 return false;
232 }
233 if (out->has_issuer_unique_id) {
234 if (!der::ParseBitString(issuer_unique_id, &out->issuer_unique_id))
davidben 2015/08/11 20:31:56 [Verified that ParseBitString does NOT expect a TL
235 return false;
236 if (out->version != CertificateVersion::V2 &&
237 out->version != CertificateVersion::V3) {
238 return false;
239 }
240 }
241
242 // subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL,
243 // -- If present, version MUST be v2 or v3
244 der::Input subject_unique_id;
245 if (!tbs_parser.ReadOptionalTag(der::ContextSpecificPrimitive(2),
246 &subject_unique_id,
247 &out->has_subject_unique_id)) {
248 return false;
249 }
250 if (out->has_subject_unique_id) {
251 if (!der::ParseBitString(subject_unique_id, &out->subject_unique_id))
252 return false;
253 if (out->version != CertificateVersion::V2 &&
254 out->version != CertificateVersion::V3) {
255 return false;
256 }
257 }
258
259 // extensions [3] EXPLICIT Extensions OPTIONAL
260 // -- If present, version MUST be v3
261 if (!tbs_parser.ReadOptionalTag(der::ContextSpecificConstructed(3),
262 &out->extensions_tlv, &out->has_extensions)) {
263 return false;
264 }
265 if (out->has_extensions) {
266 if (out->version != CertificateVersion::V3)
267 return false;
268 }
269
270 // By definition the input was a single TBSCertificate, so there shouldn't be
271 // unconsumed data.
272 if (parser.HasMore())
273 return false;
274
275 // Note that there IS an extension point at the end of TBSCertificate
276 // (according to RFC 5912), so from that interpretation, unconsumed data would
277 // be allowed in |tbs_parser|.
278 //
279 // However because only v1, v2, and v3 certificates are supported by the
280 // parsing, there shouldn't be any subsequent data in those versions, so
281 // reject.
282 if (tbs_parser.HasMore())
283 return false;
davidben 2015/08/11 20:31:56 Nit: Ditto re parser vs tbs_parser order.
eroman 2015/08/12 00:37:10 Done.
284
285 return true;
286 }
287
288 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698