|
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 |