2 * Copyright (c) 2006 - 2007 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the Institute nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * @page page_revoke Revocation methods
37 * There are two revocation method for PKIX/X.509: CRL and OCSP.
38 * Revocation is needed if the private key is lost and
39 * stolen. Depending on how picky you are, you might want to make
40 * revocation for destroyed private keys too (smartcard broken), but
41 * that should not be a problem.
43 * CRL is a list of certifiates that have expired.
45 * OCSP is an online checking method where the requestor sends a list
46 * of certificates to the OCSP server to return a signed reply if they
47 * are valid or not. Some services sends a OCSP reply as part of the
48 * hand-shake to make the revoktion decision simpler/faster for the
57 CRLCertificateList crl
;
65 OCSPBasicOCSPResponse ocsp
;
71 struct hx509_revoke_ctx_data
{
74 struct revoke_crl
*val
;
78 struct revoke_ocsp
*val
;
84 * Allocate a revokation context. Free with hx509_revoke_free().
86 * @param context A hx509 context.
87 * @param ctx returns a newly allocated revokation context.
89 * @return An hx509 error code, see hx509_get_error_string().
91 * @ingroup hx509_revoke
95 hx509_revoke_init(hx509_context context
, hx509_revoke_ctx
*ctx
)
97 *ctx
= calloc(1, sizeof(**ctx
));
102 (*ctx
)->crls
.len
= 0;
103 (*ctx
)->crls
.val
= NULL
;
104 (*ctx
)->ocsps
.len
= 0;
105 (*ctx
)->ocsps
.val
= NULL
;
111 _hx509_revoke_ref(hx509_revoke_ctx ctx
)
116 _hx509_abort("revoke ctx refcount == 0 on ref");
118 if (ctx
->ref
== UINT_MAX
)
119 _hx509_abort("revoke ctx refcount == UINT_MAX on ref");
124 free_ocsp(struct revoke_ocsp
*ocsp
)
127 free_OCSPBasicOCSPResponse(&ocsp
->ocsp
);
128 hx509_certs_free(&ocsp
->certs
);
129 hx509_cert_free(ocsp
->signer
);
133 * Free a hx509 revokation context.
135 * @param ctx context to be freed
137 * @ingroup hx509_revoke
141 hx509_revoke_free(hx509_revoke_ctx
*ctx
)
145 if (ctx
== NULL
|| *ctx
== NULL
)
148 if ((*ctx
)->ref
== 0)
149 _hx509_abort("revoke ctx refcount == 0 on free");
150 if (--(*ctx
)->ref
> 0)
153 for (i
= 0; i
< (*ctx
)->crls
.len
; i
++) {
154 free((*ctx
)->crls
.val
[i
].path
);
155 free_CRLCertificateList(&(*ctx
)->crls
.val
[i
].crl
);
158 for (i
= 0; i
< (*ctx
)->ocsps
.len
; i
++)
159 free_ocsp(&(*ctx
)->ocsps
.val
[i
]);
160 free((*ctx
)->ocsps
.val
);
162 free((*ctx
)->crls
.val
);
164 memset(*ctx
, 0, sizeof(**ctx
));
170 verify_ocsp(hx509_context context
,
171 struct revoke_ocsp
*ocsp
,
176 hx509_cert signer
= NULL
;
180 _hx509_query_clear(&q
);
183 * Need to match on issuer too in case there are two CA that have
184 * issued the same name to a certificate. One example of this is
185 * the www.openvalidation.org test's ocsp validator.
188 q
.match
= HX509_QUERY_MATCH_ISSUER_NAME
;
189 q
.issuer_name
= &_hx509_get_cert(parent
)->tbsCertificate
.issuer
;
191 switch(ocsp
->ocsp
.tbsResponseData
.responderID
.element
) {
192 case choice_OCSPResponderID_byName
:
193 q
.match
|= HX509_QUERY_MATCH_SUBJECT_NAME
;
194 q
.subject_name
= &ocsp
->ocsp
.tbsResponseData
.responderID
.u
.byName
;
196 case choice_OCSPResponderID_byKey
:
197 q
.match
|= HX509_QUERY_MATCH_KEY_HASH_SHA1
;
198 q
.keyhash_sha1
= &ocsp
->ocsp
.tbsResponseData
.responderID
.u
.byKey
;
202 ret
= hx509_certs_find(context
, certs
, &q
, &signer
);
203 if (ret
&& ocsp
->certs
)
204 ret
= hx509_certs_find(context
, ocsp
->certs
, &q
, &signer
);
209 * If signer certificate isn't the CA certificate, lets check the
210 * it is the CA that signed the signer certificate and the OCSP EKU
213 if (hx509_cert_cmp(signer
, parent
) != 0) {
214 Certificate
*p
= _hx509_get_cert(parent
);
215 Certificate
*s
= _hx509_get_cert(signer
);
217 ret
= _hx509_cert_is_parent_cmp(s
, p
, 0);
219 ret
= HX509_PARENT_NOT_CA
;
220 hx509_set_error_string(context
, 0, ret
, "Revoke OCSP signer is "
221 "doesn't have CA as signer certificate");
225 ret
= _hx509_verify_signature_bitstring(context
,
227 &s
->signatureAlgorithm
,
228 &s
->tbsCertificate
._save
,
231 hx509_set_error_string(context
, HX509_ERROR_APPEND
, ret
,
232 "OCSP signer signature invalid");
236 ret
= hx509_cert_check_eku(context
, signer
,
237 &asn1_oid_id_pkix_kp_OCSPSigning
, 0);
242 ret
= _hx509_verify_signature_bitstring(context
,
244 &ocsp
->ocsp
.signatureAlgorithm
,
245 &ocsp
->ocsp
.tbsResponseData
._save
,
246 &ocsp
->ocsp
.signature
);
248 hx509_set_error_string(context
, HX509_ERROR_APPEND
, ret
,
249 "OCSP signature invalid");
253 ocsp
->signer
= signer
;
257 hx509_cert_free(signer
);
267 parse_ocsp_basic(const void *data
, size_t length
, OCSPBasicOCSPResponse
*basic
)
273 memset(basic
, 0, sizeof(*basic
));
275 ret
= decode_OCSPResponse(data
, length
, &resp
, &size
);
278 if (length
!= size
) {
279 free_OCSPResponse(&resp
);
280 return ASN1_EXTRA_DATA
;
283 switch (resp
.responseStatus
) {
287 free_OCSPResponse(&resp
);
288 return HX509_REVOKE_WRONG_DATA
;
291 if (resp
.responseBytes
== NULL
) {
292 free_OCSPResponse(&resp
);
296 ret
= der_heim_oid_cmp(&resp
.responseBytes
->responseType
,
297 &asn1_oid_id_pkix_ocsp_basic
);
299 free_OCSPResponse(&resp
);
300 return HX509_REVOKE_WRONG_DATA
;
303 ret
= decode_OCSPBasicOCSPResponse(resp
.responseBytes
->response
.data
,
304 resp
.responseBytes
->response
.length
,
308 free_OCSPResponse(&resp
);
311 if (size
!= resp
.responseBytes
->response
.length
) {
312 free_OCSPResponse(&resp
);
313 free_OCSPBasicOCSPResponse(basic
);
314 return ASN1_EXTRA_DATA
;
316 free_OCSPResponse(&resp
);
326 load_ocsp(hx509_context context
, struct revoke_ocsp
*ocsp
)
328 OCSPBasicOCSPResponse basic
;
329 hx509_certs certs
= NULL
;
335 ret
= rk_undumpdata(ocsp
->path
, &data
, &length
);
339 ret
= stat(ocsp
->path
, &sb
);
343 ret
= parse_ocsp_basic(data
, length
, &basic
);
346 hx509_set_error_string(context
, 0, ret
,
347 "Failed to parse OCSP response");
354 ret
= hx509_certs_init(context
, "MEMORY:ocsp-certs", 0,
357 free_OCSPBasicOCSPResponse(&basic
);
361 for (i
= 0; i
< basic
.certs
->len
; i
++) {
364 c
= hx509_cert_init(context
, &basic
.certs
->val
[i
], NULL
);
368 ret
= hx509_certs_add(context
, certs
, c
);
375 ocsp
->last_modfied
= sb
.st_mtime
;
377 free_OCSPBasicOCSPResponse(&ocsp
->ocsp
);
378 hx509_certs_free(&ocsp
->certs
);
379 hx509_cert_free(ocsp
->signer
);
389 * Add a OCSP file to the revokation context.
391 * @param context hx509 context
392 * @param ctx hx509 revokation context
393 * @param path path to file that is going to be added to the context.
395 * @return An hx509 error code, see hx509_get_error_string().
397 * @ingroup hx509_revoke
401 hx509_revoke_add_ocsp(hx509_context context
,
402 hx509_revoke_ctx ctx
,
409 if (strncmp(path
, "FILE:", 5) != 0) {
410 hx509_set_error_string(context
, 0, HX509_UNSUPPORTED_OPERATION
,
411 "unsupport type in %s", path
);
412 return HX509_UNSUPPORTED_OPERATION
;
417 for (i
= 0; i
< ctx
->ocsps
.len
; i
++) {
418 if (strcmp(ctx
->ocsps
.val
[0].path
, path
) == 0)
422 data
= realloc(ctx
->ocsps
.val
,
423 (ctx
->ocsps
.len
+ 1) * sizeof(ctx
->ocsps
.val
[0]));
425 hx509_clear_error_string(context
);
429 ctx
->ocsps
.val
= data
;
431 memset(&ctx
->ocsps
.val
[ctx
->ocsps
.len
], 0,
432 sizeof(ctx
->ocsps
.val
[0]));
434 ctx
->ocsps
.val
[ctx
->ocsps
.len
].path
= strdup(path
);
435 if (ctx
->ocsps
.val
[ctx
->ocsps
.len
].path
== NULL
) {
436 hx509_clear_error_string(context
);
440 ret
= load_ocsp(context
, &ctx
->ocsps
.val
[ctx
->ocsps
.len
]);
442 free(ctx
->ocsps
.val
[ctx
->ocsps
.len
].path
);
455 verify_crl(hx509_context context
,
456 hx509_revoke_ctx ctx
,
457 CRLCertificateList
*crl
,
467 t
= _hx509_Time2time_t(&crl
->tbsCertList
.thisUpdate
);
469 hx509_set_error_string(context
, 0, HX509_CRL_USED_BEFORE_TIME
,
470 "CRL used before time");
471 return HX509_CRL_USED_BEFORE_TIME
;
474 if (crl
->tbsCertList
.nextUpdate
== NULL
) {
475 hx509_set_error_string(context
, 0, HX509_CRL_INVALID_FORMAT
,
476 "CRL missing nextUpdate");
477 return HX509_CRL_INVALID_FORMAT
;
480 t
= _hx509_Time2time_t(crl
->tbsCertList
.nextUpdate
);
482 hx509_set_error_string(context
, 0, HX509_CRL_USED_AFTER_TIME
,
483 "CRL used after time");
484 return HX509_CRL_USED_AFTER_TIME
;
487 _hx509_query_clear(&q
);
490 * If it's the signer have CRLSIGN bit set, use that as the signer
491 * cert for the certificate, otherwise, search for a certificate.
493 if (_hx509_check_key_usage(context
, parent
, 1 << 6, FALSE
) == 0) {
494 signer
= hx509_cert_ref(parent
);
496 q
.match
= HX509_QUERY_MATCH_SUBJECT_NAME
;
497 q
.match
|= HX509_QUERY_KU_CRLSIGN
;
498 q
.subject_name
= &crl
->tbsCertList
.issuer
;
500 ret
= hx509_certs_find(context
, certs
, &q
, &signer
);
502 hx509_set_error_string(context
, HX509_ERROR_APPEND
, ret
,
503 "Failed to find certificate for CRL");
508 ret
= _hx509_verify_signature_bitstring(context
,
510 &crl
->signatureAlgorithm
,
511 &crl
->tbsCertList
._save
,
512 &crl
->signatureValue
);
514 hx509_set_error_string(context
, HX509_ERROR_APPEND
, ret
,
515 "CRL signature invalid");
520 * If signer is not CA cert, need to check revoke status of this
521 * CRL signing cert too, this include all parent CRL signer cert
522 * up to the root *sigh*, assume root at least hve CERTSIGN flag
525 while (_hx509_check_key_usage(context
, signer
, 1 << 5, TRUE
)) {
526 hx509_cert crl_parent
;
528 _hx509_query_clear(&q
);
530 q
.match
= HX509_QUERY_MATCH_SUBJECT_NAME
;
531 q
.match
|= HX509_QUERY_KU_CRLSIGN
;
532 q
.subject_name
= &_hx509_get_cert(signer
)->tbsCertificate
.issuer
;
534 ret
= hx509_certs_find(context
, certs
, &q
, &crl_parent
);
536 hx509_set_error_string(context
, HX509_ERROR_APPEND
, ret
,
537 "Failed to find parent of CRL signer");
541 ret
= hx509_revoke_verify(context
,
547 hx509_cert_free(signer
);
550 hx509_set_error_string(context
, HX509_ERROR_APPEND
, ret
,
551 "Failed to verify revoke "
552 "status of CRL signer");
558 hx509_cert_free(signer
);
564 crl_parser(hx509_context context
, const char *type
,
565 const hx509_pem_header
*header
,
566 const void *data
, size_t len
, void *ctx
)
568 CRLCertificateList
*crl
= (CRLCertificateList
*)ctx
;
572 if (strcasecmp("X509 CRL", type
) != 0)
573 return HX509_CRYPTO_SIG_INVALID_FORMAT
;
575 ret
= decode_CRLCertificateList(data
, len
, crl
, &size
);
579 /* check signature is aligned */
580 if (crl
->signatureValue
.length
& 7) {
581 free_CRLCertificateList(crl
);
582 return HX509_CRYPTO_SIG_INVALID_FORMAT
;
589 load_crl(hx509_context context
, const char *path
, time_t *t
, CRLCertificateList
*crl
)
597 memset(crl
, 0, sizeof(*crl
));
599 ret
= stat(path
, &sb
);
605 if ((f
= fopen(path
, "r")) == NULL
)
610 ret
= hx509_pem_read(context
, f
, crl_parser
, crl
);
613 if (ret
== HX509_PARSING_KEY_FAILED
) {
615 ret
= rk_undumpdata(path
, &data
, &length
);
619 ret
= crl_parser(context
, "X509 CRL", NULL
, data
, length
, crl
);
626 * Add a CRL file to the revokation context.
628 * @param context hx509 context
629 * @param ctx hx509 revokation context
630 * @param path path to file that is going to be added to the context.
632 * @return An hx509 error code, see hx509_get_error_string().
634 * @ingroup hx509_revoke
638 hx509_revoke_add_crl(hx509_context context
,
639 hx509_revoke_ctx ctx
,
646 if (strncmp(path
, "FILE:", 5) != 0) {
647 hx509_set_error_string(context
, 0, HX509_UNSUPPORTED_OPERATION
,
648 "unsupport type in %s", path
);
649 return HX509_UNSUPPORTED_OPERATION
;
655 for (i
= 0; i
< ctx
->crls
.len
; i
++) {
656 if (strcmp(ctx
->crls
.val
[i
].path
, path
) == 0)
660 data
= realloc(ctx
->crls
.val
,
661 (ctx
->crls
.len
+ 1) * sizeof(ctx
->crls
.val
[0]));
663 hx509_clear_error_string(context
);
666 ctx
->crls
.val
= data
;
668 memset(&ctx
->crls
.val
[ctx
->crls
.len
], 0, sizeof(ctx
->crls
.val
[0]));
670 ctx
->crls
.val
[ctx
->crls
.len
].path
= strdup(path
);
671 if (ctx
->crls
.val
[ctx
->crls
.len
].path
== NULL
) {
672 hx509_clear_error_string(context
);
676 ret
= load_crl(context
,
678 &ctx
->crls
.val
[ctx
->crls
.len
].last_modfied
,
679 &ctx
->crls
.val
[ctx
->crls
.len
].crl
);
681 free(ctx
->crls
.val
[ctx
->crls
.len
].path
);
691 * Check that a certificate is not expired according to a revokation
692 * context. Also need the parent certificte to the check OCSP
695 * @param context hx509 context
696 * @param ctx hx509 revokation context
702 * @return An hx509 error code, see hx509_get_error_string().
704 * @ingroup hx509_revoke
708 hx509_revoke_verify(hx509_context context
,
709 hx509_revoke_ctx ctx
,
713 hx509_cert parent_cert
)
715 const Certificate
*c
= _hx509_get_cert(cert
);
716 const Certificate
*p
= _hx509_get_cert(parent_cert
);
717 unsigned long i
, j
, k
;
720 hx509_clear_error_string(context
);
722 for (i
= 0; i
< ctx
->ocsps
.len
; i
++) {
723 struct revoke_ocsp
*ocsp
= &ctx
->ocsps
.val
[i
];
726 /* check this ocsp apply to this cert */
728 /* check if there is a newer version of the file */
729 ret
= stat(ocsp
->path
, &sb
);
730 if (ret
== 0 && ocsp
->last_modfied
!= sb
.st_mtime
) {
731 ret
= load_ocsp(context
, ocsp
);
736 /* verify signature in ocsp if not already done */
737 if (ocsp
->signer
== NULL
) {
738 ret
= verify_ocsp(context
, ocsp
, now
, certs
, parent_cert
);
743 for (j
= 0; j
< ocsp
->ocsp
.tbsResponseData
.responses
.len
; j
++) {
744 heim_octet_string os
;
746 ret
= der_heim_integer_cmp(&ocsp
->ocsp
.tbsResponseData
.responses
.val
[j
].certID
.serialNumber
,
747 &c
->tbsCertificate
.serialNumber
);
751 /* verify issuer hashes hash */
752 ret
= _hx509_verify_signature(context
,
754 &ocsp
->ocsp
.tbsResponseData
.responses
.val
[i
].certID
.hashAlgorithm
,
755 &c
->tbsCertificate
.issuer
._save
,
756 &ocsp
->ocsp
.tbsResponseData
.responses
.val
[i
].certID
.issuerNameHash
);
760 os
.data
= p
->tbsCertificate
.subjectPublicKeyInfo
.subjectPublicKey
.data
;
761 os
.length
= p
->tbsCertificate
.subjectPublicKeyInfo
.subjectPublicKey
.length
/ 8;
763 ret
= _hx509_verify_signature(context
,
765 &ocsp
->ocsp
.tbsResponseData
.responses
.val
[j
].certID
.hashAlgorithm
,
767 &ocsp
->ocsp
.tbsResponseData
.responses
.val
[j
].certID
.issuerKeyHash
);
771 switch (ocsp
->ocsp
.tbsResponseData
.responses
.val
[j
].certStatus
.element
) {
772 case choice_OCSPCertStatus_good
:
774 case choice_OCSPCertStatus_revoked
:
775 hx509_set_error_string(context
, 0,
777 "Certificate revoked by issuer in OCSP");
778 return HX509_CERT_REVOKED
;
779 case choice_OCSPCertStatus_unknown
:
783 /* don't allow the update to be in the future */
784 if (ocsp
->ocsp
.tbsResponseData
.responses
.val
[j
].thisUpdate
>
785 now
+ context
->ocsp_time_diff
)
788 /* don't allow the next update to be in the past */
789 if (ocsp
->ocsp
.tbsResponseData
.responses
.val
[j
].nextUpdate
) {
790 if (*ocsp
->ocsp
.tbsResponseData
.responses
.val
[j
].nextUpdate
< now
)
792 } /* else should force a refetch, but can we ? */
798 for (i
= 0; i
< ctx
->crls
.len
; i
++) {
799 struct revoke_crl
*crl
= &ctx
->crls
.val
[i
];
803 /* check if cert.issuer == crls.val[i].crl.issuer */
804 ret
= _hx509_name_cmp(&c
->tbsCertificate
.issuer
,
805 &crl
->crl
.tbsCertList
.issuer
, &diff
);
809 ret
= stat(crl
->path
, &sb
);
810 if (ret
== 0 && crl
->last_modfied
!= sb
.st_mtime
) {
811 CRLCertificateList cl
;
813 ret
= load_crl(context
, crl
->path
, &crl
->last_modfied
, &cl
);
815 free_CRLCertificateList(&crl
->crl
);
818 crl
->failed_verify
= 0;
821 if (crl
->failed_verify
)
824 /* verify signature in crl if not already done */
825 if (crl
->verified
== 0) {
826 ret
= verify_crl(context
, ctx
, &crl
->crl
, now
, certs
, parent_cert
);
828 crl
->failed_verify
= 1;
834 if (crl
->crl
.tbsCertList
.crlExtensions
) {
835 for (j
= 0; j
< crl
->crl
.tbsCertList
.crlExtensions
->len
; j
++) {
836 if (crl
->crl
.tbsCertList
.crlExtensions
->val
[j
].critical
) {
837 hx509_set_error_string(context
, 0,
838 HX509_CRL_UNKNOWN_EXTENSION
,
839 "Unknown CRL extension");
840 return HX509_CRL_UNKNOWN_EXTENSION
;
845 if (crl
->crl
.tbsCertList
.revokedCertificates
== NULL
)
848 /* check if cert is in crl */
849 for (j
= 0; j
< crl
->crl
.tbsCertList
.revokedCertificates
->len
; j
++) {
852 ret
= der_heim_integer_cmp(&crl
->crl
.tbsCertList
.revokedCertificates
->val
[j
].userCertificate
,
853 &c
->tbsCertificate
.serialNumber
);
857 t
= _hx509_Time2time_t(&crl
->crl
.tbsCertList
.revokedCertificates
->val
[j
].revocationDate
);
861 if (crl
->crl
.tbsCertList
.revokedCertificates
->val
[j
].crlEntryExtensions
)
862 for (k
= 0; k
< crl
->crl
.tbsCertList
.revokedCertificates
->val
[j
].crlEntryExtensions
->len
; k
++)
863 if (crl
->crl
.tbsCertList
.revokedCertificates
->val
[j
].crlEntryExtensions
->val
[k
].critical
)
864 return HX509_CRL_UNKNOWN_EXTENSION
;
866 hx509_set_error_string(context
, 0,
868 "Certificate revoked by issuer in CRL");
869 return HX509_CERT_REVOKED
;
876 if (context
->flags
& HX509_CTX_VERIFY_MISSING_OK
)
878 hx509_set_error_string(context
, HX509_ERROR_APPEND
,
879 HX509_REVOKE_STATUS_MISSING
,
880 "No revoke status found for "
882 return HX509_REVOKE_STATUS_MISSING
;
885 struct ocsp_add_ctx
{
888 const AlgorithmIdentifier
*digest
;
893 add_to_req(hx509_context context
, void *ptr
, hx509_cert cert
)
895 struct ocsp_add_ctx
*ctx
= ptr
;
896 OCSPInnerRequest
*one
;
897 hx509_cert parent
= NULL
;
898 Certificate
*p
, *c
= _hx509_get_cert(cert
);
899 heim_octet_string os
;
904 d
= realloc(ctx
->req
->requestList
.val
,
905 sizeof(ctx
->req
->requestList
.val
[0]) *
906 (ctx
->req
->requestList
.len
+ 1));
909 ctx
->req
->requestList
.val
= d
;
911 one
= &ctx
->req
->requestList
.val
[ctx
->req
->requestList
.len
];
912 memset(one
, 0, sizeof(*one
));
914 _hx509_query_clear(&q
);
916 q
.match
|= HX509_QUERY_FIND_ISSUER_CERT
;
919 ret
= hx509_certs_find(context
, ctx
->certs
, &q
, &parent
);
924 if (hx509_cert_cmp(ctx
->parent
, parent
) != 0) {
925 ret
= HX509_REVOKE_NOT_SAME_PARENT
;
926 hx509_set_error_string(context
, 0, ret
,
927 "Not same parent certifate as "
928 "last certificate in request");
932 ctx
->parent
= hx509_cert_ref(parent
);
934 p
= _hx509_get_cert(parent
);
936 ret
= copy_AlgorithmIdentifier(ctx
->digest
, &one
->reqCert
.hashAlgorithm
);
940 ret
= _hx509_create_signature(context
,
942 &one
->reqCert
.hashAlgorithm
,
943 &c
->tbsCertificate
.issuer
._save
,
945 &one
->reqCert
.issuerNameHash
);
949 os
.data
= p
->tbsCertificate
.subjectPublicKeyInfo
.subjectPublicKey
.data
;
951 p
->tbsCertificate
.subjectPublicKeyInfo
.subjectPublicKey
.length
/ 8;
953 ret
= _hx509_create_signature(context
,
955 &one
->reqCert
.hashAlgorithm
,
958 &one
->reqCert
.issuerKeyHash
);
962 ret
= copy_CertificateSerialNumber(&c
->tbsCertificate
.serialNumber
,
963 &one
->reqCert
.serialNumber
);
967 ctx
->req
->requestList
.len
++;
969 hx509_cert_free(parent
);
971 free_OCSPInnerRequest(one
);
972 memset(one
, 0, sizeof(*one
));
979 * Create an OCSP request for a set of certificates.
981 * @param context a hx509 context
982 * @param reqcerts list of certificates to request ocsp data for
983 * @param pool certificate pool to use when signing
984 * @param signer certificate to use to sign the request
985 * @param digest the signing algorithm in the request, if NULL use the
986 * default signature algorithm,
987 * @param request the encoded request, free with free_heim_octet_string().
988 * @param nonce nonce in the request, free with free_heim_octet_string().
990 * @return An hx509 error code, see hx509_get_error_string().
992 * @ingroup hx509_revoke
996 hx509_ocsp_request(hx509_context context
,
997 hx509_certs reqcerts
,
1000 const AlgorithmIdentifier
*digest
,
1001 heim_octet_string
*request
,
1002 heim_octet_string
*nonce
)
1007 struct ocsp_add_ctx ctx
;
1010 memset(&req
, 0, sizeof(req
));
1013 digest
= _hx509_crypto_default_digest_alg
;
1015 ctx
.req
= &req
.tbsRequest
;
1017 ctx
.digest
= digest
;
1020 ret
= hx509_certs_iter_f(context
, reqcerts
, add_to_req
, &ctx
);
1021 hx509_cert_free(ctx
.parent
);
1026 req
.tbsRequest
.requestExtensions
=
1027 calloc(1, sizeof(*req
.tbsRequest
.requestExtensions
));
1028 if (req
.tbsRequest
.requestExtensions
== NULL
) {
1033 es
= req
.tbsRequest
.requestExtensions
;
1035 es
->val
= calloc(es
->len
, sizeof(es
->val
[0]));
1036 if (es
->val
== NULL
) {
1041 ret
= der_copy_oid(&asn1_oid_id_pkix_ocsp_nonce
, &es
->val
[0].extnID
);
1043 free_OCSPRequest(&req
);
1047 es
->val
[0].extnValue
.data
= malloc(10);
1048 if (es
->val
[0].extnValue
.data
== NULL
) {
1052 es
->val
[0].extnValue
.length
= 10;
1054 ret
= RAND_bytes(es
->val
[0].extnValue
.data
,
1055 es
->val
[0].extnValue
.length
);
1057 ret
= HX509_CRYPTO_INTERNAL_ERROR
;
1060 ret
= der_copy_octet_string(nonce
, &es
->val
[0].extnValue
);
1067 ASN1_MALLOC_ENCODE(OCSPRequest
, request
->data
, request
->length
,
1069 free_OCSPRequest(&req
);
1072 if (size
!= request
->length
)
1073 _hx509_abort("internal ASN.1 encoder error");
1078 free_OCSPRequest(&req
);
1083 printable_time(time_t t
)
1087 if ((p
= ctime(&t
)) == NULL
)
1088 strlcpy(s
, "?", sizeof(s
));
1090 strlcpy(s
, p
+ 4, sizeof(s
));
1101 print_ocsp(hx509_context context
, struct revoke_ocsp
*ocsp
, FILE *out
)
1106 fprintf(out
, "signer: ");
1108 switch(ocsp
->ocsp
.tbsResponseData
.responderID
.element
) {
1109 case choice_OCSPResponderID_byName
: {
1112 _hx509_name_from_Name(&ocsp
->ocsp
.tbsResponseData
.responderID
.u
.byName
, &n
);
1113 hx509_name_to_string(n
, &s
);
1114 hx509_name_free(&n
);
1115 fprintf(out
, " byName: %s\n", s
);
1119 case choice_OCSPResponderID_byKey
: {
1121 hex_encode(ocsp
->ocsp
.tbsResponseData
.responderID
.u
.byKey
.data
,
1122 ocsp
->ocsp
.tbsResponseData
.responderID
.u
.byKey
.length
,
1124 fprintf(out
, " byKey: %s\n", s
);
1129 _hx509_abort("choice_OCSPResponderID unknown");
1133 fprintf(out
, "producedAt: %s\n",
1134 printable_time(ocsp
->ocsp
.tbsResponseData
.producedAt
));
1136 fprintf(out
, "replies: %d\n", ocsp
->ocsp
.tbsResponseData
.responses
.len
);
1138 for (i
= 0; i
< ocsp
->ocsp
.tbsResponseData
.responses
.len
; i
++) {
1140 switch (ocsp
->ocsp
.tbsResponseData
.responses
.val
[i
].certStatus
.element
) {
1141 case choice_OCSPCertStatus_good
:
1144 case choice_OCSPCertStatus_revoked
:
1147 case choice_OCSPCertStatus_unknown
:
1151 status
= "element unknown";
1154 fprintf(out
, "\t%zu. status: %s\n", i
, status
);
1156 fprintf(out
, "\tthisUpdate: %s\n",
1157 printable_time(ocsp
->ocsp
.tbsResponseData
.responses
.val
[i
].thisUpdate
));
1158 if (ocsp
->ocsp
.tbsResponseData
.responses
.val
[i
].nextUpdate
)
1159 fprintf(out
, "\tproducedAt: %s\n",
1160 printable_time(ocsp
->ocsp
.tbsResponseData
.responses
.val
[i
].thisUpdate
));
1164 fprintf(out
, "appended certs:\n");
1166 ret
= hx509_certs_iter_f(context
, ocsp
->certs
, hx509_ci_print_names
, out
);
1172 print_crl(hx509_context context
, struct revoke_crl
*crl
, FILE *out
)
1177 _hx509_name_from_Name(&crl
->crl
.tbsCertList
.issuer
, &n
);
1178 hx509_name_to_string(n
, &s
);
1179 hx509_name_free(&n
);
1180 fprintf(out
, " issuer: %s\n", s
);
1184 fprintf(out
, " thisUpdate: %s\n",
1185 printable_time(_hx509_Time2time_t(&crl
->crl
.tbsCertList
.thisUpdate
)));
1196 hx509_revoke_print(hx509_context context
,
1197 hx509_revoke_ctx ctx
,
1200 int saved_ret
= 0, ret
;
1203 for (n
= 0; n
< ctx
->ocsps
.len
; n
++) {
1204 struct revoke_ocsp
*ocsp
= &ctx
->ocsps
.val
[n
];
1206 fprintf(out
, "OCSP %s\n", ocsp
->path
);
1208 ret
= print_ocsp(context
, ocsp
, out
);
1210 fprintf(out
, "failure printing OCSP: %d\n", ret
);
1215 for (n
= 0; n
< ctx
->crls
.len
; n
++) {
1216 struct revoke_crl
*crl
= &ctx
->crls
.val
[n
];
1218 fprintf(out
, "CRL %s\n", crl
->path
);
1220 ret
= print_crl(context
, crl
, out
);
1222 fprintf(out
, "failure printing CRL: %d\n", ret
);
1231 * Print the OCSP reply stored in a file.
1233 * @param context a hx509 context
1234 * @param path path to a file with a OCSP reply
1235 * @param out the out FILE descriptor to print the reply on
1237 * @return An hx509 error code, see hx509_get_error_string().
1239 * @ingroup hx509_revoke
1243 hx509_revoke_ocsp_print(hx509_context context
, const char *path
, FILE *out
)
1245 struct revoke_ocsp ocsp
;
1251 memset(&ocsp
, 0, sizeof(ocsp
));
1253 ocsp
.path
= strdup(path
);
1254 if (ocsp
.path
== NULL
)
1257 ret
= load_ocsp(context
, &ocsp
);
1263 ret
= print_ocsp(context
, &ocsp
, out
);
1270 * Verify that the certificate is part of the OCSP reply and it's not
1271 * expired. Doesn't verify signature the OCSP reply or it's done by a
1272 * authorized sender, that is assumed to be already done.
1274 * @param context a hx509 context
1275 * @param now the time right now, if 0, use the current time.
1276 * @param cert the certificate to verify
1277 * @param flags flags control the behavior
1278 * @param data pointer to the encode ocsp reply
1279 * @param length the length of the encode ocsp reply
1280 * @param expiration return the time the OCSP will expire and need to
1283 * @return An hx509 error code, see hx509_get_error_string().
1285 * @ingroup hx509_verify
1289 hx509_ocsp_verify(hx509_context context
,
1293 const void *data
, size_t length
,
1296 const Certificate
*c
= _hx509_get_cert(cert
);
1297 OCSPBasicOCSPResponse basic
;
1306 ret
= parse_ocsp_basic(data
, length
, &basic
);
1308 hx509_set_error_string(context
, 0, ret
,
1309 "Failed to parse OCSP response");
1313 for (i
= 0; i
< basic
.tbsResponseData
.responses
.len
; i
++) {
1315 ret
= der_heim_integer_cmp(&basic
.tbsResponseData
.responses
.val
[i
].certID
.serialNumber
,
1316 &c
->tbsCertificate
.serialNumber
);
1320 /* verify issuer hashes hash */
1321 ret
= _hx509_verify_signature(context
,
1323 &basic
.tbsResponseData
.responses
.val
[i
].certID
.hashAlgorithm
,
1324 &c
->tbsCertificate
.issuer
._save
,
1325 &basic
.tbsResponseData
.responses
.val
[i
].certID
.issuerNameHash
);
1329 switch (basic
.tbsResponseData
.responses
.val
[i
].certStatus
.element
) {
1330 case choice_OCSPCertStatus_good
:
1332 case choice_OCSPCertStatus_revoked
:
1333 case choice_OCSPCertStatus_unknown
:
1337 /* don't allow the update to be in the future */
1338 if (basic
.tbsResponseData
.responses
.val
[i
].thisUpdate
>
1339 now
+ context
->ocsp_time_diff
)
1342 /* don't allow the next update to be in the past */
1343 if (basic
.tbsResponseData
.responses
.val
[i
].nextUpdate
) {
1344 if (*basic
.tbsResponseData
.responses
.val
[i
].nextUpdate
< now
)
1346 *expiration
= *basic
.tbsResponseData
.responses
.val
[i
].nextUpdate
;
1350 free_OCSPBasicOCSPResponse(&basic
);
1354 free_OCSPBasicOCSPResponse(&basic
);
1360 ret
= hx509_cert_get_subject(cert
, &name
);
1362 hx509_clear_error_string(context
);
1365 ret
= hx509_name_to_string(name
, &subject
);
1366 hx509_name_free(&name
);
1368 hx509_clear_error_string(context
);
1371 hx509_set_error_string(context
, 0, HX509_CERT_NOT_IN_OCSP
,
1372 "Certificate %s not in OCSP response "
1378 return HX509_CERT_NOT_IN_OCSP
;
1382 hx509_certs revoked
;
1387 * Create a CRL context. Use hx509_crl_free() to free the CRL context.
1389 * @param context a hx509 context.
1390 * @param crl return pointer to a newly allocated CRL context.
1392 * @return An hx509 error code, see hx509_get_error_string().
1394 * @ingroup hx509_verify
1398 hx509_crl_alloc(hx509_context context
, hx509_crl
*crl
)
1402 *crl
= calloc(1, sizeof(**crl
));
1404 hx509_set_error_string(context
, 0, ENOMEM
, "out of memory");
1408 ret
= hx509_certs_init(context
, "MEMORY:crl", 0, NULL
, &(*crl
)->revoked
);
1419 * Add revoked certificate to an CRL context.
1421 * @param context a hx509 context.
1422 * @param crl the CRL to add the revoked certificate to.
1423 * @param certs keyset of certificate to revoke.
1425 * @return An hx509 error code, see hx509_get_error_string().
1427 * @ingroup hx509_verify
1431 hx509_crl_add_revoked_certs(hx509_context context
,
1435 return hx509_certs_merge(context
, crl
->revoked
, certs
);
1439 * Set the lifetime of a CRL context.
1441 * @param context a hx509 context.
1442 * @param crl a CRL context
1443 * @param delta delta time the certificate is valid, library adds the
1444 * current time to this.
1446 * @return An hx509 error code, see hx509_get_error_string().
1448 * @ingroup hx509_verify
1452 hx509_crl_lifetime(hx509_context context
, hx509_crl crl
, int delta
)
1454 crl
->expire
= time(NULL
) + delta
;
1459 * Free a CRL context.
1461 * @param context a hx509 context.
1462 * @param crl a CRL context to free.
1464 * @ingroup hx509_verify
1468 hx509_crl_free(hx509_context context
, hx509_crl
*crl
)
1472 hx509_certs_free(&(*crl
)->revoked
);
1473 memset(*crl
, 0, sizeof(**crl
));
1479 add_revoked(hx509_context context
, void *ctx
, hx509_cert cert
)
1481 TBSCRLCertList
*c
= ctx
;
1486 num
= c
->revokedCertificates
->len
;
1487 ptr
= realloc(c
->revokedCertificates
->val
,
1488 (num
+ 1) * sizeof(c
->revokedCertificates
->val
[0]));
1490 hx509_clear_error_string(context
);
1493 c
->revokedCertificates
->val
= ptr
;
1495 ret
= hx509_cert_get_serialnumber(cert
,
1496 &c
->revokedCertificates
->val
[num
].userCertificate
);
1498 hx509_clear_error_string(context
);
1501 c
->revokedCertificates
->val
[num
].revocationDate
.element
=
1502 choice_Time_generalTime
;
1503 c
->revokedCertificates
->val
[num
].revocationDate
.u
.generalTime
=
1504 time(NULL
) - 3600 * 24;
1505 c
->revokedCertificates
->val
[num
].crlEntryExtensions
= NULL
;
1507 c
->revokedCertificates
->len
++;
1513 * Sign a CRL and return an encode certificate.
1515 * @param context a hx509 context.
1516 * @param signer certificate to sign the CRL with
1517 * @param crl the CRL to sign
1518 * @param os return the signed and encoded CRL, free with
1519 * free_heim_octet_string()
1521 * @return An hx509 error code, see hx509_get_error_string().
1523 * @ingroup hx509_verify
1527 hx509_crl_sign(hx509_context context
,
1530 heim_octet_string
*os
)
1532 const AlgorithmIdentifier
*sigalg
= _hx509_crypto_default_sig_alg
;
1533 CRLCertificateList c
;
1536 hx509_private_key signerkey
;
1538 memset(&c
, 0, sizeof(c
));
1540 signerkey
= _hx509_cert_private_key(signer
);
1541 if (signerkey
== NULL
) {
1542 ret
= HX509_PRIVATE_KEY_MISSING
;
1543 hx509_set_error_string(context
, 0, ret
,
1544 "Private key missing for CRL signing");
1548 c
.tbsCertList
.version
= malloc(sizeof(*c
.tbsCertList
.version
));
1549 if (c
.tbsCertList
.version
== NULL
) {
1550 hx509_set_error_string(context
, 0, ENOMEM
, "out of memory");
1554 *c
.tbsCertList
.version
= 1;
1556 ret
= copy_AlgorithmIdentifier(sigalg
, &c
.tbsCertList
.signature
);
1558 hx509_clear_error_string(context
);
1562 ret
= copy_Name(&_hx509_get_cert(signer
)->tbsCertificate
.issuer
,
1563 &c
.tbsCertList
.issuer
);
1565 hx509_clear_error_string(context
);
1569 c
.tbsCertList
.thisUpdate
.element
= choice_Time_generalTime
;
1570 c
.tbsCertList
.thisUpdate
.u
.generalTime
= time(NULL
) - 24 * 3600;
1572 c
.tbsCertList
.nextUpdate
= malloc(sizeof(*c
.tbsCertList
.nextUpdate
));
1573 if (c
.tbsCertList
.nextUpdate
== NULL
) {
1574 hx509_set_error_string(context
, 0, ENOMEM
, "out of memory");
1580 time_t next
= crl
->expire
;
1582 next
= time(NULL
) + 24 * 3600 * 365;
1584 c
.tbsCertList
.nextUpdate
->element
= choice_Time_generalTime
;
1585 c
.tbsCertList
.nextUpdate
->u
.generalTime
= next
;
1588 c
.tbsCertList
.revokedCertificates
=
1589 calloc(1, sizeof(*c
.tbsCertList
.revokedCertificates
));
1590 if (c
.tbsCertList
.revokedCertificates
== NULL
) {
1591 hx509_set_error_string(context
, 0, ENOMEM
, "out of memory");
1595 c
.tbsCertList
.crlExtensions
= NULL
;
1597 ret
= hx509_certs_iter_f(context
, crl
->revoked
, add_revoked
, &c
.tbsCertList
);
1601 /* if not revoked certs, remove OPTIONAL entry */
1602 if (c
.tbsCertList
.revokedCertificates
->len
== 0) {
1603 free(c
.tbsCertList
.revokedCertificates
);
1604 c
.tbsCertList
.revokedCertificates
= NULL
;
1607 ASN1_MALLOC_ENCODE(TBSCRLCertList
, os
->data
, os
->length
,
1608 &c
.tbsCertList
, &size
, ret
);
1610 hx509_set_error_string(context
, 0, ret
, "failed to encode tbsCRL");
1613 if (size
!= os
->length
)
1614 _hx509_abort("internal ASN.1 encoder error");
1617 ret
= _hx509_create_signature_bitstring(context
,
1621 &c
.signatureAlgorithm
,
1625 hx509_set_error_string(context
, 0, ret
, "Failed to sign CRL");
1629 ASN1_MALLOC_ENCODE(CRLCertificateList
, os
->data
, os
->length
,
1632 hx509_set_error_string(context
, 0, ret
, "failed to encode CRL");
1635 if (size
!= os
->length
)
1636 _hx509_abort("internal ASN.1 encoder error");
1638 free_CRLCertificateList(&c
);
1643 free_CRLCertificateList(&c
);