1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/security/pkix/test/lib/pkixtestutil.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,746 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ 1.6 +/* Copyright 2013 Mozilla Foundation 1.7 + * 1.8 + * Licensed under the Apache License, Version 2.0 (the "License"); 1.9 + * you may not use this file except in compliance with the License. 1.10 + * You may obtain a copy of the License at 1.11 + * 1.12 + * http://www.apache.org/licenses/LICENSE-2.0 1.13 + * 1.14 + * Unless required by applicable law or agreed to in writing, software 1.15 + * distributed under the License is distributed on an "AS IS" BASIS, 1.16 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1.17 + * See the License for the specific language governing permissions and 1.18 + * limitations under the License. 1.19 + */ 1.20 + 1.21 +#include "pkixcheck.h" 1.22 +#include "pkixder.h" 1.23 +#include "pkixtestutil.h" 1.24 + 1.25 +#include "cryptohi.h" 1.26 +#include "hasht.h" 1.27 +#include "pk11pub.h" 1.28 +#include "prinit.h" 1.29 +#include "secder.h" 1.30 + 1.31 +namespace mozilla { namespace pkix { namespace test { 1.32 + 1.33 +class Output 1.34 +{ 1.35 +public: 1.36 + Output() 1.37 + : numItems(0) 1.38 + , length(0) 1.39 + { 1.40 + } 1.41 + 1.42 + // Makes a shallow copy of the input item. All input items must have a 1.43 + // lifetime that extends at least to where Squash is called. 1.44 + der::Result Add(const SECItem* item) 1.45 + { 1.46 + PR_ASSERT(item); 1.47 + PR_ASSERT(item->data); 1.48 + 1.49 + if (numItems >= MaxSequenceItems) { 1.50 + return der::Fail(SEC_ERROR_INVALID_ARGS); 1.51 + } 1.52 + if (length + item->len > 65535) { 1.53 + return der::Fail(SEC_ERROR_INVALID_ARGS); 1.54 + } 1.55 + 1.56 + contents[numItems] = item; 1.57 + numItems++; 1.58 + length += item->len; 1.59 + return der::Success; 1.60 + } 1.61 + 1.62 + SECItem* Squash(PLArenaPool* arena, uint8_t tag) 1.63 + { 1.64 + PR_ASSERT(arena); 1.65 + 1.66 + size_t lengthLength = length < 128 ? 1 1.67 + : length < 256 ? 2 1.68 + : 3; 1.69 + size_t totalLength = 1 + lengthLength + length; 1.70 + SECItem* output = SECITEM_AllocItem(arena, nullptr, totalLength); 1.71 + if (!output) { 1.72 + return nullptr; 1.73 + } 1.74 + uint8_t* d = output->data; 1.75 + *d++ = tag; 1.76 + EncodeLength(d, length, lengthLength); 1.77 + d += lengthLength; 1.78 + for (size_t i = 0; i < numItems; i++) { 1.79 + memcpy(d, contents[i]->data, contents[i]->len); 1.80 + d += contents[i]->len; 1.81 + } 1.82 + return output; 1.83 + } 1.84 + 1.85 +private: 1.86 + void 1.87 + EncodeLength(uint8_t* data, size_t length, size_t lengthLength) 1.88 + { 1.89 + switch (lengthLength) { 1.90 + case 1: 1.91 + data[0] = length; 1.92 + break; 1.93 + case 2: 1.94 + data[0] = 0x81; 1.95 + data[1] = length; 1.96 + break; 1.97 + case 3: 1.98 + data[0] = 0x82; 1.99 + data[1] = length / 256; 1.100 + data[2] = length % 256; 1.101 + break; 1.102 + default: 1.103 + PR_NOT_REACHED("EncodeLength: bad lengthLength"); 1.104 + PR_Abort(); 1.105 + } 1.106 + } 1.107 + 1.108 + static const size_t MaxSequenceItems = 5; 1.109 + const SECItem* contents[MaxSequenceItems]; 1.110 + size_t numItems; 1.111 + size_t length; 1.112 + 1.113 + Output(const Output&) /* = delete */; 1.114 + void operator=(const Output&) /* = delete */; 1.115 +}; 1.116 + 1.117 +OCSPResponseContext::OCSPResponseContext(PLArenaPool* arena, 1.118 + CERTCertificate* cert, 1.119 + PRTime time) 1.120 + : arena(arena) 1.121 + , cert(CERT_DupCertificate(cert)) 1.122 + , issuerCert(nullptr) 1.123 + , signerCert(nullptr) 1.124 + , responseStatus(0) 1.125 + , skipResponseBytes(false) 1.126 + , producedAt(time) 1.127 + , thisUpdate(time) 1.128 + , nextUpdate(time + 10 * PR_USEC_PER_SEC) 1.129 + , includeNextUpdate(true) 1.130 + , certIDHashAlg(SEC_OID_SHA1) 1.131 + , certStatus(0) 1.132 + , revocationTime(0) 1.133 + , badSignature(false) 1.134 + , responderIDType(ByKeyHash) 1.135 + , extensions(nullptr) 1.136 + , includeEmptyExtensions(false) 1.137 +{ 1.138 + for (size_t i = 0; i < MaxIncludedCertificates; i++) { 1.139 + includedCertificates[i] = nullptr; 1.140 + } 1.141 +} 1.142 + 1.143 +static SECItem* ResponseBytes(OCSPResponseContext& context); 1.144 +static SECItem* BasicOCSPResponse(OCSPResponseContext& context); 1.145 +static SECItem* ResponseData(OCSPResponseContext& context); 1.146 +static SECItem* ResponderID(OCSPResponseContext& context); 1.147 +static SECItem* KeyHash(OCSPResponseContext& context); 1.148 +static SECItem* SingleResponse(OCSPResponseContext& context); 1.149 +static SECItem* CertID(OCSPResponseContext& context); 1.150 +static SECItem* CertStatus(OCSPResponseContext& context); 1.151 +static SECItem* Certificates(OCSPResponseContext& context); 1.152 + 1.153 +static SECItem* 1.154 +EncodeNested(PLArenaPool* arena, uint8_t tag, SECItem* inner) 1.155 +{ 1.156 + Output output; 1.157 + if (output.Add(inner) != der::Success) { 1.158 + return nullptr; 1.159 + } 1.160 + return output.Squash(arena, tag); 1.161 +} 1.162 + 1.163 +// A return value of 0 is an error, but this should never happen in practice 1.164 +// because this function aborts in that case. 1.165 +static size_t 1.166 +HashAlgorithmToLength(SECOidTag hashAlg) 1.167 +{ 1.168 + switch (hashAlg) { 1.169 + case SEC_OID_SHA1: 1.170 + return SHA1_LENGTH; 1.171 + case SEC_OID_SHA256: 1.172 + return SHA256_LENGTH; 1.173 + case SEC_OID_SHA384: 1.174 + return SHA384_LENGTH; 1.175 + case SEC_OID_SHA512: 1.176 + return SHA512_LENGTH; 1.177 + default: 1.178 + PR_NOT_REACHED("HashAlgorithmToLength: bad hashAlg"); 1.179 + PR_Abort(); 1.180 + } 1.181 + return 0; 1.182 +} 1.183 + 1.184 +static SECItem* 1.185 +HashedOctetString(PLArenaPool* arena, const SECItem* bytes, SECOidTag hashAlg) 1.186 +{ 1.187 + size_t hashLen = HashAlgorithmToLength(hashAlg); 1.188 + if (hashLen == 0) { 1.189 + return nullptr; 1.190 + } 1.191 + SECItem* hashBuf = SECITEM_AllocItem(arena, nullptr, hashLen); 1.192 + if (!hashBuf) { 1.193 + return nullptr; 1.194 + } 1.195 + if (PK11_HashBuf(hashAlg, hashBuf->data, bytes->data, bytes->len) 1.196 + != SECSuccess) { 1.197 + return nullptr; 1.198 + } 1.199 + 1.200 + return EncodeNested(arena, der::OCTET_STRING, hashBuf); 1.201 +} 1.202 + 1.203 +static SECItem* 1.204 +KeyHashHelper(PLArenaPool* arena, const CERTCertificate* cert) 1.205 +{ 1.206 + // We only need a shallow copy here. 1.207 + SECItem spk = cert->subjectPublicKeyInfo.subjectPublicKey; 1.208 + DER_ConvertBitString(&spk); // bits to bytes 1.209 + return HashedOctetString(arena, &spk, SEC_OID_SHA1); 1.210 +} 1.211 + 1.212 +static SECItem* 1.213 +AlgorithmIdentifier(PLArenaPool* arena, SECOidTag algTag) 1.214 +{ 1.215 + SECAlgorithmIDStr aid; 1.216 + aid.algorithm.data = nullptr; 1.217 + aid.algorithm.len = 0; 1.218 + aid.parameters.data = nullptr; 1.219 + aid.parameters.len = 0; 1.220 + if (SECOID_SetAlgorithmID(arena, &aid, algTag, nullptr) != SECSuccess) { 1.221 + return nullptr; 1.222 + } 1.223 + static const SEC_ASN1Template algorithmIDTemplate[] = { 1.224 + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SECAlgorithmID) }, 1.225 + { SEC_ASN1_OBJECT_ID, offsetof(SECAlgorithmID, algorithm) }, 1.226 + { SEC_ASN1_OPTIONAL | SEC_ASN1_ANY, offsetof(SECAlgorithmID, parameters) }, 1.227 + { 0 } 1.228 + }; 1.229 + SECItem* algorithmID = SEC_ASN1EncodeItem(arena, nullptr, &aid, 1.230 + algorithmIDTemplate); 1.231 + return algorithmID; 1.232 +} 1.233 + 1.234 +static SECItem* 1.235 +PRTimeToEncodedTime(PLArenaPool* arena, PRTime time) 1.236 +{ 1.237 + SECItem derTime; 1.238 + if (DER_TimeToGeneralizedTimeArena(arena, &derTime, time) != SECSuccess) { 1.239 + return nullptr; 1.240 + } 1.241 + return EncodeNested(arena, der::GENERALIZED_TIME, &derTime); 1.242 +} 1.243 + 1.244 +SECItem* 1.245 +CreateEncodedOCSPResponse(OCSPResponseContext& context) 1.246 +{ 1.247 + if (!context.arena || !context.cert || !context.issuerCert || 1.248 + !context.signerCert) { 1.249 + PR_SetError(SEC_ERROR_INVALID_ARGS, 0); 1.250 + return nullptr; 1.251 + } 1.252 + 1.253 + // OCSPResponse ::= SEQUENCE { 1.254 + // responseStatus OCSPResponseStatus, 1.255 + // responseBytes [0] EXPLICIT ResponseBytes OPTIONAL } 1.256 + 1.257 + // OCSPResponseStatus ::= ENUMERATED { 1.258 + // successful (0), -- Response has valid confirmations 1.259 + // malformedRequest (1), -- Illegal confirmation request 1.260 + // internalError (2), -- Internal error in issuer 1.261 + // tryLater (3), -- Try again later 1.262 + // -- (4) is not used 1.263 + // sigRequired (5), -- Must sign the request 1.264 + // unauthorized (6) -- Request unauthorized 1.265 + // } 1.266 + SECItem* responseStatus = SECITEM_AllocItem(context.arena, nullptr, 3); 1.267 + if (!responseStatus) { 1.268 + return nullptr; 1.269 + } 1.270 + responseStatus->data[0] = der::ENUMERATED; 1.271 + responseStatus->data[1] = 1; 1.272 + responseStatus->data[2] = context.responseStatus; 1.273 + 1.274 + SECItem* responseBytesNested = nullptr; 1.275 + if (!context.skipResponseBytes) { 1.276 + SECItem* responseBytes = ResponseBytes(context); 1.277 + if (!responseBytes) { 1.278 + return nullptr; 1.279 + } 1.280 + 1.281 + responseBytesNested = EncodeNested(context.arena, 1.282 + der::CONSTRUCTED | 1.283 + der::CONTEXT_SPECIFIC, 1.284 + responseBytes); 1.285 + if (!responseBytesNested) { 1.286 + return nullptr; 1.287 + } 1.288 + } 1.289 + 1.290 + Output output; 1.291 + if (output.Add(responseStatus) != der::Success) { 1.292 + return nullptr; 1.293 + } 1.294 + if (responseBytesNested) { 1.295 + if (output.Add(responseBytesNested) != der::Success) { 1.296 + return nullptr; 1.297 + } 1.298 + } 1.299 + return output.Squash(context.arena, der::SEQUENCE); 1.300 +} 1.301 + 1.302 +// ResponseBytes ::= SEQUENCE { 1.303 +// responseType OBJECT IDENTIFIER, 1.304 +// response OCTET STRING } 1.305 +SECItem* 1.306 +ResponseBytes(OCSPResponseContext& context) 1.307 +{ 1.308 + // Includes tag and length 1.309 + static const uint8_t id_pkix_ocsp_basic_encoded[] = { 1.310 + 0x06, 0x09, 0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x01 1.311 + }; 1.312 + SECItem id_pkix_ocsp_basic = { 1.313 + siBuffer, 1.314 + const_cast<uint8_t*>(id_pkix_ocsp_basic_encoded), 1.315 + PR_ARRAY_SIZE(id_pkix_ocsp_basic_encoded) 1.316 + }; 1.317 + SECItem* response = BasicOCSPResponse(context); 1.318 + if (!response) { 1.319 + return nullptr; 1.320 + } 1.321 + SECItem* responseNested = EncodeNested(context.arena, der::OCTET_STRING, 1.322 + response); 1.323 + if (!responseNested) { 1.324 + return nullptr; 1.325 + } 1.326 + 1.327 + Output output; 1.328 + if (output.Add(&id_pkix_ocsp_basic) != der::Success) { 1.329 + return nullptr; 1.330 + } 1.331 + if (output.Add(responseNested) != der::Success) { 1.332 + return nullptr; 1.333 + } 1.334 + return output.Squash(context.arena, der::SEQUENCE); 1.335 +} 1.336 + 1.337 +// BasicOCSPResponse ::= SEQUENCE { 1.338 +// tbsResponseData ResponseData, 1.339 +// signatureAlgorithm AlgorithmIdentifier, 1.340 +// signature BIT STRING, 1.341 +// certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL } 1.342 +SECItem* 1.343 +BasicOCSPResponse(OCSPResponseContext& context) 1.344 +{ 1.345 + SECItem* tbsResponseData = ResponseData(context); 1.346 + if (!tbsResponseData) { 1.347 + return nullptr; 1.348 + } 1.349 + 1.350 + 1.351 + pkix::ScopedPtr<SECKEYPrivateKey, SECKEY_DestroyPrivateKey> privKey( 1.352 + PK11_FindKeyByAnyCert(context.signerCert.get(), nullptr)); 1.353 + if (!privKey) { 1.354 + return nullptr; 1.355 + } 1.356 + SECOidTag signatureAlgTag = SEC_GetSignatureAlgorithmOidTag(privKey->keyType, 1.357 + SEC_OID_SHA1); 1.358 + if (signatureAlgTag == SEC_OID_UNKNOWN) { 1.359 + return nullptr; 1.360 + } 1.361 + SECItem* signatureAlgorithm = AlgorithmIdentifier(context.arena, 1.362 + signatureAlgTag); 1.363 + if (!signatureAlgorithm) { 1.364 + return nullptr; 1.365 + } 1.366 + 1.367 + // SEC_SignData doesn't take an arena parameter, so we have to manage 1.368 + // the memory allocated in signature. 1.369 + SECItem signature; 1.370 + if (SEC_SignData(&signature, tbsResponseData->data, tbsResponseData->len, 1.371 + privKey.get(), signatureAlgTag) != SECSuccess) 1.372 + { 1.373 + return nullptr; 1.374 + } 1.375 + // We have to add a byte at the beginning indicating no unused bits. 1.376 + // TODO: add ability to have signatures of bit length not divisible by 8, 1.377 + // resulting in unused bits in the bitstring encoding 1.378 + SECItem* prefixedSignature = SECITEM_AllocItem(context.arena, nullptr, 1.379 + signature.len + 1); 1.380 + if (!prefixedSignature) { 1.381 + SECITEM_FreeItem(&signature, false); 1.382 + return nullptr; 1.383 + } 1.384 + prefixedSignature->data[0] = 0; 1.385 + memcpy(prefixedSignature->data + 1, signature.data, signature.len); 1.386 + SECITEM_FreeItem(&signature, false); 1.387 + if (context.badSignature) { 1.388 + PR_ASSERT(prefixedSignature->len > 8); 1.389 + prefixedSignature->data[8]++; 1.390 + } 1.391 + SECItem* signatureNested = EncodeNested(context.arena, der::BIT_STRING, 1.392 + prefixedSignature); 1.393 + if (!signatureNested) { 1.394 + return nullptr; 1.395 + } 1.396 + SECItem* certificatesNested = nullptr; 1.397 + if (context.includedCertificates[0]) { 1.398 + SECItem* certificates = Certificates(context); 1.399 + if (!certificates) { 1.400 + return nullptr; 1.401 + } 1.402 + certificatesNested = EncodeNested(context.arena, 1.403 + der::CONSTRUCTED | 1.404 + der::CONTEXT_SPECIFIC | 1.405 + 0, 1.406 + certificates); 1.407 + if (!certificatesNested) { 1.408 + return nullptr; 1.409 + } 1.410 + } 1.411 + 1.412 + Output output; 1.413 + if (output.Add(tbsResponseData) != der::Success) { 1.414 + return nullptr; 1.415 + } 1.416 + if (output.Add(signatureAlgorithm) != der::Success) { 1.417 + return nullptr; 1.418 + } 1.419 + if (output.Add(signatureNested) != der::Success) { 1.420 + return nullptr; 1.421 + } 1.422 + if (certificatesNested) { 1.423 + if (output.Add(certificatesNested) != der::Success) { 1.424 + return nullptr; 1.425 + } 1.426 + } 1.427 + return output.Squash(context.arena, der::SEQUENCE); 1.428 +} 1.429 + 1.430 +// Extension ::= SEQUENCE { 1.431 +// id OBJECT IDENTIFIER, 1.432 +// critical BOOLEAN DEFAULT FALSE 1.433 +// value OCTET STRING 1.434 +// } 1.435 +static SECItem* 1.436 +OCSPExtension(OCSPResponseContext& context, OCSPResponseExtension* extension) 1.437 +{ 1.438 + Output output; 1.439 + if (output.Add(&extension->id) != der::Success) { 1.440 + return nullptr; 1.441 + } 1.442 + if (extension->critical) { 1.443 + static const uint8_t trueEncoded[3] = { 0x01, 0x01, 0xFF }; 1.444 + SECItem critical = { 1.445 + siBuffer, 1.446 + const_cast<uint8_t*>(trueEncoded), 1.447 + PR_ARRAY_SIZE(trueEncoded) 1.448 + }; 1.449 + if (output.Add(&critical) != der::Success) { 1.450 + return nullptr; 1.451 + } 1.452 + } 1.453 + SECItem* value = EncodeNested(context.arena, der::OCTET_STRING, 1.454 + &extension->value); 1.455 + if (!value) { 1.456 + return nullptr; 1.457 + } 1.458 + if (output.Add(value) != der::Success) { 1.459 + return nullptr; 1.460 + } 1.461 + return output.Squash(context.arena, der::SEQUENCE); 1.462 +} 1.463 + 1.464 +// Extensions ::= [1] { 1.465 +// SEQUENCE OF Extension 1.466 +// } 1.467 +static SECItem* 1.468 +Extensions(OCSPResponseContext& context) 1.469 +{ 1.470 + Output output; 1.471 + for (OCSPResponseExtension* extension = context.extensions; 1.472 + extension; extension = extension->next) { 1.473 + SECItem* extensionEncoded = OCSPExtension(context, extension); 1.474 + if (!extensionEncoded) { 1.475 + return nullptr; 1.476 + } 1.477 + if (output.Add(extensionEncoded) != der::Success) { 1.478 + return nullptr; 1.479 + } 1.480 + } 1.481 + SECItem* extensionsEncoded = output.Squash(context.arena, der::SEQUENCE); 1.482 + if (!extensionsEncoded) { 1.483 + return nullptr; 1.484 + } 1.485 + return EncodeNested(context.arena, 1.486 + der::CONSTRUCTED | 1.487 + der::CONTEXT_SPECIFIC | 1.488 + 1, 1.489 + extensionsEncoded); 1.490 +} 1.491 + 1.492 +// ResponseData ::= SEQUENCE { 1.493 +// version [0] EXPLICIT Version DEFAULT v1, 1.494 +// responderID ResponderID, 1.495 +// producedAt GeneralizedTime, 1.496 +// responses SEQUENCE OF SingleResponse, 1.497 +// responseExtensions [1] EXPLICIT Extensions OPTIONAL } 1.498 +SECItem* 1.499 +ResponseData(OCSPResponseContext& context) 1.500 +{ 1.501 + SECItem* responderID = ResponderID(context); 1.502 + if (!responderID) { 1.503 + return nullptr; 1.504 + } 1.505 + SECItem* producedAtEncoded = PRTimeToEncodedTime(context.arena, 1.506 + context.producedAt); 1.507 + if (!producedAtEncoded) { 1.508 + return nullptr; 1.509 + } 1.510 + SECItem* responses = SingleResponse(context); 1.511 + if (!responses) { 1.512 + return nullptr; 1.513 + } 1.514 + SECItem* responsesNested = EncodeNested(context.arena, der::SEQUENCE, 1.515 + responses); 1.516 + if (!responsesNested) { 1.517 + return nullptr; 1.518 + } 1.519 + SECItem* responseExtensions = nullptr; 1.520 + if (context.extensions || context.includeEmptyExtensions) { 1.521 + responseExtensions = Extensions(context); 1.522 + } 1.523 + 1.524 + Output output; 1.525 + if (output.Add(responderID) != der::Success) { 1.526 + return nullptr; 1.527 + } 1.528 + if (output.Add(producedAtEncoded) != der::Success) { 1.529 + return nullptr; 1.530 + } 1.531 + if (output.Add(responsesNested) != der::Success) { 1.532 + return nullptr; 1.533 + } 1.534 + if (responseExtensions) { 1.535 + if (output.Add(responseExtensions) != der::Success) { 1.536 + return nullptr; 1.537 + } 1.538 + } 1.539 + return output.Squash(context.arena, der::SEQUENCE); 1.540 +} 1.541 + 1.542 +// ResponderID ::= CHOICE { 1.543 +// byName [1] Name, 1.544 +// byKey [2] KeyHash } 1.545 +// } 1.546 +SECItem* 1.547 +ResponderID(OCSPResponseContext& context) 1.548 +{ 1.549 + SECItem* contents = nullptr; 1.550 + if (context.responderIDType == OCSPResponseContext::ByName) { 1.551 + contents = &context.signerCert->derSubject; 1.552 + } else if (context.responderIDType == OCSPResponseContext::ByKeyHash) { 1.553 + contents = KeyHash(context); 1.554 + if (!contents) { 1.555 + return nullptr; 1.556 + } 1.557 + } else { 1.558 + return nullptr; 1.559 + } 1.560 + 1.561 + return EncodeNested(context.arena, 1.562 + der::CONSTRUCTED | 1.563 + der::CONTEXT_SPECIFIC | 1.564 + context.responderIDType, 1.565 + contents); 1.566 +} 1.567 + 1.568 +// KeyHash ::= OCTET STRING -- SHA-1 hash of responder's public key 1.569 +// -- (i.e., the SHA-1 hash of the value of the 1.570 +// -- BIT STRING subjectPublicKey [excluding 1.571 +// -- the tag, length, and number of unused 1.572 +// -- bits] in the responder's certificate) 1.573 +SECItem* 1.574 +KeyHash(OCSPResponseContext& context) 1.575 +{ 1.576 + return KeyHashHelper(context.arena, context.signerCert.get()); 1.577 +} 1.578 + 1.579 +// SingleResponse ::= SEQUENCE { 1.580 +// certID CertID, 1.581 +// certStatus CertStatus, 1.582 +// thisUpdate GeneralizedTime, 1.583 +// nextUpdate [0] EXPLICIT GeneralizedTime OPTIONAL, 1.584 +// singleExtensions [1] EXPLICIT Extensions OPTIONAL } 1.585 +SECItem* 1.586 +SingleResponse(OCSPResponseContext& context) 1.587 +{ 1.588 + SECItem* certID = CertID(context); 1.589 + if (!certID) { 1.590 + return nullptr; 1.591 + } 1.592 + SECItem* certStatus = CertStatus(context); 1.593 + if (!certStatus) { 1.594 + return nullptr; 1.595 + } 1.596 + SECItem* thisUpdateEncoded = PRTimeToEncodedTime(context.arena, 1.597 + context.thisUpdate); 1.598 + if (!thisUpdateEncoded) { 1.599 + return nullptr; 1.600 + } 1.601 + SECItem* nextUpdateEncodedNested = nullptr; 1.602 + if (context.includeNextUpdate) { 1.603 + SECItem* nextUpdateEncoded = PRTimeToEncodedTime(context.arena, 1.604 + context.nextUpdate); 1.605 + if (!nextUpdateEncoded) { 1.606 + return nullptr; 1.607 + } 1.608 + nextUpdateEncodedNested = EncodeNested(context.arena, 1.609 + der::CONSTRUCTED | 1.610 + der::CONTEXT_SPECIFIC | 1.611 + 0, 1.612 + nextUpdateEncoded); 1.613 + if (!nextUpdateEncodedNested) { 1.614 + return nullptr; 1.615 + } 1.616 + } 1.617 + 1.618 + Output output; 1.619 + if (output.Add(certID) != der::Success) { 1.620 + return nullptr; 1.621 + } 1.622 + if (output.Add(certStatus) != der::Success) { 1.623 + return nullptr; 1.624 + } 1.625 + if (output.Add(thisUpdateEncoded) != der::Success) { 1.626 + return nullptr; 1.627 + } 1.628 + if (nextUpdateEncodedNested) { 1.629 + if (output.Add(nextUpdateEncodedNested) != der::Success) { 1.630 + return nullptr; 1.631 + } 1.632 + } 1.633 + return output.Squash(context.arena, der::SEQUENCE); 1.634 +} 1.635 + 1.636 +// CertID ::= SEQUENCE { 1.637 +// hashAlgorithm AlgorithmIdentifier, 1.638 +// issuerNameHash OCTET STRING, -- Hash of issuer's DN 1.639 +// issuerKeyHash OCTET STRING, -- Hash of issuer's public key 1.640 +// serialNumber CertificateSerialNumber } 1.641 +SECItem* 1.642 +CertID(OCSPResponseContext& context) 1.643 +{ 1.644 + SECItem* hashAlgorithm = AlgorithmIdentifier(context.arena, 1.645 + context.certIDHashAlg); 1.646 + if (!hashAlgorithm) { 1.647 + return nullptr; 1.648 + } 1.649 + SECItem* issuerNameHash = HashedOctetString(context.arena, 1.650 + &context.issuerCert->derSubject, 1.651 + context.certIDHashAlg); 1.652 + if (!issuerNameHash) { 1.653 + return nullptr; 1.654 + } 1.655 + SECItem* issuerKeyHash = KeyHashHelper(context.arena, 1.656 + context.issuerCert.get()); 1.657 + if (!issuerKeyHash) { 1.658 + return nullptr; 1.659 + } 1.660 + static const SEC_ASN1Template serialTemplate[] = { 1.661 + { SEC_ASN1_INTEGER, offsetof(CERTCertificate, serialNumber) }, 1.662 + { 0 } 1.663 + }; 1.664 + SECItem* serialNumber = SEC_ASN1EncodeItem(context.arena, nullptr, 1.665 + context.cert.get(), 1.666 + serialTemplate); 1.667 + if (!serialNumber) { 1.668 + return nullptr; 1.669 + } 1.670 + 1.671 + Output output; 1.672 + if (output.Add(hashAlgorithm) != der::Success) { 1.673 + return nullptr; 1.674 + } 1.675 + if (output.Add(issuerNameHash) != der::Success) { 1.676 + return nullptr; 1.677 + } 1.678 + if (output.Add(issuerKeyHash) != der::Success) { 1.679 + return nullptr; 1.680 + } 1.681 + if (output.Add(serialNumber) != der::Success) { 1.682 + return nullptr; 1.683 + } 1.684 + return output.Squash(context.arena, der::SEQUENCE); 1.685 +} 1.686 + 1.687 +// CertStatus ::= CHOICE { 1.688 +// good [0] IMPLICIT NULL, 1.689 +// revoked [1] IMPLICIT RevokedInfo, 1.690 +// unknown [2] IMPLICIT UnknownInfo } 1.691 +// 1.692 +// RevokedInfo ::= SEQUENCE { 1.693 +// revocationTime GeneralizedTime, 1.694 +// revocationReason [0] EXPLICIT CRLReason OPTIONAL } 1.695 +// 1.696 +// UnknownInfo ::= NULL 1.697 +// 1.698 +SECItem* 1.699 +CertStatus(OCSPResponseContext& context) 1.700 +{ 1.701 + switch (context.certStatus) { 1.702 + // Both good and unknown are ultimately represented as NULL - the only 1.703 + // difference is in the tag that identifies them. 1.704 + case 0: 1.705 + case 2: 1.706 + { 1.707 + SECItem* status = SECITEM_AllocItem(context.arena, nullptr, 2); 1.708 + if (!status) { 1.709 + return nullptr; 1.710 + } 1.711 + status->data[0] = der::CONTEXT_SPECIFIC | context.certStatus; 1.712 + status->data[1] = 0; 1.713 + return status; 1.714 + } 1.715 + case 1: 1.716 + { 1.717 + SECItem* revocationTime = PRTimeToEncodedTime(context.arena, 1.718 + context.revocationTime); 1.719 + if (!revocationTime) { 1.720 + return nullptr; 1.721 + } 1.722 + // TODO(bug 980536): add support for revocationReason 1.723 + return EncodeNested(context.arena, 1.724 + der::CONSTRUCTED | der::CONTEXT_SPECIFIC | 1, 1.725 + revocationTime); 1.726 + } 1.727 + default: 1.728 + PR_NOT_REACHED("CertStatus: bad context.certStatus"); 1.729 + PR_Abort(); 1.730 + } 1.731 + return nullptr; 1.732 +} 1.733 + 1.734 +// SEQUENCE OF Certificate 1.735 +SECItem* 1.736 +Certificates(OCSPResponseContext& context) 1.737 +{ 1.738 + Output output; 1.739 + for (size_t i = 0; i < context.MaxIncludedCertificates; i++) { 1.740 + CERTCertificate* cert = context.includedCertificates[i].get(); 1.741 + if (!cert) { 1.742 + break; 1.743 + } 1.744 + output.Add(&cert->derCert); 1.745 + } 1.746 + return output.Squash(context.arena, der::SEQUENCE); 1.747 +} 1.748 + 1.749 +} } } // namespace mozilla::pkix::test