/src/mozilla-central/security/pkix/lib/pkixocsp.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 2013 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 <limits> |
26 | | |
27 | | #include "pkix/pkix.h" |
28 | | #include "pkixcheck.h" |
29 | | #include "pkixutil.h" |
30 | | |
31 | | namespace { |
32 | | |
33 | | const size_t SHA1_DIGEST_LENGTH = 160 / 8; |
34 | | |
35 | | } // namespace |
36 | | |
37 | | namespace mozilla { namespace pkix { |
38 | | |
39 | | // These values correspond to the tag values in the ASN.1 CertStatus |
40 | | enum class CertStatus : uint8_t { |
41 | | Good = der::CONTEXT_SPECIFIC | 0, |
42 | | Revoked = der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 1, |
43 | | Unknown = der::CONTEXT_SPECIFIC | 2 |
44 | | }; |
45 | | |
46 | | class Context final |
47 | | { |
48 | | public: |
49 | | Context(TrustDomain& aTrustDomain, const CertID& aCertID, Time aTime, |
50 | | uint16_t aMaxLifetimeInDays, /*optional out*/ Time* aThisUpdate, |
51 | | /*optional out*/ Time* aValidThrough) |
52 | | : trustDomain(aTrustDomain) |
53 | | , certID(aCertID) |
54 | | , time(aTime) |
55 | | , maxLifetimeInDays(aMaxLifetimeInDays) |
56 | | , certStatus(CertStatus::Unknown) |
57 | | , thisUpdate(aThisUpdate) |
58 | | , validThrough(aValidThrough) |
59 | | , expired(false) |
60 | | , matchFound(false) |
61 | 0 | { |
62 | 0 | if (thisUpdate) { |
63 | 0 | *thisUpdate = TimeFromElapsedSecondsAD(0); |
64 | 0 | } |
65 | 0 | if (validThrough) { |
66 | 0 | *validThrough = TimeFromElapsedSecondsAD(0); |
67 | 0 | } |
68 | 0 | } |
69 | | |
70 | | TrustDomain& trustDomain; |
71 | | const CertID& certID; |
72 | | const Time time; |
73 | | const uint16_t maxLifetimeInDays; |
74 | | CertStatus certStatus; |
75 | | Time* thisUpdate; |
76 | | Time* validThrough; |
77 | | bool expired; |
78 | | |
79 | | Input signedCertificateTimestamps; |
80 | | |
81 | | // Keep track of whether the OCSP response contains the status of the |
82 | | // certificate we're interested in. Responders might reply without |
83 | | // including the status of any of the requested certs, we should |
84 | | // indicate a server failure in those cases. |
85 | | bool matchFound; |
86 | | |
87 | | Context(const Context&) = delete; |
88 | | void operator=(const Context&) = delete; |
89 | | }; |
90 | | |
91 | | // Verify that potentialSigner is a valid delegated OCSP response signing cert |
92 | | // according to RFC 6960 section 4.2.2.2. |
93 | | static Result |
94 | | CheckOCSPResponseSignerCert(TrustDomain& trustDomain, |
95 | | BackCert& potentialSigner, |
96 | | Input issuerSubject, |
97 | | Input issuerSubjectPublicKeyInfo, |
98 | | Time time) |
99 | 0 | { |
100 | 0 | Result rv; |
101 | 0 |
|
102 | 0 | // We don't need to do a complete verification of the signer (i.e. we don't |
103 | 0 | // have to call BuildCertChain to verify the entire chain) because we |
104 | 0 | // already know that the issuer is valid, since revocation checking is done |
105 | 0 | // from the root to the parent after we've built a complete chain that we |
106 | 0 | // know is otherwise valid. Rather, we just need to do a one-step validation |
107 | 0 | // from potentialSigner to the issuer. |
108 | 0 | // |
109 | 0 | // It seems reasonable to require the KU_DIGITAL_SIGNATURE key usage on the |
110 | 0 | // OCSP responder certificate if the OCSP responder certificate has a |
111 | 0 | // key usage extension. However, according to bug 240456, some OCSP responder |
112 | 0 | // certificates may have only the nonRepudiation bit set. Also, the OCSP |
113 | 0 | // specification (RFC 6960) does not mandate any particular key usage to be |
114 | 0 | // asserted for OCSP responde signers. Oddly, the CABForum Baseline |
115 | 0 | // Requirements v.1.1.5 do say "If the Root CA Private Key is used for |
116 | 0 | // signing OCSP responses, then the digitalSignature bit MUST be set." |
117 | 0 | // |
118 | 0 | // Note that CheckIssuerIndependentProperties processes |
119 | 0 | // SEC_OID_OCSP_RESPONDER in the way that the OCSP specification requires us |
120 | 0 | // to--in particular, it doesn't allow SEC_OID_OCSP_RESPONDER to be implied |
121 | 0 | // by a missing EKU extension, unlike other EKUs. |
122 | 0 | // |
123 | 0 | // TODO(bug 926261): If we're validating for a policy then the policy OID we |
124 | 0 | // are validating for should be passed to CheckIssuerIndependentProperties. |
125 | 0 | TrustLevel unusedTrustLevel; |
126 | 0 | rv = CheckIssuerIndependentProperties(trustDomain, potentialSigner, time, |
127 | 0 | KeyUsage::noParticularKeyUsageRequired, |
128 | 0 | KeyPurposeId::id_kp_OCSPSigning, |
129 | 0 | CertPolicyId::anyPolicy, 0, |
130 | 0 | unusedTrustLevel); |
131 | 0 | if (rv != Success) { |
132 | 0 | return rv; |
133 | 0 | } |
134 | 0 | |
135 | 0 | // It is possible that there exists a certificate with the same key as the |
136 | 0 | // issuer but with a different name, so we need to compare names |
137 | 0 | // XXX(bug 926270) XXX(bug 1008133) XXX(bug 980163): Improve name |
138 | 0 | // comparison. |
139 | 0 | // TODO: needs test |
140 | 0 | if (!InputsAreEqual(potentialSigner.GetIssuer(), issuerSubject)) { |
141 | 0 | return Result::ERROR_OCSP_RESPONDER_CERT_INVALID; |
142 | 0 | } |
143 | 0 | |
144 | 0 | // TODO(bug 926260): check name constraints |
145 | 0 | |
146 | 0 | rv = VerifySignedData(trustDomain, potentialSigner.GetSignedData(), |
147 | 0 | issuerSubjectPublicKeyInfo); |
148 | 0 |
|
149 | 0 | // TODO: check for revocation of the OCSP responder certificate unless no-check |
150 | 0 | // or the caller forcing no-check. To properly support the no-check policy, we'd |
151 | 0 | // need to enforce policy constraints from the issuerChain. |
152 | 0 |
|
153 | 0 | return rv; |
154 | 0 | } |
155 | | |
156 | | enum class ResponderIDType : uint8_t |
157 | | { |
158 | | byName = der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 1, |
159 | | byKey = der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 2 |
160 | | }; |
161 | | |
162 | | static inline Result OCSPResponse(Reader&, Context&); |
163 | | static inline Result ResponseBytes(Reader&, Context&); |
164 | | static inline Result BasicResponse(Reader&, Context&); |
165 | | static inline Result ResponseData( |
166 | | Reader& tbsResponseData, |
167 | | Context& context, |
168 | | const der::SignedDataWithSignature& signedResponseData, |
169 | | const DERArray& certs); |
170 | | static inline Result SingleResponse(Reader& input, Context& context); |
171 | | static Result ExtensionNotUnderstood(Reader& extnID, Input extnValue, |
172 | | bool critical, /*out*/ bool& understood); |
173 | | static Result RememberSingleExtension(Context& context, Reader& extnID, |
174 | | Input extnValue, bool critical, |
175 | | /*out*/ bool& understood); |
176 | | // It is convention to name the function after the part of the data structure |
177 | | // we're parsing from the RFC (e.g. OCSPResponse, ResponseBytes). |
178 | | // But since we also have a C++ type called CertID, this function doesn't |
179 | | // follow the convention to prevent shadowing. |
180 | | static inline Result MatchCertID(Reader& input, |
181 | | const Context& context, |
182 | | /*out*/ bool& match); |
183 | | static Result MatchKeyHash(TrustDomain& trustDomain, |
184 | | Input issuerKeyHash, |
185 | | Input issuerSubjectPublicKeyInfo, |
186 | | /*out*/ bool& match); |
187 | | static Result KeyHash(TrustDomain& trustDomain, |
188 | | Input subjectPublicKeyInfo, |
189 | | /*out*/ uint8_t* hashBuf, size_t hashBufSize); |
190 | | |
191 | | static Result |
192 | | MatchResponderID(TrustDomain& trustDomain, |
193 | | ResponderIDType responderIDType, |
194 | | Input responderID, |
195 | | Input potentialSignerSubject, |
196 | | Input potentialSignerSubjectPublicKeyInfo, |
197 | | /*out*/ bool& match) |
198 | 0 | { |
199 | 0 | match = false; |
200 | 0 |
|
201 | 0 | switch (responderIDType) { |
202 | 0 | case ResponderIDType::byName: |
203 | 0 | // XXX(bug 926270) XXX(bug 1008133) XXX(bug 980163): Improve name |
204 | 0 | // comparison. |
205 | 0 | match = InputsAreEqual(responderID, potentialSignerSubject); |
206 | 0 | return Success; |
207 | 0 |
|
208 | 0 | case ResponderIDType::byKey: |
209 | 0 | { |
210 | 0 | Reader input(responderID); |
211 | 0 | Input keyHash; |
212 | 0 | Result rv = der::ExpectTagAndGetValue(input, der::OCTET_STRING, keyHash); |
213 | 0 | if (rv != Success) { |
214 | 0 | return rv; |
215 | 0 | } |
216 | 0 | return MatchKeyHash(trustDomain, keyHash, |
217 | 0 | potentialSignerSubjectPublicKeyInfo, match); |
218 | 0 | } |
219 | 0 |
|
220 | 0 | MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM |
221 | 0 | } |
222 | 0 | } |
223 | | |
224 | | static Result |
225 | | VerifyOCSPSignedData(TrustDomain& trustDomain, |
226 | | const der::SignedDataWithSignature& signedResponseData, |
227 | | Input spki) |
228 | 0 | { |
229 | 0 | Result rv = VerifySignedData(trustDomain, signedResponseData, spki); |
230 | 0 | if (rv == Result::ERROR_BAD_SIGNATURE) { |
231 | 0 | rv = Result::ERROR_OCSP_BAD_SIGNATURE; |
232 | 0 | } |
233 | 0 | return rv; |
234 | 0 | } |
235 | | |
236 | | // RFC 6960 section 4.2.2.2: The OCSP responder must either be the issuer of |
237 | | // the cert or it must be a delegated OCSP response signing cert directly |
238 | | // issued by the issuer. If the OCSP responder is a delegated OCSP response |
239 | | // signer, then its certificate is (probably) embedded within the OCSP |
240 | | // response and we'll need to verify that it is a valid certificate that chains |
241 | | // *directly* to issuerCert. |
242 | | static Result |
243 | | VerifySignature(Context& context, ResponderIDType responderIDType, |
244 | | Input responderID, const DERArray& certs, |
245 | | const der::SignedDataWithSignature& signedResponseData) |
246 | 0 | { |
247 | 0 | bool match; |
248 | 0 | Result rv = MatchResponderID(context.trustDomain, responderIDType, |
249 | 0 | responderID, context.certID.issuer, |
250 | 0 | context.certID.issuerSubjectPublicKeyInfo, |
251 | 0 | match); |
252 | 0 | if (rv != Success) { |
253 | 0 | return rv; |
254 | 0 | } |
255 | 0 | if (match) { |
256 | 0 | return VerifyOCSPSignedData(context.trustDomain, signedResponseData, |
257 | 0 | context.certID.issuerSubjectPublicKeyInfo); |
258 | 0 | } |
259 | 0 | |
260 | 0 | size_t numCerts = certs.GetLength(); |
261 | 0 | for (size_t i = 0; i < numCerts; ++i) { |
262 | 0 | BackCert cert(*certs.GetDER(i), EndEntityOrCA::MustBeEndEntity, nullptr); |
263 | 0 | rv = cert.Init(); |
264 | 0 | if (rv != Success) { |
265 | 0 | return rv; |
266 | 0 | } |
267 | 0 | rv = MatchResponderID(context.trustDomain, responderIDType, responderID, |
268 | 0 | cert.GetSubject(), cert.GetSubjectPublicKeyInfo(), |
269 | 0 | match); |
270 | 0 | if (rv != Success) { |
271 | 0 | if (IsFatalError(rv)) { |
272 | 0 | return rv; |
273 | 0 | } |
274 | 0 | continue; |
275 | 0 | } |
276 | 0 | |
277 | 0 | if (match) { |
278 | 0 | rv = CheckOCSPResponseSignerCert(context.trustDomain, cert, |
279 | 0 | context.certID.issuer, |
280 | 0 | context.certID.issuerSubjectPublicKeyInfo, |
281 | 0 | context.time); |
282 | 0 | if (rv != Success) { |
283 | 0 | if (IsFatalError(rv)) { |
284 | 0 | return rv; |
285 | 0 | } |
286 | 0 | continue; |
287 | 0 | } |
288 | 0 | |
289 | 0 | return VerifyOCSPSignedData(context.trustDomain, signedResponseData, |
290 | 0 | cert.GetSubjectPublicKeyInfo()); |
291 | 0 | } |
292 | 0 | } |
293 | 0 |
|
294 | 0 | return Result::ERROR_OCSP_INVALID_SIGNING_CERT; |
295 | 0 | } |
296 | | |
297 | | static inline Result |
298 | | MapBadDERToMalformedOCSPResponse(Result rv) |
299 | 0 | { |
300 | 0 | if (rv == Result::ERROR_BAD_DER) { |
301 | 0 | return Result::ERROR_OCSP_MALFORMED_RESPONSE; |
302 | 0 | } |
303 | 0 | return rv; |
304 | 0 | } |
305 | | |
306 | | Result |
307 | | VerifyEncodedOCSPResponse(TrustDomain& trustDomain, const struct CertID& certID, |
308 | | Time time, uint16_t maxOCSPLifetimeInDays, |
309 | | Input encodedResponse, |
310 | | /*out*/ bool& expired, |
311 | | /*optional out*/ Time* thisUpdate, |
312 | | /*optional out*/ Time* validThrough) |
313 | 0 | { |
314 | 0 | // Always initialize this to something reasonable. |
315 | 0 | expired = false; |
316 | 0 |
|
317 | 0 | Context context(trustDomain, certID, time, maxOCSPLifetimeInDays, |
318 | 0 | thisUpdate, validThrough); |
319 | 0 |
|
320 | 0 | Reader input(encodedResponse); |
321 | 0 | Result rv = der::Nested(input, der::SEQUENCE, [&context](Reader& r) { |
322 | 0 | return OCSPResponse(r, context); |
323 | 0 | }); |
324 | 0 | if (rv != Success) { |
325 | 0 | return MapBadDERToMalformedOCSPResponse(rv); |
326 | 0 | } |
327 | 0 | rv = der::End(input); |
328 | 0 | if (rv != Success) { |
329 | 0 | return MapBadDERToMalformedOCSPResponse(rv); |
330 | 0 | } |
331 | 0 | if (!context.matchFound) { |
332 | 0 | return Result::ERROR_OCSP_RESPONSE_FOR_CERT_MISSING; |
333 | 0 | } |
334 | 0 | |
335 | 0 | expired = context.expired; |
336 | 0 |
|
337 | 0 | switch (context.certStatus) { |
338 | 0 | case CertStatus::Good: |
339 | 0 | if (expired) { |
340 | 0 | return Result::ERROR_OCSP_OLD_RESPONSE; |
341 | 0 | } |
342 | 0 | if (context.signedCertificateTimestamps.GetLength()) { |
343 | 0 | Input sctList; |
344 | 0 | rv = ExtractSignedCertificateTimestampListFromExtension( |
345 | 0 | context.signedCertificateTimestamps, sctList); |
346 | 0 | if (rv != Success) { |
347 | 0 | return MapBadDERToMalformedOCSPResponse(rv); |
348 | 0 | } |
349 | 0 | context.trustDomain.NoteAuxiliaryExtension( |
350 | 0 | AuxiliaryExtension::SCTListFromOCSPResponse, sctList); |
351 | 0 | } |
352 | 0 | return Success; |
353 | 0 | case CertStatus::Revoked: |
354 | 0 | return Result::ERROR_REVOKED_CERTIFICATE; |
355 | 0 | case CertStatus::Unknown: |
356 | 0 | return Result::ERROR_OCSP_UNKNOWN_CERT; |
357 | 0 | MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM |
358 | 0 | } |
359 | 0 | } |
360 | | |
361 | | // OCSPResponse ::= SEQUENCE { |
362 | | // responseStatus OCSPResponseStatus, |
363 | | // responseBytes [0] EXPLICIT ResponseBytes OPTIONAL } |
364 | | // |
365 | | static inline Result |
366 | | OCSPResponse(Reader& input, Context& context) |
367 | 0 | { |
368 | 0 | // OCSPResponseStatus ::= ENUMERATED { |
369 | 0 | // successful (0), -- Response has valid confirmations |
370 | 0 | // malformedRequest (1), -- Illegal confirmation request |
371 | 0 | // internalError (2), -- Internal error in issuer |
372 | 0 | // tryLater (3), -- Try again later |
373 | 0 | // -- (4) is not used |
374 | 0 | // sigRequired (5), -- Must sign the request |
375 | 0 | // unauthorized (6) -- Request unauthorized |
376 | 0 | // } |
377 | 0 | uint8_t responseStatus; |
378 | 0 |
|
379 | 0 | Result rv = der::Enumerated(input, responseStatus); |
380 | 0 | if (rv != Success) { |
381 | 0 | return rv; |
382 | 0 | } |
383 | 0 | switch (responseStatus) { |
384 | 0 | case 0: break; // successful |
385 | 0 | case 1: return Result::ERROR_OCSP_MALFORMED_REQUEST; |
386 | 0 | case 2: return Result::ERROR_OCSP_SERVER_ERROR; |
387 | 0 | case 3: return Result::ERROR_OCSP_TRY_SERVER_LATER; |
388 | 0 | case 5: return Result::ERROR_OCSP_REQUEST_NEEDS_SIG; |
389 | 0 | case 6: return Result::ERROR_OCSP_UNAUTHORIZED_REQUEST; |
390 | 0 | default: return Result::ERROR_OCSP_UNKNOWN_RESPONSE_STATUS; |
391 | 0 | } |
392 | 0 | |
393 | 0 | return der::Nested(input, der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 0, |
394 | 0 | der::SEQUENCE, [&context](Reader& r) { |
395 | 0 | return ResponseBytes(r, context); |
396 | 0 | }); |
397 | 0 | } |
398 | | |
399 | | // ResponseBytes ::= SEQUENCE { |
400 | | // responseType OBJECT IDENTIFIER, |
401 | | // response OCTET STRING } |
402 | | static inline Result |
403 | | ResponseBytes(Reader& input, Context& context) |
404 | 0 | { |
405 | 0 | static const uint8_t id_pkix_ocsp_basic[] = { |
406 | 0 | 0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x01 |
407 | 0 | }; |
408 | 0 |
|
409 | 0 | Result rv = der::OID(input, id_pkix_ocsp_basic); |
410 | 0 | if (rv != Success) { |
411 | 0 | return rv; |
412 | 0 | } |
413 | 0 | |
414 | 0 | return der::Nested(input, der::OCTET_STRING, der::SEQUENCE, |
415 | 0 | [&context](Reader& r) { |
416 | 0 | return BasicResponse(r, context); |
417 | 0 | }); |
418 | 0 | } |
419 | | |
420 | | // BasicOCSPResponse ::= SEQUENCE { |
421 | | // tbsResponseData ResponseData, |
422 | | // signatureAlgorithm AlgorithmIdentifier, |
423 | | // signature BIT STRING, |
424 | | // certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL } |
425 | | Result |
426 | | BasicResponse(Reader& input, Context& context) |
427 | 0 | { |
428 | 0 | Reader tbsResponseData; |
429 | 0 | der::SignedDataWithSignature signedData; |
430 | 0 | Result rv = der::SignedData(input, tbsResponseData, signedData); |
431 | 0 | if (rv != Success) { |
432 | 0 | if (rv == Result::ERROR_BAD_SIGNATURE) { |
433 | 0 | return Result::ERROR_OCSP_BAD_SIGNATURE; |
434 | 0 | } |
435 | 0 | return rv; |
436 | 0 | } |
437 | 0 | |
438 | 0 | // Parse certificates, if any |
439 | 0 | NonOwningDERArray certs; |
440 | 0 | if (!input.AtEnd()) { |
441 | 0 | rv = der::Nested(input, der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 0, |
442 | 0 | der::SEQUENCE, [&certs](Reader& certsDER) -> Result { |
443 | 0 | while (!certsDER.AtEnd()) { |
444 | 0 | Input cert; |
445 | 0 | Result nestedRv = |
446 | 0 | der::ExpectTagAndGetTLV(certsDER, der::SEQUENCE, cert); |
447 | 0 | if (nestedRv != Success) { |
448 | 0 | return nestedRv; |
449 | 0 | } |
450 | 0 | nestedRv = certs.Append(cert); |
451 | 0 | if (nestedRv != Success) { |
452 | 0 | return Result::ERROR_BAD_DER; // Too many certs |
453 | 0 | } |
454 | 0 | } |
455 | 0 | return Success; |
456 | 0 | }); |
457 | 0 | if (rv != Success) { |
458 | 0 | return rv; |
459 | 0 | } |
460 | 0 | } |
461 | 0 | |
462 | 0 | return ResponseData(tbsResponseData, context, signedData, certs); |
463 | 0 | } |
464 | | |
465 | | // ResponseData ::= SEQUENCE { |
466 | | // version [0] EXPLICIT Version DEFAULT v1, |
467 | | // responderID ResponderID, |
468 | | // producedAt GeneralizedTime, |
469 | | // responses SEQUENCE OF SingleResponse, |
470 | | // responseExtensions [1] EXPLICIT Extensions OPTIONAL } |
471 | | static inline Result |
472 | | ResponseData(Reader& input, Context& context, |
473 | | const der::SignedDataWithSignature& signedResponseData, |
474 | | const DERArray& certs) |
475 | 0 | { |
476 | 0 | der::Version version; |
477 | 0 | Result rv = der::OptionalVersion(input, version); |
478 | 0 | if (rv != Success) { |
479 | 0 | return rv; |
480 | 0 | } |
481 | 0 | if (version != der::Version::v1) { |
482 | 0 | // TODO: more specific error code for bad version? |
483 | 0 | return Result::ERROR_BAD_DER; |
484 | 0 | } |
485 | 0 | |
486 | 0 | // ResponderID ::= CHOICE { |
487 | 0 | // byName [1] Name, |
488 | 0 | // byKey [2] KeyHash } |
489 | 0 | Input responderID; |
490 | 0 | ResponderIDType responderIDType |
491 | 0 | = input.Peek(static_cast<uint8_t>(ResponderIDType::byName)) |
492 | 0 | ? ResponderIDType::byName |
493 | 0 | : ResponderIDType::byKey; |
494 | 0 | rv = der::ExpectTagAndGetValue(input, static_cast<uint8_t>(responderIDType), |
495 | 0 | responderID); |
496 | 0 | if (rv != Success) { |
497 | 0 | return rv; |
498 | 0 | } |
499 | 0 | |
500 | 0 | // This is the soonest we can verify the signature. We verify the signature |
501 | 0 | // right away to follow the principal of minimizing the processing of data |
502 | 0 | // before verifying its signature. |
503 | 0 | rv = VerifySignature(context, responderIDType, responderID, certs, |
504 | 0 | signedResponseData); |
505 | 0 | if (rv != Success) { |
506 | 0 | return rv; |
507 | 0 | } |
508 | 0 | |
509 | 0 | // TODO: Do we even need to parse this? Should we just skip it? |
510 | 0 | Time producedAt(Time::uninitialized); |
511 | 0 | rv = der::GeneralizedTime(input, producedAt); |
512 | 0 | if (rv != Success) { |
513 | 0 | return rv; |
514 | 0 | } |
515 | 0 | |
516 | 0 | // We don't accept an empty sequence of responses. In practice, a legit OCSP |
517 | 0 | // responder will never return an empty response, and handling the case of an |
518 | 0 | // empty response makes things unnecessarily complicated. |
519 | 0 | rv = der::NestedOf(input, der::SEQUENCE, der::SEQUENCE, |
520 | 0 | der::EmptyAllowed::No, [&context](Reader& r) { |
521 | 0 | return SingleResponse(r, context); |
522 | 0 | }); |
523 | 0 | if (rv != Success) { |
524 | 0 | return rv; |
525 | 0 | } |
526 | 0 | |
527 | 0 | return der::OptionalExtensions(input, |
528 | 0 | der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 1, |
529 | 0 | ExtensionNotUnderstood); |
530 | 0 | } |
531 | | |
532 | | // SingleResponse ::= SEQUENCE { |
533 | | // certID CertID, |
534 | | // certStatus CertStatus, |
535 | | // thisUpdate GeneralizedTime, |
536 | | // nextUpdate [0] EXPLICIT GeneralizedTime OPTIONAL, |
537 | | // singleExtensions [1] EXPLICIT Extensions{{re-ocsp-crl | |
538 | | // re-ocsp-archive-cutoff | |
539 | | // CrlEntryExtensions, ...} |
540 | | // } OPTIONAL } |
541 | | static inline Result |
542 | | SingleResponse(Reader& input, Context& context) |
543 | 0 | { |
544 | 0 | bool match = false; |
545 | 0 | Result rv = der::Nested(input, der::SEQUENCE, [&context, &match](Reader& r) { |
546 | 0 | return MatchCertID(r, context, match); |
547 | 0 | }); |
548 | 0 | if (rv != Success) { |
549 | 0 | return rv; |
550 | 0 | } |
551 | 0 | |
552 | 0 | if (!match) { |
553 | 0 | // This response does not reference the certificate we're interested in. |
554 | 0 | // By consuming the rest of our input and returning successfully, we can |
555 | 0 | // continue processing and examine another response that might have what |
556 | 0 | // we want. |
557 | 0 | input.SkipToEnd(); |
558 | 0 | return Success; |
559 | 0 | } |
560 | 0 | |
561 | 0 | // We found a response for the cert we're interested in. |
562 | 0 | context.matchFound = true; |
563 | 0 |
|
564 | 0 | // CertStatus ::= CHOICE { |
565 | 0 | // good [0] IMPLICIT NULL, |
566 | 0 | // revoked [1] IMPLICIT RevokedInfo, |
567 | 0 | // unknown [2] IMPLICIT UnknownInfo } |
568 | 0 | // |
569 | 0 | // In the event of multiple SingleResponses for a cert that have conflicting |
570 | 0 | // statuses, we use the following precedence rules: |
571 | 0 | // |
572 | 0 | // * revoked overrides good and unknown |
573 | 0 | // * good overrides unknown |
574 | 0 | if (input.Peek(static_cast<uint8_t>(CertStatus::Good))) { |
575 | 0 | rv = der::ExpectTagAndEmptyValue(input, |
576 | 0 | static_cast<uint8_t>(CertStatus::Good)); |
577 | 0 | if (rv != Success) { |
578 | 0 | return rv; |
579 | 0 | } |
580 | 0 | if (context.certStatus != CertStatus::Revoked) { |
581 | 0 | context.certStatus = CertStatus::Good; |
582 | 0 | } |
583 | 0 | } else if (input.Peek(static_cast<uint8_t>(CertStatus::Revoked))) { |
584 | 0 | // We don't need any info from the RevokedInfo structure, so we don't even |
585 | 0 | // parse it. TODO: We should mention issues like this in the explanation of |
586 | 0 | // why we treat invalid OCSP responses equivalently to revoked for OCSP |
587 | 0 | // stapling. |
588 | 0 | rv = der::ExpectTagAndSkipValue(input, |
589 | 0 | static_cast<uint8_t>(CertStatus::Revoked)); |
590 | 0 | if (rv != Success) { |
591 | 0 | return rv; |
592 | 0 | } |
593 | 0 | context.certStatus = CertStatus::Revoked; |
594 | 0 | } else { |
595 | 0 | rv = der::ExpectTagAndEmptyValue(input, |
596 | 0 | static_cast<uint8_t>(CertStatus::Unknown)); |
597 | 0 | if (rv != Success) { |
598 | 0 | return rv; |
599 | 0 | } |
600 | 0 | } |
601 | 0 | |
602 | 0 | // http://tools.ietf.org/html/rfc6960#section-3.2 |
603 | 0 | // 5. The time at which the status being indicated is known to be |
604 | 0 | // correct (thisUpdate) is sufficiently recent; |
605 | 0 | // 6. When available, the time at or before which newer information will |
606 | 0 | // be available about the status of the certificate (nextUpdate) is |
607 | 0 | // greater than the current time. |
608 | 0 | |
609 | 0 | Time thisUpdate(Time::uninitialized); |
610 | 0 | rv = der::GeneralizedTime(input, thisUpdate); |
611 | 0 | if (rv != Success) { |
612 | 0 | return rv; |
613 | 0 | } |
614 | 0 | |
615 | 0 | static const uint64_t SLOP_SECONDS = Time::ONE_DAY_IN_SECONDS; |
616 | 0 |
|
617 | 0 | Time timePlusSlop(context.time); |
618 | 0 | rv = timePlusSlop.AddSeconds(SLOP_SECONDS); |
619 | 0 | if (rv != Success) { |
620 | 0 | return rv; |
621 | 0 | } |
622 | 0 | if (thisUpdate > timePlusSlop) { |
623 | 0 | return Result::ERROR_OCSP_FUTURE_RESPONSE; |
624 | 0 | } |
625 | 0 | |
626 | 0 | Time notAfter(Time::uninitialized); |
627 | 0 | static const uint8_t NEXT_UPDATE_TAG = |
628 | 0 | der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 0; |
629 | 0 | if (input.Peek(NEXT_UPDATE_TAG)) { |
630 | 0 | Time nextUpdate(Time::uninitialized); |
631 | 0 | rv = der::Nested(input, NEXT_UPDATE_TAG, [&nextUpdate](Reader& r) { |
632 | 0 | return der::GeneralizedTime(r, nextUpdate); |
633 | 0 | }); |
634 | 0 | if (rv != Success) { |
635 | 0 | return rv; |
636 | 0 | } |
637 | 0 | |
638 | 0 | if (nextUpdate < thisUpdate) { |
639 | 0 | return Result::ERROR_OCSP_MALFORMED_RESPONSE; |
640 | 0 | } |
641 | 0 | notAfter = thisUpdate; |
642 | 0 | if (notAfter.AddSeconds(context.maxLifetimeInDays * |
643 | 0 | Time::ONE_DAY_IN_SECONDS) != Success) { |
644 | 0 | // This could only happen if we're dealing with times beyond the year |
645 | 0 | // 10,000AD. |
646 | 0 | return Result::ERROR_OCSP_FUTURE_RESPONSE; |
647 | 0 | } |
648 | 0 | if (nextUpdate <= notAfter) { |
649 | 0 | notAfter = nextUpdate; |
650 | 0 | } |
651 | 0 | } else { |
652 | 0 | // NSS requires all OCSP responses without a nextUpdate to be recent. |
653 | 0 | // Match that stricter behavior. |
654 | 0 | notAfter = thisUpdate; |
655 | 0 | if (notAfter.AddSeconds(Time::ONE_DAY_IN_SECONDS) != Success) { |
656 | 0 | // This could only happen if we're dealing with times beyond the year |
657 | 0 | // 10,000AD. |
658 | 0 | return Result::ERROR_OCSP_FUTURE_RESPONSE; |
659 | 0 | } |
660 | 0 | } |
661 | 0 | |
662 | 0 | // Add some slop to hopefully handle clock-skew. |
663 | 0 | Time notAfterPlusSlop(notAfter); |
664 | 0 | rv = notAfterPlusSlop.AddSeconds(SLOP_SECONDS); |
665 | 0 | if (rv != Success) { |
666 | 0 | // This could only happen if we're dealing with times beyond the year |
667 | 0 | // 10,000AD. |
668 | 0 | return Result::ERROR_OCSP_FUTURE_RESPONSE; |
669 | 0 | } |
670 | 0 | if (context.time > notAfterPlusSlop) { |
671 | 0 | context.expired = true; |
672 | 0 | } |
673 | 0 |
|
674 | 0 | rv = der::OptionalExtensions( |
675 | 0 | input, |
676 | 0 | der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 1, |
677 | 0 | [&context](Reader& extnID, const Input& extnValue, bool critical, |
678 | 0 | /*out*/ bool& understood) { |
679 | 0 | return RememberSingleExtension(context, extnID, extnValue, critical, |
680 | 0 | understood); |
681 | 0 | }); |
682 | 0 |
|
683 | 0 | if (rv != Success) { |
684 | 0 | return rv; |
685 | 0 | } |
686 | 0 | |
687 | 0 | if (context.thisUpdate) { |
688 | 0 | *context.thisUpdate = thisUpdate; |
689 | 0 | } |
690 | 0 | if (context.validThrough) { |
691 | 0 | *context.validThrough = notAfterPlusSlop; |
692 | 0 | } |
693 | 0 |
|
694 | 0 | return Success; |
695 | 0 | } |
696 | | |
697 | | // CertID ::= SEQUENCE { |
698 | | // hashAlgorithm AlgorithmIdentifier, |
699 | | // issuerNameHash OCTET STRING, -- Hash of issuer's DN |
700 | | // issuerKeyHash OCTET STRING, -- Hash of issuer's public key |
701 | | // serialNumber CertificateSerialNumber } |
702 | | static inline Result |
703 | | MatchCertID(Reader& input, const Context& context, /*out*/ bool& match) |
704 | 0 | { |
705 | 0 | match = false; |
706 | 0 |
|
707 | 0 | DigestAlgorithm hashAlgorithm; |
708 | 0 | Result rv = der::DigestAlgorithmIdentifier(input, hashAlgorithm); |
709 | 0 | if (rv != Success) { |
710 | 0 | if (rv == Result::ERROR_INVALID_ALGORITHM) { |
711 | 0 | // Skip entries that are hashed with algorithms we don't support. |
712 | 0 | input.SkipToEnd(); |
713 | 0 | return Success; |
714 | 0 | } |
715 | 0 | return rv; |
716 | 0 | } |
717 | 0 | |
718 | 0 | Input issuerNameHash; |
719 | 0 | rv = der::ExpectTagAndGetValue(input, der::OCTET_STRING, issuerNameHash); |
720 | 0 | if (rv != Success) { |
721 | 0 | return rv; |
722 | 0 | } |
723 | 0 | |
724 | 0 | Input issuerKeyHash; |
725 | 0 | rv = der::ExpectTagAndGetValue(input, der::OCTET_STRING, issuerKeyHash); |
726 | 0 | if (rv != Success) { |
727 | 0 | return rv; |
728 | 0 | } |
729 | 0 | |
730 | 0 | Input serialNumber; |
731 | 0 | rv = der::CertificateSerialNumber(input, serialNumber); |
732 | 0 | if (rv != Success) { |
733 | 0 | return rv; |
734 | 0 | } |
735 | 0 | |
736 | 0 | if (!InputsAreEqual(serialNumber, context.certID.serialNumber)) { |
737 | 0 | // This does not reference the certificate we're interested in. |
738 | 0 | // Consume the rest of the input and return successfully to |
739 | 0 | // potentially continue processing other responses. |
740 | 0 | input.SkipToEnd(); |
741 | 0 | return Success; |
742 | 0 | } |
743 | 0 | |
744 | 0 | // TODO: support SHA-2 hashes. |
745 | 0 | |
746 | 0 | if (hashAlgorithm != DigestAlgorithm::sha1) { |
747 | 0 | // Again, not interested in this response. Consume input, return success. |
748 | 0 | input.SkipToEnd(); |
749 | 0 | return Success; |
750 | 0 | } |
751 | 0 | |
752 | 0 | if (issuerNameHash.GetLength() != SHA1_DIGEST_LENGTH) { |
753 | 0 | return Result::ERROR_OCSP_MALFORMED_RESPONSE; |
754 | 0 | } |
755 | 0 | |
756 | 0 | // From http://tools.ietf.org/html/rfc6960#section-4.1.1: |
757 | 0 | // "The hash shall be calculated over the DER encoding of the |
758 | 0 | // issuer's name field in the certificate being checked." |
759 | 0 | uint8_t hashBuf[SHA1_DIGEST_LENGTH]; |
760 | 0 | rv = context.trustDomain.DigestBuf(context.certID.issuer, |
761 | 0 | DigestAlgorithm::sha1, hashBuf, |
762 | 0 | sizeof(hashBuf)); |
763 | 0 | if (rv != Success) { |
764 | 0 | return rv; |
765 | 0 | } |
766 | 0 | Input computed(hashBuf); |
767 | 0 | if (!InputsAreEqual(computed, issuerNameHash)) { |
768 | 0 | // Again, not interested in this response. Consume input, return success. |
769 | 0 | input.SkipToEnd(); |
770 | 0 | return Success; |
771 | 0 | } |
772 | 0 | |
773 | 0 | return MatchKeyHash(context.trustDomain, issuerKeyHash, |
774 | 0 | context.certID.issuerSubjectPublicKeyInfo, match); |
775 | 0 | } |
776 | | |
777 | | // From http://tools.ietf.org/html/rfc6960#section-4.1.1: |
778 | | // "The hash shall be calculated over the value (excluding tag and length) of |
779 | | // the subject public key field in the issuer's certificate." |
780 | | // |
781 | | // From http://tools.ietf.org/html/rfc6960#appendix-B.1: |
782 | | // KeyHash ::= OCTET STRING -- SHA-1 hash of responder's public key |
783 | | // -- (i.e., the SHA-1 hash of the value of the |
784 | | // -- BIT STRING subjectPublicKey [excluding |
785 | | // -- the tag, length, and number of unused |
786 | | // -- bits] in the responder's certificate) |
787 | | static Result |
788 | | MatchKeyHash(TrustDomain& trustDomain, Input keyHash, |
789 | | const Input subjectPublicKeyInfo, /*out*/ bool& match) |
790 | 0 | { |
791 | 0 | if (keyHash.GetLength() != SHA1_DIGEST_LENGTH) { |
792 | 0 | return Result::ERROR_OCSP_MALFORMED_RESPONSE; |
793 | 0 | } |
794 | 0 | uint8_t hashBuf[SHA1_DIGEST_LENGTH]; |
795 | 0 | Result rv = KeyHash(trustDomain, subjectPublicKeyInfo, hashBuf, |
796 | 0 | sizeof hashBuf); |
797 | 0 | if (rv != Success) { |
798 | 0 | return rv; |
799 | 0 | } |
800 | 0 | Input computed(hashBuf); |
801 | 0 | match = InputsAreEqual(computed, keyHash); |
802 | 0 | return Success; |
803 | 0 | } |
804 | | |
805 | | // TODO(bug 966856): support SHA-2 hashes |
806 | | Result |
807 | | KeyHash(TrustDomain& trustDomain, const Input subjectPublicKeyInfo, |
808 | | /*out*/ uint8_t* hashBuf, size_t hashBufSize) |
809 | 0 | { |
810 | 0 | if (!hashBuf || hashBufSize != SHA1_DIGEST_LENGTH) { |
811 | 0 | return Result::FATAL_ERROR_LIBRARY_FAILURE; |
812 | 0 | } |
813 | 0 | |
814 | 0 | // RFC 5280 Section 4.1 |
815 | 0 | // |
816 | 0 | // SubjectPublicKeyInfo ::= SEQUENCE { |
817 | 0 | // algorithm AlgorithmIdentifier, |
818 | 0 | // subjectPublicKey BIT STRING } |
819 | 0 | |
820 | 0 | Reader spki; |
821 | 0 | Result rv = der::ExpectTagAndGetValueAtEnd(subjectPublicKeyInfo, |
822 | 0 | der::SEQUENCE, spki); |
823 | 0 | if (rv != Success) { |
824 | 0 | return rv; |
825 | 0 | } |
826 | 0 | |
827 | 0 | // Skip AlgorithmIdentifier |
828 | 0 | rv = der::ExpectTagAndSkipValue(spki, der::SEQUENCE); |
829 | 0 | if (rv != Success) { |
830 | 0 | return rv; |
831 | 0 | } |
832 | 0 | |
833 | 0 | Input subjectPublicKey; |
834 | 0 | rv = der::BitStringWithNoUnusedBits(spki, subjectPublicKey); |
835 | 0 | if (rv != Success) { |
836 | 0 | return rv; |
837 | 0 | } |
838 | 0 | rv = der::End(spki); |
839 | 0 | if (rv != Success) { |
840 | 0 | return rv; |
841 | 0 | } |
842 | 0 | |
843 | 0 | return trustDomain.DigestBuf(subjectPublicKey, DigestAlgorithm::sha1, |
844 | 0 | hashBuf, hashBufSize); |
845 | 0 | } |
846 | | |
847 | | Result |
848 | | ExtensionNotUnderstood(Reader& /*extnID*/, Input /*extnValue*/, |
849 | | bool /*critical*/, /*out*/ bool& understood) |
850 | 0 | { |
851 | 0 | understood = false; |
852 | 0 | return Success; |
853 | 0 | } |
854 | | |
855 | | Result |
856 | | RememberSingleExtension(Context& context, Reader& extnID, Input extnValue, |
857 | | bool /*critical*/, /*out*/ bool& understood) |
858 | 0 | { |
859 | 0 | understood = false; |
860 | 0 |
|
861 | 0 | // SingleExtension for Signed Certificate Timestamp List. |
862 | 0 | // See Section 3.3 of RFC 6962. |
863 | 0 | // python DottedOIDToCode.py |
864 | 0 | // id_ocsp_singleExtensionSctList 1.3.6.1.4.1.11129.2.4.5 |
865 | 0 | static const uint8_t id_ocsp_singleExtensionSctList[] = { |
866 | 0 | 0x2b, 0x06, 0x01, 0x04, 0x01, 0xd6, 0x79, 0x02, 0x04, 0x05 |
867 | 0 | }; |
868 | 0 |
|
869 | 0 | if (extnID.MatchRest(id_ocsp_singleExtensionSctList)) { |
870 | 0 | // Empty values are not allowed for this extension. Note that |
871 | 0 | // we assume this later, when checking if the extension was present. |
872 | 0 | if (extnValue.GetLength() == 0) { |
873 | 0 | return Result::ERROR_EXTENSION_VALUE_INVALID; |
874 | 0 | } |
875 | 0 | if (context.signedCertificateTimestamps.Init(extnValue) != Success) { |
876 | 0 | // Duplicate extension. |
877 | 0 | return Result::ERROR_EXTENSION_VALUE_INVALID; |
878 | 0 | } |
879 | 0 | understood = true; |
880 | 0 | } |
881 | 0 |
|
882 | 0 | return Success; |
883 | 0 | } |
884 | | |
885 | | // 1. The certificate identified in a received response corresponds to |
886 | | // the certificate that was identified in the corresponding request; |
887 | | // 2. The signature on the response is valid; |
888 | | // 3. The identity of the signer matches the intended recipient of the |
889 | | // request; |
890 | | // 4. The signer is currently authorized to provide a response for the |
891 | | // certificate in question; |
892 | | // 5. The time at which the status being indicated is known to be |
893 | | // correct (thisUpdate) is sufficiently recent; |
894 | | // 6. When available, the time at or before which newer information will |
895 | | // be available about the status of the certificate (nextUpdate) is |
896 | | // greater than the current time. |
897 | | // |
898 | | // Responses whose nextUpdate value is earlier than |
899 | | // the local system time value SHOULD be considered unreliable. |
900 | | // Responses whose thisUpdate time is later than the local system time |
901 | | // SHOULD be considered unreliable. |
902 | | // |
903 | | // If nextUpdate is not set, the responder is indicating that newer |
904 | | // revocation information is available all the time. |
905 | | // |
906 | | // http://tools.ietf.org/html/rfc5019#section-4 |
907 | | |
908 | | Result |
909 | | CreateEncodedOCSPRequest(TrustDomain& trustDomain, const struct CertID& certID, |
910 | | /*out*/ uint8_t (&out)[OCSP_REQUEST_MAX_LENGTH], |
911 | | /*out*/ size_t& outLen) |
912 | 0 | { |
913 | 0 | // We do not add any extensions to the request. |
914 | 0 |
|
915 | 0 | // RFC 6960 says "An OCSP client MAY wish to specify the kinds of response |
916 | 0 | // types it understands. To do so, it SHOULD use an extension with the OID |
917 | 0 | // id-pkix-ocsp-response." This use of MAY and SHOULD is unclear. MSIE11 |
918 | 0 | // on Windows 8.1 does not include any extensions, whereas NSS has always |
919 | 0 | // included the id-pkix-ocsp-response extension. Avoiding the sending the |
920 | 0 | // extension is better for OCSP GET because it makes the request smaller, |
921 | 0 | // and thus more likely to fit within the 255 byte limit for OCSP GET that |
922 | 0 | // is specified in RFC 5019 Section 5. |
923 | 0 |
|
924 | 0 | // Bug 966856: Add the id-pkix-ocsp-pref-sig-algs extension. |
925 | 0 |
|
926 | 0 | // Since we don't know whether the OCSP responder supports anything other |
927 | 0 | // than SHA-1, we have no choice but to use SHA-1 for issuerNameHash and |
928 | 0 | // issuerKeyHash. |
929 | 0 | static const uint8_t hashAlgorithm[11] = { |
930 | 0 | 0x30, 0x09, // SEQUENCE |
931 | 0 | 0x06, 0x05, 0x2B, 0x0E, 0x03, 0x02, 0x1A, // OBJECT IDENTIFIER id-sha1 |
932 | 0 | 0x05, 0x00, // NULL |
933 | 0 | }; |
934 | 0 | static const uint8_t hashLen = 160 / 8; |
935 | 0 |
|
936 | 0 | static const unsigned int totalLenWithoutSerialNumberData |
937 | 0 | = 2 // OCSPRequest |
938 | 0 | + 2 // tbsRequest |
939 | 0 | + 2 // requestList |
940 | 0 | + 2 // Request |
941 | 0 | + 2 // reqCert (CertID) |
942 | 0 | + sizeof(hashAlgorithm) // hashAlgorithm |
943 | 0 | + 2 + hashLen // issuerNameHash |
944 | 0 | + 2 + hashLen // issuerKeyHash |
945 | 0 | + 2; // serialNumber (header) |
946 | 0 |
|
947 | 0 | // The only way we could have a request this large is if the serialNumber was |
948 | 0 | // ridiculously and unreasonably large. RFC 5280 says "Conforming CAs MUST |
949 | 0 | // NOT use serialNumber values longer than 20 octets." With this restriction, |
950 | 0 | // we allow for some amount of non-conformance with that requirement while |
951 | 0 | // still ensuring we can encode the length values in the ASN.1 TLV structures |
952 | 0 | // in a single byte. |
953 | 0 | static_assert(totalLenWithoutSerialNumberData < OCSP_REQUEST_MAX_LENGTH, |
954 | 0 | "totalLenWithoutSerialNumberData too big"); |
955 | 0 | if (certID.serialNumber.GetLength() > |
956 | 0 | OCSP_REQUEST_MAX_LENGTH - totalLenWithoutSerialNumberData) { |
957 | 0 | return Result::ERROR_BAD_DER; |
958 | 0 | } |
959 | 0 | |
960 | 0 | outLen = totalLenWithoutSerialNumberData + certID.serialNumber.GetLength(); |
961 | 0 |
|
962 | 0 | uint8_t totalLen = static_cast<uint8_t>(outLen); |
963 | 0 |
|
964 | 0 | uint8_t* d = out; |
965 | 0 | *d++ = 0x30; *d++ = totalLen - 2u; // OCSPRequest (SEQUENCE) |
966 | 0 | *d++ = 0x30; *d++ = totalLen - 4u; // tbsRequest (SEQUENCE) |
967 | 0 | *d++ = 0x30; *d++ = totalLen - 6u; // requestList (SEQUENCE OF) |
968 | 0 | *d++ = 0x30; *d++ = totalLen - 8u; // Request (SEQUENCE) |
969 | 0 | *d++ = 0x30; *d++ = totalLen - 10u; // reqCert (CertID SEQUENCE) |
970 | 0 |
|
971 | 0 | // reqCert.hashAlgorithm |
972 | 0 | for (const uint8_t hashAlgorithmByte : hashAlgorithm) { |
973 | 0 | *d++ = hashAlgorithmByte; |
974 | 0 | } |
975 | 0 |
|
976 | 0 | // reqCert.issuerNameHash (OCTET STRING) |
977 | 0 | *d++ = 0x04; |
978 | 0 | *d++ = hashLen; |
979 | 0 | Result rv = trustDomain.DigestBuf(certID.issuer, DigestAlgorithm::sha1, d, |
980 | 0 | hashLen); |
981 | 0 | if (rv != Success) { |
982 | 0 | return rv; |
983 | 0 | } |
984 | 0 | d += hashLen; |
985 | 0 |
|
986 | 0 | // reqCert.issuerKeyHash (OCTET STRING) |
987 | 0 | *d++ = 0x04; |
988 | 0 | *d++ = hashLen; |
989 | 0 | rv = KeyHash(trustDomain, certID.issuerSubjectPublicKeyInfo, d, hashLen); |
990 | 0 | if (rv != Success) { |
991 | 0 | return rv; |
992 | 0 | } |
993 | 0 | d += hashLen; |
994 | 0 |
|
995 | 0 | // reqCert.serialNumber (INTEGER) |
996 | 0 | *d++ = 0x02; // INTEGER |
997 | 0 | *d++ = static_cast<uint8_t>(certID.serialNumber.GetLength()); |
998 | 0 | Reader serialNumber(certID.serialNumber); |
999 | 0 | do { |
1000 | 0 | rv = serialNumber.Read(*d); |
1001 | 0 | if (rv != Success) { |
1002 | 0 | return rv; |
1003 | 0 | } |
1004 | 0 | ++d; |
1005 | 0 | } while (!serialNumber.AtEnd()); |
1006 | 0 |
|
1007 | 0 | assert(d == out + totalLen); |
1008 | 0 |
|
1009 | 0 | return Success; |
1010 | 0 | } |
1011 | | |
1012 | | } } // namespace mozilla::pkix |