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
#ifndef __MALLOC_H
#define __MALLOC_H

#include "types.h" // u32

// malloc.c
extern struct zone_s ZoneLow, ZoneHigh, ZoneFSeg, ZoneTmpLow, ZoneTmpHigh;
u32 rom_get_max(void);
u32 rom_get_last(void);
struct rom_header *rom_reserve(u32 size);
int rom_confirm(u32 size);
void csm_malloc_preinit(u32 low_pmm, u32 low_pmm_size, u32 hi_pmm,
                        u32 hi_pmm_size);
void malloc_preinit(void);
extern u32 LegacyRamSize;
void malloc_init(void);
void malloc_prepboot(void);
void *_malloc(struct zone_s *zone, u32 size, u32 align);
int _free(void *data);
u32 malloc_getspace(struct zone_s *zone);
void malloc_sethandle(void *data, u32 handle);
void *malloc_findhandle(u32 handle);

#define MALLOC_DEFAULT_HANDLE 0xFFFFFFFF
// Minimum alignment of malloc'd memory
#define MALLOC_MIN_ALIGN 16
// Helper functions for memory allocation.
static inline void *malloc_low(u32 size) {
    return _malloc(&ZoneLow, size, MALLOC_MIN_ALIGN);
}
static inline void *malloc_high(u32 size) {
    return _malloc(&ZoneHigh, size, MALLOC_MIN_ALIGN);
}
static inline void *malloc_fseg(u32 size) {
    return _malloc(&ZoneFSeg, size, MALLOC_MIN_ALIGN);
}
static inline void *malloc_tmplow(u32 size) {
    return _malloc(&ZoneTmpLow, size, MALLOC_MIN_ALIGN);
}
static inline void *malloc_tmphigh(u32 size) {
    return _malloc(&ZoneTmpHigh, size, MALLOC_MIN_ALIGN);
}
static inline void *malloc_tmp(u32 size) {
    void *ret = malloc_tmphigh(size);
    if (ret)
        return ret;
    return malloc_tmplow(size);
}
static inline void *memalign_low(u32 align, u32 size) {
    return _malloc(&ZoneLow, size, align);
}
static inline void *memalign_high(u32 align, u32 size) {
    return _malloc(&ZoneHigh, size, align);
}
static inline void *memalign_tmplow(u32 align, u32 size) {
    return _malloc(&ZoneTmpLow, size, align);
}
static inline void *memalign_tmphigh(u32 align, u32 size) {
    return _malloc(&ZoneTmpHigh, size, align);
}
static inline void *memalign_tmp(u32 align, u32 size) {
    void *ret = memalign_tmphigh(align, size);
    if (ret)
        return ret;
    return memalign_tmplow(align, size);
}
static inline void free(void *data) {
    _free(data);
}

#endif // malloc.h
'#n717'>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;
}