/src/mozilla-central/security/pkix/lib/pkixcert.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
3 | | /* This code is made available to you under your choice of the following sets |
4 | | * of licensing terms: |
5 | | */ |
6 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
7 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
8 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
9 | | */ |
10 | | /* Copyright 2014 Mozilla Contributors |
11 | | * |
12 | | * Licensed under the Apache License, Version 2.0 (the "License"); |
13 | | * you may not use this file except in compliance with the License. |
14 | | * You may obtain a copy of the License at |
15 | | * |
16 | | * http://www.apache.org/licenses/LICENSE-2.0 |
17 | | * |
18 | | * Unless required by applicable law or agreed to in writing, software |
19 | | * distributed under the License is distributed on an "AS IS" BASIS, |
20 | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
21 | | * See the License for the specific language governing permissions and |
22 | | * limitations under the License. |
23 | | */ |
24 | | |
25 | | #include "pkixutil.h" |
26 | | |
27 | | namespace mozilla { namespace pkix { |
28 | | |
29 | | Result |
30 | | BackCert::Init() |
31 | 0 | { |
32 | 0 | Result rv; |
33 | 0 |
|
34 | 0 | // Certificate ::= SEQUENCE { |
35 | 0 | // tbsCertificate TBSCertificate, |
36 | 0 | // signatureAlgorithm AlgorithmIdentifier, |
37 | 0 | // signatureValue BIT STRING } |
38 | 0 |
|
39 | 0 | Reader tbsCertificate; |
40 | 0 |
|
41 | 0 | // The scope of |input| and |certificate| are limited to this block so we |
42 | 0 | // don't accidentally confuse them for tbsCertificate later. |
43 | 0 | { |
44 | 0 | Reader certificate; |
45 | 0 | rv = der::ExpectTagAndGetValueAtEnd(der, der::SEQUENCE, certificate); |
46 | 0 | if (rv != Success) { |
47 | 0 | return rv; |
48 | 0 | } |
49 | 0 | rv = der::SignedData(certificate, tbsCertificate, signedData); |
50 | 0 | if (rv != Success) { |
51 | 0 | return rv; |
52 | 0 | } |
53 | 0 | rv = der::End(certificate); |
54 | 0 | if (rv != Success) { |
55 | 0 | return rv; |
56 | 0 | } |
57 | 0 | } |
58 | 0 | |
59 | 0 | // TBSCertificate ::= SEQUENCE { |
60 | 0 | // version [0] EXPLICIT Version DEFAULT v1, |
61 | 0 | // serialNumber CertificateSerialNumber, |
62 | 0 | // signature AlgorithmIdentifier, |
63 | 0 | // issuer Name, |
64 | 0 | // validity Validity, |
65 | 0 | // subject Name, |
66 | 0 | // subjectPublicKeyInfo SubjectPublicKeyInfo, |
67 | 0 | // issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL, |
68 | 0 | // -- If present, version MUST be v2 or v3 |
69 | 0 | // subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL, |
70 | 0 | // -- If present, version MUST be v2 or v3 |
71 | 0 | // extensions [3] EXPLICIT Extensions OPTIONAL |
72 | 0 | // -- If present, version MUST be v3 |
73 | 0 | // } |
74 | 0 | rv = der::OptionalVersion(tbsCertificate, version); |
75 | 0 | if (rv != Success) { |
76 | 0 | return rv; |
77 | 0 | } |
78 | 0 | rv = der::CertificateSerialNumber(tbsCertificate, serialNumber); |
79 | 0 | if (rv != Success) { |
80 | 0 | return rv; |
81 | 0 | } |
82 | 0 | rv = der::ExpectTagAndGetValue(tbsCertificate, der::SEQUENCE, signature); |
83 | 0 | if (rv != Success) { |
84 | 0 | return rv; |
85 | 0 | } |
86 | 0 | rv = der::ExpectTagAndGetTLV(tbsCertificate, der::SEQUENCE, issuer); |
87 | 0 | if (rv != Success) { |
88 | 0 | return rv; |
89 | 0 | } |
90 | 0 | rv = der::ExpectTagAndGetValue(tbsCertificate, der::SEQUENCE, validity); |
91 | 0 | if (rv != Success) { |
92 | 0 | return rv; |
93 | 0 | } |
94 | 0 | // TODO(bug XXXXXXX): We rely on the the caller of mozilla::pkix to validate |
95 | 0 | // that the name is syntactically valid, if they care. In Gecko we do this |
96 | 0 | // implicitly by parsing the certificate into a CERTCertificate object. |
97 | 0 | // Instead of relying on the caller to do this, we should do it ourselves. |
98 | 0 | rv = der::ExpectTagAndGetTLV(tbsCertificate, der::SEQUENCE, subject); |
99 | 0 | if (rv != Success) { |
100 | 0 | return rv; |
101 | 0 | } |
102 | 0 | rv = der::ExpectTagAndGetTLV(tbsCertificate, der::SEQUENCE, |
103 | 0 | subjectPublicKeyInfo); |
104 | 0 | if (rv != Success) { |
105 | 0 | return rv; |
106 | 0 | } |
107 | 0 | |
108 | 0 | static const uint8_t CSC = der::CONTEXT_SPECIFIC | der::CONSTRUCTED; |
109 | 0 |
|
110 | 0 | // According to RFC 5280, all fields below this line are forbidden for |
111 | 0 | // certificate versions less than v3. However, for compatibility reasons, |
112 | 0 | // we parse v1/v2 certificates in the same way as v3 certificates. So if |
113 | 0 | // these fields appear in a v1 certificate, they will be used. |
114 | 0 |
|
115 | 0 | // Ignore issuerUniqueID if present. |
116 | 0 | if (tbsCertificate.Peek(CSC | 1)) { |
117 | 0 | rv = der::ExpectTagAndSkipValue(tbsCertificate, CSC | 1); |
118 | 0 | if (rv != Success) { |
119 | 0 | return rv; |
120 | 0 | } |
121 | 0 | } |
122 | 0 | |
123 | 0 | // Ignore subjectUniqueID if present. |
124 | 0 | if (tbsCertificate.Peek(CSC | 2)) { |
125 | 0 | rv = der::ExpectTagAndSkipValue(tbsCertificate, CSC | 2); |
126 | 0 | if (rv != Success) { |
127 | 0 | return rv; |
128 | 0 | } |
129 | 0 | } |
130 | 0 | |
131 | 0 | rv = der::OptionalExtensions( |
132 | 0 | tbsCertificate, CSC | 3, |
133 | 0 | [this](Reader& extnID, const Input& extnValue, bool critical, |
134 | 0 | /*out*/ bool& understood) { |
135 | 0 | return RememberExtension(extnID, extnValue, critical, understood); |
136 | 0 | }); |
137 | 0 | if (rv != Success) { |
138 | 0 | return rv; |
139 | 0 | } |
140 | 0 | |
141 | 0 | // The Netscape Certificate Type extension is an obsolete |
142 | 0 | // Netscape-proprietary mechanism that we ignore in favor of the standard |
143 | 0 | // extensions. However, some CAs have issued certificates with the Netscape |
144 | 0 | // Cert Type extension marked critical. Thus, for compatibility reasons, we |
145 | 0 | // "understand" this extension by ignoring it when it is not critical, and |
146 | 0 | // by ensuring that the equivalent standardized extensions are present when |
147 | 0 | // it is marked critical, based on the assumption that the information in |
148 | 0 | // the Netscape Cert Type extension is consistent with the information in |
149 | 0 | // the standard extensions. |
150 | 0 | // |
151 | 0 | // Here is a mapping between the Netscape Cert Type extension and the |
152 | 0 | // standard extensions: |
153 | 0 | // |
154 | 0 | // Netscape Cert Type | BasicConstraints.cA | Extended Key Usage |
155 | 0 | // --------------------+-----------------------+---------------------- |
156 | 0 | // SSL Server | false | id_kp_serverAuth |
157 | 0 | // SSL Client | false | id_kp_clientAuth |
158 | 0 | // S/MIME Client | false | id_kp_emailProtection |
159 | 0 | // Object Signing | false | id_kp_codeSigning |
160 | 0 | // SSL Server CA | true | id_kp_serverAuth |
161 | 0 | // SSL Client CA | true | id_kp_clientAuth |
162 | 0 | // S/MIME CA | true | id_kp_emailProtection |
163 | 0 | // Object Signing CA | true | id_kp_codeSigning |
164 | 0 | if (criticalNetscapeCertificateType.GetLength() > 0 && |
165 | 0 | (basicConstraints.GetLength() == 0 || extKeyUsage.GetLength() == 0)) { |
166 | 0 | return Result::ERROR_UNKNOWN_CRITICAL_EXTENSION; |
167 | 0 | } |
168 | 0 | |
169 | 0 | return der::End(tbsCertificate); |
170 | 0 | } |
171 | | |
172 | | Result |
173 | | BackCert::RememberExtension(Reader& extnID, Input extnValue, |
174 | | bool critical, /*out*/ bool& understood) |
175 | 0 | { |
176 | 0 | understood = false; |
177 | 0 |
|
178 | 0 | // python DottedOIDToCode.py id-ce-keyUsage 2.5.29.15 |
179 | 0 | static const uint8_t id_ce_keyUsage[] = { |
180 | 0 | 0x55, 0x1d, 0x0f |
181 | 0 | }; |
182 | 0 | // python DottedOIDToCode.py id-ce-subjectAltName 2.5.29.17 |
183 | 0 | static const uint8_t id_ce_subjectAltName[] = { |
184 | 0 | 0x55, 0x1d, 0x11 |
185 | 0 | }; |
186 | 0 | // python DottedOIDToCode.py id-ce-basicConstraints 2.5.29.19 |
187 | 0 | static const uint8_t id_ce_basicConstraints[] = { |
188 | 0 | 0x55, 0x1d, 0x13 |
189 | 0 | }; |
190 | 0 | // python DottedOIDToCode.py id-ce-nameConstraints 2.5.29.30 |
191 | 0 | static const uint8_t id_ce_nameConstraints[] = { |
192 | 0 | 0x55, 0x1d, 0x1e |
193 | 0 | }; |
194 | 0 | // python DottedOIDToCode.py id-ce-certificatePolicies 2.5.29.32 |
195 | 0 | static const uint8_t id_ce_certificatePolicies[] = { |
196 | 0 | 0x55, 0x1d, 0x20 |
197 | 0 | }; |
198 | 0 | // python DottedOIDToCode.py id-ce-policyConstraints 2.5.29.36 |
199 | 0 | static const uint8_t id_ce_policyConstraints[] = { |
200 | 0 | 0x55, 0x1d, 0x24 |
201 | 0 | }; |
202 | 0 | // python DottedOIDToCode.py id-ce-extKeyUsage 2.5.29.37 |
203 | 0 | static const uint8_t id_ce_extKeyUsage[] = { |
204 | 0 | 0x55, 0x1d, 0x25 |
205 | 0 | }; |
206 | 0 | // python DottedOIDToCode.py id-ce-inhibitAnyPolicy 2.5.29.54 |
207 | 0 | static const uint8_t id_ce_inhibitAnyPolicy[] = { |
208 | 0 | 0x55, 0x1d, 0x36 |
209 | 0 | }; |
210 | 0 | // python DottedOIDToCode.py id-pe-authorityInfoAccess 1.3.6.1.5.5.7.1.1 |
211 | 0 | static const uint8_t id_pe_authorityInfoAccess[] = { |
212 | 0 | 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01 |
213 | 0 | }; |
214 | 0 | // python DottedOIDToCode.py id-pkix-ocsp-nocheck 1.3.6.1.5.5.7.48.1.5 |
215 | 0 | static const uint8_t id_pkix_ocsp_nocheck[] = { |
216 | 0 | 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x05 |
217 | 0 | }; |
218 | 0 | // python DottedOIDToCode.py Netscape-certificate-type 2.16.840.1.113730.1.1 |
219 | 0 | static const uint8_t Netscape_certificate_type[] = { |
220 | 0 | 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x01, 0x01 |
221 | 0 | }; |
222 | 0 | // python DottedOIDToCode.py id-pe-tlsfeature 1.3.6.1.5.5.7.1.24 |
223 | 0 | static const uint8_t id_pe_tlsfeature[] = { |
224 | 0 | 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x18 |
225 | 0 | }; |
226 | 0 | // python DottedOIDToCode.py id-embeddedSctList 1.3.6.1.4.1.11129.2.4.2 |
227 | 0 | // See Section 3.3 of RFC 6962. |
228 | 0 | static const uint8_t id_embeddedSctList[] = { |
229 | 0 | 0x2b, 0x06, 0x01, 0x04, 0x01, 0xd6, 0x79, 0x02, 0x04, 0x02 |
230 | 0 | }; |
231 | 0 |
|
232 | 0 | Input* out = nullptr; |
233 | 0 |
|
234 | 0 | // We already enforce the maximum possible constraints for policies so we |
235 | 0 | // can safely ignore even critical policy constraint extensions. |
236 | 0 | // |
237 | 0 | // XXX: Doing it this way won't allow us to detect duplicate |
238 | 0 | // policyConstraints extensions, but that's OK because (and only because) we |
239 | 0 | // ignore the extension. |
240 | 0 | Input dummyPolicyConstraints; |
241 | 0 |
|
242 | 0 | // We don't need to save the contents of this extension if it is present. We |
243 | 0 | // just need to handle its presence (it is essentially ignored right now). |
244 | 0 | Input dummyOCSPNocheck; |
245 | 0 |
|
246 | 0 | // For compatibility reasons, for some extensions we have to allow empty |
247 | 0 | // extension values. This would normally interfere with our duplicate |
248 | 0 | // extension checking code. However, as long as the extensions we allow to |
249 | 0 | // have empty values are also the ones we implicitly allow duplicates of, |
250 | 0 | // this will work fine. |
251 | 0 | bool emptyValueAllowed = false; |
252 | 0 |
|
253 | 0 | // RFC says "Conforming CAs MUST mark this extension as non-critical" for |
254 | 0 | // both authorityKeyIdentifier and subjectKeyIdentifier, and we do not use |
255 | 0 | // them for anything, so we totally ignore them here. |
256 | 0 |
|
257 | 0 | if (extnID.MatchRest(id_ce_keyUsage)) { |
258 | 0 | out = &keyUsage; |
259 | 0 | } else if (extnID.MatchRest(id_ce_subjectAltName)) { |
260 | 0 | out = &subjectAltName; |
261 | 0 | } else if (extnID.MatchRest(id_ce_basicConstraints)) { |
262 | 0 | out = &basicConstraints; |
263 | 0 | } else if (extnID.MatchRest(id_ce_nameConstraints)) { |
264 | 0 | out = &nameConstraints; |
265 | 0 | } else if (extnID.MatchRest(id_ce_certificatePolicies)) { |
266 | 0 | out = &certificatePolicies; |
267 | 0 | } else if (extnID.MatchRest(id_ce_policyConstraints)) { |
268 | 0 | out = &dummyPolicyConstraints; |
269 | 0 | } else if (extnID.MatchRest(id_ce_extKeyUsage)) { |
270 | 0 | out = &extKeyUsage; |
271 | 0 | } else if (extnID.MatchRest(id_ce_inhibitAnyPolicy)) { |
272 | 0 | out = &inhibitAnyPolicy; |
273 | 0 | } else if (extnID.MatchRest(id_pe_authorityInfoAccess)) { |
274 | 0 | out = &authorityInfoAccess; |
275 | 0 | } else if (extnID.MatchRest(id_pe_tlsfeature)) { |
276 | 0 | out = &requiredTLSFeatures; |
277 | 0 | } else if (extnID.MatchRest(id_embeddedSctList)) { |
278 | 0 | out = &signedCertificateTimestamps; |
279 | 0 | } else if (extnID.MatchRest(id_pkix_ocsp_nocheck) && critical) { |
280 | 0 | // We need to make sure we don't reject delegated OCSP response signing |
281 | 0 | // certificates that contain the id-pkix-ocsp-nocheck extension marked as |
282 | 0 | // critical when validating OCSP responses. Without this, an application |
283 | 0 | // that implements soft-fail OCSP might ignore a valid Revoked or Unknown |
284 | 0 | // response, and an application that implements hard-fail OCSP might fail |
285 | 0 | // to connect to a server given a valid Good response. |
286 | 0 | out = &dummyOCSPNocheck; |
287 | 0 | // We allow this extension to have an empty value. |
288 | 0 | // See http://comments.gmane.org/gmane.ietf.x509/30947 |
289 | 0 | emptyValueAllowed = true; |
290 | 0 | } else if (extnID.MatchRest(Netscape_certificate_type) && critical) { |
291 | 0 | out = &criticalNetscapeCertificateType; |
292 | 0 | } |
293 | 0 |
|
294 | 0 | if (out) { |
295 | 0 | // Don't allow an empty value for any extension we understand. This way, we |
296 | 0 | // can test out->GetLength() != 0 or out->Init() to check for duplicates. |
297 | 0 | if (extnValue.GetLength() == 0 && !emptyValueAllowed) { |
298 | 0 | return Result::ERROR_EXTENSION_VALUE_INVALID; |
299 | 0 | } |
300 | 0 | if (out->Init(extnValue) != Success) { |
301 | 0 | // Duplicate extension |
302 | 0 | return Result::ERROR_EXTENSION_VALUE_INVALID; |
303 | 0 | } |
304 | 0 | understood = true; |
305 | 0 | } |
306 | 0 |
|
307 | 0 | return Success; |
308 | 0 | } |
309 | | |
310 | | Result |
311 | | ExtractSignedCertificateTimestampListFromExtension(Input extnValue, |
312 | | Input& sctList) |
313 | 0 | { |
314 | 0 | Reader decodedValue; |
315 | 0 | Result rv = der::ExpectTagAndGetValueAtEnd(extnValue, der::OCTET_STRING, |
316 | 0 | decodedValue); |
317 | 0 | if (rv != Success) { |
318 | 0 | return rv; |
319 | 0 | } |
320 | 0 | return decodedValue.SkipToEnd(sctList); |
321 | 0 | } |
322 | | |
323 | | } } // namespace mozilla::pkix |