security/pkix/test/lib/pkixtestutil.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

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 "pkixcheck.h"
michael@0 19 #include "pkixder.h"
michael@0 20 #include "pkixtestutil.h"
michael@0 21
michael@0 22 #include "cryptohi.h"
michael@0 23 #include "hasht.h"
michael@0 24 #include "pk11pub.h"
michael@0 25 #include "prinit.h"
michael@0 26 #include "secder.h"
michael@0 27
michael@0 28 namespace mozilla { namespace pkix { namespace test {
michael@0 29
michael@0 30 class Output
michael@0 31 {
michael@0 32 public:
michael@0 33 Output()
michael@0 34 : numItems(0)
michael@0 35 , length(0)
michael@0 36 {
michael@0 37 }
michael@0 38
michael@0 39 // Makes a shallow copy of the input item. All input items must have a
michael@0 40 // lifetime that extends at least to where Squash is called.
michael@0 41 der::Result Add(const SECItem* item)
michael@0 42 {
michael@0 43 PR_ASSERT(item);
michael@0 44 PR_ASSERT(item->data);
michael@0 45
michael@0 46 if (numItems >= MaxSequenceItems) {
michael@0 47 return der::Fail(SEC_ERROR_INVALID_ARGS);
michael@0 48 }
michael@0 49 if (length + item->len > 65535) {
michael@0 50 return der::Fail(SEC_ERROR_INVALID_ARGS);
michael@0 51 }
michael@0 52
michael@0 53 contents[numItems] = item;
michael@0 54 numItems++;
michael@0 55 length += item->len;
michael@0 56 return der::Success;
michael@0 57 }
michael@0 58
michael@0 59 SECItem* Squash(PLArenaPool* arena, uint8_t tag)
michael@0 60 {
michael@0 61 PR_ASSERT(arena);
michael@0 62
michael@0 63 size_t lengthLength = length < 128 ? 1
michael@0 64 : length < 256 ? 2
michael@0 65 : 3;
michael@0 66 size_t totalLength = 1 + lengthLength + length;
michael@0 67 SECItem* output = SECITEM_AllocItem(arena, nullptr, totalLength);
michael@0 68 if (!output) {
michael@0 69 return nullptr;
michael@0 70 }
michael@0 71 uint8_t* d = output->data;
michael@0 72 *d++ = tag;
michael@0 73 EncodeLength(d, length, lengthLength);
michael@0 74 d += lengthLength;
michael@0 75 for (size_t i = 0; i < numItems; i++) {
michael@0 76 memcpy(d, contents[i]->data, contents[i]->len);
michael@0 77 d += contents[i]->len;
michael@0 78 }
michael@0 79 return output;
michael@0 80 }
michael@0 81
michael@0 82 private:
michael@0 83 void
michael@0 84 EncodeLength(uint8_t* data, size_t length, size_t lengthLength)
michael@0 85 {
michael@0 86 switch (lengthLength) {
michael@0 87 case 1:
michael@0 88 data[0] = length;
michael@0 89 break;
michael@0 90 case 2:
michael@0 91 data[0] = 0x81;
michael@0 92 data[1] = length;
michael@0 93 break;
michael@0 94 case 3:
michael@0 95 data[0] = 0x82;
michael@0 96 data[1] = length / 256;
michael@0 97 data[2] = length % 256;
michael@0 98 break;
michael@0 99 default:
michael@0 100 PR_NOT_REACHED("EncodeLength: bad lengthLength");
michael@0 101 PR_Abort();
michael@0 102 }
michael@0 103 }
michael@0 104
michael@0 105 static const size_t MaxSequenceItems = 5;
michael@0 106 const SECItem* contents[MaxSequenceItems];
michael@0 107 size_t numItems;
michael@0 108 size_t length;
michael@0 109
michael@0 110 Output(const Output&) /* = delete */;
michael@0 111 void operator=(const Output&) /* = delete */;
michael@0 112 };
michael@0 113
michael@0 114 OCSPResponseContext::OCSPResponseContext(PLArenaPool* arena,
michael@0 115 CERTCertificate* cert,
michael@0 116 PRTime time)
michael@0 117 : arena(arena)
michael@0 118 , cert(CERT_DupCertificate(cert))
michael@0 119 , issuerCert(nullptr)
michael@0 120 , signerCert(nullptr)
michael@0 121 , responseStatus(0)
michael@0 122 , skipResponseBytes(false)
michael@0 123 , producedAt(time)
michael@0 124 , thisUpdate(time)
michael@0 125 , nextUpdate(time + 10 * PR_USEC_PER_SEC)
michael@0 126 , includeNextUpdate(true)
michael@0 127 , certIDHashAlg(SEC_OID_SHA1)
michael@0 128 , certStatus(0)
michael@0 129 , revocationTime(0)
michael@0 130 , badSignature(false)
michael@0 131 , responderIDType(ByKeyHash)
michael@0 132 , extensions(nullptr)
michael@0 133 , includeEmptyExtensions(false)
michael@0 134 {
michael@0 135 for (size_t i = 0; i < MaxIncludedCertificates; i++) {
michael@0 136 includedCertificates[i] = nullptr;
michael@0 137 }
michael@0 138 }
michael@0 139
michael@0 140 static SECItem* ResponseBytes(OCSPResponseContext& context);
michael@0 141 static SECItem* BasicOCSPResponse(OCSPResponseContext& context);
michael@0 142 static SECItem* ResponseData(OCSPResponseContext& context);
michael@0 143 static SECItem* ResponderID(OCSPResponseContext& context);
michael@0 144 static SECItem* KeyHash(OCSPResponseContext& context);
michael@0 145 static SECItem* SingleResponse(OCSPResponseContext& context);
michael@0 146 static SECItem* CertID(OCSPResponseContext& context);
michael@0 147 static SECItem* CertStatus(OCSPResponseContext& context);
michael@0 148 static SECItem* Certificates(OCSPResponseContext& context);
michael@0 149
michael@0 150 static SECItem*
michael@0 151 EncodeNested(PLArenaPool* arena, uint8_t tag, SECItem* inner)
michael@0 152 {
michael@0 153 Output output;
michael@0 154 if (output.Add(inner) != der::Success) {
michael@0 155 return nullptr;
michael@0 156 }
michael@0 157 return output.Squash(arena, tag);
michael@0 158 }
michael@0 159
michael@0 160 // A return value of 0 is an error, but this should never happen in practice
michael@0 161 // because this function aborts in that case.
michael@0 162 static size_t
michael@0 163 HashAlgorithmToLength(SECOidTag hashAlg)
michael@0 164 {
michael@0 165 switch (hashAlg) {
michael@0 166 case SEC_OID_SHA1:
michael@0 167 return SHA1_LENGTH;
michael@0 168 case SEC_OID_SHA256:
michael@0 169 return SHA256_LENGTH;
michael@0 170 case SEC_OID_SHA384:
michael@0 171 return SHA384_LENGTH;
michael@0 172 case SEC_OID_SHA512:
michael@0 173 return SHA512_LENGTH;
michael@0 174 default:
michael@0 175 PR_NOT_REACHED("HashAlgorithmToLength: bad hashAlg");
michael@0 176 PR_Abort();
michael@0 177 }
michael@0 178 return 0;
michael@0 179 }
michael@0 180
michael@0 181 static SECItem*
michael@0 182 HashedOctetString(PLArenaPool* arena, const SECItem* bytes, SECOidTag hashAlg)
michael@0 183 {
michael@0 184 size_t hashLen = HashAlgorithmToLength(hashAlg);
michael@0 185 if (hashLen == 0) {
michael@0 186 return nullptr;
michael@0 187 }
michael@0 188 SECItem* hashBuf = SECITEM_AllocItem(arena, nullptr, hashLen);
michael@0 189 if (!hashBuf) {
michael@0 190 return nullptr;
michael@0 191 }
michael@0 192 if (PK11_HashBuf(hashAlg, hashBuf->data, bytes->data, bytes->len)
michael@0 193 != SECSuccess) {
michael@0 194 return nullptr;
michael@0 195 }
michael@0 196
michael@0 197 return EncodeNested(arena, der::OCTET_STRING, hashBuf);
michael@0 198 }
michael@0 199
michael@0 200 static SECItem*
michael@0 201 KeyHashHelper(PLArenaPool* arena, const CERTCertificate* cert)
michael@0 202 {
michael@0 203 // We only need a shallow copy here.
michael@0 204 SECItem spk = cert->subjectPublicKeyInfo.subjectPublicKey;
michael@0 205 DER_ConvertBitString(&spk); // bits to bytes
michael@0 206 return HashedOctetString(arena, &spk, SEC_OID_SHA1);
michael@0 207 }
michael@0 208
michael@0 209 static SECItem*
michael@0 210 AlgorithmIdentifier(PLArenaPool* arena, SECOidTag algTag)
michael@0 211 {
michael@0 212 SECAlgorithmIDStr aid;
michael@0 213 aid.algorithm.data = nullptr;
michael@0 214 aid.algorithm.len = 0;
michael@0 215 aid.parameters.data = nullptr;
michael@0 216 aid.parameters.len = 0;
michael@0 217 if (SECOID_SetAlgorithmID(arena, &aid, algTag, nullptr) != SECSuccess) {
michael@0 218 return nullptr;
michael@0 219 }
michael@0 220 static const SEC_ASN1Template algorithmIDTemplate[] = {
michael@0 221 { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SECAlgorithmID) },
michael@0 222 { SEC_ASN1_OBJECT_ID, offsetof(SECAlgorithmID, algorithm) },
michael@0 223 { SEC_ASN1_OPTIONAL | SEC_ASN1_ANY, offsetof(SECAlgorithmID, parameters) },
michael@0 224 { 0 }
michael@0 225 };
michael@0 226 SECItem* algorithmID = SEC_ASN1EncodeItem(arena, nullptr, &aid,
michael@0 227 algorithmIDTemplate);
michael@0 228 return algorithmID;
michael@0 229 }
michael@0 230
michael@0 231 static SECItem*
michael@0 232 PRTimeToEncodedTime(PLArenaPool* arena, PRTime time)
michael@0 233 {
michael@0 234 SECItem derTime;
michael@0 235 if (DER_TimeToGeneralizedTimeArena(arena, &derTime, time) != SECSuccess) {
michael@0 236 return nullptr;
michael@0 237 }
michael@0 238 return EncodeNested(arena, der::GENERALIZED_TIME, &derTime);
michael@0 239 }
michael@0 240
michael@0 241 SECItem*
michael@0 242 CreateEncodedOCSPResponse(OCSPResponseContext& context)
michael@0 243 {
michael@0 244 if (!context.arena || !context.cert || !context.issuerCert ||
michael@0 245 !context.signerCert) {
michael@0 246 PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
michael@0 247 return nullptr;
michael@0 248 }
michael@0 249
michael@0 250 // OCSPResponse ::= SEQUENCE {
michael@0 251 // responseStatus OCSPResponseStatus,
michael@0 252 // responseBytes [0] EXPLICIT ResponseBytes OPTIONAL }
michael@0 253
michael@0 254 // OCSPResponseStatus ::= ENUMERATED {
michael@0 255 // successful (0), -- Response has valid confirmations
michael@0 256 // malformedRequest (1), -- Illegal confirmation request
michael@0 257 // internalError (2), -- Internal error in issuer
michael@0 258 // tryLater (3), -- Try again later
michael@0 259 // -- (4) is not used
michael@0 260 // sigRequired (5), -- Must sign the request
michael@0 261 // unauthorized (6) -- Request unauthorized
michael@0 262 // }
michael@0 263 SECItem* responseStatus = SECITEM_AllocItem(context.arena, nullptr, 3);
michael@0 264 if (!responseStatus) {
michael@0 265 return nullptr;
michael@0 266 }
michael@0 267 responseStatus->data[0] = der::ENUMERATED;
michael@0 268 responseStatus->data[1] = 1;
michael@0 269 responseStatus->data[2] = context.responseStatus;
michael@0 270
michael@0 271 SECItem* responseBytesNested = nullptr;
michael@0 272 if (!context.skipResponseBytes) {
michael@0 273 SECItem* responseBytes = ResponseBytes(context);
michael@0 274 if (!responseBytes) {
michael@0 275 return nullptr;
michael@0 276 }
michael@0 277
michael@0 278 responseBytesNested = EncodeNested(context.arena,
michael@0 279 der::CONSTRUCTED |
michael@0 280 der::CONTEXT_SPECIFIC,
michael@0 281 responseBytes);
michael@0 282 if (!responseBytesNested) {
michael@0 283 return nullptr;
michael@0 284 }
michael@0 285 }
michael@0 286
michael@0 287 Output output;
michael@0 288 if (output.Add(responseStatus) != der::Success) {
michael@0 289 return nullptr;
michael@0 290 }
michael@0 291 if (responseBytesNested) {
michael@0 292 if (output.Add(responseBytesNested) != der::Success) {
michael@0 293 return nullptr;
michael@0 294 }
michael@0 295 }
michael@0 296 return output.Squash(context.arena, der::SEQUENCE);
michael@0 297 }
michael@0 298
michael@0 299 // ResponseBytes ::= SEQUENCE {
michael@0 300 // responseType OBJECT IDENTIFIER,
michael@0 301 // response OCTET STRING }
michael@0 302 SECItem*
michael@0 303 ResponseBytes(OCSPResponseContext& context)
michael@0 304 {
michael@0 305 // Includes tag and length
michael@0 306 static const uint8_t id_pkix_ocsp_basic_encoded[] = {
michael@0 307 0x06, 0x09, 0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x01
michael@0 308 };
michael@0 309 SECItem id_pkix_ocsp_basic = {
michael@0 310 siBuffer,
michael@0 311 const_cast<uint8_t*>(id_pkix_ocsp_basic_encoded),
michael@0 312 PR_ARRAY_SIZE(id_pkix_ocsp_basic_encoded)
michael@0 313 };
michael@0 314 SECItem* response = BasicOCSPResponse(context);
michael@0 315 if (!response) {
michael@0 316 return nullptr;
michael@0 317 }
michael@0 318 SECItem* responseNested = EncodeNested(context.arena, der::OCTET_STRING,
michael@0 319 response);
michael@0 320 if (!responseNested) {
michael@0 321 return nullptr;
michael@0 322 }
michael@0 323
michael@0 324 Output output;
michael@0 325 if (output.Add(&id_pkix_ocsp_basic) != der::Success) {
michael@0 326 return nullptr;
michael@0 327 }
michael@0 328 if (output.Add(responseNested) != der::Success) {
michael@0 329 return nullptr;
michael@0 330 }
michael@0 331 return output.Squash(context.arena, der::SEQUENCE);
michael@0 332 }
michael@0 333
michael@0 334 // BasicOCSPResponse ::= SEQUENCE {
michael@0 335 // tbsResponseData ResponseData,
michael@0 336 // signatureAlgorithm AlgorithmIdentifier,
michael@0 337 // signature BIT STRING,
michael@0 338 // certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL }
michael@0 339 SECItem*
michael@0 340 BasicOCSPResponse(OCSPResponseContext& context)
michael@0 341 {
michael@0 342 SECItem* tbsResponseData = ResponseData(context);
michael@0 343 if (!tbsResponseData) {
michael@0 344 return nullptr;
michael@0 345 }
michael@0 346
michael@0 347
michael@0 348 pkix::ScopedPtr<SECKEYPrivateKey, SECKEY_DestroyPrivateKey> privKey(
michael@0 349 PK11_FindKeyByAnyCert(context.signerCert.get(), nullptr));
michael@0 350 if (!privKey) {
michael@0 351 return nullptr;
michael@0 352 }
michael@0 353 SECOidTag signatureAlgTag = SEC_GetSignatureAlgorithmOidTag(privKey->keyType,
michael@0 354 SEC_OID_SHA1);
michael@0 355 if (signatureAlgTag == SEC_OID_UNKNOWN) {
michael@0 356 return nullptr;
michael@0 357 }
michael@0 358 SECItem* signatureAlgorithm = AlgorithmIdentifier(context.arena,
michael@0 359 signatureAlgTag);
michael@0 360 if (!signatureAlgorithm) {
michael@0 361 return nullptr;
michael@0 362 }
michael@0 363
michael@0 364 // SEC_SignData doesn't take an arena parameter, so we have to manage
michael@0 365 // the memory allocated in signature.
michael@0 366 SECItem signature;
michael@0 367 if (SEC_SignData(&signature, tbsResponseData->data, tbsResponseData->len,
michael@0 368 privKey.get(), signatureAlgTag) != SECSuccess)
michael@0 369 {
michael@0 370 return nullptr;
michael@0 371 }
michael@0 372 // We have to add a byte at the beginning indicating no unused bits.
michael@0 373 // TODO: add ability to have signatures of bit length not divisible by 8,
michael@0 374 // resulting in unused bits in the bitstring encoding
michael@0 375 SECItem* prefixedSignature = SECITEM_AllocItem(context.arena, nullptr,
michael@0 376 signature.len + 1);
michael@0 377 if (!prefixedSignature) {
michael@0 378 SECITEM_FreeItem(&signature, false);
michael@0 379 return nullptr;
michael@0 380 }
michael@0 381 prefixedSignature->data[0] = 0;
michael@0 382 memcpy(prefixedSignature->data + 1, signature.data, signature.len);
michael@0 383 SECITEM_FreeItem(&signature, false);
michael@0 384 if (context.badSignature) {
michael@0 385 PR_ASSERT(prefixedSignature->len > 8);
michael@0 386 prefixedSignature->data[8]++;
michael@0 387 }
michael@0 388 SECItem* signatureNested = EncodeNested(context.arena, der::BIT_STRING,
michael@0 389 prefixedSignature);
michael@0 390 if (!signatureNested) {
michael@0 391 return nullptr;
michael@0 392 }
michael@0 393 SECItem* certificatesNested = nullptr;
michael@0 394 if (context.includedCertificates[0]) {
michael@0 395 SECItem* certificates = Certificates(context);
michael@0 396 if (!certificates) {
michael@0 397 return nullptr;
michael@0 398 }
michael@0 399 certificatesNested = EncodeNested(context.arena,
michael@0 400 der::CONSTRUCTED |
michael@0 401 der::CONTEXT_SPECIFIC |
michael@0 402 0,
michael@0 403 certificates);
michael@0 404 if (!certificatesNested) {
michael@0 405 return nullptr;
michael@0 406 }
michael@0 407 }
michael@0 408
michael@0 409 Output output;
michael@0 410 if (output.Add(tbsResponseData) != der::Success) {
michael@0 411 return nullptr;
michael@0 412 }
michael@0 413 if (output.Add(signatureAlgorithm) != der::Success) {
michael@0 414 return nullptr;
michael@0 415 }
michael@0 416 if (output.Add(signatureNested) != der::Success) {
michael@0 417 return nullptr;
michael@0 418 }
michael@0 419 if (certificatesNested) {
michael@0 420 if (output.Add(certificatesNested) != der::Success) {
michael@0 421 return nullptr;
michael@0 422 }
michael@0 423 }
michael@0 424 return output.Squash(context.arena, der::SEQUENCE);
michael@0 425 }
michael@0 426
michael@0 427 // Extension ::= SEQUENCE {
michael@0 428 // id OBJECT IDENTIFIER,
michael@0 429 // critical BOOLEAN DEFAULT FALSE
michael@0 430 // value OCTET STRING
michael@0 431 // }
michael@0 432 static SECItem*
michael@0 433 OCSPExtension(OCSPResponseContext& context, OCSPResponseExtension* extension)
michael@0 434 {
michael@0 435 Output output;
michael@0 436 if (output.Add(&extension->id) != der::Success) {
michael@0 437 return nullptr;
michael@0 438 }
michael@0 439 if (extension->critical) {
michael@0 440 static const uint8_t trueEncoded[3] = { 0x01, 0x01, 0xFF };
michael@0 441 SECItem critical = {
michael@0 442 siBuffer,
michael@0 443 const_cast<uint8_t*>(trueEncoded),
michael@0 444 PR_ARRAY_SIZE(trueEncoded)
michael@0 445 };
michael@0 446 if (output.Add(&critical) != der::Success) {
michael@0 447 return nullptr;
michael@0 448 }
michael@0 449 }
michael@0 450 SECItem* value = EncodeNested(context.arena, der::OCTET_STRING,
michael@0 451 &extension->value);
michael@0 452 if (!value) {
michael@0 453 return nullptr;
michael@0 454 }
michael@0 455 if (output.Add(value) != der::Success) {
michael@0 456 return nullptr;
michael@0 457 }
michael@0 458 return output.Squash(context.arena, der::SEQUENCE);
michael@0 459 }
michael@0 460
michael@0 461 // Extensions ::= [1] {
michael@0 462 // SEQUENCE OF Extension
michael@0 463 // }
michael@0 464 static SECItem*
michael@0 465 Extensions(OCSPResponseContext& context)
michael@0 466 {
michael@0 467 Output output;
michael@0 468 for (OCSPResponseExtension* extension = context.extensions;
michael@0 469 extension; extension = extension->next) {
michael@0 470 SECItem* extensionEncoded = OCSPExtension(context, extension);
michael@0 471 if (!extensionEncoded) {
michael@0 472 return nullptr;
michael@0 473 }
michael@0 474 if (output.Add(extensionEncoded) != der::Success) {
michael@0 475 return nullptr;
michael@0 476 }
michael@0 477 }
michael@0 478 SECItem* extensionsEncoded = output.Squash(context.arena, der::SEQUENCE);
michael@0 479 if (!extensionsEncoded) {
michael@0 480 return nullptr;
michael@0 481 }
michael@0 482 return EncodeNested(context.arena,
michael@0 483 der::CONSTRUCTED |
michael@0 484 der::CONTEXT_SPECIFIC |
michael@0 485 1,
michael@0 486 extensionsEncoded);
michael@0 487 }
michael@0 488
michael@0 489 // ResponseData ::= SEQUENCE {
michael@0 490 // version [0] EXPLICIT Version DEFAULT v1,
michael@0 491 // responderID ResponderID,
michael@0 492 // producedAt GeneralizedTime,
michael@0 493 // responses SEQUENCE OF SingleResponse,
michael@0 494 // responseExtensions [1] EXPLICIT Extensions OPTIONAL }
michael@0 495 SECItem*
michael@0 496 ResponseData(OCSPResponseContext& context)
michael@0 497 {
michael@0 498 SECItem* responderID = ResponderID(context);
michael@0 499 if (!responderID) {
michael@0 500 return nullptr;
michael@0 501 }
michael@0 502 SECItem* producedAtEncoded = PRTimeToEncodedTime(context.arena,
michael@0 503 context.producedAt);
michael@0 504 if (!producedAtEncoded) {
michael@0 505 return nullptr;
michael@0 506 }
michael@0 507 SECItem* responses = SingleResponse(context);
michael@0 508 if (!responses) {
michael@0 509 return nullptr;
michael@0 510 }
michael@0 511 SECItem* responsesNested = EncodeNested(context.arena, der::SEQUENCE,
michael@0 512 responses);
michael@0 513 if (!responsesNested) {
michael@0 514 return nullptr;
michael@0 515 }
michael@0 516 SECItem* responseExtensions = nullptr;
michael@0 517 if (context.extensions || context.includeEmptyExtensions) {
michael@0 518 responseExtensions = Extensions(context);
michael@0 519 }
michael@0 520
michael@0 521 Output output;
michael@0 522 if (output.Add(responderID) != der::Success) {
michael@0 523 return nullptr;
michael@0 524 }
michael@0 525 if (output.Add(producedAtEncoded) != der::Success) {
michael@0 526 return nullptr;
michael@0 527 }
michael@0 528 if (output.Add(responsesNested) != der::Success) {
michael@0 529 return nullptr;
michael@0 530 }
michael@0 531 if (responseExtensions) {
michael@0 532 if (output.Add(responseExtensions) != der::Success) {
michael@0 533 return nullptr;
michael@0 534 }
michael@0 535 }
michael@0 536 return output.Squash(context.arena, der::SEQUENCE);
michael@0 537 }
michael@0 538
michael@0 539 // ResponderID ::= CHOICE {
michael@0 540 // byName [1] Name,
michael@0 541 // byKey [2] KeyHash }
michael@0 542 // }
michael@0 543 SECItem*
michael@0 544 ResponderID(OCSPResponseContext& context)
michael@0 545 {
michael@0 546 SECItem* contents = nullptr;
michael@0 547 if (context.responderIDType == OCSPResponseContext::ByName) {
michael@0 548 contents = &context.signerCert->derSubject;
michael@0 549 } else if (context.responderIDType == OCSPResponseContext::ByKeyHash) {
michael@0 550 contents = KeyHash(context);
michael@0 551 if (!contents) {
michael@0 552 return nullptr;
michael@0 553 }
michael@0 554 } else {
michael@0 555 return nullptr;
michael@0 556 }
michael@0 557
michael@0 558 return EncodeNested(context.arena,
michael@0 559 der::CONSTRUCTED |
michael@0 560 der::CONTEXT_SPECIFIC |
michael@0 561 context.responderIDType,
michael@0 562 contents);
michael@0 563 }
michael@0 564
michael@0 565 // KeyHash ::= OCTET STRING -- SHA-1 hash of responder's public key
michael@0 566 // -- (i.e., the SHA-1 hash of the value of the
michael@0 567 // -- BIT STRING subjectPublicKey [excluding
michael@0 568 // -- the tag, length, and number of unused
michael@0 569 // -- bits] in the responder's certificate)
michael@0 570 SECItem*
michael@0 571 KeyHash(OCSPResponseContext& context)
michael@0 572 {
michael@0 573 return KeyHashHelper(context.arena, context.signerCert.get());
michael@0 574 }
michael@0 575
michael@0 576 // SingleResponse ::= SEQUENCE {
michael@0 577 // certID CertID,
michael@0 578 // certStatus CertStatus,
michael@0 579 // thisUpdate GeneralizedTime,
michael@0 580 // nextUpdate [0] EXPLICIT GeneralizedTime OPTIONAL,
michael@0 581 // singleExtensions [1] EXPLICIT Extensions OPTIONAL }
michael@0 582 SECItem*
michael@0 583 SingleResponse(OCSPResponseContext& context)
michael@0 584 {
michael@0 585 SECItem* certID = CertID(context);
michael@0 586 if (!certID) {
michael@0 587 return nullptr;
michael@0 588 }
michael@0 589 SECItem* certStatus = CertStatus(context);
michael@0 590 if (!certStatus) {
michael@0 591 return nullptr;
michael@0 592 }
michael@0 593 SECItem* thisUpdateEncoded = PRTimeToEncodedTime(context.arena,
michael@0 594 context.thisUpdate);
michael@0 595 if (!thisUpdateEncoded) {
michael@0 596 return nullptr;
michael@0 597 }
michael@0 598 SECItem* nextUpdateEncodedNested = nullptr;
michael@0 599 if (context.includeNextUpdate) {
michael@0 600 SECItem* nextUpdateEncoded = PRTimeToEncodedTime(context.arena,
michael@0 601 context.nextUpdate);
michael@0 602 if (!nextUpdateEncoded) {
michael@0 603 return nullptr;
michael@0 604 }
michael@0 605 nextUpdateEncodedNested = EncodeNested(context.arena,
michael@0 606 der::CONSTRUCTED |
michael@0 607 der::CONTEXT_SPECIFIC |
michael@0 608 0,
michael@0 609 nextUpdateEncoded);
michael@0 610 if (!nextUpdateEncodedNested) {
michael@0 611 return nullptr;
michael@0 612 }
michael@0 613 }
michael@0 614
michael@0 615 Output output;
michael@0 616 if (output.Add(certID) != der::Success) {
michael@0 617 return nullptr;
michael@0 618 }
michael@0 619 if (output.Add(certStatus) != der::Success) {
michael@0 620 return nullptr;
michael@0 621 }
michael@0 622 if (output.Add(thisUpdateEncoded) != der::Success) {
michael@0 623 return nullptr;
michael@0 624 }
michael@0 625 if (nextUpdateEncodedNested) {
michael@0 626 if (output.Add(nextUpdateEncodedNested) != der::Success) {
michael@0 627 return nullptr;
michael@0 628 }
michael@0 629 }
michael@0 630 return output.Squash(context.arena, der::SEQUENCE);
michael@0 631 }
michael@0 632
michael@0 633 // CertID ::= SEQUENCE {
michael@0 634 // hashAlgorithm AlgorithmIdentifier,
michael@0 635 // issuerNameHash OCTET STRING, -- Hash of issuer's DN
michael@0 636 // issuerKeyHash OCTET STRING, -- Hash of issuer's public key
michael@0 637 // serialNumber CertificateSerialNumber }
michael@0 638 SECItem*
michael@0 639 CertID(OCSPResponseContext& context)
michael@0 640 {
michael@0 641 SECItem* hashAlgorithm = AlgorithmIdentifier(context.arena,
michael@0 642 context.certIDHashAlg);
michael@0 643 if (!hashAlgorithm) {
michael@0 644 return nullptr;
michael@0 645 }
michael@0 646 SECItem* issuerNameHash = HashedOctetString(context.arena,
michael@0 647 &context.issuerCert->derSubject,
michael@0 648 context.certIDHashAlg);
michael@0 649 if (!issuerNameHash) {
michael@0 650 return nullptr;
michael@0 651 }
michael@0 652 SECItem* issuerKeyHash = KeyHashHelper(context.arena,
michael@0 653 context.issuerCert.get());
michael@0 654 if (!issuerKeyHash) {
michael@0 655 return nullptr;
michael@0 656 }
michael@0 657 static const SEC_ASN1Template serialTemplate[] = {
michael@0 658 { SEC_ASN1_INTEGER, offsetof(CERTCertificate, serialNumber) },
michael@0 659 { 0 }
michael@0 660 };
michael@0 661 SECItem* serialNumber = SEC_ASN1EncodeItem(context.arena, nullptr,
michael@0 662 context.cert.get(),
michael@0 663 serialTemplate);
michael@0 664 if (!serialNumber) {
michael@0 665 return nullptr;
michael@0 666 }
michael@0 667
michael@0 668 Output output;
michael@0 669 if (output.Add(hashAlgorithm) != der::Success) {
michael@0 670 return nullptr;
michael@0 671 }
michael@0 672 if (output.Add(issuerNameHash) != der::Success) {
michael@0 673 return nullptr;
michael@0 674 }
michael@0 675 if (output.Add(issuerKeyHash) != der::Success) {
michael@0 676 return nullptr;
michael@0 677 }
michael@0 678 if (output.Add(serialNumber) != der::Success) {
michael@0 679 return nullptr;
michael@0 680 }
michael@0 681 return output.Squash(context.arena, der::SEQUENCE);
michael@0 682 }
michael@0 683
michael@0 684 // CertStatus ::= CHOICE {
michael@0 685 // good [0] IMPLICIT NULL,
michael@0 686 // revoked [1] IMPLICIT RevokedInfo,
michael@0 687 // unknown [2] IMPLICIT UnknownInfo }
michael@0 688 //
michael@0 689 // RevokedInfo ::= SEQUENCE {
michael@0 690 // revocationTime GeneralizedTime,
michael@0 691 // revocationReason [0] EXPLICIT CRLReason OPTIONAL }
michael@0 692 //
michael@0 693 // UnknownInfo ::= NULL
michael@0 694 //
michael@0 695 SECItem*
michael@0 696 CertStatus(OCSPResponseContext& context)
michael@0 697 {
michael@0 698 switch (context.certStatus) {
michael@0 699 // Both good and unknown are ultimately represented as NULL - the only
michael@0 700 // difference is in the tag that identifies them.
michael@0 701 case 0:
michael@0 702 case 2:
michael@0 703 {
michael@0 704 SECItem* status = SECITEM_AllocItem(context.arena, nullptr, 2);
michael@0 705 if (!status) {
michael@0 706 return nullptr;
michael@0 707 }
michael@0 708 status->data[0] = der::CONTEXT_SPECIFIC | context.certStatus;
michael@0 709 status->data[1] = 0;
michael@0 710 return status;
michael@0 711 }
michael@0 712 case 1:
michael@0 713 {
michael@0 714 SECItem* revocationTime = PRTimeToEncodedTime(context.arena,
michael@0 715 context.revocationTime);
michael@0 716 if (!revocationTime) {
michael@0 717 return nullptr;
michael@0 718 }
michael@0 719 // TODO(bug 980536): add support for revocationReason
michael@0 720 return EncodeNested(context.arena,
michael@0 721 der::CONSTRUCTED | der::CONTEXT_SPECIFIC | 1,
michael@0 722 revocationTime);
michael@0 723 }
michael@0 724 default:
michael@0 725 PR_NOT_REACHED("CertStatus: bad context.certStatus");
michael@0 726 PR_Abort();
michael@0 727 }
michael@0 728 return nullptr;
michael@0 729 }
michael@0 730
michael@0 731 // SEQUENCE OF Certificate
michael@0 732 SECItem*
michael@0 733 Certificates(OCSPResponseContext& context)
michael@0 734 {
michael@0 735 Output output;
michael@0 736 for (size_t i = 0; i < context.MaxIncludedCertificates; i++) {
michael@0 737 CERTCertificate* cert = context.includedCertificates[i].get();
michael@0 738 if (!cert) {
michael@0 739 break;
michael@0 740 }
michael@0 741 output.Add(&cert->derCert);
michael@0 742 }
michael@0 743 return output.Squash(context.arena, der::SEQUENCE);
michael@0 744 }
michael@0 745
michael@0 746 } } } // namespace mozilla::pkix::test

mercurial