security/pkix/lib/pkixocsp.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
michael@0 3 /* Copyright 2013 Mozilla Foundation
michael@0 4 *
michael@0 5 * Licensed under the Apache License, Version 2.0 (the "License");
michael@0 6 * you may not use this file except in compliance with the License.
michael@0 7 * You may obtain a copy of the License at
michael@0 8 *
michael@0 9 * http://www.apache.org/licenses/LICENSE-2.0
michael@0 10 *
michael@0 11 * Unless required by applicable law or agreed to in writing, software
michael@0 12 * distributed under the License is distributed on an "AS IS" BASIS,
michael@0 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
michael@0 14 * See the License for the specific language governing permissions and
michael@0 15 * limitations under the License.
michael@0 16 */
michael@0 17
michael@0 18 #include <limits>
michael@0 19
michael@0 20 #include "pkix/bind.h"
michael@0 21 #include "pkix/pkix.h"
michael@0 22 #include "pkixcheck.h"
michael@0 23 #include "pkixder.h"
michael@0 24
michael@0 25 #include "hasht.h"
michael@0 26 #include "pk11pub.h"
michael@0 27 #include "secder.h"
michael@0 28
michael@0 29 #ifdef _MSC_VER
michael@0 30 // C4480: nonstandard extension used: specifying underlying type for enum
michael@0 31 #define ENUM_CLASS __pragma(warning(disable: 4480)) enum
michael@0 32 #else
michael@0 33 #define ENUM_CLASS enum class
michael@0 34 #endif
michael@0 35
michael@0 36 // TODO: use typed/qualified typedefs everywhere?
michael@0 37 // TODO: When should we return SEC_ERROR_OCSP_UNAUTHORIZED_RESPONSE?
michael@0 38
michael@0 39 namespace mozilla { namespace pkix {
michael@0 40
michael@0 41 static const PRTime ONE_DAY
michael@0 42 = INT64_C(24) * INT64_C(60) * INT64_C(60) * PR_USEC_PER_SEC;
michael@0 43 static const PRTime SLOP = ONE_DAY;
michael@0 44
michael@0 45 // These values correspond to the tag values in the ASN.1 CertStatus
michael@0 46 ENUM_CLASS CertStatus : uint8_t {
michael@0 47 Good = der::CONTEXT_SPECIFIC | 0,
michael@0 48 Revoked = der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 1,
michael@0 49 Unknown = der::CONTEXT_SPECIFIC | 2
michael@0 50 };
michael@0 51
michael@0 52 class Context
michael@0 53 {
michael@0 54 public:
michael@0 55 Context(TrustDomain& trustDomain,
michael@0 56 const CERTCertificate& cert,
michael@0 57 CERTCertificate& issuerCert,
michael@0 58 PRTime time,
michael@0 59 uint16_t maxLifetimeInDays,
michael@0 60 PRTime* thisUpdate,
michael@0 61 PRTime* validThrough)
michael@0 62 : trustDomain(trustDomain)
michael@0 63 , cert(cert)
michael@0 64 , issuerCert(issuerCert)
michael@0 65 , time(time)
michael@0 66 , maxLifetimeInDays(maxLifetimeInDays)
michael@0 67 , certStatus(CertStatus::Unknown)
michael@0 68 , thisUpdate(thisUpdate)
michael@0 69 , validThrough(validThrough)
michael@0 70 , expired(false)
michael@0 71 {
michael@0 72 if (thisUpdate) {
michael@0 73 *thisUpdate = 0;
michael@0 74 }
michael@0 75 if (validThrough) {
michael@0 76 *validThrough = 0;
michael@0 77 }
michael@0 78 }
michael@0 79
michael@0 80 TrustDomain& trustDomain;
michael@0 81 const CERTCertificate& cert;
michael@0 82 CERTCertificate& issuerCert;
michael@0 83 const PRTime time;
michael@0 84 const uint16_t maxLifetimeInDays;
michael@0 85 CertStatus certStatus;
michael@0 86 PRTime* thisUpdate;
michael@0 87 PRTime* validThrough;
michael@0 88 bool expired;
michael@0 89
michael@0 90 private:
michael@0 91 Context(const Context&); // delete
michael@0 92 void operator=(const Context&); // delete
michael@0 93 };
michael@0 94
michael@0 95 // Verify that potentialSigner is a valid delegated OCSP response signing cert
michael@0 96 // according to RFC 6960 section 4.2.2.2.
michael@0 97 static Result
michael@0 98 CheckOCSPResponseSignerCert(TrustDomain& trustDomain,
michael@0 99 CERTCertificate& potentialSigner,
michael@0 100 const CERTCertificate& issuerCert, PRTime time)
michael@0 101 {
michael@0 102 Result rv;
michael@0 103
michael@0 104 BackCert cert(&potentialSigner, nullptr, BackCert::ExcludeCN);
michael@0 105 rv = cert.Init();
michael@0 106 if (rv != Success) {
michael@0 107 return rv;
michael@0 108 }
michael@0 109
michael@0 110 // We don't need to do a complete verification of the signer (i.e. we don't
michael@0 111 // have to call BuildCertChain to verify the entire chain) because we
michael@0 112 // already know that the issuerCert is valid, since revocation checking is
michael@0 113 // done from the root to the parent after we've built a complete chain that
michael@0 114 // we know is otherwise valid. Rather, we just need to do a one-step
michael@0 115 // validation from potentialSigner to issuerCert.
michael@0 116 //
michael@0 117 // It seems reasonable to require the KU_DIGITAL_SIGNATURE key usage on the
michael@0 118 // OCSP responder certificate if the OCSP responder certificate has a
michael@0 119 // key usage extension. However, according to bug 240456, some OCSP responder
michael@0 120 // certificates may have only the nonRepudiation bit set. Also, the OCSP
michael@0 121 // specification (RFC 6960) does not mandate any particular key usage to be
michael@0 122 // asserted for OCSP responde signers. Oddly, the CABForum Baseline
michael@0 123 // Requirements v.1.1.5 do say "If the Root CA Private Key is used for
michael@0 124 // signing OCSP responses, then the digitalSignature bit MUST be set."
michael@0 125 //
michael@0 126 // Note that CheckIssuerIndependentProperties processes
michael@0 127 // SEC_OID_OCSP_RESPONDER in the way that the OCSP specification requires us
michael@0 128 // to--in particular, it doesn't allow SEC_OID_OCSP_RESPONDER to be implied
michael@0 129 // by a missing EKU extension, unlike other EKUs.
michael@0 130 //
michael@0 131 // TODO(bug 926261): If we're validating for a policy then the policy OID we
michael@0 132 // are validating for should be passed to CheckIssuerIndependentProperties.
michael@0 133 rv = CheckIssuerIndependentProperties(trustDomain, cert, time,
michael@0 134 MustBeEndEntity,
michael@0 135 KeyUsage::noParticularKeyUsageRequired,
michael@0 136 SEC_OID_OCSP_RESPONDER,
michael@0 137 SEC_OID_X509_ANY_POLICY, 0);
michael@0 138 if (rv != Success) {
michael@0 139 return rv;
michael@0 140 }
michael@0 141
michael@0 142 // It is possible that there exists a certificate with the same key as the
michael@0 143 // issuer but with a different name, so we need to compare names
michael@0 144 // TODO: needs test
michael@0 145 if (!SECITEM_ItemsAreEqual(&cert.GetNSSCert()->derIssuer,
michael@0 146 &issuerCert.derSubject) &&
michael@0 147 CERT_CompareName(&cert.GetNSSCert()->issuer,
michael@0 148 &issuerCert.subject) != SECEqual) {
michael@0 149 return Fail(RecoverableError, SEC_ERROR_OCSP_RESPONDER_CERT_INVALID);
michael@0 150 }
michael@0 151
michael@0 152 // TODO(bug 926260): check name constraints
michael@0 153
michael@0 154 if (trustDomain.VerifySignedData(&potentialSigner.signatureWrap,
michael@0 155 &issuerCert) != SECSuccess) {
michael@0 156 return MapSECStatus(SECFailure);
michael@0 157 }
michael@0 158
michael@0 159 // TODO: check for revocation of the OCSP responder certificate unless no-check
michael@0 160 // or the caller forcing no-check. To properly support the no-check policy, we'd
michael@0 161 // need to enforce policy constraints from the issuerChain.
michael@0 162
michael@0 163 return Success;
michael@0 164 }
michael@0 165
michael@0 166 //typedef enum {
michael@0 167 // ocspResponderID_byName = 1,
michael@0 168 // ocspResponderID_byKey = 2
michael@0 169 //} ResponderIDType;
michael@0 170
michael@0 171 ENUM_CLASS ResponderIDType : uint8_t
michael@0 172 {
michael@0 173 byName = der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 1,
michael@0 174 byKey = der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 2
michael@0 175 };
michael@0 176
michael@0 177 static inline der::Result OCSPResponse(der::Input&, Context&);
michael@0 178 static inline der::Result ResponseBytes(der::Input&, Context&);
michael@0 179 static inline der::Result BasicResponse(der::Input&, Context&);
michael@0 180 static inline der::Result ResponseData(
michael@0 181 der::Input& tbsResponseData, Context& context,
michael@0 182 const CERTSignedData& signedResponseData,
michael@0 183 /*const*/ SECItem* certs, size_t numCerts);
michael@0 184 static inline der::Result SingleResponse(der::Input& input,
michael@0 185 Context& context);
michael@0 186 static inline der::Result CheckExtensionsForCriticality(der::Input&);
michael@0 187 static inline der::Result CertID(der::Input& input,
michael@0 188 const Context& context,
michael@0 189 /*out*/ bool& match);
michael@0 190 static der::Result MatchIssuerKey(const SECItem& issuerKeyHash,
michael@0 191 const CERTCertificate& issuer,
michael@0 192 /*out*/ bool& match);
michael@0 193
michael@0 194 // RFC 6960 section 4.2.2.2: The OCSP responder must either be the issuer of
michael@0 195 // the cert or it must be a delegated OCSP response signing cert directly
michael@0 196 // issued by the issuer. If the OCSP responder is a delegated OCSP response
michael@0 197 // signer, then its certificate is (probably) embedded within the OCSP
michael@0 198 // response and we'll need to verify that it is a valid certificate that chains
michael@0 199 // *directly* to issuerCert.
michael@0 200 static CERTCertificate*
michael@0 201 GetOCSPSignerCertificate(TrustDomain& trustDomain,
michael@0 202 ResponderIDType responderIDType,
michael@0 203 const SECItem& responderIDItem,
michael@0 204 const SECItem* certs, size_t numCerts,
michael@0 205 CERTCertificate& issuerCert, PRTime time)
michael@0 206 {
michael@0 207 bool isIssuer = true;
michael@0 208 size_t i = 0;
michael@0 209 for (;;) {
michael@0 210 ScopedCERTCertificate potentialSigner;
michael@0 211 if (isIssuer) {
michael@0 212 potentialSigner = CERT_DupCertificate(&issuerCert);
michael@0 213 } else if (i < numCerts) {
michael@0 214 potentialSigner = CERT_NewTempCertificate(
michael@0 215 CERT_GetDefaultCertDB(),
michael@0 216 /*TODO*/const_cast<SECItem*>(&certs[i]), nullptr,
michael@0 217 false, false);
michael@0 218 if (!potentialSigner) {
michael@0 219 return nullptr;
michael@0 220 }
michael@0 221 ++i;
michael@0 222 } else {
michael@0 223 PR_SetError(SEC_ERROR_OCSP_INVALID_SIGNING_CERT, 0);
michael@0 224 return nullptr;
michael@0 225 }
michael@0 226
michael@0 227 bool match;
michael@0 228 switch (responderIDType) {
michael@0 229 case ResponderIDType::byName:
michael@0 230 // The CA is very likely to have encoded the name in the OCSP response
michael@0 231 // exactly the same as the name is encoded in the signing certificate.
michael@0 232 // Consequently, most of the time we will avoid parsing the name
michael@0 233 // completely. We're assuming here that the signer's subject name is
michael@0 234 // correctly formatted.
michael@0 235 // TODO: need test for exact name
michael@0 236 // TODO: need test for non-exact name match
michael@0 237 match = SECITEM_ItemsAreEqual(&responderIDItem,
michael@0 238 &potentialSigner->derSubject);
michael@0 239 if (!match) {
michael@0 240 ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
michael@0 241 if (!arena) {
michael@0 242 return nullptr;
michael@0 243 }
michael@0 244 CERTName name;
michael@0 245 if (SEC_QuickDERDecodeItem(arena.get(), &name,
michael@0 246 SEC_ASN1_GET(CERT_NameTemplate),
michael@0 247 &responderIDItem) != SECSuccess) {
michael@0 248 return nullptr;
michael@0 249 }
michael@0 250 match = CERT_CompareName(&name, &potentialSigner->subject) == SECEqual;
michael@0 251 }
michael@0 252 break;
michael@0 253
michael@0 254 case ResponderIDType::byKey:
michael@0 255 {
michael@0 256 der::Input responderID;
michael@0 257 if (responderID.Init(responderIDItem.data, responderIDItem.len)
michael@0 258 != der::Success) {
michael@0 259 return nullptr;
michael@0 260 }
michael@0 261 SECItem issuerKeyHash;
michael@0 262 if (der::Skip(responderID, der::OCTET_STRING, issuerKeyHash) != der::Success) {
michael@0 263 return nullptr;
michael@0 264 }
michael@0 265 if (MatchIssuerKey(issuerKeyHash, *potentialSigner.get(), match)
michael@0 266 != der::Success) {
michael@0 267 return nullptr;
michael@0 268 }
michael@0 269 break;
michael@0 270 }
michael@0 271
michael@0 272 default:
michael@0 273 PR_SetError(SEC_ERROR_OCSP_MALFORMED_RESPONSE, 0);
michael@0 274 return nullptr;
michael@0 275 }
michael@0 276
michael@0 277 if (match && !isIssuer) {
michael@0 278 Result rv = CheckOCSPResponseSignerCert(trustDomain,
michael@0 279 *potentialSigner.get(),
michael@0 280 issuerCert, time);
michael@0 281 if (rv == RecoverableError) {
michael@0 282 match = false;
michael@0 283 } else if (rv != Success) {
michael@0 284 return nullptr;
michael@0 285 }
michael@0 286 }
michael@0 287
michael@0 288 if (match) {
michael@0 289 return potentialSigner.release();
michael@0 290 }
michael@0 291
michael@0 292 isIssuer = false;
michael@0 293 }
michael@0 294 }
michael@0 295
michael@0 296 static SECStatus
michael@0 297 VerifySignature(Context& context, ResponderIDType responderIDType,
michael@0 298 const SECItem& responderID, const SECItem* certs,
michael@0 299 size_t numCerts, const CERTSignedData& signedResponseData)
michael@0 300 {
michael@0 301 ScopedCERTCertificate signer(
michael@0 302 GetOCSPSignerCertificate(context.trustDomain, responderIDType, responderID,
michael@0 303 certs, numCerts, context.issuerCert,
michael@0 304 context.time));
michael@0 305 if (!signer) {
michael@0 306 return SECFailure;
michael@0 307 }
michael@0 308
michael@0 309 if (context.trustDomain.VerifySignedData(&signedResponseData, signer.get())
michael@0 310 != SECSuccess) {
michael@0 311 if (PR_GetError() == SEC_ERROR_BAD_SIGNATURE) {
michael@0 312 PR_SetError(SEC_ERROR_OCSP_BAD_SIGNATURE, 0);
michael@0 313 }
michael@0 314 return SECFailure;
michael@0 315 }
michael@0 316
michael@0 317 return SECSuccess;
michael@0 318 }
michael@0 319
michael@0 320 static inline void
michael@0 321 SetErrorToMalformedResponseOnBadDERError()
michael@0 322 {
michael@0 323 if (PR_GetError() == SEC_ERROR_BAD_DER) {
michael@0 324 PR_SetError(SEC_ERROR_OCSP_MALFORMED_RESPONSE, 0);
michael@0 325 }
michael@0 326 }
michael@0 327
michael@0 328 SECStatus
michael@0 329 VerifyEncodedOCSPResponse(TrustDomain& trustDomain,
michael@0 330 const CERTCertificate* cert,
michael@0 331 CERTCertificate* issuerCert, PRTime time,
michael@0 332 uint16_t maxOCSPLifetimeInDays,
michael@0 333 const SECItem* encodedResponse,
michael@0 334 bool& expired,
michael@0 335 PRTime* thisUpdate,
michael@0 336 PRTime* validThrough)
michael@0 337 {
michael@0 338 PR_ASSERT(cert);
michael@0 339 PR_ASSERT(issuerCert);
michael@0 340 // TODO: PR_Assert(pinArg)
michael@0 341 PR_ASSERT(encodedResponse);
michael@0 342 if (!cert || !issuerCert || !encodedResponse || !encodedResponse->data) {
michael@0 343 PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
michael@0 344 return SECFailure;
michael@0 345 }
michael@0 346
michael@0 347 // Always initialize this to something reasonable.
michael@0 348 expired = false;
michael@0 349
michael@0 350 der::Input input;
michael@0 351 if (input.Init(encodedResponse->data, encodedResponse->len) != der::Success) {
michael@0 352 SetErrorToMalformedResponseOnBadDERError();
michael@0 353 return SECFailure;
michael@0 354 }
michael@0 355 Context context(trustDomain, *cert, *issuerCert, time, maxOCSPLifetimeInDays,
michael@0 356 thisUpdate, validThrough);
michael@0 357
michael@0 358 if (der::Nested(input, der::SEQUENCE,
michael@0 359 bind(OCSPResponse, _1, ref(context))) != der::Success) {
michael@0 360 SetErrorToMalformedResponseOnBadDERError();
michael@0 361 return SECFailure;
michael@0 362 }
michael@0 363
michael@0 364 if (der::End(input) != der::Success) {
michael@0 365 SetErrorToMalformedResponseOnBadDERError();
michael@0 366 return SECFailure;
michael@0 367 }
michael@0 368
michael@0 369 expired = context.expired;
michael@0 370
michael@0 371 switch (context.certStatus) {
michael@0 372 case CertStatus::Good:
michael@0 373 if (expired) {
michael@0 374 PR_SetError(SEC_ERROR_OCSP_OLD_RESPONSE, 0);
michael@0 375 return SECFailure;
michael@0 376 }
michael@0 377 return SECSuccess;
michael@0 378 case CertStatus::Revoked:
michael@0 379 PR_SetError(SEC_ERROR_REVOKED_CERTIFICATE, 0);
michael@0 380 return SECFailure;
michael@0 381 case CertStatus::Unknown:
michael@0 382 PR_SetError(SEC_ERROR_OCSP_UNKNOWN_CERT, 0);
michael@0 383 return SECFailure;
michael@0 384 }
michael@0 385
michael@0 386 PR_NOT_REACHED("unknown CertStatus");
michael@0 387 PR_SetError(SEC_ERROR_OCSP_UNKNOWN_CERT, 0);
michael@0 388 return SECFailure;
michael@0 389 }
michael@0 390
michael@0 391 // OCSPResponse ::= SEQUENCE {
michael@0 392 // responseStatus OCSPResponseStatus,
michael@0 393 // responseBytes [0] EXPLICIT ResponseBytes OPTIONAL }
michael@0 394 //
michael@0 395 static inline der::Result
michael@0 396 OCSPResponse(der::Input& input, Context& context)
michael@0 397 {
michael@0 398 // OCSPResponseStatus ::= ENUMERATED {
michael@0 399 // successful (0), -- Response has valid confirmations
michael@0 400 // malformedRequest (1), -- Illegal confirmation request
michael@0 401 // internalError (2), -- Internal error in issuer
michael@0 402 // tryLater (3), -- Try again later
michael@0 403 // -- (4) is not used
michael@0 404 // sigRequired (5), -- Must sign the request
michael@0 405 // unauthorized (6) -- Request unauthorized
michael@0 406 // }
michael@0 407 uint8_t responseStatus;
michael@0 408
michael@0 409 if (der::Enumerated(input, responseStatus) != der::Success) {
michael@0 410 return der::Failure;
michael@0 411 }
michael@0 412 switch (responseStatus) {
michael@0 413 case 0: break; // successful
michael@0 414 case 1: return der::Fail(SEC_ERROR_OCSP_MALFORMED_REQUEST);
michael@0 415 case 2: return der::Fail(SEC_ERROR_OCSP_SERVER_ERROR);
michael@0 416 case 3: return der::Fail(SEC_ERROR_OCSP_TRY_SERVER_LATER);
michael@0 417 case 5: return der::Fail(SEC_ERROR_OCSP_REQUEST_NEEDS_SIG);
michael@0 418 case 6: return der::Fail(SEC_ERROR_OCSP_UNAUTHORIZED_REQUEST);
michael@0 419 default: return der::Fail(SEC_ERROR_OCSP_UNKNOWN_RESPONSE_STATUS);
michael@0 420 }
michael@0 421
michael@0 422 return der::Nested(input, der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 0,
michael@0 423 der::SEQUENCE, bind(ResponseBytes, _1, ref(context)));
michael@0 424 }
michael@0 425
michael@0 426 // ResponseBytes ::= SEQUENCE {
michael@0 427 // responseType OBJECT IDENTIFIER,
michael@0 428 // response OCTET STRING }
michael@0 429 static inline der::Result
michael@0 430 ResponseBytes(der::Input& input, Context& context)
michael@0 431 {
michael@0 432 static const uint8_t id_pkix_ocsp_basic[] = {
michael@0 433 0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x01
michael@0 434 };
michael@0 435
michael@0 436 if (der::OID(input, id_pkix_ocsp_basic) != der::Success) {
michael@0 437 return der::Failure;
michael@0 438 }
michael@0 439
michael@0 440 return der::Nested(input, der::OCTET_STRING, der::SEQUENCE,
michael@0 441 bind(BasicResponse, _1, ref(context)));
michael@0 442 }
michael@0 443
michael@0 444 // BasicOCSPResponse ::= SEQUENCE {
michael@0 445 // tbsResponseData ResponseData,
michael@0 446 // signatureAlgorithm AlgorithmIdentifier,
michael@0 447 // signature BIT STRING,
michael@0 448 // certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL }
michael@0 449 der::Result
michael@0 450 BasicResponse(der::Input& input, Context& context)
michael@0 451 {
michael@0 452 der::Input::Mark mark(input.GetMark());
michael@0 453
michael@0 454 uint16_t length;
michael@0 455 if (der::ExpectTagAndGetLength(input, der::SEQUENCE, length)
michael@0 456 != der::Success) {
michael@0 457 return der::Failure;
michael@0 458 }
michael@0 459
michael@0 460 // The signature covers the entire DER encoding of tbsResponseData, including
michael@0 461 // the beginning tag and length. However, when we're parsing tbsResponseData,
michael@0 462 // we want to strip off the tag and length because we don't need it after
michael@0 463 // we've confirmed it's there and figured out what length it is.
michael@0 464
michael@0 465 der::Input tbsResponseData;
michael@0 466
michael@0 467 if (input.Skip(length, tbsResponseData) != der::Success) {
michael@0 468 return der::Failure;
michael@0 469 }
michael@0 470
michael@0 471 CERTSignedData signedData;
michael@0 472
michael@0 473 input.GetSECItem(siBuffer, mark, signedData.data);
michael@0 474
michael@0 475 if (der::Nested(input, der::SEQUENCE,
michael@0 476 bind(der::AlgorithmIdentifier, _1,
michael@0 477 ref(signedData.signatureAlgorithm))) != der::Success) {
michael@0 478 return der::Failure;
michael@0 479 }
michael@0 480
michael@0 481 if (der::Skip(input, der::BIT_STRING, signedData.signature) != der::Success) {
michael@0 482 return der::Failure;
michael@0 483 }
michael@0 484 if (signedData.signature.len == 0) {
michael@0 485 return der::Fail(SEC_ERROR_OCSP_BAD_SIGNATURE);
michael@0 486 }
michael@0 487 unsigned int unusedBitsAtEnd = signedData.signature.data[0];
michael@0 488 // XXX: Really the constraint should be that unusedBitsAtEnd must be less
michael@0 489 // than 7. But, we suspect there are no valid OCSP response signatures with
michael@0 490 // non-zero unused bits. It seems like NSS assumes this in various places, so
michael@0 491 // we enforce it. If we find compatibility issues, we'll know we're wrong.
michael@0 492 if (unusedBitsAtEnd != 0) {
michael@0 493 return der::Fail(SEC_ERROR_OCSP_BAD_SIGNATURE);
michael@0 494 }
michael@0 495 ++signedData.signature.data;
michael@0 496 --signedData.signature.len;
michael@0 497 signedData.signature.len = (signedData.signature.len << 3); // Bytes to bits
michael@0 498
michael@0 499 // Parse certificates, if any
michael@0 500
michael@0 501 SECItem certs[8];
michael@0 502 size_t numCerts = 0;
michael@0 503
michael@0 504 if (!input.AtEnd()) {
michael@0 505 // We ignore the lengths of the wrappers because we'll detect bad lengths
michael@0 506 // during parsing--too short and we'll run out of input for parsing a cert,
michael@0 507 // and too long and we'll have leftover data that won't parse as a cert.
michael@0 508
michael@0 509 // [0] wrapper
michael@0 510 if (der::ExpectTagAndIgnoreLength(
michael@0 511 input, der::CONSTRUCTED | der::CONTEXT_SPECIFIC | 0)
michael@0 512 != der::Success) {
michael@0 513 return der::Failure;
michael@0 514 }
michael@0 515
michael@0 516 // SEQUENCE wrapper
michael@0 517 if (der::ExpectTagAndIgnoreLength(input, der::SEQUENCE) != der::Success) {
michael@0 518 return der::Failure;
michael@0 519 }
michael@0 520
michael@0 521 // sequence of certificates
michael@0 522 while (!input.AtEnd()) {
michael@0 523 if (numCerts == PR_ARRAY_SIZE(certs)) {
michael@0 524 return der::Fail(SEC_ERROR_BAD_DER);
michael@0 525 }
michael@0 526
michael@0 527 // Unwrap the SEQUENCE that contains the certificate, which is itself a
michael@0 528 // SEQUENCE.
michael@0 529 der::Input::Mark mark(input.GetMark());
michael@0 530 if (der::Skip(input, der::SEQUENCE) != der::Success) {
michael@0 531 return der::Failure;
michael@0 532 }
michael@0 533
michael@0 534 input.GetSECItem(siBuffer, mark, certs[numCerts]);
michael@0 535 ++numCerts;
michael@0 536 }
michael@0 537 }
michael@0 538
michael@0 539 return ResponseData(tbsResponseData, context, signedData, certs, numCerts);
michael@0 540 }
michael@0 541
michael@0 542 // ResponseData ::= SEQUENCE {
michael@0 543 // version [0] EXPLICIT Version DEFAULT v1,
michael@0 544 // responderID ResponderID,
michael@0 545 // producedAt GeneralizedTime,
michael@0 546 // responses SEQUENCE OF SingleResponse,
michael@0 547 // responseExtensions [1] EXPLICIT Extensions OPTIONAL }
michael@0 548 static inline der::Result
michael@0 549 ResponseData(der::Input& input, Context& context,
michael@0 550 const CERTSignedData& signedResponseData,
michael@0 551 /*const*/ SECItem* certs, size_t numCerts)
michael@0 552 {
michael@0 553 uint8_t version;
michael@0 554 if (der::OptionalVersion(input, version) != der::Success) {
michael@0 555 return der::Failure;
michael@0 556 }
michael@0 557 if (version != der::v1) {
michael@0 558 // TODO: more specific error code for bad version?
michael@0 559 return der::Fail(SEC_ERROR_BAD_DER);
michael@0 560 }
michael@0 561
michael@0 562 // ResponderID ::= CHOICE {
michael@0 563 // byName [1] Name,
michael@0 564 // byKey [2] KeyHash }
michael@0 565 SECItem responderID;
michael@0 566 uint16_t responderIDLength;
michael@0 567 ResponderIDType responderIDType
michael@0 568 = input.Peek(static_cast<uint8_t>(ResponderIDType::byName))
michael@0 569 ? ResponderIDType::byName
michael@0 570 : ResponderIDType::byKey;
michael@0 571 if (ExpectTagAndGetLength(input, static_cast<uint8_t>(responderIDType),
michael@0 572 responderIDLength) != der::Success) {
michael@0 573 return der::Failure;
michael@0 574 }
michael@0 575 // TODO: responderID probably needs to have another level of ASN1 tag/length
michael@0 576 // checked and stripped.
michael@0 577 if (input.Skip(responderIDLength, responderID) != der::Success) {
michael@0 578 return der::Failure;
michael@0 579 }
michael@0 580
michael@0 581 // This is the soonest we can verify the signature. We verify the signature
michael@0 582 // right away to follow the principal of minimizing the processing of data
michael@0 583 // before verifying its signature.
michael@0 584 if (VerifySignature(context, responderIDType, responderID, certs, numCerts,
michael@0 585 signedResponseData) != SECSuccess) {
michael@0 586 return der::Failure;
michael@0 587 }
michael@0 588
michael@0 589 // TODO: Do we even need to parse this? Should we just skip it?
michael@0 590 PRTime producedAt;
michael@0 591 if (der::GeneralizedTime(input, producedAt) != der::Success) {
michael@0 592 return der::Failure;
michael@0 593 }
michael@0 594
michael@0 595 // We don't accept an empty sequence of responses. In practice, a legit OCSP
michael@0 596 // responder will never return an empty response, and handling the case of an
michael@0 597 // empty response makes things unnecessarily complicated.
michael@0 598 if (der::NestedOf(input, der::SEQUENCE, der::SEQUENCE,
michael@0 599 der::MustNotBeEmpty,
michael@0 600 bind(SingleResponse, _1, ref(context))) != der::Success) {
michael@0 601 return der::Failure;
michael@0 602 }
michael@0 603
michael@0 604 if (!input.AtEnd()) {
michael@0 605 if (der::Nested(input, der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 1,
michael@0 606 CheckExtensionsForCriticality) != der::Success) {
michael@0 607 return der::Failure;
michael@0 608 }
michael@0 609 }
michael@0 610
michael@0 611 return der::Success;
michael@0 612 }
michael@0 613
michael@0 614 // SingleResponse ::= SEQUENCE {
michael@0 615 // certID CertID,
michael@0 616 // certStatus CertStatus,
michael@0 617 // thisUpdate GeneralizedTime,
michael@0 618 // nextUpdate [0] EXPLICIT GeneralizedTime OPTIONAL,
michael@0 619 // singleExtensions [1] EXPLICIT Extensions{{re-ocsp-crl |
michael@0 620 // re-ocsp-archive-cutoff |
michael@0 621 // CrlEntryExtensions, ...}
michael@0 622 // } OPTIONAL }
michael@0 623 static inline der::Result
michael@0 624 SingleResponse(der::Input& input, Context& context)
michael@0 625 {
michael@0 626 bool match = false;
michael@0 627 if (der::Nested(input, der::SEQUENCE,
michael@0 628 bind(CertID, _1, cref(context), ref(match)))
michael@0 629 != der::Success) {
michael@0 630 return der::Failure;
michael@0 631 }
michael@0 632
michael@0 633 if (!match) {
michael@0 634 // This response does not reference the certificate we're interested in.
michael@0 635 // By consuming the rest of our input and returning successfully, we can
michael@0 636 // continue processing and examine another response that might have what
michael@0 637 // we want.
michael@0 638 input.SkipToEnd();
michael@0 639 return der::Success;
michael@0 640 }
michael@0 641
michael@0 642 // CertStatus ::= CHOICE {
michael@0 643 // good [0] IMPLICIT NULL,
michael@0 644 // revoked [1] IMPLICIT RevokedInfo,
michael@0 645 // unknown [2] IMPLICIT UnknownInfo }
michael@0 646 //
michael@0 647 // In the event of multiple SingleResponses for a cert that have conflicting
michael@0 648 // statuses, we use the following precedence rules:
michael@0 649 //
michael@0 650 // * revoked overrides good and unknown
michael@0 651 // * good overrides unknown
michael@0 652 if (input.Peek(static_cast<uint8_t>(CertStatus::Good))) {
michael@0 653 if (ExpectTagAndLength(input, static_cast<uint8_t>(CertStatus::Good), 0)
michael@0 654 != der::Success) {
michael@0 655 return der::Failure;
michael@0 656 }
michael@0 657 if (context.certStatus != CertStatus::Revoked) {
michael@0 658 context.certStatus = CertStatus::Good;
michael@0 659 }
michael@0 660 } else if (input.Peek(static_cast<uint8_t>(CertStatus::Revoked))) {
michael@0 661 // We don't need any info from the RevokedInfo structure, so we don't even
michael@0 662 // parse it. TODO: We should mention issues like this in the explanation of
michael@0 663 // why we treat invalid OCSP responses equivalently to revoked for OCSP
michael@0 664 // stapling.
michael@0 665 if (der::Skip(input, static_cast<uint8_t>(CertStatus::Revoked))
michael@0 666 != der::Success) {
michael@0 667 return der::Failure;
michael@0 668 }
michael@0 669 context.certStatus = CertStatus::Revoked;
michael@0 670 } else if (ExpectTagAndLength(input,
michael@0 671 static_cast<uint8_t>(CertStatus::Unknown),
michael@0 672 0) != der::Success) {
michael@0 673 return der::Failure;
michael@0 674 }
michael@0 675
michael@0 676 // http://tools.ietf.org/html/rfc6960#section-3.2
michael@0 677 // 5. The time at which the status being indicated is known to be
michael@0 678 // correct (thisUpdate) is sufficiently recent;
michael@0 679 // 6. When available, the time at or before which newer information will
michael@0 680 // be available about the status of the certificate (nextUpdate) is
michael@0 681 // greater than the current time.
michael@0 682
michael@0 683 const PRTime maxLifetime =
michael@0 684 context.maxLifetimeInDays * ONE_DAY;
michael@0 685
michael@0 686 PRTime thisUpdate;
michael@0 687 if (der::GeneralizedTime(input, thisUpdate) != der::Success) {
michael@0 688 return der::Failure;
michael@0 689 }
michael@0 690
michael@0 691 if (thisUpdate > context.time + SLOP) {
michael@0 692 return der::Fail(SEC_ERROR_OCSP_FUTURE_RESPONSE);
michael@0 693 }
michael@0 694
michael@0 695 PRTime notAfter;
michael@0 696 static const uint8_t NEXT_UPDATE_TAG =
michael@0 697 der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 0;
michael@0 698 if (input.Peek(NEXT_UPDATE_TAG)) {
michael@0 699 PRTime nextUpdate;
michael@0 700 if (der::Nested(input, NEXT_UPDATE_TAG,
michael@0 701 bind(der::GeneralizedTime, _1, ref(nextUpdate)))
michael@0 702 != der::Success) {
michael@0 703 return der::Failure;
michael@0 704 }
michael@0 705
michael@0 706 if (nextUpdate < thisUpdate) {
michael@0 707 return der::Fail(SEC_ERROR_OCSP_MALFORMED_RESPONSE);
michael@0 708 }
michael@0 709 if (nextUpdate - thisUpdate <= maxLifetime) {
michael@0 710 notAfter = nextUpdate;
michael@0 711 } else {
michael@0 712 notAfter = thisUpdate + maxLifetime;
michael@0 713 }
michael@0 714 } else {
michael@0 715 // NSS requires all OCSP responses without a nextUpdate to be recent.
michael@0 716 // Match that stricter behavior.
michael@0 717 notAfter = thisUpdate + ONE_DAY;
michael@0 718 }
michael@0 719
michael@0 720 if (context.time < SLOP) { // prevent underflow
michael@0 721 return der::Fail(SEC_ERROR_INVALID_ARGS);
michael@0 722 }
michael@0 723
michael@0 724 if (context.time - SLOP > notAfter) {
michael@0 725 context.expired = true;
michael@0 726 }
michael@0 727
michael@0 728 if (!input.AtEnd()) {
michael@0 729 if (der::Nested(input, der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 1,
michael@0 730 CheckExtensionsForCriticality) != der::Success) {
michael@0 731 return der::Failure;
michael@0 732 }
michael@0 733 }
michael@0 734
michael@0 735 if (context.thisUpdate) {
michael@0 736 *context.thisUpdate = thisUpdate;
michael@0 737 }
michael@0 738 if (context.validThrough) {
michael@0 739 *context.validThrough = notAfter;
michael@0 740 }
michael@0 741
michael@0 742 return der::Success;
michael@0 743 }
michael@0 744
michael@0 745 // CertID ::= SEQUENCE {
michael@0 746 // hashAlgorithm AlgorithmIdentifier,
michael@0 747 // issuerNameHash OCTET STRING, -- Hash of issuer's DN
michael@0 748 // issuerKeyHash OCTET STRING, -- Hash of issuer's public key
michael@0 749 // serialNumber CertificateSerialNumber }
michael@0 750 static inline der::Result
michael@0 751 CertID(der::Input& input, const Context& context, /*out*/ bool& match)
michael@0 752 {
michael@0 753 match = false;
michael@0 754
michael@0 755 SECAlgorithmID hashAlgorithm;
michael@0 756 if (der::Nested(input, der::SEQUENCE,
michael@0 757 bind(der::AlgorithmIdentifier, _1, ref(hashAlgorithm)))
michael@0 758 != der::Success) {
michael@0 759 return der::Failure;
michael@0 760 }
michael@0 761
michael@0 762 SECItem issuerNameHash;
michael@0 763 if (der::Skip(input, der::OCTET_STRING, issuerNameHash) != der::Success) {
michael@0 764 return der::Failure;
michael@0 765 }
michael@0 766
michael@0 767 SECItem issuerKeyHash;
michael@0 768 if (der::Skip(input, der::OCTET_STRING, issuerKeyHash) != der::Success) {
michael@0 769 return der::Failure;
michael@0 770 }
michael@0 771
michael@0 772 SECItem serialNumber;
michael@0 773 if (der::CertificateSerialNumber(input, serialNumber) != der::Success) {
michael@0 774 return der::Failure;
michael@0 775 }
michael@0 776
michael@0 777 const CERTCertificate& cert = context.cert;
michael@0 778 const CERTCertificate& issuerCert = context.issuerCert;
michael@0 779
michael@0 780 if (!SECITEM_ItemsAreEqual(&serialNumber, &cert.serialNumber)) {
michael@0 781 // This does not reference the certificate we're interested in.
michael@0 782 // Consume the rest of the input and return successfully to
michael@0 783 // potentially continue processing other responses.
michael@0 784 input.SkipToEnd();
michael@0 785 return der::Success;
michael@0 786 }
michael@0 787
michael@0 788 // TODO: support SHA-2 hashes.
michael@0 789
michael@0 790 SECOidTag hashAlg = SECOID_GetAlgorithmTag(&hashAlgorithm);
michael@0 791 if (hashAlg != SEC_OID_SHA1) {
michael@0 792 // Again, not interested in this response. Consume input, return success.
michael@0 793 input.SkipToEnd();
michael@0 794 return der::Success;
michael@0 795 }
michael@0 796
michael@0 797 if (issuerNameHash.len != SHA1_LENGTH) {
michael@0 798 return der::Fail(SEC_ERROR_OCSP_MALFORMED_RESPONSE);
michael@0 799 }
michael@0 800
michael@0 801 // From http://tools.ietf.org/html/rfc6960#section-4.1.1:
michael@0 802 // "The hash shall be calculated over the DER encoding of the
michael@0 803 // issuer's name field in the certificate being checked."
michael@0 804 uint8_t hashBuf[SHA1_LENGTH];
michael@0 805 if (PK11_HashBuf(SEC_OID_SHA1, hashBuf, cert.derIssuer.data,
michael@0 806 cert.derIssuer.len) != SECSuccess) {
michael@0 807 return der::Failure;
michael@0 808 }
michael@0 809 if (memcmp(hashBuf, issuerNameHash.data, issuerNameHash.len)) {
michael@0 810 // Again, not interested in this response. Consume input, return success.
michael@0 811 input.SkipToEnd();
michael@0 812 return der::Success;
michael@0 813 }
michael@0 814
michael@0 815 return MatchIssuerKey(issuerKeyHash, issuerCert, match);
michael@0 816 }
michael@0 817
michael@0 818 // From http://tools.ietf.org/html/rfc6960#section-4.1.1:
michael@0 819 // "The hash shall be calculated over the value (excluding tag and length) of
michael@0 820 // the subject public key field in the issuer's certificate."
michael@0 821 static der::Result
michael@0 822 MatchIssuerKey(const SECItem& issuerKeyHash, const CERTCertificate& issuer,
michael@0 823 /*out*/ bool& match)
michael@0 824 {
michael@0 825 if (issuerKeyHash.len != SHA1_LENGTH) {
michael@0 826 return der::Fail(SEC_ERROR_OCSP_MALFORMED_RESPONSE);
michael@0 827 }
michael@0 828
michael@0 829 // TODO(bug 966856): support SHA-2 hashes
michael@0 830
michael@0 831 // Copy just the length and data pointer (nothing needs to be freed) of the
michael@0 832 // subject public key so we can convert the length from bits to bytes, which
michael@0 833 // is what the digest function expects.
michael@0 834 SECItem spk = issuer.subjectPublicKeyInfo.subjectPublicKey;
michael@0 835 DER_ConvertBitString(&spk);
michael@0 836
michael@0 837 static uint8_t hashBuf[SHA1_LENGTH];
michael@0 838 if (PK11_HashBuf(SEC_OID_SHA1, hashBuf, spk.data, spk.len) != SECSuccess) {
michael@0 839 return der::Failure;
michael@0 840 }
michael@0 841
michael@0 842 match = !memcmp(hashBuf, issuerKeyHash.data, issuerKeyHash.len);
michael@0 843 return der::Success;
michael@0 844 }
michael@0 845
michael@0 846 // Extension ::= SEQUENCE {
michael@0 847 // extnID OBJECT IDENTIFIER,
michael@0 848 // critical BOOLEAN DEFAULT FALSE,
michael@0 849 // extnValue OCTET STRING
michael@0 850 // }
michael@0 851 static der::Result
michael@0 852 CheckExtensionForCriticality(der::Input& input)
michael@0 853 {
michael@0 854 uint16_t toSkip;
michael@0 855 if (ExpectTagAndGetLength(input, der::OIDTag, toSkip) != der::Success) {
michael@0 856 return der::Failure;
michael@0 857 }
michael@0 858
michael@0 859 // TODO: maybe we should check the syntax of the OID value
michael@0 860 if (input.Skip(toSkip) != der::Success) {
michael@0 861 return der::Failure;
michael@0 862 }
michael@0 863
michael@0 864 // The only valid explicit encoding of the value is TRUE, so don't even
michael@0 865 // bother parsing it, since we're going to fail either way.
michael@0 866 if (input.Peek(der::BOOLEAN)) {
michael@0 867 return der::Fail(SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION);
michael@0 868 }
michael@0 869
michael@0 870 if (ExpectTagAndGetLength(input, der::OCTET_STRING, toSkip)
michael@0 871 != der::Success) {
michael@0 872 return der::Failure;
michael@0 873 }
michael@0 874 return input.Skip(toSkip);
michael@0 875 }
michael@0 876
michael@0 877 // Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension
michael@0 878 static der::Result
michael@0 879 CheckExtensionsForCriticality(der::Input& input)
michael@0 880 {
michael@0 881 // TODO(bug 997994): some responders include an empty SEQUENCE OF
michael@0 882 // Extension, which is invalid (der::MayBeEmpty should really be
michael@0 883 // der::MustNotBeEmpty).
michael@0 884 return der::NestedOf(input, der::SEQUENCE, der::SEQUENCE,
michael@0 885 der::MayBeEmpty, CheckExtensionForCriticality);
michael@0 886 }
michael@0 887
michael@0 888 // 1. The certificate identified in a received response corresponds to
michael@0 889 // the certificate that was identified in the corresponding request;
michael@0 890 // 2. The signature on the response is valid;
michael@0 891 // 3. The identity of the signer matches the intended recipient of the
michael@0 892 // request;
michael@0 893 // 4. The signer is currently authorized to provide a response for the
michael@0 894 // certificate in question;
michael@0 895 // 5. The time at which the status being indicated is known to be
michael@0 896 // correct (thisUpdate) is sufficiently recent;
michael@0 897 // 6. When available, the time at or before which newer information will
michael@0 898 // be available about the status of the certificate (nextUpdate) is
michael@0 899 // greater than the current time.
michael@0 900 //
michael@0 901 // Responses whose nextUpdate value is earlier than
michael@0 902 // the local system time value SHOULD be considered unreliable.
michael@0 903 // Responses whose thisUpdate time is later than the local system time
michael@0 904 // SHOULD be considered unreliable.
michael@0 905 //
michael@0 906 // If nextUpdate is not set, the responder is indicating that newer
michael@0 907 // revocation information is available all the time.
michael@0 908 //
michael@0 909 // http://tools.ietf.org/html/rfc5019#section-4
michael@0 910
michael@0 911 SECItem*
michael@0 912 CreateEncodedOCSPRequest(PLArenaPool* arena,
michael@0 913 const CERTCertificate* cert,
michael@0 914 const CERTCertificate* issuerCert)
michael@0 915 {
michael@0 916 if (!arena || !cert || !issuerCert) {
michael@0 917 PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
michael@0 918 return nullptr;
michael@0 919 }
michael@0 920
michael@0 921 // We do not add any extensions to the request.
michael@0 922
michael@0 923 // RFC 6960 says "An OCSP client MAY wish to specify the kinds of response
michael@0 924 // types it understands. To do so, it SHOULD use an extension with the OID
michael@0 925 // id-pkix-ocsp-response." This use of MAY and SHOULD is unclear. MSIE11
michael@0 926 // on Windows 8.1 does not include any extensions, whereas NSS has always
michael@0 927 // included the id-pkix-ocsp-response extension. Avoiding the sending the
michael@0 928 // extension is better for OCSP GET because it makes the request smaller,
michael@0 929 // and thus more likely to fit within the 255 byte limit for OCSP GET that
michael@0 930 // is specified in RFC 5019 Section 5.
michael@0 931
michael@0 932 // Bug 966856: Add the id-pkix-ocsp-pref-sig-algs extension.
michael@0 933
michael@0 934 // Since we don't know whether the OCSP responder supports anything other
michael@0 935 // than SHA-1, we have no choice but to use SHA-1 for issuerNameHash and
michael@0 936 // issuerKeyHash.
michael@0 937 static const uint8_t hashAlgorithm[11] = {
michael@0 938 0x30, 0x09, // SEQUENCE
michael@0 939 0x06, 0x05, 0x2B, 0x0E, 0x03, 0x02, 0x1A, // OBJECT IDENTIFIER id-sha1
michael@0 940 0x05, 0x00, // NULL
michael@0 941 };
michael@0 942 static const uint8_t hashLen = SHA1_LENGTH;
michael@0 943
michael@0 944 static const unsigned int totalLenWithoutSerialNumberData
michael@0 945 = 2 // OCSPRequest
michael@0 946 + 2 // tbsRequest
michael@0 947 + 2 // requestList
michael@0 948 + 2 // Request
michael@0 949 + 2 // reqCert (CertID)
michael@0 950 + PR_ARRAY_SIZE(hashAlgorithm) // hashAlgorithm
michael@0 951 + 2 + hashLen // issuerNameHash
michael@0 952 + 2 + hashLen // issuerKeyHash
michael@0 953 + 2; // serialNumber (header)
michael@0 954
michael@0 955 // The only way we could have a request this large is if the serialNumber was
michael@0 956 // ridiculously and unreasonably large. RFC 5280 says "Conforming CAs MUST
michael@0 957 // NOT use serialNumber values longer than 20 octets." With this restriction,
michael@0 958 // we allow for some amount of non-conformance with that requirement while
michael@0 959 // still ensuring we can encode the length values in the ASN.1 TLV structures
michael@0 960 // in a single byte.
michael@0 961 if (issuerCert->serialNumber.len > 127u - totalLenWithoutSerialNumberData) {
michael@0 962 PR_SetError(SEC_ERROR_BAD_DATA, 0);
michael@0 963 return nullptr;
michael@0 964 }
michael@0 965
michael@0 966 uint8_t totalLen = static_cast<uint8_t>(totalLenWithoutSerialNumberData +
michael@0 967 cert->serialNumber.len);
michael@0 968
michael@0 969 SECItem* encodedRequest = SECITEM_AllocItem(arena, nullptr, totalLen);
michael@0 970 if (!encodedRequest) {
michael@0 971 return nullptr;
michael@0 972 }
michael@0 973
michael@0 974 uint8_t* d = encodedRequest->data;
michael@0 975 *d++ = 0x30; *d++ = totalLen - 2; // OCSPRequest (SEQUENCE)
michael@0 976 *d++ = 0x30; *d++ = totalLen - 4; // tbsRequest (SEQUENCE)
michael@0 977 *d++ = 0x30; *d++ = totalLen - 6; // requestList (SEQUENCE OF)
michael@0 978 *d++ = 0x30; *d++ = totalLen - 8; // Request (SEQUENCE)
michael@0 979 *d++ = 0x30; *d++ = totalLen - 10; // reqCert (CertID SEQUENCE)
michael@0 980
michael@0 981 // reqCert.hashAlgorithm
michael@0 982 for (size_t i = 0; i < PR_ARRAY_SIZE(hashAlgorithm); ++i) {
michael@0 983 *d++ = hashAlgorithm[i];
michael@0 984 }
michael@0 985
michael@0 986 // reqCert.issuerNameHash (OCTET STRING)
michael@0 987 *d++ = 0x04;
michael@0 988 *d++ = hashLen;
michael@0 989 if (PK11_HashBuf(SEC_OID_SHA1, d, issuerCert->derSubject.data,
michael@0 990 issuerCert->derSubject.len) != SECSuccess) {
michael@0 991 return nullptr;
michael@0 992 }
michael@0 993 d += hashLen;
michael@0 994
michael@0 995 // reqCert.issuerKeyHash (OCTET STRING)
michael@0 996 *d++ = 0x04;
michael@0 997 *d++ = hashLen;
michael@0 998 SECItem key = issuerCert->subjectPublicKeyInfo.subjectPublicKey;
michael@0 999 DER_ConvertBitString(&key);
michael@0 1000 if (PK11_HashBuf(SEC_OID_SHA1, d, key.data, key.len) != SECSuccess) {
michael@0 1001 return nullptr;
michael@0 1002 }
michael@0 1003 d += hashLen;
michael@0 1004
michael@0 1005 // reqCert.serialNumber (INTEGER)
michael@0 1006 *d++ = 0x02; // INTEGER
michael@0 1007 *d++ = static_cast<uint8_t>(cert->serialNumber.len);
michael@0 1008 for (size_t i = 0; i < cert->serialNumber.len; ++i) {
michael@0 1009 *d++ = cert->serialNumber.data[i];
michael@0 1010 }
michael@0 1011
michael@0 1012 PR_ASSERT(d == encodedRequest->data + totalLen);
michael@0 1013
michael@0 1014 return encodedRequest;
michael@0 1015 }
michael@0 1016
michael@0 1017 } } // namespace mozilla::pkix

mercurial