security/pkix/test/lib/pkixtestutil.cpp

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

mercurial