summaryrefslogtreecommitdiffstats
path: root/qemu/roms/ipxe/src/crypto/ocsp.c
blob: 66e47c57e57ee750275a14d6e323e4a05705da68 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
@media only all and (prefers-color-scheme: dark) {
.highlight .hll { background-color: #49483e }
.highlight .c { color: #75715e } /* Comment */
.highlight .err { color: #960050; background-color: #1e0010 } /* Error */
.highlight .k { color: #66d9ef } /* Keyword */
.highlight .l { color: #ae81ff } /* Literal */
.highlight .n { color: #f8f8f2 } /* Name */
.highlight .o { color: #f92672 } /* Operator */
.highlight .p { color: #f8f8f2 } /* Punctuation */
.highlight .ch { color: #75715e } /* Comment.Hashbang */
.highlight .cm { color: #75715e } /* Comment.Multiline */
.highlight .cp { color: #75715e } /* Comment.Preproc */
.highlight .cpf { color: #75715e } /* Comment.PreprocFile */
.highlight .c1 { color: #75715e } /* Comment.Single */
.highlight .cs { color: #75715e } /* Comment.Special */
.highlight .gd { color: #f92672 } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gi { color: #a6e22e } /* Generic.Inserted */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #75715e } /* Generic.Subheading */
.highlight .kc { color: #66d9ef } /* Keyword.Constant */
.highlight .kd { color: #66d9ef } /* Keyword.Declaration */
.highlight .kn { color: #f92672 } /* Keyword.Namespace */
.highlight .kp { color: #66d9ef } /* Keyword.Pseudo */
.highlight .kr { color: #66d9ef } /* Keyword.Reserved */
.highlight .kt { color: #66d9ef } /* Keyword.Type */
.highlight .ld { color: #e6db74 } /* Literal.Date */
.highlight .m { color: #ae81ff } /* Literal.Number */
.highlight .s { color: #e6db74 } /* Literal.String */
.highlight .na { color: #a6e22e } /* Name.Attribute */
.highlight .nb { color: #f8f8f2 } /* Name.Builtin */
.highlight .nc { color: #a6e22e } /* Name.Class */
.highlight .no { color: #66d9ef } /* Name.Constant */
.highlight .nd { color: #a6e22e } /* Name.Decorator */
.highlight .ni { color: #f8f8f2 } /* Name.Entity */
.highlight .ne { color: #a6e22e } /* Name.Exception */
.highlight .nf { color: #a6e22e } /* Name.Function */
.highlight .nl { color: #f8f8f2 } /* Name.Label */
.highlight .nn { color: #f8f8f2 } /* Name.Namespace */
.highlight .nx { color: #a6e22e } /* Name.Other */
.highlight .py { color: #f8f8f2 } /* Name.Property */
.highlight .nt { color: #f92672 } /* Name.Tag */
.highlight .nv { color: #f8f8f2 } /* Name.Variable */
.highlight .ow { color: #f92672 } /* Operator.Word */
.highlight .w { color: #f8f8f2 } /* Text.Whitespace */
.highlight .mb { color: #ae81ff } /* Literal.Number.Bin */
.highlight .mf { color: #ae81ff } /* Literal.Number.Float */
.highlight .mh { color: #ae81ff } /* Literal.Number.Hex */
.highlight .mi { color: #ae81ff } /* Literal.Number.Integer */
.highlight .mo { color: #ae81ff } /* Literal.Number.Oct */
.highlight .sa { color: #e6db74 } /* Literal.String.Affix */
.highlight .sb { color: #e6db74 } /* Literal.String.Backtick */
.highlight .sc { color: #e6db74 } /* Literal.String.Char */
.highlight .dl { color: #e6db74 } /* Literal.String.Delimiter */
.highlight .sd { color: #e6db74 } /* Literal.String.Doc */
.highlight .s2 { color: #e6db74 } /* Literal.String.Double */
.highlight .se { color: #ae81ff } /* Literal.String.Escape */
.highlight .sh { color: #e6db74 } /* Literal.String.Heredoc */
.highlight .si { color: #e6db74 } /* Literal.String.Interpol */
.highlight .sx { color: #e6db74 } /* Literal.String.Other */
.highlight .sr { color: #e6db74 } /* Literal.String.Regex */
.highlight .s1 { color: #e6db74 } /* Literal.String.Single */
.highlight .ss { color: #e6db74 } /* Literal.String.Symbol */
.highlight .bp { color: #f8f8f2 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #a6e22e } /* Name.Function.Magic */
.highlight .vc { color: #f8f8f2 } /* Name.Variable.Class */
.highlight .vg { color: #f8f8f2 } /* Name.Variable.Global */
.highlight .vi { color: #f8f8f2 } /* Name.Variable.Instance */
.highlight .vm { color: #f8f8f2 } /* Name.Variable.Magic */
.highlight .il { color: #ae81ff } /* Literal.Number.Integer.Long */
}
@media (prefers-color-scheme: light) {
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
}
/*
// Copyright (c) 2010-2020 Intel Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
*/

#ifndef _DEFAULTS_H_
#define _DEFAULTS_H_

#include <rte_ether.h>
#include "prox_compat.h"

struct prox_cfg;
struct lcore_cfg;

void set_global_defaults(struct prox_cfg* prox_cfg);
void set_task_defaults(struct prox_cfg* prox_cfg, struct lcore_cfg* lcore_cfg_init);
void set_port_defaults(void);

#define MAX_PKT_SIZE	10000
#define MAX_PKT_BURST   64
#define MAX_RING_BURST	64
#define DUMP_PKT_LEN 	MAX_PKT_SIZE
#define MAX_IMIX_PKTS 128

#if MAX_RING_BURST < MAX_PKT_BURST
#error MAX_RING_BURST < MAX_PKT_BURST
#endif

#define NUM_VCPES               65536
#define GRE_BUCKET_ENTRIES      4
#define MAX_GRE                 (NUM_VCPES * GRE_BUCKET_ENTRIES)
#define MAX_RSS_QUEUE_BITS      9

#define PROX_VLAN_TAG_SIZE	4

/* MBUF_SIZE can be configured based on the following:
   - If only one segment is used ETH_TXQ_FLAGS_NOMULTSEGS can be used resulting
     in vector mode used for transmission hence higher performance
   - Only one segment is used by the rx function if the mbuf size is big enough
   - Bigger mbufs result in more memory used, hence slighly lower performance (DTLB misses)
   - Selecting the smaller mbuf is not obvious as pmds might behave slighly differently:
     - on ixgbe a 1526 + 256 mbuf size will cause any packets bigger than 1024 bytes to be segmented
     - on i40e a 1526 + 256 mbuf size will cause any packets bigger than 1408 bytes to be segmented
     - other pmds might have additional requirements
   As the performance decrease due to the usage of bigger mbuf is not very important, we prefer
   here to use  the same, bigger, mbuf size for all pmds, making the code easier to support.
   An mbuf size of 2048 + 128 + 128 + 8 can hold a 2048 packet, and only one segment will be used
   except if jumbo frames are enabled. +8 (VLAN) is needed for i40e (and maybe other pmds).
   TX_MBUF_SIZE is used for when transmitting only: in this case the mbuf size can be smaller.
*/
#define MBUF_SIZE (2048 + (unsigned)sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM + 2 * PROX_VLAN_TAG_SIZE)
#define TX_MBUF_SIZE (PROX_RTE_ETHER_MAX_LEN + (unsigned)sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM +  2 * PROX_VLAN_TAG_SIZE)

#define PROX_MTU   PROX_RTE_ETHER_MAX_LEN - PROX_RTE_ETHER_HDR_LEN - PROX_RTE_ETHER_CRC_LEN
#define NO_VDEV_PORT	0xFF

#endif /* _DEFAULTS_H_ */
f='#n520'>520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960
/*
 * Copyright (C) 2012 Michael Brown <mbrown@fensystems.co.uk>.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301, USA.
 */

FILE_LICENCE ( GPL2_OR_LATER );

#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <ipxe/asn1.h>
#include <ipxe/x509.h>
#include <ipxe/sha1.h>
#include <ipxe/base64.h>
#include <ipxe/uri.h>
#include <ipxe/ocsp.h>
#include <config/crypto.h>

/** @file
 *
 * Online Certificate Status Protocol
 *
 */

/* Disambiguate the various error causes */
#define EACCES_CERT_STATUS						\
	__einfo_error ( EINFO_EACCES_CERT_STATUS )
#define EINFO_EACCES_CERT_STATUS					\
	__einfo_uniqify ( EINFO_EACCES, 0x01,				\
			  "Certificate status not good" )
#define EACCES_CERT_MISMATCH						\
	__einfo_error ( EINFO_EACCES_CERT_MISMATCH )
#define EINFO_EACCES_CERT_MISMATCH					\
	__einfo_uniqify ( EINFO_EACCES, 0x02,				\
			  "Certificate ID mismatch" )
#define EACCES_NON_OCSP_SIGNING						\
	__einfo_error ( EINFO_EACCES_NON_OCSP_SIGNING )
#define EINFO_EACCES_NON_OCSP_SIGNING					\
	__einfo_uniqify ( EINFO_EACCES, 0x03,				\
			  "Not an OCSP signing certificate" )
#define EACCES_STALE							\
	__einfo_error ( EINFO_EACCES_STALE )
#define EINFO_EACCES_STALE						\
	__einfo_uniqify ( EINFO_EACCES, 0x04,				\
			  "Stale (or premature) OCSP repsonse" )
#define EACCES_NO_RESPONDER						\
	__einfo_error ( EINFO_EACCES_NO_RESPONDER )
#define EINFO_EACCES_NO_RESPONDER					\
	__einfo_uniqify ( EINFO_EACCES, 0x05,				\
			  "Missing OCSP responder certificate" )
#define ENOTSUP_RESPONSE_TYPE						\
	__einfo_error ( EINFO_ENOTSUP_RESPONSE_TYPE )
#define EINFO_ENOTSUP_RESPONSE_TYPE					\
	__einfo_uniqify ( EINFO_ENOTSUP, 0x01,				\
			  "Unsupported OCSP response type" )
#define ENOTSUP_RESPONDER_ID						\
	__einfo_error ( EINFO_ENOTSUP_RESPONDER_ID )
#define EINFO_ENOTSUP_RESPONDER_ID					\
	__einfo_uniqify ( EINFO_ENOTSUP, 0x02,				\
			  "Unsupported OCSP responder ID" )
#define EPROTO_MALFORMED_REQUEST					\
	__einfo_error ( EINFO_EPROTO_MALFORMED_REQUEST )
#define EINFO_EPROTO_MALFORMED_REQUEST					\
	__einfo_uniqify ( EINFO_EPROTO, OCSP_STATUS_MALFORMED_REQUEST,	\
			  "Illegal confirmation request" )
#define EPROTO_INTERNAL_ERROR						\
	__einfo_error ( EINFO_EPROTO_INTERNAL_ERROR )
#define EINFO_EPROTO_INTERNAL_ERROR					\
	__einfo_uniqify ( EINFO_EPROTO, OCSP_STATUS_INTERNAL_ERROR,	\
			  "Internal error in issuer" )
#define EPROTO_TRY_LATER						\
	__einfo_error ( EINFO_EPROTO_TRY_LATER )
#define EINFO_EPROTO_TRY_LATER						\
	__einfo_uniqify ( EINFO_EPROTO, OCSP_STATUS_TRY_LATER,		\
			  "Try again later" )
#define EPROTO_SIG_REQUIRED						\
	__einfo_error ( EINFO_EPROTO_SIG_REQUIRED )
#define EINFO_EPROTO_SIG_REQUIRED					\
	__einfo_uniqify ( EINFO_EPROTO, OCSP_STATUS_SIG_REQUIRED,	\
			  "Must sign the request" )
#define EPROTO_UNAUTHORIZED						\
	__einfo_error ( EINFO_EPROTO_UNAUTHORIZED )
#define EINFO_EPROTO_UNAUTHORIZED					\
	__einfo_uniqify ( EINFO_EPROTO, OCSP_STATUS_UNAUTHORIZED,	\
			  "Request unauthorized" )
#define EPROTO_STATUS( status )						\
	EUNIQ ( EINFO_EPROTO, (status), EPROTO_MALFORMED_REQUEST,	\
		EPROTO_INTERNAL_ERROR, EPROTO_TRY_LATER,		\
		EPROTO_SIG_REQUIRED, EPROTO_UNAUTHORIZED )

/** OCSP digest algorithm */
#define ocsp_digest_algorithm sha1_algorithm

/** OCSP digest algorithm identifier */
static const uint8_t ocsp_algorithm_id[] =
	{ OCSP_ALGORITHM_IDENTIFIER ( ASN1_OID_SHA1 ) };

/** OCSP basic response type */
static const uint8_t oid_basic_response_type[] = { ASN1_OID_OCSP_BASIC };

/** OCSP basic response type cursor */
static struct asn1_cursor oid_basic_response_type_cursor =
	ASN1_OID_CURSOR ( oid_basic_response_type );

/**
 * Free OCSP check
 *
 * @v refcnt		Reference count
 */
static void ocsp_free ( struct refcnt *refcnt ) {
	struct ocsp_check *ocsp =
		container_of ( refcnt, struct ocsp_check, refcnt );

	x509_put ( ocsp->cert );
	x509_put ( ocsp->issuer );
	free ( ocsp->uri_string );
	free ( ocsp->request.builder.data );
	free ( ocsp->response.data );
	x509_put ( ocsp->response.signer );
	free ( ocsp );
}

/**
 * Build OCSP request
 *
 * @v ocsp		OCSP check
 * @ret rc		Return status code
 */
static int ocsp_request ( struct ocsp_check *ocsp ) {
	struct digest_algorithm *digest = &ocsp_digest_algorithm;
	struct asn1_builder *builder = &ocsp->request.builder;
	struct asn1_cursor *cert_id = &ocsp->request.cert_id;
	uint8_t digest_ctx[digest->ctxsize];
	uint8_t name_digest[digest->digestsize];
	uint8_t pubkey_digest[digest->digestsize];
	int rc;

	/* Generate digests */
	digest_init ( digest, digest_ctx );
	digest_update ( digest, digest_ctx, ocsp->cert->issuer.raw.data,
			ocsp->cert->issuer.raw.len );
	digest_final ( digest, digest_ctx, name_digest );
	digest_init ( digest, digest_ctx );
	digest_update ( digest, digest_ctx,
			ocsp->issuer->subject.public_key.raw_bits.data,
			ocsp->issuer->subject.public_key.raw_bits.len );
	digest_final ( digest, digest_ctx, pubkey_digest );

	/* Construct request */
	if ( ( rc = ( asn1_prepend_raw ( builder, ocsp->cert->serial.raw.data,
					 ocsp->cert->serial.raw.len ),
		      asn1_prepend ( builder, ASN1_OCTET_STRING,
				     pubkey_digest, sizeof ( pubkey_digest ) ),
		      asn1_prepend ( builder, ASN1_OCTET_STRING,
				     name_digest, sizeof ( name_digest ) ),
		      asn1_prepend ( builder, ASN1_SEQUENCE,
				     ocsp_algorithm_id,
				     sizeof ( ocsp_algorithm_id ) ),
		      asn1_wrap ( builder, ASN1_SEQUENCE ),
		      asn1_wrap ( builder, ASN1_SEQUENCE ),
		      asn1_wrap ( builder, ASN1_SEQUENCE ),
		      asn1_wrap ( builder, ASN1_SEQUENCE ),
		      asn1_wrap ( builder, ASN1_SEQUENCE ) ) ) != 0 ) {
		DBGC ( ocsp, "OCSP %p \"%s\" could not build request: %s\n",
		       ocsp, x509_name ( ocsp->cert ), strerror ( rc ) );
		return rc;
	}
	DBGC2 ( ocsp, "OCSP %p \"%s\" request is:\n",
		ocsp, x509_name ( ocsp->cert ) );
	DBGC2_HDA ( ocsp, 0, builder->data, builder->len );

	/* Parse certificate ID for comparison with response */
	cert_id->data = builder->data;
	cert_id->len = builder->len;
	if ( ( rc = ( asn1_enter ( cert_id, ASN1_SEQUENCE ),
		      asn1_enter ( cert_id, ASN1_SEQUENCE ),
		      asn1_enter ( cert_id, ASN1_SEQUENCE ),
		      asn1_enter ( cert_id, ASN1_SEQUENCE ) ) ) != 0 ) {
		DBGC ( ocsp, "OCSP %p \"%s\" could not locate certID: %s\n",
		       ocsp, x509_name ( ocsp->cert ), strerror ( rc ) );
		return rc;
	}

	return 0;
}

/**
 * Build OCSP URI string
 *
 * @v ocsp		OCSP check
 * @ret rc		Return status code
 */
static int ocsp_uri_string ( struct ocsp_check *ocsp ) {
	struct x509_ocsp_responder *responder =
		&ocsp->cert->extensions.auth_info.ocsp;
	struct uri path_uri;
	char *path_base64_string;
	char *path_uri_string;
	size_t path_len;
	size_t len;
	int rc;

	/* Sanity check */
	if ( ! responder->uri.len ) {
		DBGC ( ocsp, "OCSP %p \"%s\" has no OCSP URI\n",
		       ocsp, x509_name ( ocsp->cert ) );
		rc = -ENOTTY;
		goto err_no_uri;
	}

	/* Base64-encode the request as the URI path */
	path_len = ( base64_encoded_len ( ocsp->request.builder.len )
		     + 1 /* NUL */ );
	path_base64_string = malloc ( path_len );
	if ( ! path_base64_string ) {
		rc = -ENOMEM;
		goto err_path_base64;
	}
	base64_encode ( ocsp->request.builder.data, ocsp->request.builder.len,
			path_base64_string );

	/* URI-encode the Base64-encoded request */
	memset ( &path_uri, 0, sizeof ( path_uri ) );
	path_uri.path = path_base64_string;
	path_uri_string = format_uri_alloc ( &path_uri );
	if ( ! path_uri_string ) {
		rc = -ENOMEM;
		goto err_path_uri;
	}

	/* Construct URI string */
	len = ( responder->uri.len + strlen ( path_uri_string ) + 1 /* NUL */ );
	ocsp->uri_string = zalloc ( len );
	if ( ! ocsp->uri_string ) {
		rc = -ENOMEM;
		goto err_ocsp_uri;
	}
	memcpy ( ocsp->uri_string, responder->uri.data, responder->uri.len );
	strcpy ( &ocsp->uri_string[responder->uri.len], path_uri_string );
	DBGC2 ( ocsp, "OCSP %p \"%s\" URI is %s\n",
		ocsp, x509_name ( ocsp->cert ), ocsp->uri_string );

	/* Success */
	rc = 0;

 err_ocsp_uri:
	free ( path_uri_string );
 err_path_uri:
	free ( path_base64_string );
 err_path_base64:
 err_no_uri:
	return rc;
}

/**
 * Create OCSP check
 *
 * @v cert		Certificate to check
 * @v issuer		Issuing certificate
 * @ret ocsp		OCSP check
 * @ret rc		Return status code
 */
int ocsp_check ( struct x509_certificate *cert,
		 struct x509_certificate *issuer,
		 struct ocsp_check **ocsp ) {
	int rc;

	/* Sanity checks */
	assert ( cert != NULL );
	assert ( issuer != NULL );
	assert ( issuer->valid );

	/* Allocate and initialise check */
	*ocsp = zalloc ( sizeof ( **ocsp ) );
	if ( ! *ocsp ) {
		rc = -ENOMEM;
		goto err_alloc;
	}
	ref_init ( &(*ocsp)->refcnt, ocsp_free );
	(*ocsp)->cert = x509_get ( cert );
	(*ocsp)->issuer = x509_get ( issuer );

	/* Build request */
	if ( ( rc = ocsp_request ( *ocsp ) ) != 0 )
		goto err_request;

	/* Build URI string */
	if ( ( rc = ocsp_uri_string ( *ocsp ) ) != 0 )
		goto err_uri_string;

	return 0;

 err_uri_string:
 err_request:
	ocsp_put ( *ocsp );
 err_alloc:
	*ocsp = NULL;
	return rc;
}

/**
 * Parse OCSP response status
 *
 * @v ocsp		OCSP check
 * @v raw		ASN.1 cursor
 * @ret rc		Return status code
 */
static int ocsp_parse_response_status ( struct ocsp_check *ocsp,
					const struct asn1_cursor *raw ) {
	struct asn1_cursor cursor;
	uint8_t status;
	int rc;

	/* Enter responseStatus */
	memcpy ( &cursor, raw, sizeof ( cursor ) );
	if ( ( rc = asn1_enter ( &cursor, ASN1_ENUMERATED ) ) != 0 ) {
		DBGC ( ocsp, "OCSP %p \"%s\" could not locate responseStatus: "
		       "%s\n", ocsp, x509_name ( ocsp->cert ), strerror ( rc ));
		return rc;
	}

	/* Extract response status */
	if ( cursor.len != sizeof ( status ) ) {
		DBGC ( ocsp, "OCSP %p \"%s\" invalid status:\n",
		       ocsp, x509_name ( ocsp->cert ) );
		DBGC_HDA ( ocsp, 0, cursor.data, cursor.len );
		return -EINVAL;
	}
	memcpy ( &status, cursor.data, sizeof ( status ) );

	/* Check response status */
	if ( status != OCSP_STATUS_SUCCESSFUL ) {
		DBGC ( ocsp, "OCSP %p \"%s\" response status %d\n",
		       ocsp, x509_name ( ocsp->cert ), status );
		return EPROTO_STATUS ( status );
	}

	return 0;
}

/**
 * Parse OCSP response type
 *
 * @v ocsp		OCSP check
 * @v raw		ASN.1 cursor
 * @ret rc		Return status code
 */
static int ocsp_parse_response_type ( struct ocsp_check *ocsp,
				      const struct asn1_cursor *raw ) {
	struct asn1_cursor cursor;

	/* Enter responseType */
	memcpy ( &cursor, raw, sizeof ( cursor ) );
	asn1_enter ( &cursor, ASN1_OID );

	/* Check responseType is "basic" */
	if ( asn1_compare ( &oid_basic_response_type_cursor, &cursor ) != 0 ) {
		DBGC ( ocsp, "OCSP %p \"%s\" response type not supported:\n",
		       ocsp, x509_name ( ocsp->cert ) );
		DBGC_HDA ( ocsp, 0, cursor.data, cursor.len );
		return -ENOTSUP_RESPONSE_TYPE;
	}

	return 0;
}

/**
 * Compare responder's certificate name
 *
 * @v ocsp		OCSP check
 * @v cert		Certificate
 * @ret difference	Difference as returned by memcmp()
 */
static int ocsp_compare_responder_name ( struct ocsp_check *ocsp,
					 struct x509_certificate *cert ) {
	struct ocsp_responder *responder = &ocsp->response.responder;

	/* Compare responder ID with certificate's subject */
	return asn1_compare ( &responder->id, &cert->subject.raw );
}

/**
 * Compare responder's certificate public key hash
 *
 * @v ocsp		OCSP check
 * @v cert		Certificate
 * @ret difference	Difference as returned by memcmp()
 */
static int ocsp_compare_responder_key_hash ( struct ocsp_check *ocsp,
					     struct x509_certificate *cert ) {
	struct ocsp_responder *responder = &ocsp->response.responder;
	struct asn1_cursor key_hash;
	uint8_t ctx[SHA1_CTX_SIZE];
	uint8_t digest[SHA1_DIGEST_SIZE];
	int difference;

	/* Enter responder key hash */
	memcpy ( &key_hash, &responder->id, sizeof ( key_hash ) );
	asn1_enter ( &key_hash, ASN1_OCTET_STRING );

	/* Sanity check */
	difference = ( sizeof ( digest ) - key_hash.len );
	if ( difference )
		return difference;

	/* Generate SHA1 hash of certificate's public key */
	digest_init ( &sha1_algorithm, ctx );
	digest_update ( &sha1_algorithm, ctx,
			cert->subject.public_key.raw_bits.data,
			cert->subject.public_key.raw_bits.len );
	digest_final ( &sha1_algorithm, ctx, digest );

	/* Compare responder key hash with hash of certificate's public key */
	return memcmp ( digest, key_hash.data, sizeof ( digest ) );
}

/**
 * Parse OCSP responder ID
 *
 * @v ocsp		OCSP check
 * @v raw		ASN.1 cursor
 * @ret rc		Return status code
 */
static int ocsp_parse_responder_id ( struct ocsp_check *ocsp,
				     const struct asn1_cursor *raw ) {
	struct ocsp_responder *responder = &ocsp->response.responder;
	struct asn1_cursor *responder_id = &responder->id;
	unsigned int type;

	/* Enter responder ID */
	memcpy ( responder_id, raw, sizeof ( *responder_id ) );
	type = asn1_type ( responder_id );
	asn1_enter_any ( responder_id );

	/* Identify responder ID type */
	switch ( type ) {
	case ASN1_EXPLICIT_TAG ( 1 ) :
		DBGC2 ( ocsp, "OCSP %p \"%s\" responder identified by name\n",
			ocsp, x509_name ( ocsp->cert ) );
		responder->compare = ocsp_compare_responder_name;
		return 0;
	case ASN1_EXPLICIT_TAG ( 2 ) :
		DBGC2 ( ocsp, "OCSP %p \"%s\" responder identified by key "
			"hash\n", ocsp, x509_name ( ocsp->cert ) );
		responder->compare = ocsp_compare_responder_key_hash;
		return 0;
	default:
		DBGC ( ocsp, "OCSP %p \"%s\" unsupported responder ID type "
		       "%d\n", ocsp, x509_name ( ocsp->cert ), type );
		return -ENOTSUP_RESPONDER_ID;
	}
}

/**
 * Parse OCSP certificate ID
 *
 * @v ocsp		OCSP check
 * @v raw		ASN.1 cursor
 * @ret rc		Return status code
 */
static int ocsp_parse_cert_id ( struct ocsp_check *ocsp,
				const struct asn1_cursor *raw ) {
	struct asn1_cursor cursor;

	/* Check certID matches request */
	memcpy ( &cursor, raw, sizeof ( cursor ) );
	asn1_shrink_any ( &cursor );
	if ( asn1_compare ( &cursor, &ocsp->request.cert_id ) != 0 ) {
		DBGC ( ocsp, "OCSP %p \"%s\" certID mismatch:\n",
		       ocsp, x509_name ( ocsp->cert ) );
		DBGC_HDA ( ocsp, 0, ocsp->request.cert_id.data,
			   ocsp->request.cert_id.len );
		DBGC_HDA ( ocsp, 0, cursor.data, cursor.len );
		return -EACCES_CERT_MISMATCH;
	}

	return 0;
}

/**
 * Parse OCSP responses
 *
 * @v ocsp		OCSP check
 * @v raw		ASN.1 cursor
 * @ret rc		Return status code
 */
static int ocsp_parse_responses ( struct ocsp_check *ocsp,
				  const struct asn1_cursor *raw ) {
	struct ocsp_response *response = &ocsp->response;
	struct asn1_cursor cursor;
	int rc;

	/* Enter responses */
	memcpy ( &cursor, raw, sizeof ( cursor ) );
	asn1_enter ( &cursor, ASN1_SEQUENCE );

	/* Enter first singleResponse */
	asn1_enter ( &cursor, ASN1_SEQUENCE );

	/* Parse certID */
	if ( ( rc = ocsp_parse_cert_id ( ocsp, &cursor ) ) != 0 )
		return rc;
	asn1_skip_any ( &cursor );

	/* Check certStatus */
	if ( asn1_type ( &cursor ) != ASN1_IMPLICIT_TAG ( 0 ) ) {
		DBGC ( ocsp, "OCSP %p \"%s\" non-good certStatus:\n",
		       ocsp, x509_name ( ocsp->cert ) );
		DBGC_HDA ( ocsp, 0, cursor.data, cursor.len );
		return -EACCES_CERT_STATUS;
	}
	asn1_skip_any ( &cursor );

	/* Parse thisUpdate */
	if ( ( rc = asn1_generalized_time ( &cursor,
					    &response->this_update ) ) != 0 ) {
		DBGC ( ocsp, "OCSP %p \"%s\" could not parse thisUpdate: %s\n",
		       ocsp, x509_name ( ocsp->cert ), strerror ( rc ) );
		return rc;
	}
	DBGC2 ( ocsp, "OCSP %p \"%s\" this update was at time %lld\n",
		ocsp, x509_name ( ocsp->cert ), response->this_update );
	asn1_skip_any ( &cursor );

	/* Parse nextUpdate, if present */
	if ( asn1_type ( &cursor ) == ASN1_EXPLICIT_TAG ( 0 ) ) {
		asn1_enter ( &cursor, ASN1_EXPLICIT_TAG ( 0 ) );
		if ( ( rc = asn1_generalized_time ( &cursor,
					     &response->next_update ) ) != 0 ) {
			DBGC ( ocsp, "OCSP %p \"%s\" could not parse "
			       "nextUpdate: %s\n", ocsp,
			       x509_name ( ocsp->cert ), strerror ( rc ) );
			return rc;
		}
		DBGC2 ( ocsp, "OCSP %p \"%s\" next update is at time %lld\n",
			ocsp, x509_name ( ocsp->cert ), response->next_update );
	} else {
		/* If no nextUpdate is present, this indicates that
		 * "newer revocation information is available all the
		 * time".  Actually, this indicates that there is no
		 * point to performing the OCSP check, since an
		 * attacker could replay the response at any future
		 * time and it would still be valid.
		 */
		DBGC ( ocsp, "OCSP %p \"%s\" responder is a moron\n",
		       ocsp, x509_name ( ocsp->cert ) );
		response->next_update = time ( NULL );
	}

	return 0;
}

/**
 * Parse OCSP response data
 *
 * @v ocsp		OCSP check
 * @v raw		ASN.1 cursor
 * @ret rc		Return status code
 */
static int ocsp_parse_tbs_response_data ( struct ocsp_check *ocsp,
					  const struct asn1_cursor *raw ) {
	struct ocsp_response *response = &ocsp->response;
	struct asn1_cursor cursor;
	int rc;

	/* Record raw tbsResponseData */
	memcpy ( &cursor, raw, sizeof ( cursor ) );
	asn1_shrink_any ( &cursor );
	memcpy ( &response->tbs, &cursor, sizeof ( response->tbs ) );

	/* Enter tbsResponseData */
	asn1_enter ( &cursor, ASN1_SEQUENCE );

	/* Skip version, if present */
	asn1_skip_if_exists ( &cursor, ASN1_EXPLICIT_TAG ( 0 ) );

	/* Parse responderID */
	if ( ( rc = ocsp_parse_responder_id ( ocsp, &cursor ) ) != 0 )
		return rc;
	asn1_skip_any ( &cursor );

	/* Skip producedAt */
	asn1_skip_any ( &cursor );

	/* Parse responses */
	if ( ( rc = ocsp_parse_responses ( ocsp, &cursor ) ) != 0 )
		return rc;

	return 0;
}

/**
 * Parse OCSP certificates
 *
 * @v ocsp		OCSP check
 * @v raw		ASN.1 cursor
 * @ret rc		Return status code
 */
static int ocsp_parse_certs ( struct ocsp_check *ocsp,
			      const struct asn1_cursor *raw ) {
	struct ocsp_response *response = &ocsp->response;
	struct asn1_cursor cursor;
	struct x509_certificate *cert;
	int rc;

	/* Enter certs */
	memcpy ( &cursor, raw, sizeof ( cursor ) );
	asn1_enter ( &cursor, ASN1_EXPLICIT_TAG ( 0 ) );
	asn1_enter ( &cursor, ASN1_SEQUENCE );

	/* Parse certificate, if present.  The data structure permits
	 * multiple certificates, but the protocol requires that the
	 * OCSP signing certificate must either be the issuer itself,
	 * or must be directly issued by the issuer (see RFC2560
	 * section 4.2.2.2 "Authorized Responders").  We therefore
	 * need to identify only the single certificate matching the
	 * Responder ID.
	 */
	while ( cursor.len ) {

		/* Parse certificate */
		if ( ( rc = x509_certificate ( cursor.data, cursor.len,
					       &cert ) ) != 0 ) {
			DBGC ( ocsp, "OCSP %p \"%s\" could not parse "
			       "certificate: %s\n", ocsp,
			       x509_name ( ocsp->cert ), strerror ( rc ) );
			DBGC_HDA ( ocsp, 0, cursor.data, cursor.len );
			return rc;
		}

		/* Use if this certificate matches the responder ID */
		if ( response->responder.compare ( ocsp, cert ) == 0 ) {
			response->signer = cert;
			DBGC2 ( ocsp, "OCSP %p \"%s\" response is signed by ",
				ocsp, x509_name ( ocsp->cert ) );
			DBGC2 ( ocsp, "\"%s\"\n",
				x509_name ( response->signer ) );
			return 0;
		}

		/* Otherwise, discard this certificate */
		x509_put ( cert );
		asn1_skip_any ( &cursor );
	}

	DBGC ( ocsp, "OCSP %p \"%s\" missing responder certificate\n",
	       ocsp, x509_name ( ocsp->cert ) );
	return -EACCES_NO_RESPONDER;
}

/**
 * Parse OCSP basic response
 *
 * @v ocsp		OCSP check
 * @v raw		ASN.1 cursor
 * @ret rc		Return status code
 */
static int ocsp_parse_basic_response ( struct ocsp_check *ocsp,
				       const struct asn1_cursor *raw ) {
	struct ocsp_response *response = &ocsp->response;
	struct asn1_algorithm **algorithm = &response->algorithm;
	struct asn1_bit_string *signature = &response->signature;
	struct asn1_cursor cursor;
	int rc;

	/* Enter BasicOCSPResponse */
	memcpy ( &cursor, raw, sizeof ( cursor ) );
	asn1_enter ( &cursor, ASN1_SEQUENCE );

	/* Parse tbsResponseData */
	if ( ( rc = ocsp_parse_tbs_response_data ( ocsp, &cursor ) ) != 0 )
		return rc;
	asn1_skip_any ( &cursor );

	/* Parse signatureAlgorithm */
	if ( ( rc = asn1_signature_algorithm ( &cursor, algorithm ) ) != 0 ) {
		DBGC ( ocsp, "OCSP %p \"%s\" cannot parse signature "
		       "algorithm: %s\n",
		       ocsp, x509_name ( ocsp->cert ), strerror ( rc ) );
		return rc;
	}
	DBGC2 ( ocsp, "OCSP %p \"%s\" signature algorithm is %s\n",
		ocsp, x509_name ( ocsp->cert ), (*algorithm)->name );
	asn1_skip_any ( &cursor );

	/* Parse signature */
	if ( ( rc = asn1_integral_bit_string ( &cursor, signature ) ) != 0 ) {
		DBGC ( ocsp, "OCSP %p \"%s\" cannot parse signature: %s\n",
		       ocsp, x509_name ( ocsp->cert ), strerror ( rc ) );
		return rc;
	}
	asn1_skip_any ( &cursor );

	/* Parse certs, if present */
	if ( ( asn1_type ( &cursor ) == ASN1_EXPLICIT_TAG ( 0 ) ) &&
	     ( ( rc = ocsp_parse_certs ( ocsp, &cursor ) ) != 0 ) )
		return rc;

	return 0;
}

/**
 * Parse OCSP response bytes
 *
 * @v ocsp		OCSP check
 * @v raw		ASN.1 cursor
 * @ret rc		Return status code
 */
static int ocsp_parse_response_bytes ( struct ocsp_check *ocsp,
				       const struct asn1_cursor *raw ) {
	struct asn1_cursor cursor;
	int rc;

	/* Enter responseBytes */
	memcpy ( &cursor, raw, sizeof ( cursor ) );
	asn1_enter ( &cursor, ASN1_EXPLICIT_TAG ( 0 ) );
	asn1_enter ( &cursor, ASN1_SEQUENCE );

	/* Parse responseType */
	if ( ( rc = ocsp_parse_response_type ( ocsp, &cursor ) ) != 0 )
		return rc;
	asn1_skip_any ( &cursor );

	/* Enter response */
	asn1_enter ( &cursor, ASN1_OCTET_STRING );

	/* Parse response */
	if ( ( rc = ocsp_parse_basic_response ( ocsp, &cursor ) ) != 0 )
		return rc;

	return 0;
}

/**
 * Parse OCSP response
 *
 * @v ocsp		OCSP check
 * @v raw		ASN.1 cursor
 * @ret rc		Return status code
 */
static int ocsp_parse_response ( struct ocsp_check *ocsp,
				 const struct asn1_cursor *raw ) {
	struct asn1_cursor cursor;
	int rc;

	/* Enter OCSPResponse */
	memcpy ( &cursor, raw, sizeof ( cursor ) );
	asn1_enter ( &cursor, ASN1_SEQUENCE );

	/* Parse responseStatus */
	if ( ( rc = ocsp_parse_response_status ( ocsp, &cursor ) ) != 0 )
		return rc;
	asn1_skip_any ( &cursor );

	/* Parse responseBytes */
	if ( ( rc = ocsp_parse_response_bytes ( ocsp, &cursor ) ) != 0 )
		return rc;

	return 0;
}

/**
 * Receive OCSP response
 *
 * @v ocsp		OCSP check
 * @v data		Response data
 * @v len		Length of response data
 * @ret rc		Return status code
 */
int ocsp_response ( struct ocsp_check *ocsp, const void *data, size_t len ) {
	struct ocsp_response *response = &ocsp->response;
	struct asn1_cursor cursor;
	int rc;

	/* Duplicate data */
	x509_put ( response->signer );
	response->signer = NULL;
	free ( response->data );
	response->data = malloc ( len );
	if ( ! response->data )
		return -ENOMEM;
	memcpy ( response->data, data, len );
	cursor.data = response->data;
	cursor.len = len;

	/* Parse response */
	if ( ( rc = ocsp_parse_response ( ocsp, &cursor ) ) != 0 )
		return rc;

	return 0;
}

/**
 * OCSP dummy root certificate store
 *
 * OCSP validation uses no root certificates, since it takes place
 * only when there already exists a validated issuer certificate.
 */
static struct x509_root ocsp_root = {
	.digest = &ocsp_digest_algorithm,
	.count = 0,
	.fingerprints = NULL,
};

/**
 * Check OCSP response signature
 *
 * @v ocsp		OCSP check
 * @v signer		Signing certificate
 * @ret rc		Return status code
 */
static int ocsp_check_signature ( struct ocsp_check *ocsp,
				  struct x509_certificate *signer ) {
	struct ocsp_response *response = &ocsp->response;
	struct digest_algorithm *digest = response->algorithm->digest;
	struct pubkey_algorithm *pubkey = response->algorithm->pubkey;
	struct x509_public_key *public_key = &signer->subject.public_key;
	uint8_t digest_ctx[ digest->ctxsize ];
	uint8_t digest_out[ digest->digestsize ];
	uint8_t pubkey_ctx[ pubkey->ctxsize ];
	int rc;

	/* Generate digest */
	digest_init ( digest, digest_ctx );
	digest_update ( digest, digest_ctx, response->tbs.data,
			response->tbs.len );
	digest_final ( digest, digest_ctx, digest_out );

	/* Initialise public-key algorithm */
	if ( ( rc = pubkey_init ( pubkey, pubkey_ctx, public_key->raw.data,
				  public_key->raw.len ) ) != 0 ) {
		DBGC ( ocsp, "OCSP %p \"%s\" could not initialise public key: "
		       "%s\n", ocsp, x509_name ( ocsp->cert ), strerror ( rc ));
		goto err_init;
	}

	/* Verify digest */
	if ( ( rc = pubkey_verify ( pubkey, pubkey_ctx, digest, digest_out,
				    response->signature.data,
				    response->signature.len ) ) != 0 ) {
		DBGC ( ocsp, "OCSP %p \"%s\" signature verification failed: "
		       "%s\n", ocsp, x509_name ( ocsp->cert ), strerror ( rc ));
		goto err_verify;
	}

	DBGC2 ( ocsp, "OCSP %p \"%s\" signature is correct\n",
		ocsp, x509_name ( ocsp->cert ) );

 err_verify:
	pubkey_final ( pubkey, pubkey_ctx );
 err_init:
	return rc;
}

/**
 * Validate OCSP response
 *
 * @v ocsp		OCSP check
 * @v time		Time at which to validate response
 * @ret rc		Return status code
 */
int ocsp_validate ( struct ocsp_check *ocsp, time_t time ) {
	struct ocsp_response *response = &ocsp->response;
	struct x509_certificate *signer;
	int rc;

	/* Sanity checks */
	assert ( response->data != NULL );

	/* The response may include a signer certificate; if this is
	 * not present then the response must have been signed
	 * directly by the issuer.
	 */
	signer = ( response->signer ? response->signer : ocsp->issuer );

	/* Validate signer, if applicable.  If the signer is not the
	 * issuer, then it must be signed directly by the issuer.
	 */
	if ( signer != ocsp->issuer ) {
		/* Forcibly invalidate the signer, since we need to
		 * ensure that it was signed by our issuer (and not
		 * some other issuer).  This prevents a sub-CA's OCSP
		 * certificate from fraudulently signing OCSP
		 * responses from the parent CA.
		 */
		x509_invalidate ( signer );
		if ( ( rc = x509_validate ( signer, ocsp->issuer, time,
					    &ocsp_root ) ) != 0 ) {
			DBGC ( ocsp, "OCSP %p \"%s\" could not validate ",
			       ocsp, x509_name ( ocsp->cert ) );
			DBGC ( ocsp, "signer \"%s\": %s\n",
			       x509_name ( signer ), strerror ( rc ) );
			return rc;
		}

		/* If signer is not the issuer, then it must have the
		 * extendedKeyUsage id-kp-OCSPSigning.
		 */
		if ( ! ( signer->extensions.ext_usage.bits &
			 X509_OCSP_SIGNING ) ) {
			DBGC ( ocsp, "OCSP %p \"%s\" ",
			       ocsp, x509_name ( ocsp->cert ) );
			DBGC ( ocsp, "signer \"%s\" is not an OCSP-signing "
			       "certificate\n", x509_name ( signer ) );
			return -EACCES_NON_OCSP_SIGNING;
		}
	}

	/* Check OCSP response signature */
	if ( ( rc = ocsp_check_signature ( ocsp, signer ) ) != 0 )
		return rc;

	/* Check OCSP response is valid at the specified time
	 * (allowing for some margin of error).
	 */
	if ( response->this_update > ( time + TIMESTAMP_ERROR_MARGIN ) ) {
		DBGC ( ocsp, "OCSP %p \"%s\" response is not yet valid (at "
		       "time %lld)\n", ocsp, x509_name ( ocsp->cert ), time );
		return -EACCES_STALE;
	}
	if ( response->next_update < ( time - TIMESTAMP_ERROR_MARGIN ) ) {
		DBGC ( ocsp, "OCSP %p \"%s\" response is stale (at time "
		       "%lld)\n", ocsp, x509_name ( ocsp->cert ), time );
		return -EACCES_STALE;
	}
	DBGC2 ( ocsp, "OCSP %p \"%s\" response is valid (at time %lld)\n",
		ocsp, x509_name ( ocsp->cert ), time );

	/* Mark certificate as passing OCSP verification */
	ocsp->cert->extensions.auth_info.ocsp.good = 1;

	/* Validate certificate against issuer */
	if ( ( rc = x509_validate ( ocsp->cert, ocsp->issuer, time,
				    &ocsp_root ) ) != 0 ) {
		DBGC ( ocsp, "OCSP %p \"%s\" could not validate certificate: "
		       "%s\n", ocsp, x509_name ( ocsp->cert ), strerror ( rc ));
		return rc;
	}
	DBGC ( ocsp, "OCSP %p \"%s\" successfully validated ",
	       ocsp, x509_name ( ocsp->cert ) );
	DBGC ( ocsp, "using \"%s\"\n", x509_name ( signer ) );

	return 0;
}