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