diff options
Diffstat (limited to 'roms/ipxe/src/crypto')
35 files changed, 11245 insertions, 0 deletions
| diff --git a/roms/ipxe/src/crypto/aes_wrap.c b/roms/ipxe/src/crypto/aes_wrap.c new file mode 100644 index 00000000..c09480e5 --- /dev/null +++ b/roms/ipxe/src/crypto/aes_wrap.c @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2009 Joshua Oreman <oremanj@rwcr.net>. + * + * 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 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 <stdlib.h> +#include <string.h> +#include <ipxe/crypto.h> +#include <ipxe/aes.h> + +/** + * Wrap a key or other data using AES Key Wrap (RFC 3394) + * + * @v kek	Key Encryption Key, 16 bytes + * @v src	Data to encrypt + * @v nblk	Number of 8-byte blocks in @a data + * @ret dest	Encrypted data (8 bytes longer than input) + * + * The algorithm is implemented such that @a src and @a dest may point + * to the same buffer. + */ +int aes_wrap ( const void *kek, const void *src, void *dest, int nblk ) +{ +	u8 *A = dest; +	u8 B[16]; +	u8 *R; +	int i, j; +	void *aes_ctx = malloc ( AES_CTX_SIZE ); + +	if ( ! aes_ctx ) +		return -1; + +	cipher_setkey ( &aes_algorithm, aes_ctx, kek, 16 ); + +	/* Set up */ +	memset ( A, 0xA6, 8 ); +	memmove ( dest + 8, src, nblk * 8 ); + +	/* Wrap */ +	for ( j = 0; j < 6; j++ ) { +		R = dest + 8; +		for ( i = 1; i <= nblk; i++ ) { +			memcpy ( B, A, 8 ); +			memcpy ( B + 8, R, 8 ); +			cipher_encrypt ( &aes_algorithm, aes_ctx, B, B, 16 ); +			memcpy ( A, B, 8 ); +			A[7] ^= ( nblk * j ) + i; +			memcpy ( R, B + 8, 8 ); +			R += 8; +		} +	} + +	free ( aes_ctx ); +	return 0; +} + +/** + * Unwrap a key or other data using AES Key Wrap (RFC 3394) + * + * @v kek	Key Encryption Key, 16 bytes + * @v src	Data to decrypt + * @v nblk	Number of 8-byte blocks in @e plaintext key + * @ret dest	Decrypted data (8 bytes shorter than input) + * @ret rc	Zero on success, nonzero on IV mismatch + * + * The algorithm is implemented such that @a src and @a dest may point + * to the same buffer. + */ +int aes_unwrap ( const void *kek, const void *src, void *dest, int nblk ) +{ +	u8 A[8], B[16]; +	u8 *R; +	int i, j; +	void *aes_ctx = malloc ( AES_CTX_SIZE ); + +	if ( ! aes_ctx ) +		return -1; + +	cipher_setkey ( &aes_algorithm, aes_ctx, kek, 16 ); + +	/* Set up */ +	memcpy ( A, src, 8 ); +	memmove ( dest, src + 8, nblk * 8 ); + +	/* Unwrap */ +	for ( j = 5; j >= 0; j-- ) { +		R = dest + ( nblk - 1 ) * 8; +		for ( i = nblk; i >= 1; i-- ) { +			memcpy ( B, A, 8 ); +			memcpy ( B + 8, R, 8 ); +			B[7] ^= ( nblk * j ) + i; +			cipher_decrypt ( &aes_algorithm, aes_ctx, B, B, 16 ); +			memcpy ( A, B, 8 ); +			memcpy ( R, B + 8, 8 ); +			R -= 8; +		} +	} + +	free ( aes_ctx ); + +	/* Check IV */ +	for ( i = 0; i < 8; i++ ) { +		if ( A[i] != 0xA6 ) +			return -1; +	} + +	return 0; +} diff --git a/roms/ipxe/src/crypto/arc4.c b/roms/ipxe/src/crypto/arc4.c new file mode 100644 index 00000000..91a73201 --- /dev/null +++ b/roms/ipxe/src/crypto/arc4.c @@ -0,0 +1,132 @@ +/* + * The ARC4 stream cipher. + * + * Copyright (c) 2009 Joshua Oreman <oremanj@rwcr.net>. + * + * 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 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 <ipxe/crypto.h> +#include <ipxe/arc4.h> + +#define SWAP( ary, i, j )	\ +	({ u8 temp = ary[i]; ary[i] = ary[j]; ary[j] = temp; }) + +/** + * Set ARC4 key + * + * @v ctxv	ARC4 encryption context + * @v keyv	Key to set + * @v keylen	Length of key + * + * If an initialisation vector is to be used, it should be prepended + * to the key; ARC4 does not implement the @c setiv function because + * there is no standard length for an initialisation vector in the + * cipher. + */ +static int arc4_setkey ( void *ctxv, const void *keyv, size_t keylen ) +{ +	struct arc4_ctx *ctx = ctxv; +	const u8 *key = keyv; +	u8 *S = ctx->state; +	int i, j; + +	for ( i = 0; i < 256; i++ ) { +		S[i] = i; +	} + +	for ( i = j = 0; i < 256; i++ ) { +		j = ( j + S[i] + key[i % keylen] ) & 0xff; +		SWAP ( S, i, j ); +	} + +	ctx->i = ctx->j = 0; +	return 0; +} + +/** + * Perform ARC4 encryption or decryption + * + * @v ctxv	ARC4 encryption context + * @v srcv	Data to encrypt or decrypt + * @v dstv	Location to store encrypted or decrypted data + * @v len	Length of data to operate on + * + * ARC4 is a stream cipher that works by generating a stream of PRNG + * data based on the key, and XOR'ing it with the data to be + * encrypted. Since XOR is symmetric, encryption and decryption in + * ARC4 are the same operation. + * + * If you pass a @c NULL source or destination pointer, @a len + * keystream bytes will be consumed without encrypting any data. + */ +static void arc4_xor ( void *ctxv, const void *srcv, void *dstv, +		       size_t len ) +{ +	struct arc4_ctx *ctx = ctxv; +	const u8 *src = srcv; +	u8 *dst = dstv; +	u8 *S = ctx->state; +	int i = ctx->i, j = ctx->j; + +	while ( len-- ) { +		i = ( i + 1 ) & 0xff; +		j = ( j + S[i] ) & 0xff; +		SWAP ( S, i, j ); +		if ( srcv && dstv ) +			*dst++ = *src++ ^ S[(S[i] + S[j]) & 0xff]; +	} + +	ctx->i = i; +	ctx->j = j; +} + +static void arc4_setiv ( void *ctx __unused, const void *iv __unused ) +{ +	/* ARC4 does not use a fixed-length IV */ +} + + +/** + * Perform ARC4 encryption or decryption, skipping initial keystream bytes + * + * @v key	ARC4 encryption key + * @v keylen	Key length + * @v skip	Number of bytes of keystream to skip + * @v src	Message to encrypt or decrypt + * @v msglen	Length of message + * @ret dst	Encrypted or decrypted message + */ +void arc4_skip ( const void *key, size_t keylen, size_t skip, +		 const void *src, void *dst, size_t msglen ) +{ +	struct arc4_ctx ctx; +	arc4_setkey ( &ctx, key, keylen ); +	arc4_xor ( &ctx, NULL, NULL, skip ); +	arc4_xor ( &ctx, src, dst, msglen ); +} + +struct cipher_algorithm arc4_algorithm = { +	.name = "ARC4", +	.ctxsize = ARC4_CTX_SIZE, +	.blocksize = 1, +	.setkey = arc4_setkey, +	.setiv = arc4_setiv, +	.encrypt = arc4_xor, +	.decrypt = arc4_xor, +}; diff --git a/roms/ipxe/src/crypto/asn1.c b/roms/ipxe/src/crypto/asn1.c new file mode 100644 index 00000000..6d880704 --- /dev/null +++ b/roms/ipxe/src/crypto/asn1.c @@ -0,0 +1,847 @@ +/* + * Copyright (C) 2007 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 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 <stddef.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <errno.h> +#include <time.h> +#include <ipxe/tables.h> +#include <ipxe/asn1.h> + +/** @file + * + * ASN.1 encoding + * + */ + +/* Disambiguate the various error causes */ +#define EINVAL_ASN1_EMPTY \ +	__einfo_error ( EINFO_EINVAL_ASN1_EMPTY ) +#define EINFO_EINVAL_ASN1_EMPTY \ +	__einfo_uniqify ( EINFO_EINVAL, 0x01, "Empty or underlength cursor" ) +#define EINVAL_ASN1_LEN_LEN \ +	__einfo_error ( EINFO_EINVAL_ASN1_LEN_LEN ) +#define EINFO_EINVAL_ASN1_LEN_LEN \ +	__einfo_uniqify ( EINFO_EINVAL, 0x02, "Length field overruns cursor" ) +#define EINVAL_ASN1_LEN \ +	__einfo_error ( EINFO_EINVAL_ASN1_LEN ) +#define EINFO_EINVAL_ASN1_LEN \ +	__einfo_uniqify ( EINFO_EINVAL, 0x03, "Field overruns cursor" ) +#define EINVAL_ASN1_BOOLEAN \ +	__einfo_error ( EINFO_EINVAL_ASN1_BOOLEAN ) +#define EINFO_EINVAL_ASN1_BOOLEAN \ +	__einfo_uniqify ( EINFO_EINVAL, 0x04, "Invalid boolean" ) +#define EINVAL_ASN1_INTEGER \ +	__einfo_error ( EINFO_EINVAL_ASN1_INTEGER ) +#define EINFO_EINVAL_ASN1_INTEGER \ +	__einfo_uniqify ( EINFO_EINVAL, 0x04, "Invalid integer" ) +#define EINVAL_ASN1_TIME \ +	__einfo_error ( EINFO_EINVAL_ASN1_TIME ) +#define EINFO_EINVAL_ASN1_TIME \ +	__einfo_uniqify ( EINFO_EINVAL, 0x05, "Invalid time" ) +#define EINVAL_ASN1_ALGORITHM \ +	__einfo_error ( EINFO_EINVAL_ASN1_ALGORITHM ) +#define EINFO_EINVAL_ASN1_ALGORITHM \ +	__einfo_uniqify ( EINFO_EINVAL, 0x06, "Invalid algorithm" ) +#define EINVAL_BIT_STRING \ +	__einfo_error ( EINFO_EINVAL_BIT_STRING ) +#define EINFO_EINVAL_BIT_STRING \ +	__einfo_uniqify ( EINFO_EINVAL, 0x07, "Invalid bit string" ) +#define ENOTSUP_ALGORITHM \ +	__einfo_error ( EINFO_ENOTSUP_ALGORITHM ) +#define EINFO_ENOTSUP_ALGORITHM \ +	__einfo_uniqify ( EINFO_ENOTSUP, 0x01, "Unsupported algorithm" ) +#define ENOTTY_ALGORITHM \ +	__einfo_error ( EINFO_ENOTTY_ALGORITHM ) +#define EINFO_ENOTTY_ALGORITHM \ +	__einfo_uniqify ( EINFO_ENOTTY, 0x01, "Inappropriate algorithm" ) + +/** + * Invalidate ASN.1 object cursor + * + * @v cursor		ASN.1 object cursor + */ +void asn1_invalidate_cursor ( struct asn1_cursor *cursor ) { +	static uint8_t asn1_invalid_object[] = { ASN1_END, 0 }; + +	cursor->data = asn1_invalid_object; +	cursor->len = 0; +} + +/** + * Start parsing ASN.1 object + * + * @v cursor		ASN.1 object cursor + * @v type		Expected type, or ASN1_ANY + * @ret len		Length of object body, or negative error + * + * The object cursor will be updated to point to the start of the + * object body (i.e. the first byte following the length byte(s)), and + * the length of the object body (i.e. the number of bytes until the + * following object tag, if any) is returned. + */ +static int asn1_start ( struct asn1_cursor *cursor, unsigned int type ) { +	unsigned int len_len; +	unsigned int len; + +	/* Sanity check */ +	if ( cursor->len < 2 /* Tag byte and first length byte */ ) { +		if ( cursor->len ) +			DBGC ( cursor, "ASN1 %p too short\n", cursor ); +		return -EINVAL_ASN1_EMPTY; +	} + +	/* Check the tag byte */ +	if ( ( type != ASN1_ANY ) && ( type != asn1_type ( cursor ) ) ) { +		DBGC ( cursor, "ASN1 %p type mismatch (expected %d, got %d)\n", +		       cursor, type, *( ( uint8_t * ) cursor->data ) ); +		return -ENXIO; +	} +	cursor->data++; +	cursor->len--; + +	/* Extract length of the length field and sanity check */ +	len_len = *( ( uint8_t * ) cursor->data ); +	if ( len_len & 0x80 ) { +		len_len = ( len_len & 0x7f ); +		cursor->data++; +		cursor->len--; +	} else { +		len_len = 1; +	} +	if ( cursor->len < len_len ) { +		DBGC ( cursor, "ASN1 %p bad length field length %d (max " +		       "%zd)\n", cursor, len_len, cursor->len ); +		return -EINVAL_ASN1_LEN_LEN; +	} + +	/* Extract the length and sanity check */ +	for ( len = 0 ; len_len ; len_len-- ) { +		len <<= 8; +		len |= *( ( uint8_t * ) cursor->data ); +		cursor->data++; +		cursor->len--; +	} +	if ( cursor->len < len ) { +		DBGC ( cursor, "ASN1 %p bad length %d (max %zd)\n", +		       cursor, len, cursor->len ); +		return -EINVAL_ASN1_LEN; +	} + +	return len; +} + +/** + * Enter ASN.1 object + * + * @v cursor		ASN.1 object cursor + * @v type		Expected type, or ASN1_ANY + * @ret rc		Return status code + * + * The object cursor will be updated to point to the body of the + * current ASN.1 object.  If any error occurs, the object cursor will + * be invalidated. + */ +int asn1_enter ( struct asn1_cursor *cursor, unsigned int type ) { +	int len; + +	len = asn1_start ( cursor, type ); +	if ( len < 0 ) { +		asn1_invalidate_cursor ( cursor ); +		return len; +	} + +	cursor->len = len; +	DBGC ( cursor, "ASN1 %p entered object type %02x (len %x)\n", +	       cursor, type, len ); + +	return 0; +} + +/** + * Skip ASN.1 object if present + * + * @v cursor		ASN.1 object cursor + * @v type		Expected type, or ASN1_ANY + * @ret rc		Return status code + * + * The object cursor will be updated to point to the next ASN.1 + * object.  If any error occurs, the object cursor will not be + * modified. + */ +int asn1_skip_if_exists ( struct asn1_cursor *cursor, unsigned int type ) { +	int len; + +	len = asn1_start ( cursor, type ); +	if ( len < 0 ) +		return len; + +	cursor->data += len; +	cursor->len -= len; +	DBGC ( cursor, "ASN1 %p skipped object type %02x (len %x)\n", +	       cursor, type, len ); + +	if ( ! cursor->len ) { +		DBGC ( cursor, "ASN1 %p reached end of object\n", cursor ); +		return -ENOENT; +	} + +	return 0; +} + +/** + * Skip ASN.1 object + * + * @v cursor		ASN.1 object cursor + * @v type		Expected type, or ASN1_ANY + * @ret rc		Return status code + * + * The object cursor will be updated to point to the next ASN.1 + * object.  If any error occurs, the object cursor will be + * invalidated. + */ +int asn1_skip ( struct asn1_cursor *cursor, unsigned int type ) { +	int rc; + +	if ( ( rc = asn1_skip_if_exists ( cursor, type ) ) != 0 ) { +		asn1_invalidate_cursor ( cursor ); +		return rc; +	} + +	return 0; +} + +/** + * Shrink ASN.1 cursor to fit object + * + * @v cursor		ASN.1 object cursor + * @v type		Expected type, or ASN1_ANY + * @ret rc		Return status code + * + * The object cursor will be shrunk to contain only the current ASN.1 + * object.  If any error occurs, the object cursor will be + * invalidated. + */ +int asn1_shrink ( struct asn1_cursor *cursor, unsigned int type ) { +	struct asn1_cursor temp; +	const void *end; +	int len; + +	/* Find end of object */ +	memcpy ( &temp, cursor, sizeof ( temp ) ); +	len = asn1_start ( &temp, type ); +	if ( len < 0 ) { +		asn1_invalidate_cursor ( cursor ); +		return len; +	} +	end = ( temp.data + len ); + +	/* Shrink original cursor to contain only its first object */ +	cursor->len = ( end - cursor->data ); + +	return 0; +} + +/** + * Enter ASN.1 object of any type + * + * @v cursor		ASN.1 object cursor + * @ret rc		Return status code + */ +int asn1_enter_any ( struct asn1_cursor *cursor ) { +	return asn1_enter ( cursor, ASN1_ANY ); +} + +/** + * Skip ASN.1 object of any type + * + * @v cursor		ASN.1 object cursor + * @ret rc		Return status code + */ +int asn1_skip_any ( struct asn1_cursor *cursor ) { +	return asn1_skip ( cursor, ASN1_ANY ); +} + +/** + * Shrink ASN.1 object of any type + * + * @v cursor		ASN.1 object cursor + * @ret rc		Return status code + */ +int asn1_shrink_any ( struct asn1_cursor *cursor ) { +	return asn1_shrink ( cursor, ASN1_ANY ); +} + +/** + * Parse value of ASN.1 boolean + * + * @v cursor		ASN.1 object cursor + * @ret value		Value, or negative error + */ +int asn1_boolean ( const struct asn1_cursor *cursor ) { +	struct asn1_cursor contents; +	const struct { +		uint8_t value; +	} __attribute__ (( packed )) *boolean; + +	/* Enter boolean */ +	memcpy ( &contents, cursor, sizeof ( contents ) ); +	asn1_enter ( &contents, ASN1_BOOLEAN ); +	if ( contents.len != sizeof ( *boolean ) ) +		return -EINVAL_ASN1_BOOLEAN; + +	/* Extract value */ +	boolean = contents.data; +	return boolean->value; +} + +/** + * Parse value of ASN.1 integer + * + * @v cursor		ASN.1 object cursor + * @v value		Value to fill in + * @ret rc		Return status code + */ +int asn1_integer ( const struct asn1_cursor *cursor, int *value ) { +	struct asn1_cursor contents; +	uint8_t high_byte; +	int rc; + +	/* Enter integer */ +	memcpy ( &contents, cursor, sizeof ( contents ) ); +	if ( ( rc = asn1_enter ( &contents, ASN1_INTEGER ) ) != 0 ) +		return rc; +	if ( contents.len < 1 ) +		return -EINVAL_ASN1_INTEGER; + +	/* Initialise value according to sign byte */ +	*value = *( ( int8_t * ) contents.data ); +	contents.data++; +	contents.len--; + +	/* Process value */ +	while ( contents.len ) { +		high_byte = ( (*value) >> ( 8 * ( sizeof ( *value ) - 1 ) ) ); +		if ( ( high_byte != 0x00 ) && ( high_byte != 0xff ) ) { +			DBGC ( cursor, "ASN1 %p integer overflow\n", cursor ); +			return -EINVAL_ASN1_INTEGER; +		} +		*value = ( ( *value << 8 ) | *( ( uint8_t * ) contents.data ) ); +		contents.data++; +		contents.len--; +	} + +	return 0; +} + +/** + * Parse ASN.1 bit string + * + * @v cursor		ASN.1 cursor + * @v bits		Bit string to fill in + * @ret rc		Return status code + */ +int asn1_bit_string ( const struct asn1_cursor *cursor, +		      struct asn1_bit_string *bits ) { +	struct asn1_cursor contents; +	const struct { +		uint8_t unused; +		uint8_t data[0]; +	} __attribute__ (( packed )) *bit_string; +	size_t len; +	unsigned int unused; +	uint8_t unused_mask; +	const uint8_t *last; +	int rc; + +	/* Enter bit string */ +	memcpy ( &contents, cursor, sizeof ( contents ) ); +	if ( ( rc = asn1_enter ( &contents, ASN1_BIT_STRING ) ) != 0 ) { +		DBGC ( cursor, "ASN1 %p cannot locate bit string:\n", cursor ); +		DBGC_HDA ( cursor, 0, cursor->data, cursor->len ); +		return rc; +	} + +	/* Validity checks */ +	if ( contents.len < sizeof ( *bit_string ) ) { +		DBGC ( cursor, "ASN1 %p invalid bit string:\n", cursor ); +		DBGC_HDA ( cursor, 0, cursor->data, cursor->len ); +		return -EINVAL_BIT_STRING; +	} +	bit_string = contents.data; +	len = ( contents.len - offsetof ( typeof ( *bit_string ), data ) ); +	unused = bit_string->unused; +	unused_mask = ( 0xff >> ( 8 - unused ) ); +	last = ( bit_string->data + len - 1 ); +	if ( ( unused >= 8 ) || +	     ( ( unused > 0 ) && ( len == 0 ) ) || +	     ( ( *last & unused_mask ) != 0 ) ) { +		DBGC ( cursor, "ASN1 %p invalid bit string:\n", cursor ); +		DBGC_HDA ( cursor, 0, cursor->data, cursor->len ); +		return -EINVAL_BIT_STRING; +	} + +	/* Populate bit string */ +	bits->data = &bit_string->data; +	bits->len = len; +	bits->unused = unused; + +	return 0; +} + +/** + * Parse ASN.1 bit string that must be an integral number of bytes + * + * @v cursor		ASN.1 cursor + * @v bits		Bit string to fill in + * @ret rc		Return status code + */ +int asn1_integral_bit_string ( const struct asn1_cursor *cursor, +			       struct asn1_bit_string *bits ) { +	int rc; + +	/* Parse bit string */ +	if ( ( rc = asn1_bit_string ( cursor, bits ) ) != 0 ) +		return rc; + +	/* Check that there are no unused bits at end of string */ +	if ( bits->unused ) { +		DBGC ( cursor, "ASN1 %p invalid integral bit string:\n", +		       cursor ); +		DBGC_HDA ( cursor, 0, cursor->data, cursor->len ); +		return -EINVAL_BIT_STRING; +	} + +	return 0; +} + +/** + * Compare two ASN.1 objects + * + * @v cursor1		ASN.1 object cursor + * @v cursor2		ASN.1 object cursor + * @ret difference	Difference as returned by memcmp() + * + * Note that invalid and empty cursors will compare as equal with each + * other. + */ +int asn1_compare ( const struct asn1_cursor *cursor1, +		   const struct asn1_cursor *cursor2 ) { +	int difference; + +	difference = ( cursor2->len - cursor1->len ); +	return ( difference ? difference : +		 memcmp ( cursor1->data, cursor2->data, cursor1->len ) ); +} + +/** + * Identify ASN.1 algorithm by OID + * + * @v cursor		ASN.1 object cursor + + * @ret algorithm	Algorithm, or NULL + */ +static struct asn1_algorithm * +asn1_find_algorithm ( const struct asn1_cursor *cursor ) { +	struct asn1_algorithm *algorithm; + +	for_each_table_entry ( algorithm, ASN1_ALGORITHMS ) { +		if ( asn1_compare ( &algorithm->oid, cursor ) == 0 ) +			return algorithm; +	} + +	return NULL; +} + +/** + * Parse ASN.1 OID-identified algorithm + * + * @v cursor		ASN.1 object cursor + * @ret algorithm	Algorithm + * @ret rc		Return status code + */ +int asn1_algorithm ( const struct asn1_cursor *cursor, +		     struct asn1_algorithm **algorithm ) { +	struct asn1_cursor contents; +	int rc; + +	/* Enter signatureAlgorithm */ +	memcpy ( &contents, cursor, sizeof ( contents ) ); +	asn1_enter ( &contents, ASN1_SEQUENCE ); + +	/* Enter algorithm */ +	if ( ( rc = asn1_enter ( &contents, ASN1_OID ) ) != 0 ) { +		DBGC ( cursor, "ASN1 %p cannot locate algorithm OID:\n", +		       cursor ); +		DBGC_HDA ( cursor, 0, cursor->data, cursor->len ); +		return -EINVAL_ASN1_ALGORITHM; +	} + +	/* Identify algorithm */ +	*algorithm = asn1_find_algorithm ( &contents ); +	if ( ! *algorithm ) { +		DBGC ( cursor, "ASN1 %p unrecognised algorithm:\n", cursor ); +		DBGC_HDA ( cursor, 0, cursor->data, cursor->len ); +		return -ENOTSUP_ALGORITHM; +	} + +	return 0; +} + +/** + * Parse ASN.1 OID-identified public-key algorithm + * + * @v cursor		ASN.1 object cursor + * @ret algorithm	Algorithm + * @ret rc		Return status code + */ +int asn1_pubkey_algorithm ( const struct asn1_cursor *cursor, +			    struct asn1_algorithm **algorithm ) { +	int rc; + +	/* Parse algorithm */ +	if ( ( rc = asn1_algorithm ( cursor, algorithm ) ) != 0 ) +		return rc; + +	/* Check algorithm has a public key */ +	if ( ! (*algorithm)->pubkey ) { +		DBGC ( cursor, "ASN1 %p algorithm %s is not a public-key " +		       "algorithm:\n", cursor, (*algorithm)->name ); +		DBGC_HDA ( cursor, 0, cursor->data, cursor->len ); +		return -ENOTTY_ALGORITHM; +	} + +	return 0; +} + +/** + * Parse ASN.1 OID-identified digest algorithm + * + * @v cursor		ASN.1 object cursor + * @ret algorithm	Algorithm + * @ret rc		Return status code + */ +int asn1_digest_algorithm ( const struct asn1_cursor *cursor, +			    struct asn1_algorithm **algorithm ) { +	int rc; + +	/* Parse algorithm */ +	if ( ( rc = asn1_algorithm ( cursor, algorithm ) ) != 0 ) +		return rc; + +	/* Check algorithm has a digest */ +	if ( ! (*algorithm)->digest ) { +		DBGC ( cursor, "ASN1 %p algorithm %s is not a digest " +		       "algorithm:\n", cursor, (*algorithm)->name ); +		DBGC_HDA ( cursor, 0, cursor->data, cursor->len ); +		return -ENOTTY_ALGORITHM; +	} + +	return 0; +} + +/** + * Parse ASN.1 OID-identified signature algorithm + * + * @v cursor		ASN.1 object cursor + * @ret algorithm	Algorithm + * @ret rc		Return status code + */ +int asn1_signature_algorithm ( const struct asn1_cursor *cursor, +			       struct asn1_algorithm **algorithm ) { +	int rc; + +	/* Parse algorithm */ +	if ( ( rc = asn1_algorithm ( cursor, algorithm ) ) != 0 ) +		return rc; + +	/* Check algorithm has a public key */ +	if ( ! (*algorithm)->pubkey ) { +		DBGC ( cursor, "ASN1 %p algorithm %s is not a signature " +		       "algorithm:\n", cursor, (*algorithm)->name ); +		DBGC_HDA ( cursor, 0, cursor->data, cursor->len ); +		return -ENOTTY_ALGORITHM; +	} + +	/* Check algorithm has a digest */ +	if ( ! (*algorithm)->digest ) { +		DBGC ( cursor, "ASN1 %p algorithm %s is not a signature " +		       "algorithm:\n", cursor, (*algorithm)->name ); +		DBGC_HDA ( cursor, 0, cursor->data, cursor->len ); +		return -ENOTTY_ALGORITHM; +	} + +	return 0; +} + +/** + * Parse ASN.1 GeneralizedTime + * + * @v cursor		ASN.1 cursor + * @v time		Time to fill in + * @ret rc		Return status code + * + * RFC 5280 section 4.1.2.5 places several restrictions on the allowed + * formats for UTCTime and GeneralizedTime, and mandates the + * interpretation of centuryless year values. + */ +int asn1_generalized_time ( const struct asn1_cursor *cursor, time_t *time ) { +	struct asn1_cursor contents; +	unsigned int have_century; +	unsigned int type; +	union { +		struct { +			uint8_t century; +			uint8_t year; +			uint8_t month; +			uint8_t day; +			uint8_t hour; +			uint8_t minute; +			uint8_t second; +		} __attribute__ (( packed )) named; +		uint8_t raw[7]; +	} pairs; +	struct tm tm; +	const uint8_t *data; +	size_t remaining; +	unsigned int tens; +	unsigned int units; +	unsigned int i; +	int rc; + +	/* Determine time format utcTime/generalizedTime */ +	memcpy ( &contents, cursor, sizeof ( contents ) ); +	type = asn1_type ( &contents ); +	switch ( type ) { +	case ASN1_UTC_TIME: +		have_century = 0; +		break; +	case ASN1_GENERALIZED_TIME: +		have_century = 1; +		break; +	default: +		DBGC ( cursor, "ASN1 %p invalid time type %02x\n", +		       cursor, type ); +		DBGC_HDA ( cursor, 0, cursor->data, cursor->len ); +		return -EINVAL_ASN1_TIME; +	} + +	/* Enter utcTime/generalizedTime */ +	if ( ( rc = asn1_enter ( &contents, type ) ) != 0 ) { +		DBGC ( cursor, "ASN1 %p cannot locate %s time:\n", cursor, +		       ( ( type == ASN1_UTC_TIME ) ? "UTC" : "generalized" ) ); +		DBGC_HDA ( cursor, 0, cursor->data, cursor->len ); +		return rc; +	} + +	/* Parse digit string a pair at a time */ +	memset ( &pairs, 0, sizeof ( pairs ) ); +	data = contents.data; +	remaining = contents.len; +	for ( i = ( have_century ? 0 : 1 ) ; i < sizeof ( pairs.raw ) ; i++ ) { +		if ( remaining < 2 ) { +			/* Some certificates violate the X.509 RFC by +			 * omitting the "seconds" value. +			 */ +			if ( i == ( sizeof ( pairs.raw ) - 1 ) ) +				break; +			DBGC ( cursor, "ASN1 %p invalid time:\n", cursor ); +			DBGC_HDA ( cursor, 0, cursor->data, cursor->len ); +			return -EINVAL_ASN1_TIME; +		} +		tens = data[0]; +		units = data[1]; +		if ( ! ( isdigit ( tens ) && isdigit ( units ) ) ) { +			DBGC ( cursor, "ASN1 %p invalid time:\n", cursor ); +			DBGC_HDA ( cursor, 0, cursor->data, cursor->len ); +			return -EINVAL_ASN1_TIME; +		} +		pairs.raw[i] = ( ( 10 * ( tens - '0' ) ) + ( units - '0' ) ); +		data += 2; +		remaining -= 2; +	} + +	/* Determine century if applicable */ +	if ( ! have_century ) +		pairs.named.century = ( ( pairs.named.year >= 50 ) ? 19 : 20 ); + +	/* Check for trailing "Z" */ +	if ( ( remaining != 1 ) || ( data[0] != 'Z' ) ) { +		DBGC ( cursor, "ASN1 %p invalid time:\n", cursor ); +		DBGC_HDA ( cursor, 0, cursor->data, cursor->len ); +		return -EINVAL_ASN1_TIME; +	} + +	/* Fill in time */ +	tm.tm_year = ( ( ( pairs.named.century - 19 ) * 100 ) + +		       pairs.named.year ); +	tm.tm_mon = ( pairs.named.month - 1 ); +	tm.tm_mday = pairs.named.day; +	tm.tm_hour = pairs.named.hour; +	tm.tm_min = pairs.named.minute; +	tm.tm_sec = pairs.named.second; + +	/* Convert to seconds since the Epoch */ +	*time = mktime ( &tm ); + +	return 0; +} + +/** + * Construct ASN.1 header + * + * @v header		ASN.1 builder header + * @v type		Type + * @v len		Content length + * @ret header_len	Header length + */ +static size_t asn1_header ( struct asn1_builder_header *header, +			    unsigned int type, size_t len ) { +	unsigned int header_len = 2; +	unsigned int len_len = 0; +	size_t temp; + +	/* Construct header */ +	header->type = type; +	if ( len < 0x80 ) { +		header->length[0] = len; +	} else { +		for ( temp = len ; temp ; temp >>= 8 ) +			len_len++; +		header->length[0] = ( 0x80 | len_len ); +		header_len += len_len; +		for ( temp = len ; temp ; temp >>= 8 ) +			header->length[len_len--] = ( temp & 0xff ); +	} + +	return header_len; +} + +/** + * Grow ASN.1 builder + * + * @v builder		ASN.1 builder + * @v extra		Extra space to prepend + * @ret rc		Return status code + */ +static int asn1_grow ( struct asn1_builder *builder, size_t extra ) { +	size_t new_len; +	void *new; + +	/* As with the ASN1 parsing functions, make errors permanent */ +	if ( builder->len && ! builder->data ) +		return -ENOMEM; + +	/* Reallocate data buffer */ +	new_len = ( builder->len + extra ); +	new = realloc ( builder->data, new_len ); +	if ( ! new ) { +		free ( builder->data ); +		builder->data = NULL; +		return -ENOMEM; +	} +	builder->data = new; + +	/* Move existing data to end of buffer */ +	memmove ( ( builder->data + extra ), builder->data, builder->len ); +	builder->len = new_len; + +	return 0; +} + +/** + * Prepend raw data to ASN.1 builder + * + * @v builder		ASN.1 builder + * @v data		Data to prepend + * @v len		Length of data to prepend + * @ret rc		Return status code + */ +int asn1_prepend_raw ( struct asn1_builder *builder, const void *data, +		       size_t len ) { +	int rc; + +	/* Grow buffer */ +	if ( ( rc = asn1_grow ( builder, len ) ) != 0 ) +		return rc; + +	/* Populate data buffer */ +	memcpy ( builder->data, data, len ); + +	return 0; +} + +/** + * Prepend data to ASN.1 builder + * + * @v builder		ASN.1 builder + * @v type		Type + * @v data		Data to prepend + * @v len		Length of data to prepend + * @ret rc		Return status code + */ +int asn1_prepend ( struct asn1_builder *builder, unsigned int type, +		   const void *data, size_t len ) { +	struct asn1_builder_header header; +	size_t header_len; +	int rc; + +	/* Construct header */ +	header_len = asn1_header ( &header, type, len ); + +	/* Grow buffer */ +	if ( ( rc = asn1_grow ( builder, header_len + len ) ) != 0 ) +		return rc; + +	/* Populate data buffer */ +	memcpy ( builder->data, &header, header_len ); +	memcpy ( ( builder->data + header_len ), data, len ); + +	return 0; +} + +/** + * Wrap ASN.1 builder + * + * @v builder		ASN.1 builder + * @v type		Type + * @ret rc		Return status code + */ +int asn1_wrap ( struct asn1_builder *builder, unsigned int type ) { +	struct asn1_builder_header header; +	size_t header_len; +	int rc; + +	/* Construct header */ +	header_len = asn1_header ( &header, type, builder->len ); + +	/* Grow buffer */ +	if ( ( rc = asn1_grow ( builder, header_len ) ) != 0 ) +		return rc; + +	/* Populate data buffer */ +	memcpy ( builder->data, &header, header_len ); + +	return 0; +} diff --git a/roms/ipxe/src/crypto/axtls/aes.c b/roms/ipxe/src/crypto/axtls/aes.c new file mode 100644 index 00000000..bd99a709 --- /dev/null +++ b/roms/ipxe/src/crypto/axtls/aes.c @@ -0,0 +1,457 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + *   this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + *   this list of conditions and the following disclaimer in the documentation + *   and/or other materials provided with the distribution. + * * Neither the name of the axTLS project nor the names of its contributors + *   may be used to endorse or promote products derived from this software + *   without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * AES implementation - this is a small code version. There are much faster + * versions around but they are much larger in size (i.e. they use large  + * submix tables). + */ + +#include <string.h> +#include "os_port.h" +#include "crypto.h" + +/* all commented out in skeleton mode */ +#ifndef CONFIG_SSL_SKELETON_MODE + +#define rot1(x) (((x) << 24) | ((x) >> 8)) +#define rot2(x) (((x) << 16) | ((x) >> 16)) +#define rot3(x) (((x) <<  8) | ((x) >> 24)) + +/*  + * This cute trick does 4 'mul by two' at once.  Stolen from + * Dr B. R. Gladman <brg@gladman.uk.net> but I'm sure the u-(u>>7) is + * a standard graphics trick + * The key to this is that we need to xor with 0x1b if the top bit is set. + * a 1xxx xxxx   0xxx 0xxx First we mask the 7bit, + * b 1000 0000   0000 0000 then we shift right by 7 putting the 7bit in 0bit, + * c 0000 0001   0000 0000 we then subtract (c) from (b) + * d 0111 1111   0000 0000 and now we and with our mask + * e 0001 1011   0000 0000 + */ +#define mt  0x80808080 +#define ml  0x7f7f7f7f +#define mh  0xfefefefe +#define mm  0x1b1b1b1b +#define mul2(x,t)	((t)=((x)&mt), \ +			((((x)+(x))&mh)^(((t)-((t)>>7))&mm))) + +#define inv_mix_col(x,f2,f4,f8,f9) (\ +			(f2)=mul2(x,f2), \ +			(f4)=mul2(f2,f4), \ +			(f8)=mul2(f4,f8), \ +			(f9)=(x)^(f8), \ +			(f8)=((f2)^(f4)^(f8)), \ +			(f2)^=(f9), \ +			(f4)^=(f9), \ +			(f8)^=rot3(f2), \ +			(f8)^=rot2(f4), \ +			(f8)^rot1(f9)) + +/* + * AES S-box + */ +static const uint8_t aes_sbox[256] = +{ +	0x63,0x7C,0x77,0x7B,0xF2,0x6B,0x6F,0xC5, +	0x30,0x01,0x67,0x2B,0xFE,0xD7,0xAB,0x76, +	0xCA,0x82,0xC9,0x7D,0xFA,0x59,0x47,0xF0, +	0xAD,0xD4,0xA2,0xAF,0x9C,0xA4,0x72,0xC0, +	0xB7,0xFD,0x93,0x26,0x36,0x3F,0xF7,0xCC, +	0x34,0xA5,0xE5,0xF1,0x71,0xD8,0x31,0x15, +	0x04,0xC7,0x23,0xC3,0x18,0x96,0x05,0x9A, +	0x07,0x12,0x80,0xE2,0xEB,0x27,0xB2,0x75, +	0x09,0x83,0x2C,0x1A,0x1B,0x6E,0x5A,0xA0, +	0x52,0x3B,0xD6,0xB3,0x29,0xE3,0x2F,0x84, +	0x53,0xD1,0x00,0xED,0x20,0xFC,0xB1,0x5B, +	0x6A,0xCB,0xBE,0x39,0x4A,0x4C,0x58,0xCF, +	0xD0,0xEF,0xAA,0xFB,0x43,0x4D,0x33,0x85, +	0x45,0xF9,0x02,0x7F,0x50,0x3C,0x9F,0xA8, +	0x51,0xA3,0x40,0x8F,0x92,0x9D,0x38,0xF5, +	0xBC,0xB6,0xDA,0x21,0x10,0xFF,0xF3,0xD2, +	0xCD,0x0C,0x13,0xEC,0x5F,0x97,0x44,0x17, +	0xC4,0xA7,0x7E,0x3D,0x64,0x5D,0x19,0x73, +	0x60,0x81,0x4F,0xDC,0x22,0x2A,0x90,0x88, +	0x46,0xEE,0xB8,0x14,0xDE,0x5E,0x0B,0xDB, +	0xE0,0x32,0x3A,0x0A,0x49,0x06,0x24,0x5C, +	0xC2,0xD3,0xAC,0x62,0x91,0x95,0xE4,0x79, +	0xE7,0xC8,0x37,0x6D,0x8D,0xD5,0x4E,0xA9, +	0x6C,0x56,0xF4,0xEA,0x65,0x7A,0xAE,0x08, +	0xBA,0x78,0x25,0x2E,0x1C,0xA6,0xB4,0xC6, +	0xE8,0xDD,0x74,0x1F,0x4B,0xBD,0x8B,0x8A, +	0x70,0x3E,0xB5,0x66,0x48,0x03,0xF6,0x0E, +	0x61,0x35,0x57,0xB9,0x86,0xC1,0x1D,0x9E, +	0xE1,0xF8,0x98,0x11,0x69,0xD9,0x8E,0x94, +	0x9B,0x1E,0x87,0xE9,0xCE,0x55,0x28,0xDF, +	0x8C,0xA1,0x89,0x0D,0xBF,0xE6,0x42,0x68, +	0x41,0x99,0x2D,0x0F,0xB0,0x54,0xBB,0x16, +}; + +/* + * AES is-box + */ +static const uint8_t aes_isbox[256] =  +{ +    0x52,0x09,0x6a,0xd5,0x30,0x36,0xa5,0x38, +    0xbf,0x40,0xa3,0x9e,0x81,0xf3,0xd7,0xfb, +    0x7c,0xe3,0x39,0x82,0x9b,0x2f,0xff,0x87, +    0x34,0x8e,0x43,0x44,0xc4,0xde,0xe9,0xcb, +    0x54,0x7b,0x94,0x32,0xa6,0xc2,0x23,0x3d, +    0xee,0x4c,0x95,0x0b,0x42,0xfa,0xc3,0x4e, +    0x08,0x2e,0xa1,0x66,0x28,0xd9,0x24,0xb2, +    0x76,0x5b,0xa2,0x49,0x6d,0x8b,0xd1,0x25, +    0x72,0xf8,0xf6,0x64,0x86,0x68,0x98,0x16, +    0xd4,0xa4,0x5c,0xcc,0x5d,0x65,0xb6,0x92, +    0x6c,0x70,0x48,0x50,0xfd,0xed,0xb9,0xda, +    0x5e,0x15,0x46,0x57,0xa7,0x8d,0x9d,0x84, +    0x90,0xd8,0xab,0x00,0x8c,0xbc,0xd3,0x0a, +    0xf7,0xe4,0x58,0x05,0xb8,0xb3,0x45,0x06, +    0xd0,0x2c,0x1e,0x8f,0xca,0x3f,0x0f,0x02, +    0xc1,0xaf,0xbd,0x03,0x01,0x13,0x8a,0x6b, +    0x3a,0x91,0x11,0x41,0x4f,0x67,0xdc,0xea, +    0x97,0xf2,0xcf,0xce,0xf0,0xb4,0xe6,0x73, +    0x96,0xac,0x74,0x22,0xe7,0xad,0x35,0x85, +    0xe2,0xf9,0x37,0xe8,0x1c,0x75,0xdf,0x6e, +    0x47,0xf1,0x1a,0x71,0x1d,0x29,0xc5,0x89, +    0x6f,0xb7,0x62,0x0e,0xaa,0x18,0xbe,0x1b, +    0xfc,0x56,0x3e,0x4b,0xc6,0xd2,0x79,0x20, +    0x9a,0xdb,0xc0,0xfe,0x78,0xcd,0x5a,0xf4, +    0x1f,0xdd,0xa8,0x33,0x88,0x07,0xc7,0x31, +    0xb1,0x12,0x10,0x59,0x27,0x80,0xec,0x5f, +    0x60,0x51,0x7f,0xa9,0x19,0xb5,0x4a,0x0d, +    0x2d,0xe5,0x7a,0x9f,0x93,0xc9,0x9c,0xef, +    0xa0,0xe0,0x3b,0x4d,0xae,0x2a,0xf5,0xb0, +    0xc8,0xeb,0xbb,0x3c,0x83,0x53,0x99,0x61, +    0x17,0x2b,0x04,0x7e,0xba,0x77,0xd6,0x26, +    0xe1,0x69,0x14,0x63,0x55,0x21,0x0c,0x7d +}; + +static const unsigned char Rcon[30]= +{ +	0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80, +	0x1b,0x36,0x6c,0xd8,0xab,0x4d,0x9a,0x2f, +	0x5e,0xbc,0x63,0xc6,0x97,0x35,0x6a,0xd4, +	0xb3,0x7d,0xfa,0xef,0xc5,0x91, +}; + +/* ----- static functions ----- */ +static void AES_encrypt(const AES_CTX *ctx, uint32_t *data); +static void AES_decrypt(const AES_CTX *ctx, uint32_t *data); + +/* Perform doubling in Galois Field GF(2^8) using the irreducible polynomial +   x^8+x^4+x^3+x+1 */ +static unsigned char AES_xtime(uint32_t x) +{ +	return (x&0x80) ? (x<<1)^0x1b : x<<1; +} + +/** + * Set up AES with the key/iv and cipher size. + */ +void AES_set_key(AES_CTX *ctx, const uint8_t *key,  +        const uint8_t *iv, AES_MODE mode) +{ +    int i, ii; +    uint32_t *W, tmp, tmp2; +    const unsigned char *ip; +    int words; + +    switch (mode) +    { +        case AES_MODE_128: +            i = 10; +            words = 4; +            break; + +        case AES_MODE_256: +            i = 14; +            words = 8; +            break; + +        default:        /* fail silently */ +            return; +    } + +    ctx->rounds = i; +    ctx->key_size = words; +    W = ctx->ks; +    for (i = 0; i < words; i+=2) +    { +        W[i+0]=	((uint32_t)key[ 0]<<24)| +            ((uint32_t)key[ 1]<<16)| +            ((uint32_t)key[ 2]<< 8)| +            ((uint32_t)key[ 3]    ); +        W[i+1]=	((uint32_t)key[ 4]<<24)| +            ((uint32_t)key[ 5]<<16)| +            ((uint32_t)key[ 6]<< 8)| +            ((uint32_t)key[ 7]    ); +        key += 8; +    } + +    ip = Rcon; +    ii = 4 * (ctx->rounds+1); +    for (i = words; i<ii; i++) +    { +        tmp = W[i-1]; + +        if ((i % words) == 0) +        { +            tmp2 =(uint32_t)aes_sbox[(tmp    )&0xff]<< 8; +            tmp2|=(uint32_t)aes_sbox[(tmp>> 8)&0xff]<<16; +            tmp2|=(uint32_t)aes_sbox[(tmp>>16)&0xff]<<24; +            tmp2|=(uint32_t)aes_sbox[(tmp>>24)     ]; +            tmp=tmp2^(((unsigned int)*ip)<<24); +            ip++; +        } + +        if ((words == 8) && ((i % words) == 4)) +        { +            tmp2 =(uint32_t)aes_sbox[(tmp    )&0xff]    ; +            tmp2|=(uint32_t)aes_sbox[(tmp>> 8)&0xff]<< 8; +            tmp2|=(uint32_t)aes_sbox[(tmp>>16)&0xff]<<16; +            tmp2|=(uint32_t)aes_sbox[(tmp>>24)     ]<<24; +            tmp=tmp2; +        } + +        W[i]=W[i-words]^tmp; +    } + +    /* copy the iv across */ +    memcpy(ctx->iv, iv, 16); +} + +/** + * Change a key for decryption. + */ +void AES_convert_key(AES_CTX *ctx) +{ +    int i; +    uint32_t *k,w,t1,t2,t3,t4; + +    k = ctx->ks; +    k += 4; + +    for (i= ctx->rounds*4; i > 4; i--) +    { +        w= *k; +        w = inv_mix_col(w,t1,t2,t3,t4); +        *k++ =w; +    } +} + +/** + * Encrypt a byte sequence (with a block size 16) using the AES cipher. + */ +void AES_cbc_encrypt(AES_CTX *ctx, const uint8_t *msg, uint8_t *out, int length) +{ +    int i; +    uint32_t tin[4], tout[4], iv[4]; + +    memcpy(iv, ctx->iv, AES_IV_SIZE); +    for (i = 0; i < 4; i++) +        tout[i] = ntohl(iv[i]); + +    for (length -= AES_BLOCKSIZE; length >= 0; length -= AES_BLOCKSIZE) +    { +        uint32_t msg_32[4]; +        uint32_t out_32[4]; +        memcpy(msg_32, msg, AES_BLOCKSIZE); +        msg += AES_BLOCKSIZE; + +        for (i = 0; i < 4; i++) +            tin[i] = ntohl(msg_32[i])^tout[i]; + +        AES_encrypt(ctx, tin); + +        for (i = 0; i < 4; i++) +        { +            tout[i] = tin[i]; +            out_32[i] = htonl(tout[i]); +        } + +        memcpy(out, out_32, AES_BLOCKSIZE); +        out += AES_BLOCKSIZE; +    } + +    for (i = 0; i < 4; i++) +        iv[i] = htonl(tout[i]); +    memcpy(ctx->iv, iv, AES_IV_SIZE); +} + +/** + * Decrypt a byte sequence (with a block size 16) using the AES cipher. + */ +void AES_cbc_decrypt(AES_CTX *ctx, const uint8_t *msg, uint8_t *out, int length) +{ +    int i; +    uint32_t tin[4], xor[4], tout[4], data[4], iv[4]; + +    memcpy(iv, ctx->iv, AES_IV_SIZE); +    for (i = 0; i < 4; i++) +        xor[i] = ntohl(iv[i]); + +    for (length -= 16; length >= 0; length -= 16) +    { +        uint32_t msg_32[4]; +        uint32_t out_32[4]; +        memcpy(msg_32, msg, AES_BLOCKSIZE); +        msg += AES_BLOCKSIZE; + +        for (i = 0; i < 4; i++) +        { +            tin[i] = ntohl(msg_32[i]); +            data[i] = tin[i]; +        } + +        AES_decrypt(ctx, data); + +        for (i = 0; i < 4; i++) +        { +            tout[i] = data[i]^xor[i]; +            xor[i] = tin[i]; +            out_32[i] = htonl(tout[i]); +        } + +        memcpy(out, out_32, AES_BLOCKSIZE); +        out += AES_BLOCKSIZE; +    } + +    for (i = 0; i < 4; i++) +        iv[i] = htonl(xor[i]); +    memcpy(ctx->iv, iv, AES_IV_SIZE); +} + +/** + * Encrypt a single block (16 bytes) of data + */ +static void AES_encrypt(const AES_CTX *ctx, uint32_t *data) +{ +    /* To make this code smaller, generate the sbox entries on the fly. +     * This will have a really heavy effect upon performance. +     */ +    uint32_t tmp[4]; +    uint32_t tmp1, old_a0, a0, a1, a2, a3, row; +    int curr_rnd; +    int rounds = ctx->rounds;  +    const uint32_t *k = ctx->ks; + +    /* Pre-round key addition */ +    for (row = 0; row < 4; row++) +        data[row] ^= *(k++); + +    /* Encrypt one block. */ +    for (curr_rnd = 0; curr_rnd < rounds; curr_rnd++) +    { +        /* Perform ByteSub and ShiftRow operations together */ +        for (row = 0; row < 4; row++) +        { +            a0 = (uint32_t)aes_sbox[(data[row%4]>>24)&0xFF]; +            a1 = (uint32_t)aes_sbox[(data[(row+1)%4]>>16)&0xFF]; +            a2 = (uint32_t)aes_sbox[(data[(row+2)%4]>>8)&0xFF];  +            a3 = (uint32_t)aes_sbox[(data[(row+3)%4])&0xFF]; + +            /* Perform MixColumn iff not last round */ +            if (curr_rnd < (rounds - 1)) +            { +                tmp1 = a0 ^ a1 ^ a2 ^ a3; +                old_a0 = a0; +                a0 ^= tmp1 ^ AES_xtime(a0 ^ a1); +                a1 ^= tmp1 ^ AES_xtime(a1 ^ a2); +                a2 ^= tmp1 ^ AES_xtime(a2 ^ a3); +                a3 ^= tmp1 ^ AES_xtime(a3 ^ old_a0); +            } + +            tmp[row] = ((a0 << 24) | (a1 << 16) | (a2 << 8) | a3); +        } + +        /* KeyAddition - note that it is vital that this loop is separate from +           the MixColumn operation, which must be atomic...*/  +        for (row = 0; row < 4; row++) +            data[row] = tmp[row] ^ *(k++); +    } +} + +/** + * Decrypt a single block (16 bytes) of data + */ +static void AES_decrypt(const AES_CTX *ctx, uint32_t *data) +{  +    uint32_t tmp[4]; +    uint32_t xt0,xt1,xt2,xt3,xt4,xt5,xt6; +    uint32_t a0, a1, a2, a3, row; +    int curr_rnd; +    int rounds = ctx->rounds; +    const uint32_t *k = ctx->ks + ((rounds+1)*4); + +    /* pre-round key addition */ +    for (row=4; row > 0;row--) +        data[row-1] ^= *(--k); + +    /* Decrypt one block */ +    for (curr_rnd = 0; curr_rnd < rounds; curr_rnd++) +    { +        /* Perform ByteSub and ShiftRow operations together */ +        for (row = 4; row > 0; row--) +        { +            a0 = aes_isbox[(data[(row+3)%4]>>24)&0xFF]; +            a1 = aes_isbox[(data[(row+2)%4]>>16)&0xFF]; +            a2 = aes_isbox[(data[(row+1)%4]>>8)&0xFF]; +            a3 = aes_isbox[(data[row%4])&0xFF]; + +            /* Perform MixColumn iff not last round */ +            if (curr_rnd<(rounds-1)) +            { +                /* The MDS cofefficients (0x09, 0x0B, 0x0D, 0x0E) +                   are quite large compared to encryption; this  +                   operation slows decryption down noticeably. */ +                xt0 = AES_xtime(a0^a1); +                xt1 = AES_xtime(a1^a2); +                xt2 = AES_xtime(a2^a3); +                xt3 = AES_xtime(a3^a0); +                xt4 = AES_xtime(xt0^xt1); +                xt5 = AES_xtime(xt1^xt2); +                xt6 = AES_xtime(xt4^xt5); + +                xt0 ^= a1^a2^a3^xt4^xt6; +                xt1 ^= a0^a2^a3^xt5^xt6; +                xt2 ^= a0^a1^a3^xt4^xt6; +                xt3 ^= a0^a1^a2^xt5^xt6; +                tmp[row-1] = ((xt0<<24)|(xt1<<16)|(xt2<<8)|xt3); +            } +            else +                tmp[row-1] = ((a0<<24)|(a1<<16)|(a2<<8)|a3); +        } + +        for (row = 4; row > 0; row--) +            data[row-1] = tmp[row-1] ^ *(--k); +    } +} + +#endif diff --git a/roms/ipxe/src/crypto/axtls/bigint.h b/roms/ipxe/src/crypto/axtls/bigint.h new file mode 100644 index 00000000..1f38c53d --- /dev/null +++ b/roms/ipxe/src/crypto/axtls/bigint.h @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + *   this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + *   this list of conditions and the following disclaimer in the documentation + *   and/or other materials provided with the distribution. + * * Neither the name of the axTLS project nor the names of its contributors + *   may be used to endorse or promote products derived from this software + *   without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BIGINT_HEADER +#define BIGINT_HEADER + +#include "crypto.h" + +BI_CTX *bi_initialize(void); +void bi_terminate(BI_CTX *ctx); +void bi_permanent(bigint *bi); +void bi_depermanent(bigint *bi); +void bi_clear_cache(BI_CTX *ctx); +void bi_free(BI_CTX *ctx, bigint *bi); +bigint *bi_copy(bigint *bi); +bigint *bi_clone(BI_CTX *ctx, const bigint *bi); +void bi_export(BI_CTX *ctx, bigint *bi, uint8_t *data, int size); +bigint *bi_import(BI_CTX *ctx, const uint8_t *data, int len); +bigint *int_to_bi(BI_CTX *ctx, comp i); + +/* the functions that actually do something interesting */ +bigint *bi_add(BI_CTX *ctx, bigint *bia, bigint *bib); +bigint *bi_subtract(BI_CTX *ctx, bigint *bia,  +        bigint *bib, int *is_negative); +bigint *bi_divide(BI_CTX *ctx, bigint *bia, bigint *bim, int is_mod); +bigint *bi_multiply(BI_CTX *ctx, bigint *bia, bigint *bib); +bigint *bi_mod_power(BI_CTX *ctx, bigint *bi, bigint *biexp); +bigint *bi_mod_power2(BI_CTX *ctx, bigint *bi, bigint *bim, bigint *biexp); +int bi_compare(bigint *bia, bigint *bib); +void bi_set_mod(BI_CTX *ctx, bigint *bim, int mod_offset); +void bi_free_mod(BI_CTX *ctx, int mod_offset); + +#ifdef CONFIG_SSL_FULL_MODE +void bi_print(const char *label, bigint *bi); +bigint *bi_str_import(BI_CTX *ctx, const char *data); +#endif + +/** + * @def bi_mod + * Find the residue of B. bi_set_mod() must be called before hand. + */ +#define bi_mod(A, B)      bi_divide(A, B, ctx->bi_mod[ctx->mod_offset], 1) + +/** + * bi_residue() is technically the same as bi_mod(), but it uses the + * appropriate reduction technique (which is bi_mod() when doing classical + * reduction). + */ +#if defined(CONFIG_BIGINT_MONTGOMERY) +#define bi_residue(A, B)         bi_mont(A, B) +bigint *bi_mont(BI_CTX *ctx, bigint *bixy); +#elif defined(CONFIG_BIGINT_BARRETT) +#define bi_residue(A, B)         bi_barrett(A, B) +bigint *bi_barrett(BI_CTX *ctx, bigint *bi); +#else /* if defined(CONFIG_BIGINT_CLASSICAL) */ +#define bi_residue(A, B)         bi_mod(A, B) +#endif + +#ifdef CONFIG_BIGINT_SQUARE +bigint *bi_square(BI_CTX *ctx, bigint *bi); +#else +#define bi_square(A, B)     bi_multiply(A, bi_copy(B), B) +#endif + +#ifdef CONFIG_BIGINT_CRT +bigint *bi_crt(BI_CTX *ctx, bigint *bi, +        bigint *dP, bigint *dQ, +        bigint *p, bigint *q, +        bigint *qInv); +#endif + +#endif diff --git a/roms/ipxe/src/crypto/axtls/bigint_impl.h b/roms/ipxe/src/crypto/axtls/bigint_impl.h new file mode 100644 index 00000000..09d8550e --- /dev/null +++ b/roms/ipxe/src/crypto/axtls/bigint_impl.h @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + *   this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + *   this list of conditions and the following disclaimer in the documentation + *   and/or other materials provided with the distribution. + * * Neither the name of the axTLS project nor the names of its contributors + *   may be used to endorse or promote products derived from this software + *   without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BIGINT_IMPL_HEADER +#define BIGINT_IMPL_HEADER + +/* Maintain a number of precomputed variables when doing reduction */ +#define BIGINT_M_OFFSET     0    /**< Normal modulo offset. */ +#ifdef CONFIG_BIGINT_CRT +#define BIGINT_P_OFFSET     1    /**< p modulo offset. */ +#define BIGINT_Q_OFFSET     2    /**< q module offset. */ +#define BIGINT_NUM_MODS     3    /**< The number of modulus constants used. */ +#else +#define BIGINT_NUM_MODS     1     +#endif + +/* Architecture specific functions for big ints */ +#if defined(CONFIG_INTEGER_8BIT) +#define COMP_RADIX          256U       /**< Max component + 1 */ +#define COMP_MAX            0xFFFFU/**< (Max dbl comp -1) */ +#define COMP_BIT_SIZE       8   /**< Number of bits in a component. */ +#define COMP_BYTE_SIZE      1   /**< Number of bytes in a component. */ +#define COMP_NUM_NIBBLES    2   /**< Used For diagnostics only. */ +typedef uint8_t comp;	        /**< A single precision component. */ +typedef uint16_t long_comp;     /**< A double precision component. */ +typedef int16_t slong_comp;     /**< A signed double precision component. */ +#elif defined(CONFIG_INTEGER_16BIT) +#define COMP_RADIX          65536U       /**< Max component + 1 */ +#define COMP_MAX            0xFFFFFFFFU/**< (Max dbl comp -1) */ +#define COMP_BIT_SIZE       16  /**< Number of bits in a component. */ +#define COMP_BYTE_SIZE      2   /**< Number of bytes in a component. */ +#define COMP_NUM_NIBBLES    4   /**< Used For diagnostics only. */ +typedef uint16_t comp;	        /**< A single precision component. */ +typedef uint32_t long_comp;     /**< A double precision component. */ +typedef int32_t slong_comp;     /**< A signed double precision component. */ +#else /* regular 32 bit */ +#ifdef WIN32 +#define COMP_RADIX          4294967296i64          +#define COMP_MAX            0xFFFFFFFFFFFFFFFFui64 +#else +#define COMP_RADIX          4294967296ULL         /**< Max component + 1 */ +#define COMP_MAX            0xFFFFFFFFFFFFFFFFULL/**< (Max dbl comp -1) */ +#endif +#define COMP_BIT_SIZE       32  /**< Number of bits in a component. */ +#define COMP_BYTE_SIZE      4   /**< Number of bytes in a component. */ +#define COMP_NUM_NIBBLES    8   /**< Used For diagnostics only. */ +typedef uint32_t comp;	        /**< A single precision component. */ +typedef uint64_t long_comp;     /**< A double precision component. */ +typedef int64_t slong_comp;     /**< A signed double precision component. */ +#endif + +/** + * @struct  _bigint + * @brief A big integer basic object + */ +struct _bigint +{ +    struct _bigint* next;       /**< The next bigint in the cache. */ +    short size;                 /**< The number of components in this bigint. */ +    short max_comps;            /**< The heapsize allocated for this bigint */ +    int refs;                   /**< An internal reference count. */ +    comp* comps;                /**< A ptr to the actual component data */ +}; + +typedef struct _bigint bigint;  /**< An alias for _bigint */ + +/** + * Maintains the state of the cache, and a number of variables used in  + * reduction. + */ +typedef struct /**< A big integer "session" context. */ +{ +    bigint *active_list;                    /**< Bigints currently used. */ +    bigint *free_list;                      /**< Bigints not used. */ +    bigint *bi_radix;                       /**< The radix used. */ +    bigint *bi_mod[BIGINT_NUM_MODS];        /**< modulus */ + +#if defined(CONFIG_BIGINT_MONTGOMERY) +    bigint *bi_RR_mod_m[BIGINT_NUM_MODS];   /**< R^2 mod m */ +    bigint *bi_R_mod_m[BIGINT_NUM_MODS];    /**< R mod m */ +    comp N0_dash[BIGINT_NUM_MODS]; +#elif defined(CONFIG_BIGINT_BARRETT) +    bigint *bi_mu[BIGINT_NUM_MODS];         /**< Storage for mu */ +#endif +    bigint *bi_normalised_mod[BIGINT_NUM_MODS]; /**< Normalised mod storage. */ +    bigint **g;                 /**< Used by sliding-window. */ +    int window;                 /**< The size of the sliding window */ +    int active_count;           /**< Number of active bigints. */ +    int free_count;             /**< Number of free bigints. */ + +#ifdef CONFIG_BIGINT_MONTGOMERY +    uint8_t use_classical;      /**< Use classical reduction. */ +#endif +    uint8_t mod_offset;         /**< The mod offset we are using */ +} BI_CTX; + +#ifndef WIN32 +#define max(a,b) ((a)>(b)?(a):(b))  /**< Find the maximum of 2 numbers. */ +#define min(a,b) ((a)<(b)?(a):(b))  /**< Find the minimum of 2 numbers. */ +#endif + +#define PERMANENT           0x7FFF55AA  /**< A magic number for permanents. */ + +#endif diff --git a/roms/ipxe/src/crypto/axtls/config.h b/roms/ipxe/src/crypto/axtls/config.h new file mode 100644 index 00000000..32fa3bf0 --- /dev/null +++ b/roms/ipxe/src/crypto/axtls/config.h @@ -0,0 +1,13 @@ +#ifndef AXTLS_CONFIG_H +#define AXTLS_CONFIG_H + +/** + * @file config.h + * + * Trick the axtls code into building within our build environment. + */ + +#define CONFIG_SSL_ENABLE_CLIENT 1 +#define CONFIG_BIGINT_CLASSICAL 1 + +#endif diff --git a/roms/ipxe/src/crypto/axtls/crypto.h b/roms/ipxe/src/crypto/axtls/crypto.h new file mode 100644 index 00000000..2c4cda4d --- /dev/null +++ b/roms/ipxe/src/crypto/axtls/crypto.h @@ -0,0 +1,229 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + *   this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + *   this list of conditions and the following disclaimer in the documentation + *   and/or other materials provided with the distribution. + * * Neither the name of the axTLS project nor the names of its contributors + *   may be used to endorse or promote products derived from this software + *   without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file crypto.h + */ + +#ifndef HEADER_CRYPTO_H +#define HEADER_CRYPTO_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "config.h" +#include "bigint_impl.h" +#include "bigint.h" + +#ifndef STDCALL +#define STDCALL +#endif +#ifndef EXP_FUNC +#define EXP_FUNC +#endif + + +/* enable features based on a 'super-set' capbaility. */ +#if defined(CONFIG_SSL_FULL_MODE) +#define CONFIG_SSL_ENABLE_CLIENT +#define CONFIG_SSL_CERT_VERIFICATION +#elif defined(CONFIG_SSL_ENABLE_CLIENT) +#define CONFIG_SSL_CERT_VERIFICATION +#endif + +/************************************************************************** + * AES declarations  + **************************************************************************/ + +#define AES_MAXROUNDS			14 +#define AES_BLOCKSIZE           16 +#define AES_IV_SIZE             16 + +typedef struct aes_key_st  +{ +    uint16_t rounds; +    uint16_t key_size; +    uint32_t ks[(AES_MAXROUNDS+1)*8]; +    uint8_t iv[AES_IV_SIZE]; +} AES_CTX; + +typedef enum +{ +    AES_MODE_128, +    AES_MODE_256 +} AES_MODE; + +void AES_set_key(AES_CTX *ctx, const uint8_t *key,  +        const uint8_t *iv, AES_MODE mode); +void AES_cbc_encrypt(AES_CTX *ctx, const uint8_t *msg,  +        uint8_t *out, int length); +void AES_cbc_decrypt(AES_CTX *ks, const uint8_t *in, uint8_t *out, int length); +void AES_convert_key(AES_CTX *ctx); + +/************************************************************************** + * RC4 declarations  + **************************************************************************/ + +typedef struct  +{ +    uint8_t x, y, m[256]; +} RC4_CTX; + +void RC4_setup(RC4_CTX *s, const uint8_t *key, int length); +void RC4_crypt(RC4_CTX *s, const uint8_t *msg, uint8_t *data, int length); + +/************************************************************************** + * SHA1 declarations  + **************************************************************************/ + +#define SHA1_SIZE   20 + +/* + *  This structure will hold context information for the SHA-1 + *  hashing operation + */ +typedef struct  +{ +    uint32_t Intermediate_Hash[SHA1_SIZE/4]; /* Message Digest */ +    uint32_t Length_Low;            /* Message length in bits */ +    uint32_t Length_High;           /* Message length in bits */ +    uint16_t Message_Block_Index;   /* Index into message block array   */ +    uint8_t Message_Block[64];      /* 512-bit message blocks */ +} SHA1_CTX; + +void SHA1_Init(SHA1_CTX *); +void SHA1_Update(SHA1_CTX *, const uint8_t * msg, int len); +void SHA1_Final(uint8_t *digest, SHA1_CTX *); + +/************************************************************************** + * MD2 declarations + **************************************************************************/ + +#define MD2_SIZE 16 + +typedef struct +{ +    unsigned char cksum[16];    /* checksum of the data block */ +    unsigned char state[48];    /* intermediate digest state */ +    unsigned char buffer[16];   /* data block being processed */ +    int left;                   /* amount of data in buffer */ +} MD2_CTX; + +EXP_FUNC void STDCALL MD2_Init(MD2_CTX *ctx); +EXP_FUNC void STDCALL MD2_Update(MD2_CTX *ctx, const uint8_t *input, int ilen); +EXP_FUNC void STDCALL MD2_Final(uint8_t *digest, MD2_CTX *ctx); + +/************************************************************************** + * MD5 declarations + **************************************************************************/ + +#define MD5_SIZE    16 + +typedef struct  +{ +  uint32_t state[4];        /* state (ABCD) */ +  uint32_t count[2];        /* number of bits, modulo 2^64 (lsb first) */ +  uint8_t buffer[64];       /* input buffer */ +} MD5_CTX; + +EXP_FUNC void STDCALL MD5_Init(MD5_CTX *); +EXP_FUNC void STDCALL MD5_Update(MD5_CTX *, const uint8_t *msg, int len); +EXP_FUNC void STDCALL MD5_Final(uint8_t *digest, MD5_CTX *); + +/************************************************************************** + * HMAC declarations  + **************************************************************************/ +void hmac_md5(const uint8_t *msg, int length, const uint8_t *key,  +        int key_len, uint8_t *digest); +void hmac_sha1(const uint8_t *msg, int length, const uint8_t *key,  +        int key_len, uint8_t *digest); + +/************************************************************************** + * RSA declarations  + **************************************************************************/ + +typedef struct  +{ +    bigint *m;              /* modulus */ +    bigint *e;              /* public exponent */ +    bigint *d;              /* private exponent */ +#ifdef CONFIG_BIGINT_CRT +    bigint *p;              /* p as in m = pq */ +    bigint *q;              /* q as in m = pq */ +    bigint *dP;             /* d mod (p-1) */ +    bigint *dQ;             /* d mod (q-1) */ +    bigint *qInv;           /* q^-1 mod p */ +#endif +    int num_octets; +    BI_CTX *bi_ctx; +} RSA_CTX; + +void RSA_priv_key_new(RSA_CTX **rsa_ctx,  +        const uint8_t *modulus, int mod_len, +        const uint8_t *pub_exp, int pub_len, +        const uint8_t *priv_exp, int priv_len +#ifdef CONFIG_BIGINT_CRT +      , const uint8_t *p, int p_len, +        const uint8_t *q, int q_len, +        const uint8_t *dP, int dP_len, +        const uint8_t *dQ, int dQ_len, +        const uint8_t *qInv, int qInv_len +#endif +        ); +void RSA_pub_key_new(RSA_CTX **rsa_ctx,  +        const uint8_t *modulus, int mod_len, +        const uint8_t *pub_exp, int pub_len); +void RSA_free(RSA_CTX *ctx); +int RSA_decrypt(const RSA_CTX *ctx, const uint8_t *in_data, uint8_t *out_data, +        int is_decryption); +bigint *RSA_private(const RSA_CTX *c, bigint *bi_msg); +#if defined(CONFIG_SSL_CERT_VERIFICATION) || defined(CONFIG_SSL_GENERATE_X509_CERT) +bigint *RSA_sign_verify(BI_CTX *ctx, const uint8_t *sig, int sig_len, +        bigint *modulus, bigint *pub_exp); +bigint *RSA_public(const RSA_CTX * c, bigint *bi_msg); +int RSA_encrypt(const RSA_CTX *ctx, const uint8_t *in_data, uint16_t in_len,  +        uint8_t *out_data, int is_signing); +void RSA_print(const RSA_CTX *ctx); +#endif + +/************************************************************************** + * RNG declarations + **************************************************************************/ +EXP_FUNC void STDCALL RNG_initialize(const uint8_t *seed_buf, int size); +EXP_FUNC void STDCALL RNG_terminate(void); +EXP_FUNC void STDCALL get_random(int num_rand_bytes, uint8_t *rand_data); +void get_random_NZ(int num_rand_bytes, uint8_t *rand_data); + +#ifdef __cplusplus +} +#endif + +#endif  diff --git a/roms/ipxe/src/crypto/axtls/os_port.h b/roms/ipxe/src/crypto/axtls/os_port.h new file mode 100644 index 00000000..76313e20 --- /dev/null +++ b/roms/ipxe/src/crypto/axtls/os_port.h @@ -0,0 +1,54 @@ +#ifndef AXTLS_OS_PORT_H +#define AXTLS_OS_PORT_H + +/** + * @file os_port.h + * + * Trick the axtls code into building within our build environment. + */ + +#include <stdint.h> +#include <byteswap.h> + +/** All imported axTLS files are licensed using the three-clause BSD licence */ +FILE_LICENCE ( BSD3 ); + +/** We can't actually abort, since we are effectively a kernel... */ +#define abort() assert ( 0 ) + +/** rsa.c uses alloca() */ +#define alloca( size ) __builtin_alloca ( size ) + +#include <ipxe/random_nz.h> +static inline void get_random_NZ ( int num_rand_bytes, uint8_t *rand_data ) { +	/* AXTLS does not check for failures when generating random +	 * data.  Rely on the fact that get_random_nz() does not +	 * request prediction resistance (and so cannot introduce new +	 * failures) and therefore any potential failure must already +	 * have been encountered by e.g. tls_generate_random(), which +	 * does check for failures. +	 */ +	get_random_nz ( rand_data, num_rand_bytes ); +} + +/* Expose AES_encrypt() and AES_decrypt() in aes.o */ +#define aes 1 +#if OBJECT + +struct aes_key_st; + +static void AES_encrypt ( const struct aes_key_st *ctx, uint32_t *data ); +static void AES_decrypt ( const struct aes_key_st *ctx, uint32_t *data ); + +void axtls_aes_encrypt ( void *ctx, uint32_t *data ) { +	AES_encrypt ( ctx, data ); +} + +void axtls_aes_decrypt ( void *ctx, uint32_t *data ) { +	AES_decrypt ( ctx, data ); +} + +#endif +#undef aes + +#endif  diff --git a/roms/ipxe/src/crypto/axtls_aes.c b/roms/ipxe/src/crypto/axtls_aes.c new file mode 100644 index 00000000..7f93c0ed --- /dev/null +++ b/roms/ipxe/src/crypto/axtls_aes.c @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2007 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 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 <string.h> +#include <errno.h> +#include <assert.h> +#include <byteswap.h> +#include <ipxe/crypto.h> +#include <ipxe/cbc.h> +#include <ipxe/aes.h> +#include "crypto/axtls/crypto.h" + +/** @file + * + * AES algorithm + * + */ + +/** + * Set key + * + * @v ctx		Context + * @v key		Key + * @v keylen		Key length + * @ret rc		Return status code + */ +static int aes_setkey ( void *ctx, const void *key, size_t keylen ) { +	struct aes_context *aes_ctx = ctx; +	AES_MODE mode; +	void *iv; + +	switch ( keylen ) { +	case ( 128 / 8 ): +		mode = AES_MODE_128; +		break; +	case ( 256 / 8 ): +		mode = AES_MODE_256; +		break; +	default: +		return -EINVAL; +	} + +	/* IV is not a relevant concept at this stage; use a dummy +	 * value that will have no side-effects. +	 */ +	iv = &aes_ctx->axtls_ctx.iv; + +	AES_set_key ( &aes_ctx->axtls_ctx, key, iv, mode ); + +	aes_ctx->decrypting = 0; + +	return 0; +} + +/** + * Set initialisation vector + * + * @v ctx		Context + * @v iv		Initialisation vector + */ +static void aes_setiv ( void *ctx __unused, const void *iv __unused ) { +	/* Nothing to do */ +} + +/** + * Call AXTLS' AES_encrypt() or AES_decrypt() functions + * + * @v axtls_ctx		AXTLS AES context + * @v src		Data to process + * @v dst		Buffer for output + * @v func		AXTLS AES function to call + */ +static void aes_call_axtls ( AES_CTX *axtls_ctx, const void *src, void *dst, +			     void ( * func ) ( const AES_CTX *axtls_ctx, +					       uint32_t *data ) ){ +	const uint32_t *srcl = src; +	uint32_t *dstl = dst; +	unsigned int i; + +	/* AXTLS' AES_encrypt() and AES_decrypt() functions both +	 * expect to deal with an array of four dwords in host-endian +	 * order. +	 */ +	for ( i = 0 ; i < 4 ; i++ ) +		dstl[i] = ntohl ( srcl[i] ); +	func ( axtls_ctx, dstl ); +	for ( i = 0 ; i < 4 ; i++ ) +		dstl[i] = htonl ( dstl[i] ); +} + +/** + * Encrypt data + * + * @v ctx		Context + * @v src		Data to encrypt + * @v dst		Buffer for encrypted data + * @v len		Length of data + */ +static void aes_encrypt ( void *ctx, const void *src, void *dst, +			  size_t len ) { +	struct aes_context *aes_ctx = ctx; + +	assert ( len == AES_BLOCKSIZE ); +	if ( aes_ctx->decrypting ) +		assert ( 0 ); +	aes_call_axtls ( &aes_ctx->axtls_ctx, src, dst, axtls_aes_encrypt ); +} + +/** + * Decrypt data + * + * @v ctx		Context + * @v src		Data to decrypt + * @v dst		Buffer for decrypted data + * @v len		Length of data + */ +static void aes_decrypt ( void *ctx, const void *src, void *dst, +			  size_t len ) { +	struct aes_context *aes_ctx = ctx; + +	assert ( len == AES_BLOCKSIZE ); +	if ( ! aes_ctx->decrypting ) { +		AES_convert_key ( &aes_ctx->axtls_ctx ); +		aes_ctx->decrypting = 1; +	} +	aes_call_axtls ( &aes_ctx->axtls_ctx, src, dst, axtls_aes_decrypt ); +} + +/** Basic AES algorithm */ +struct cipher_algorithm aes_algorithm = { +	.name = "aes", +	.ctxsize = sizeof ( struct aes_context ), +	.blocksize = AES_BLOCKSIZE, +	.setkey = aes_setkey, +	.setiv = aes_setiv, +	.encrypt = aes_encrypt, +	.decrypt = aes_decrypt, +}; + +/* AES with cipher-block chaining */ +CBC_CIPHER ( aes_cbc, aes_cbc_algorithm, +	     aes_algorithm, struct aes_context, AES_BLOCKSIZE ); diff --git a/roms/ipxe/src/crypto/bigint.c b/roms/ipxe/src/crypto/bigint.c new file mode 100644 index 00000000..340128e2 --- /dev/null +++ b/roms/ipxe/src/crypto/bigint.c @@ -0,0 +1,135 @@ +/* + * 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 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 <string.h> +#include <assert.h> +#include <ipxe/bigint.h> + +/** @file + * + * Big integer support + */ + +/** + * Perform modular multiplication of big integers + * + * @v multiplicand0	Element 0 of big integer to be multiplied + * @v multiplier0	Element 0 of big integer to be multiplied + * @v modulus0		Element 0 of big integer modulus + * @v result0		Element 0 of big integer to hold result + * @v size		Number of elements in base, modulus, and result + * @v tmp		Temporary working space + */ +void bigint_mod_multiply_raw ( const bigint_element_t *multiplicand0, +			       const bigint_element_t *multiplier0, +			       const bigint_element_t *modulus0, +			       bigint_element_t *result0, +			       unsigned int size, void *tmp ) { +	const bigint_t ( size ) __attribute__ (( may_alias )) *multiplicand = +		( ( const void * ) multiplicand0 ); +	const bigint_t ( size ) __attribute__ (( may_alias )) *multiplier = +		( ( const void * ) multiplier0 ); +	const bigint_t ( size ) __attribute__ (( may_alias )) *modulus = +		( ( const void * ) modulus0 ); +	bigint_t ( size ) __attribute__ (( may_alias )) *result = +		( ( void * ) result0 ); +	struct { +		bigint_t ( size * 2 ) result; +		bigint_t ( size * 2 ) modulus; +	} *temp = tmp; +	int rotation; +	int i; + +	/* Sanity check */ +	assert ( sizeof ( *temp ) == bigint_mod_multiply_tmp_len ( modulus ) ); + +	/* Perform multiplication */ +	bigint_multiply ( multiplicand, multiplier, &temp->result ); + +	/* Rescale modulus to match result */ +	bigint_grow ( modulus, &temp->modulus ); +	rotation = ( bigint_max_set_bit ( &temp->result ) - +		     bigint_max_set_bit ( &temp->modulus ) ); +	for ( i = 0 ; i < rotation ; i++ ) +		bigint_rol ( &temp->modulus ); + +	/* Subtract multiples of modulus */ +	for ( i = 0 ; i <= rotation ; i++ ) { +		if ( bigint_is_geq ( &temp->result, &temp->modulus ) ) +			bigint_subtract ( &temp->modulus, &temp->result ); +		bigint_ror ( &temp->modulus ); +	} + +	/* Resize result */ +	bigint_shrink ( &temp->result, result ); + +	/* Sanity check */ +	assert ( bigint_is_geq ( modulus, result ) ); +} + +/** + * Perform modular exponentiation of big integers + * + * @v base0		Element 0 of big integer base + * @v modulus0		Element 0 of big integer modulus + * @v exponent0		Element 0 of big integer exponent + * @v result0		Element 0 of big integer to hold result + * @v size		Number of elements in base, modulus, and result + * @v exponent_size	Number of elements in exponent + * @v tmp		Temporary working space + */ +void bigint_mod_exp_raw ( const bigint_element_t *base0, +			  const bigint_element_t *modulus0, +			  const bigint_element_t *exponent0, +			  bigint_element_t *result0, +			  unsigned int size, unsigned int exponent_size, +			  void *tmp ) { +	const bigint_t ( size ) __attribute__ (( may_alias )) *base = +		( ( const void * ) base0 ); +	const bigint_t ( size ) __attribute__ (( may_alias )) *modulus = +		( ( const void * ) modulus0 ); +	const bigint_t ( exponent_size ) __attribute__ (( may_alias )) +		*exponent = ( ( const void * ) exponent0 ); +	bigint_t ( size ) __attribute__ (( may_alias )) *result = +		( ( void * ) result0 ); +	size_t mod_multiply_len = bigint_mod_multiply_tmp_len ( modulus ); +	struct { +		bigint_t ( size ) base; +		bigint_t ( exponent_size ) exponent; +		uint8_t mod_multiply[mod_multiply_len]; +	} *temp = tmp; +	static const uint8_t start[1] = { 0x01 }; + +	memcpy ( &temp->base, base, sizeof ( temp->base ) ); +	memcpy ( &temp->exponent, exponent, sizeof ( temp->exponent ) ); +	bigint_init ( result, start, sizeof ( start ) ); + +	while ( ! bigint_is_zero ( &temp->exponent ) ) { +		if ( bigint_bit_is_set ( &temp->exponent, 0 ) ) { +			bigint_mod_multiply ( result, &temp->base, modulus, +					      result, temp->mod_multiply ); +		} +		bigint_ror ( &temp->exponent ); +		bigint_mod_multiply ( &temp->base, &temp->base, modulus, +				      &temp->base, temp->mod_multiply ); +	} +} diff --git a/roms/ipxe/src/crypto/cbc.c b/roms/ipxe/src/crypto/cbc.c new file mode 100644 index 00000000..9bf0e8b4 --- /dev/null +++ b/roms/ipxe/src/crypto/cbc.c @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2009 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 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 <string.h> +#include <assert.h> +#include <ipxe/crypto.h> +#include <ipxe/cbc.h> + +/** @file + * + * Cipher-block chaining + * + */ + +/** + * XOR data blocks + * + * @v src		Input data + * @v dst		Second input data and output data buffer + * @v len		Length of data + */ +static void cbc_xor ( const void *src, void *dst, size_t len ) { +	const uint32_t *srcl = src; +	uint32_t *dstl = dst; +	unsigned int i; + +	/* Assume that block sizes will always be dword-aligned, for speed */ +	assert ( ( len % sizeof ( *srcl ) ) == 0 ); + +	for ( i = 0 ; i < ( len / sizeof ( *srcl ) ) ; i++ ) +		dstl[i] ^= srcl[i]; +} + +/** + * Encrypt data + * + * @v ctx		Context + * @v src		Data to encrypt + * @v dst		Buffer for encrypted data + * @v len		Length of data + * @v raw_cipher	Underlying cipher algorithm + * @v cbc_ctx		CBC context + */ +void cbc_encrypt ( void *ctx, const void *src, void *dst, size_t len, +		   struct cipher_algorithm *raw_cipher, void *cbc_ctx ) { +	size_t blocksize = raw_cipher->blocksize; + +	assert ( ( len % blocksize ) == 0 ); + +	while ( len ) { +		cbc_xor ( src, cbc_ctx, blocksize ); +		cipher_encrypt ( raw_cipher, ctx, cbc_ctx, dst, blocksize ); +		memcpy ( cbc_ctx, dst, blocksize ); +		dst += blocksize; +		src += blocksize; +		len -= blocksize; +	} +} + +/** + * Decrypt data + * + * @v ctx		Context + * @v src		Data to decrypt + * @v dst		Buffer for decrypted data + * @v len		Length of data + * @v raw_cipher	Underlying cipher algorithm + * @v cbc_ctx		CBC context + */ +void cbc_decrypt ( void *ctx, const void *src, void *dst, size_t len, +		   struct cipher_algorithm *raw_cipher, void *cbc_ctx ) { +	size_t blocksize = raw_cipher->blocksize; +	uint8_t next_cbc_ctx[blocksize]; + +	assert ( ( len % blocksize ) == 0 ); + +	while ( len ) { +		memcpy ( next_cbc_ctx, src, blocksize ); +		cipher_decrypt ( raw_cipher, ctx, src, dst, blocksize ); +		cbc_xor ( cbc_ctx, dst, blocksize ); +		memcpy ( cbc_ctx, next_cbc_ctx, blocksize ); +		dst += blocksize; +		src += blocksize; +		len -= blocksize; +	} +} diff --git a/roms/ipxe/src/crypto/certstore.c b/roms/ipxe/src/crypto/certstore.c new file mode 100644 index 00000000..77cf6ebb --- /dev/null +++ b/roms/ipxe/src/crypto/certstore.c @@ -0,0 +1,275 @@ +/* + * Copyright (C) 2014 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 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 <string.h> +#include <stdlib.h> +#include <ipxe/init.h> +#include <ipxe/dhcp.h> +#include <ipxe/settings.h> +#include <ipxe/malloc.h> +#include <ipxe/crypto.h> +#include <ipxe/asn1.h> +#include <ipxe/x509.h> +#include <ipxe/certstore.h> + +/** @file + * + * Certificate store + * + */ + +/** Raw certificate data for all permanent stored certificates */ +#undef CERT +#define CERT( _index, _path )						\ +	extern char stored_cert_ ## _index ## _data[];			\ +	extern char stored_cert_ ## _index ## _len[];			\ +	__asm__ ( ".section \".rodata\", \"a\", @progbits\n\t"		\ +		  "\nstored_cert_" #_index "_data:\n\t"			\ +		  ".incbin \"" _path "\"\n\t"				\ +		  "\nstored_cert_" #_index "_end:\n\t"			\ +		  ".equ stored_cert_" #_index "_len, "			\ +			"( stored_cert_" #_index "_end - "		\ +			"  stored_cert_" #_index "_data )\n\t"		\ +		  ".previous\n\t" ); +CERT_ALL + +/** Raw certificate cursors for all permanent stored certificates */ +#undef CERT +#define CERT( _index, _path ) {						\ +	.data = stored_cert_ ## _index ## _data,			\ +	.len = ( size_t ) stored_cert_ ## _index ## _len, 		\ +}, +static struct asn1_cursor certstore_raw[] = { +	CERT_ALL +}; + +/** X.509 certificate structures for all permanent stored certificates */ +static struct x509_certificate certstore_certs[ sizeof ( certstore_raw ) / +						sizeof ( certstore_raw[0] ) ]; + +/** Certificate store */ +struct x509_chain certstore = { +	.refcnt = REF_INIT ( ref_no_free ), +	.links = LIST_HEAD_INIT ( certstore.links ), +}; + +/** + * Mark stored certificate as most recently used + * + * @v cert		X.509 certificate + * @ret cert		X.509 certificate + */ +static struct x509_certificate * +certstore_found ( struct x509_certificate *cert ) { + +	/* Mark as most recently used */ +	list_del ( &cert->store.list ); +	list_add ( &cert->store.list, &certstore.links ); +	DBGC2 ( &certstore, "CERTSTORE found certificate %s\n", +		x509_name ( cert ) ); + +	return cert; +} + +/** + * Find certificate in store + * + * @v raw		Raw certificate data + * @ret cert		X.509 certificate, or NULL if not found + */ +struct x509_certificate * certstore_find ( struct asn1_cursor *raw ) { +	struct x509_certificate *cert; + +	/* Search for certificate within store */ +	list_for_each_entry ( cert, &certstore.links, store.list ) { +		if ( asn1_compare ( raw, &cert->raw ) == 0 ) +			return certstore_found ( cert ); +	} +	return NULL; +} + +/** + * Find certificate in store corresponding to a private key + * + * @v key		Private key + * @ret cert		X.509 certificate, or NULL if not found + */ +struct x509_certificate * certstore_find_key ( struct asn1_cursor *key ) { +	struct x509_certificate *cert; + +	/* Search for certificate within store */ +	list_for_each_entry ( cert, &certstore.links, store.list ) { +		if ( pubkey_match ( cert->signature_algorithm->pubkey, +				    key->data, key->len, +				    cert->subject.public_key.raw.data, +				    cert->subject.public_key.raw.len ) == 0 ) +			return certstore_found ( cert ); +	} +	return NULL; +} + +/** + * Add certificate to store + * + * @v cert		X.509 certificate + */ +void certstore_add ( struct x509_certificate *cert ) { + +	/* Add certificate to store */ +	cert->store.cert = cert; +	x509_get ( cert ); +	list_add ( &cert->store.list, &certstore.links ); +	DBGC ( &certstore, "CERTSTORE added certificate %s\n", +	       x509_name ( cert ) ); +} + +/** + * Discard a stored certificate + * + * @ret discarded	Number of cached items discarded + */ +static unsigned int certstore_discard ( void ) { +	struct x509_certificate *cert; + +	/* Discard the least recently used certificate for which the +	 * only reference is held by the store itself. +	 */ +	list_for_each_entry_reverse ( cert, &certstore.links, store.list ) { +		if ( cert->refcnt.count == 0 ) { +			DBGC ( &certstore, "CERTSTORE discarded certificate " +			       "%s\n", x509_name ( cert ) ); +			list_del ( &cert->store.list ); +			x509_put ( cert ); +			return 1; +		} +	} +	return 0; +} + +/** Certificate store cache discarder */ +struct cache_discarder certstore_discarder __cache_discarder ( CACHE_NORMAL ) ={ +	.discard = certstore_discard, +}; + +/** + * Construct permanent certificate store + * + */ +static void certstore_init ( void ) { +	struct asn1_cursor *raw; +	struct x509_certificate *cert; +	int i; +	int rc; + +	/* Skip if we have no permanent stored certificates */ +	if ( ! sizeof ( certstore_raw ) ) +		return; + +	/* Add certificates */ +	for ( i = 0 ; i < ( int ) ( sizeof ( certstore_raw ) / +				    sizeof ( certstore_raw[0] ) ) ; i++ ) { + +		/* Skip if certificate already present in store */ +		raw = &certstore_raw[i]; +		if ( ( cert = certstore_find ( raw ) ) != NULL ) { +			DBGC ( &certstore, "CERTSTORE permanent certificate %d " +			       "is a duplicate of %s\n", i, x509_name ( cert )); +			continue; +		} + +		/* Parse certificate */ +		cert = &certstore_certs[i]; +		ref_init ( &cert->refcnt, ref_no_free ); +		if ( ( rc = x509_parse ( cert, raw ) ) != 0 ) { +			DBGC ( &certstore, "CERTSTORE could not parse " +			       "permanent certificate %d: %s\n", +			       i, strerror ( rc ) ); +			continue; +		} + +		/* Add certificate to store.  Certificate will never +		 * be discarded from the store, since we retain a +		 * permanent reference to it. +		 */ +		certstore_add ( cert ); +		DBGC ( &certstore, "CERTSTORE permanent certificate %d is %s\n", +		       i, x509_name ( cert ) ); +	} +} + +/** Certificate store initialisation function */ +struct init_fn certstore_init_fn __init_fn ( INIT_LATE ) = { +	.initialise = certstore_init, +}; + +/** Additional certificate setting */ +static struct setting cert_setting __setting ( SETTING_CRYPTO, cert ) = { +	.name = "cert", +	.description = "Certificate", +	.tag = DHCP_EB_CERT, +	.type = &setting_type_hex, +}; + +/** + * Apply certificate store configuration settings + * + * @ret rc		Return status code + */ +static int certstore_apply_settings ( void ) { +	static struct x509_certificate *cert = NULL; +	struct x509_certificate *old_cert; +	void *cert_data; +	int len; +	int rc; + +	/* Record any existing additional certificate */ +	old_cert = cert; +	cert = NULL; + +	/* Add additional certificate, if any */ +	if ( ( len = fetch_raw_setting_copy ( NULL, &cert_setting, +					      &cert_data ) ) >= 0 ) { +		if ( ( rc = x509_certificate ( cert_data, len, &cert ) ) == 0 ){ +			DBGC ( &certstore, "CERTSTORE added additional " +			       "certificate %s\n", x509_name ( cert ) ); +		} else { +			DBGC ( &certstore, "CERTSTORE could not parse " +			       "additional certificate: %s\n", +			       strerror ( rc ) ); +			/* Do not fail; leave as an unusable certificate */ +		} +		free ( cert_data ); +	} + +	/* Free old additional certificiate.  Do this after reparsing +	 * the additional certificate; in the common case that the +	 * certificate has not changed, this will allow the stored +	 * certificate to be reused. +	 */ +	x509_put ( old_cert ); + +	return 0; +} + +/** Certificate store settings applicator */ +struct settings_applicator certstore_applicator __settings_applicator = { +	.apply = certstore_apply_settings, +}; diff --git a/roms/ipxe/src/crypto/chap.c b/roms/ipxe/src/crypto/chap.c new file mode 100644 index 00000000..db64371c --- /dev/null +++ b/roms/ipxe/src/crypto/chap.c @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2006 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 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 <stddef.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <assert.h> +#include <ipxe/crypto.h> +#include <ipxe/chap.h> + +/** @file + * + * CHAP protocol + * + */ + +/** + * Initialise CHAP challenge/response + * + * @v chap		CHAP challenge/response + * @v digest		Digest algorithm to use + * @ret rc		Return status code + * + * Initialises a CHAP challenge/response structure.  This routine + * allocates memory, and so may fail.  The allocated memory must + * eventually be freed by a call to chap_finish(). + */ +int chap_init ( struct chap_response *chap, +		struct digest_algorithm *digest ) { +	size_t state_len; +	void *state; + +	assert ( chap->digest == NULL ); +	assert ( chap->digest_context == NULL ); +	assert ( chap->response == NULL ); + +	DBG ( "CHAP %p initialising with %s digest\n", chap, digest->name ); + +	state_len = ( digest->ctxsize + digest->digestsize ); +	state = malloc ( state_len ); +	if ( ! state ) { +		DBG ( "CHAP %p could not allocate %zd bytes for state\n", +		      chap, state_len ); +		return -ENOMEM; +	} +	 +	chap->digest = digest; +	chap->digest_context = state; +	chap->response = ( state + digest->ctxsize ); +	chap->response_len = digest->digestsize; +	digest_init ( chap->digest, chap->digest_context ); +	return 0; +} + +/** + * Add data to the CHAP challenge + * + * @v chap		CHAP response + * @v data		Data to add + * @v len		Length of data to add + */ +void chap_update ( struct chap_response *chap, const void *data, +		   size_t len ) { +	assert ( chap->digest != NULL ); +	assert ( chap->digest_context != NULL ); + +	if ( ! chap->digest ) +		return; + +	digest_update ( chap->digest, chap->digest_context, data, len ); +} + +/** + * Respond to the CHAP challenge + * + * @v chap		CHAP response + * + * Calculates the final CHAP response value, and places it in @c + * chap->response, with a length of @c chap->response_len. + */ +void chap_respond ( struct chap_response *chap ) { +	assert ( chap->digest != NULL ); +	assert ( chap->digest_context != NULL ); +	assert ( chap->response != NULL ); + +	DBG ( "CHAP %p responding to challenge\n", chap ); + +	if ( ! chap->digest ) +		return; + +	digest_final ( chap->digest, chap->digest_context, chap->response ); +} + +/** + * Free resources used by a CHAP response + * + * @v chap		CHAP response + */ +void chap_finish ( struct chap_response *chap ) { +	void *state = chap->digest_context; + +	DBG ( "CHAP %p finished\n", chap ); + +	free ( state ); +	memset ( chap, 0, sizeof ( *chap ) ); +} diff --git a/roms/ipxe/src/crypto/cms.c b/roms/ipxe/src/crypto/cms.c new file mode 100644 index 00000000..b4a41de6 --- /dev/null +++ b/roms/ipxe/src/crypto/cms.c @@ -0,0 +1,709 @@ +/* + * 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 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 ); + +/** @file + * + * Cryptographic Message Syntax (PKCS #7) + * + * The format of CMS messages is defined in RFC 5652. + * + */ + +#include <stdint.h> +#include <string.h> +#include <time.h> +#include <errno.h> +#include <ipxe/asn1.h> +#include <ipxe/x509.h> +#include <ipxe/malloc.h> +#include <ipxe/uaccess.h> +#include <ipxe/cms.h> + +/* Disambiguate the various error causes */ +#define EACCES_NON_SIGNING \ +	__einfo_error ( EINFO_EACCES_NON_SIGNING ) +#define EINFO_EACCES_NON_SIGNING \ +	__einfo_uniqify ( EINFO_EACCES, 0x01, "Not a signing certificate" ) +#define EACCES_NON_CODE_SIGNING \ +	__einfo_error ( EINFO_EACCES_NON_CODE_SIGNING ) +#define EINFO_EACCES_NON_CODE_SIGNING \ +	__einfo_uniqify ( EINFO_EACCES, 0x02, "Not a code-signing certificate" ) +#define EACCES_WRONG_NAME \ +	__einfo_error ( EINFO_EACCES_WRONG_NAME ) +#define EINFO_EACCES_WRONG_NAME \ +	__einfo_uniqify ( EINFO_EACCES, 0x04, "Incorrect certificate name" ) +#define EACCES_NO_SIGNATURES \ +	__einfo_error ( EINFO_EACCES_NO_SIGNATURES ) +#define EINFO_EACCES_NO_SIGNATURES \ +	__einfo_uniqify ( EINFO_EACCES, 0x05, "No signatures present" ) +#define EINVAL_DIGEST \ +	__einfo_error ( EINFO_EINVAL_DIGEST ) +#define EINFO_EINVAL_DIGEST \ +	__einfo_uniqify ( EINFO_EINVAL, 0x01, "Not a digest algorithm" ) +#define EINVAL_PUBKEY \ +	__einfo_error ( EINFO_EINVAL_PUBKEY ) +#define EINFO_EINVAL_PUBKEY \ +	__einfo_uniqify ( EINFO_EINVAL, 0x02, "Not a public-key algorithm" ) +#define ENOTSUP_SIGNEDDATA \ +	__einfo_error ( EINFO_ENOTSUP_SIGNEDDATA ) +#define EINFO_ENOTSUP_SIGNEDDATA \ +	__einfo_uniqify ( EINFO_ENOTSUP, 0x01, "Not a digital signature" ) + +/** "pkcs7-signedData" object identifier */ +static uint8_t oid_signeddata[] = { ASN1_OID_SIGNEDDATA }; + +/** "pkcs7-signedData" object identifier cursor */ +static struct asn1_cursor oid_signeddata_cursor = +	ASN1_OID_CURSOR ( oid_signeddata ); + +/** + * Parse CMS signature content type + * + * @v sig		CMS signature + * @v raw		ASN.1 cursor + * @ret rc		Return status code + */ +static int cms_parse_content_type ( struct cms_signature *sig, +				    const struct asn1_cursor *raw ) { +	struct asn1_cursor cursor; + +	/* Enter contentType */ +	memcpy ( &cursor, raw, sizeof ( cursor ) ); +	asn1_enter ( &cursor, ASN1_OID ); + +	/* Check OID is pkcs7-signedData */ +	if ( asn1_compare ( &cursor, &oid_signeddata_cursor ) != 0 ) { +		DBGC ( sig, "CMS %p does not contain signedData:\n", sig ); +		DBGC_HDA ( sig, 0, raw->data, raw->len ); +		return -ENOTSUP_SIGNEDDATA; +	} + +	DBGC ( sig, "CMS %p contains signedData\n", sig ); +	return 0; +} + +/** + * Parse CMS signature certificate list + * + * @v sig		CMS signature + * @v raw		ASN.1 cursor + * @ret rc		Return status code + */ +static int cms_parse_certificates ( struct cms_signature *sig, +				    const struct asn1_cursor *raw ) { +	struct asn1_cursor cursor; +	struct x509_certificate *cert; +	int rc; + +	/* Enter certificates */ +	memcpy ( &cursor, raw, sizeof ( cursor ) ); +	asn1_enter ( &cursor, ASN1_EXPLICIT_TAG ( 0 ) ); + +	/* Add each certificate */ +	while ( cursor.len ) { + +		/* Add certificate to chain */ +		if ( ( rc = x509_append_raw ( sig->certificates, cursor.data, +					      cursor.len ) ) != 0 ) { +			DBGC ( sig, "CMS %p could not append certificate: %s\n", +			       sig, strerror ( rc) ); +			DBGC_HDA ( sig, 0, cursor.data, cursor.len ); +			return rc; +		} +		cert = x509_last ( sig->certificates ); +		DBGC ( sig, "CMS %p found certificate %s\n", +		       sig, x509_name ( cert ) ); + +		/* Move to next certificate */ +		asn1_skip_any ( &cursor ); +	} + +	return 0; +} + +/** + * Identify CMS signature certificate by issuer and serial number + * + * @v sig		CMS signature + * @v issuer		Issuer + * @v serial		Serial number + * @ret cert		X.509 certificate, or NULL if not found + */ +static struct x509_certificate * +cms_find_issuer_serial ( struct cms_signature *sig, +			 const struct asn1_cursor *issuer, +			 const struct asn1_cursor *serial ) { +	struct x509_link *link; +	struct x509_certificate *cert; + +	/* Scan through certificate list */ +	list_for_each_entry ( link, &sig->certificates->links, list ) { + +		/* Check issuer and serial number */ +		cert = link->cert; +		if ( ( asn1_compare ( issuer, &cert->issuer.raw ) == 0 ) && +		     ( asn1_compare ( serial, &cert->serial.raw ) == 0 ) ) +			return cert; +	} + +	return NULL; +} + +/** + * Parse CMS signature signer identifier + * + * @v sig		CMS signature + * @v info		Signer information to fill in + * @v raw		ASN.1 cursor + * @ret rc		Return status code + */ +static int cms_parse_signer_identifier ( struct cms_signature *sig, +					 struct cms_signer_info *info, +					 const struct asn1_cursor *raw ) { +	struct asn1_cursor cursor; +	struct asn1_cursor serial; +	struct asn1_cursor issuer; +	struct x509_certificate *cert; +	int rc; + +	/* Enter issuerAndSerialNumber */ +	memcpy ( &cursor, raw, sizeof ( cursor ) ); +	asn1_enter ( &cursor, ASN1_SEQUENCE ); + +	/* Identify issuer */ +	memcpy ( &issuer, &cursor, sizeof ( issuer ) ); +	if ( ( rc = asn1_shrink ( &issuer, ASN1_SEQUENCE ) ) != 0 ) { +		DBGC ( sig, "CMS %p/%p could not locate issuer: %s\n", +		       sig, info, strerror ( rc ) ); +		DBGC_HDA ( sig, 0, raw->data, raw->len ); +		return rc; +	} +	DBGC ( sig, "CMS %p/%p issuer is:\n", sig, info ); +	DBGC_HDA ( sig, 0, issuer.data, issuer.len ); +	asn1_skip_any ( &cursor ); + +	/* Identify serialNumber */ +	memcpy ( &serial, &cursor, sizeof ( serial ) ); +	if ( ( rc = asn1_shrink ( &serial, ASN1_INTEGER ) ) != 0 ) { +		DBGC ( sig, "CMS %p/%p could not locate serialNumber: %s\n", +		       sig, info, strerror ( rc ) ); +		DBGC_HDA ( sig, 0, raw->data, raw->len ); +		return rc; +	} +	DBGC ( sig, "CMS %p/%p serial number is:\n", sig, info ); +	DBGC_HDA ( sig, 0, serial.data, serial.len ); + +	/* Identify certificate */ +	cert = cms_find_issuer_serial ( sig, &issuer, &serial ); +	if ( ! cert ) { +		DBGC ( sig, "CMS %p/%p could not identify signer's " +		       "certificate\n", sig, info ); +		return -ENOENT; +	} + +	/* Append certificate to chain */ +	if ( ( rc = x509_append ( info->chain, cert ) ) != 0 ) { +		DBGC ( sig, "CMS %p/%p could not append certificate: %s\n", +		       sig, info, strerror ( rc ) ); +		return rc; +	} + +	/* Append remaining certificates to chain */ +	if ( ( rc = x509_auto_append ( info->chain, +				       sig->certificates ) ) != 0 ) { +		DBGC ( sig, "CMS %p/%p could not append certificates: %s\n", +		       sig, info, strerror ( rc ) ); +		return rc; +	} + +	return 0; +} + +/** + * Parse CMS signature digest algorithm + * + * @v sig		CMS signature + * @v info		Signer information to fill in + * @v raw		ASN.1 cursor + * @ret rc		Return status code + */ +static int cms_parse_digest_algorithm ( struct cms_signature *sig, +					struct cms_signer_info *info, +					const struct asn1_cursor *raw ) { +	struct asn1_algorithm *algorithm; +	int rc; + +	/* Identify algorithm */ +	if ( ( rc = asn1_digest_algorithm ( raw, &algorithm ) ) != 0 ) { +		DBGC ( sig, "CMS %p/%p could not identify digest algorithm: " +		       "%s\n", sig, info, strerror ( rc ) ); +		DBGC_HDA ( sig, 0, raw->data, raw->len ); +		return rc; +	} + +	/* Record digest algorithm */ +	info->digest = algorithm->digest; +	DBGC ( sig, "CMS %p/%p digest algorithm is %s\n", +	       sig, info, algorithm->name ); + +	return 0; +} + +/** + * Parse CMS signature algorithm + * + * @v sig		CMS signature + * @v info		Signer information to fill in + * @v raw		ASN.1 cursor + * @ret rc		Return status code + */ +static int cms_parse_signature_algorithm ( struct cms_signature *sig, +					   struct cms_signer_info *info, +					   const struct asn1_cursor *raw ) { +	struct asn1_algorithm *algorithm; +	int rc; + +	/* Identify algorithm */ +	if ( ( rc = asn1_pubkey_algorithm ( raw, &algorithm ) ) != 0 ) { +		DBGC ( sig, "CMS %p/%p could not identify public-key " +		       "algorithm: %s\n", sig, info, strerror ( rc ) ); +		DBGC_HDA ( sig, 0, raw->data, raw->len ); +		return rc; +	} + +	/* Record signature algorithm */ +	info->pubkey = algorithm->pubkey; +	DBGC ( sig, "CMS %p/%p public-key algorithm is %s\n", +	       sig, info, algorithm->name ); + +	return 0; +} + +/** + * Parse CMS signature value + * + * @v sig		CMS signature + * @v info		Signer information to fill in + * @v raw		ASN.1 cursor + * @ret rc		Return status code + */ +static int cms_parse_signature_value ( struct cms_signature *sig, +				       struct cms_signer_info *info, +				       const struct asn1_cursor *raw ) { +	struct asn1_cursor cursor; +	int rc; + +	/* Enter signature */ +	memcpy ( &cursor, raw, sizeof ( cursor ) ); +	if ( ( rc = asn1_enter ( &cursor, ASN1_OCTET_STRING ) ) != 0 ) { +		DBGC ( sig, "CMS %p/%p could not locate signature:\n", +		       sig, info ); +		DBGC_HDA ( sig, 0, raw->data, raw->len ); +		return rc; +	} + +	/* Record signature */ +	info->signature_len = cursor.len; +	info->signature = malloc ( info->signature_len ); +	if ( ! info->signature ) +		return -ENOMEM; +	memcpy ( info->signature, cursor.data, info->signature_len ); +	DBGC ( sig, "CMS %p/%p signature value is:\n", sig, info ); +	DBGC_HDA ( sig, 0, info->signature, info->signature_len ); + +	return 0; +} + +/** + * Parse CMS signature signer information + * + * @v sig		CMS signature + * @v info		Signer information to fill in + * @v raw		ASN.1 cursor + * @ret rc		Return status code + */ +static int cms_parse_signer_info ( struct cms_signature *sig, +				   struct cms_signer_info *info, +				   const struct asn1_cursor *raw ) { +	struct asn1_cursor cursor; +	int rc; + +	/* Enter signerInfo */ +	memcpy ( &cursor, raw, sizeof ( cursor ) ); +	asn1_enter ( &cursor, ASN1_SEQUENCE ); + +	/* Skip version */ +	asn1_skip ( &cursor, ASN1_INTEGER ); + +	/* Parse sid */ +	if ( ( rc = cms_parse_signer_identifier ( sig, info, &cursor ) ) != 0 ) +		return rc; +	asn1_skip_any ( &cursor ); + +	/* Parse digestAlgorithm */ +	if ( ( rc = cms_parse_digest_algorithm ( sig, info, &cursor ) ) != 0 ) +		return rc; +	asn1_skip_any ( &cursor ); + +	/* Skip signedAttrs, if present */ +	asn1_skip_if_exists ( &cursor, ASN1_EXPLICIT_TAG ( 0 ) ); + +	/* Parse signatureAlgorithm */ +	if ( ( rc = cms_parse_signature_algorithm ( sig, info, &cursor ) ) != 0) +		return rc; +	asn1_skip_any ( &cursor ); + +	/* Parse signature */ +	if ( ( rc = cms_parse_signature_value ( sig, info, &cursor ) ) != 0 ) +		return rc; + +	return 0; +} + +/** + * Parse CMS signature from ASN.1 data + * + * @v sig		CMS signature + * @v raw		ASN.1 cursor + * @ret rc		Return status code + */ +static int cms_parse ( struct cms_signature *sig, +		       const struct asn1_cursor *raw ) { +	struct asn1_cursor cursor; +	struct cms_signer_info *info; +	int rc; + +	/* Enter contentInfo */ +	memcpy ( &cursor, raw, sizeof ( cursor ) ); +	asn1_enter ( &cursor, ASN1_SEQUENCE ); + +	/* Parse contentType */ + +	if ( ( rc = cms_parse_content_type ( sig, &cursor ) ) != 0 ) +		return rc; +	asn1_skip_any ( &cursor ); + +	/* Enter content */ +	asn1_enter ( &cursor, ASN1_EXPLICIT_TAG ( 0 ) ); + +	/* Enter signedData */ +	asn1_enter ( &cursor, ASN1_SEQUENCE ); + +	/* Skip version */ +	asn1_skip ( &cursor, ASN1_INTEGER ); + +	/* Skip digestAlgorithms */ +	asn1_skip ( &cursor, ASN1_SET ); + +	/* Skip encapContentInfo */ +	asn1_skip ( &cursor, ASN1_SEQUENCE ); + +	/* Parse certificates */ +	if ( ( rc = cms_parse_certificates ( sig, &cursor ) ) != 0 ) +		return rc; +	asn1_skip_any ( &cursor ); + +	/* Skip crls, if present */ +	asn1_skip_if_exists ( &cursor, ASN1_EXPLICIT_TAG ( 1 ) ); + +	/* Enter signerInfos */ +	asn1_enter ( &cursor, ASN1_SET ); + +	/* Add each signerInfo.  Errors are handled by ensuring that +	 * cms_put() will always be able to free any allocated memory. +	 */ +	while ( cursor.len ) { + +		/* Allocate signer information block */ +		info = zalloc ( sizeof ( *info ) ); +		if ( ! info ) +			return -ENOMEM; +		list_add ( &info->list, &sig->info ); + +		/* Allocate certificate chain */ +		info->chain = x509_alloc_chain(); +		if ( ! info->chain ) +			return -ENOMEM; + +		/* Parse signerInfo */ +		if ( ( rc = cms_parse_signer_info ( sig, info, +						    &cursor ) ) != 0 ) +			return rc; +		asn1_skip_any ( &cursor ); +	} + +	return 0; +} + +/** + * Free CMS signature + * + * @v refcnt		Reference count + */ +static void cms_free ( struct refcnt *refcnt ) { +	struct cms_signature *sig = +		container_of ( refcnt, struct cms_signature, refcnt ); +	struct cms_signer_info *info; +	struct cms_signer_info *tmp; + +	list_for_each_entry_safe ( info, tmp, &sig->info, list ) { +		list_del ( &info->list ); +		x509_chain_put ( info->chain ); +		free ( info->signature ); +		free ( info ); +	} +	x509_chain_put ( sig->certificates ); +	free ( sig ); +} + +/** + * Create CMS signature + * + * @v data		Raw signature data + * @v len		Length of raw data + * @ret sig		CMS signature + * @ret rc		Return status code + * + * On success, the caller holds a reference to the CMS signature, and + * is responsible for ultimately calling cms_put(). + */ +int cms_signature ( const void *data, size_t len, struct cms_signature **sig ) { +	struct asn1_cursor cursor; +	int rc; + +	/* Allocate and initialise signature */ +	*sig = zalloc ( sizeof ( **sig ) ); +	if ( ! *sig ) { +		rc = -ENOMEM; +		goto err_alloc; +	} +	ref_init ( &(*sig)->refcnt, cms_free ); +	INIT_LIST_HEAD ( &(*sig)->info ); + +	/* Allocate certificate list */ +	(*sig)->certificates = x509_alloc_chain(); +	if ( ! (*sig)->certificates ) { +		rc = -ENOMEM; +		goto err_alloc_chain; +	} + +	/* Initialise cursor */ +	cursor.data = data; +	cursor.len = len; +	asn1_shrink_any ( &cursor ); + +	/* Parse signature */ +	if ( ( rc = cms_parse ( *sig, &cursor ) ) != 0 ) +		goto err_parse; + +	return 0; + + err_parse: + err_alloc_chain: +	cms_put ( *sig ); + err_alloc: +	return rc; +} + +/** + * Calculate digest of CMS-signed data + * + * @v sig		CMS signature + * @v info		Signer information + * @v data		Signed data + * @v len		Length of signed data + * @v out		Digest output + */ +static void cms_digest ( struct cms_signature *sig, +			 struct cms_signer_info *info, +			 userptr_t data, size_t len, void *out ) { +	struct digest_algorithm *digest = info->digest; +	uint8_t ctx[ digest->ctxsize ]; +	uint8_t block[ digest->blocksize ]; +	size_t offset = 0; +	size_t frag_len; + +	/* Initialise digest */ +	digest_init ( digest, ctx ); + +	/* Process data one block at a time */ +	while ( len ) { +		frag_len = len; +		if ( frag_len > sizeof ( block ) ) +			frag_len = sizeof ( block ); +		copy_from_user ( block, data, offset, frag_len ); +		digest_update ( digest, ctx, block, frag_len ); +		offset += frag_len; +		len -= frag_len; +	} + +	/* Finalise digest */ +	digest_final ( digest, ctx, out ); + +	DBGC ( sig, "CMS %p/%p digest value:\n", sig, info ); +	DBGC_HDA ( sig, 0, out, digest->digestsize ); +} + +/** + * Verify digest of CMS-signed data + * + * @v sig		CMS signature + * @v info		Signer information + * @v cert		Corresponding certificate + * @v data		Signed data + * @v len		Length of signed data + * @ret rc		Return status code + */ +static int cms_verify_digest ( struct cms_signature *sig, +			       struct cms_signer_info *info, +			       struct x509_certificate *cert, +			       userptr_t data, size_t len ) { +	struct digest_algorithm *digest = info->digest; +	struct pubkey_algorithm *pubkey = info->pubkey; +	struct x509_public_key *public_key = &cert->subject.public_key; +	uint8_t digest_out[ digest->digestsize ]; +	uint8_t ctx[ pubkey->ctxsize ]; +	int rc; + +	/* Generate digest */ +	cms_digest ( sig, info, data, len, digest_out ); + +	/* Initialise public-key algorithm */ +	if ( ( rc = pubkey_init ( pubkey, ctx, public_key->raw.data, +				  public_key->raw.len ) ) != 0 ) { +		DBGC ( sig, "CMS %p/%p could not initialise public key: %s\n", +		       sig, info, strerror ( rc ) ); +		goto err_init; +	} + +	/* Verify digest */ +	if ( ( rc = pubkey_verify ( pubkey, ctx, digest, digest_out, +				    info->signature, +				    info->signature_len ) ) != 0 ) { +		DBGC ( sig, "CMS %p/%p signature verification failed: %s\n", +		       sig, info, strerror ( rc ) ); +		goto err_verify; +	} + + err_verify: +	pubkey_final ( pubkey, ctx ); + err_init: +	return rc; +} + +/** + * Verify CMS signature signer information + * + * @v sig		CMS signature + * @v info		Signer information + * @v data		Signed data + * @v len		Length of signed data + * @v time		Time at which to validate certificates + * @v store		Certificate store, or NULL to use default + * @v root		Root certificate list, or NULL to use default + * @ret rc		Return status code + */ +static int cms_verify_signer_info ( struct cms_signature *sig, +				    struct cms_signer_info *info, +				    userptr_t data, size_t len, +				    time_t time, struct x509_chain *store, +				    struct x509_root *root ) { +	struct x509_certificate *cert; +	int rc; + +	/* Validate certificate chain */ +	if ( ( rc = x509_validate_chain ( info->chain, time, store, +					  root ) ) != 0 ) { +		DBGC ( sig, "CMS %p/%p could not validate chain: %s\n", +		       sig, info, strerror ( rc ) ); +		return rc; +	} + +	/* Extract code-signing certificate */ +	cert = x509_first ( info->chain ); +	assert ( cert != NULL ); + +	/* Check that certificate can create digital signatures */ +	if ( ! ( cert->extensions.usage.bits & X509_DIGITAL_SIGNATURE ) ) { +		DBGC ( sig, "CMS %p/%p certificate cannot create signatures\n", +		       sig, info ); +		return -EACCES_NON_SIGNING; +	} + +	/* Check that certificate can sign code */ +	if ( ! ( cert->extensions.ext_usage.bits & X509_CODE_SIGNING ) ) { +		DBGC ( sig, "CMS %p/%p certificate is not code-signing\n", +		       sig, info ); +		return -EACCES_NON_CODE_SIGNING; +	} + +	/* Verify digest */ +	if ( ( rc = cms_verify_digest ( sig, info, cert, data, len ) ) != 0 ) +		return rc; + +	return 0; +} + +/** + * Verify CMS signature + * + * @v sig		CMS signature + * @v data		Signed data + * @v len		Length of signed data + * @v name		Required common name, or NULL to check all signatures + * @v time		Time at which to validate certificates + * @v store		Certificate store, or NULL to use default + * @v root		Root certificate list, or NULL to use default + * @ret rc		Return status code + */ +int cms_verify ( struct cms_signature *sig, userptr_t data, size_t len, +		 const char *name, time_t time, struct x509_chain *store, +		 struct x509_root *root ) { +	struct cms_signer_info *info; +	struct x509_certificate *cert; +	int count = 0; +	int rc; + +	/* Verify using all signerInfos */ +	list_for_each_entry ( info, &sig->info, list ) { +		cert = x509_first ( info->chain ); +		if ( name && ( x509_check_name ( cert, name ) != 0 ) ) +			continue; +		if ( ( rc = cms_verify_signer_info ( sig, info, data, len, time, +						     store, root ) ) != 0 ) +			return rc; +		count++; +	} + +	/* Check that we have verified at least one signature */ +	if ( count == 0 ) { +		if ( name ) { +			DBGC ( sig, "CMS %p had no signatures matching name " +			       "%s\n", sig, name ); +			return -EACCES_WRONG_NAME; +		} else { +			DBGC ( sig, "CMS %p had no signatures\n", sig ); +			return -EACCES_NO_SIGNATURES; +		} +	} + +	return 0; +} diff --git a/roms/ipxe/src/crypto/crc32.c b/roms/ipxe/src/crypto/crc32.c new file mode 100644 index 00000000..cfef68c0 --- /dev/null +++ b/roms/ipxe/src/crypto/crc32.c @@ -0,0 +1,55 @@ +/* + * Little-endian CRC32 implementation. + * + * Copyright (c) 2009 Joshua Oreman <oremanj@rwcr.net>. + * + * 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 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 <ipxe/crc32.h> + +#define CRCPOLY		0xedb88320 + +/** + * Calculate 32-bit little-endian CRC checksum + * + * @v seed	Initial value + * @v data	Data to checksum + * @v len	Length of data + * + * Usually @a seed is initially zero or all one bits, depending on the + * protocol. To continue a CRC checksum over multiple calls, pass the + * return value from one call as the @a seed parameter to the next. + */ +u32 crc32_le ( u32 seed, const void *data, size_t len ) +{ +	u32 crc = seed; +	const u8 *src = data; +	u32 mult; +	int i; + +	while ( len-- ) { +		crc ^= *src++; +		for ( i = 0; i < 8; i++ ) { +			mult = ( crc & 1 ) ? CRCPOLY : 0; +			crc = ( crc >> 1 ) ^ mult; +		} +	} + +	return crc; +} diff --git a/roms/ipxe/src/crypto/crypto_null.c b/roms/ipxe/src/crypto/crypto_null.c new file mode 100644 index 00000000..ba05f726 --- /dev/null +++ b/roms/ipxe/src/crypto/crypto_null.c @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2007 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 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 ); + +/** + * @file + * + * Null crypto algorithm + */ + +#include <string.h> +#include <ipxe/crypto.h> + +static void digest_null_init ( void *ctx __unused ) { +	/* Do nothing */ +} + +static void digest_null_update ( void *ctx __unused, const void *src __unused, +				 size_t len __unused ) { +	/* Do nothing */ +} + +static void digest_null_final ( void *ctx __unused, void *out __unused ) { +	/* Do nothing */ +} + +struct digest_algorithm digest_null = { +	.name = "null", +	.ctxsize = 0, +	.blocksize = 1, +	.digestsize = 0, +	.init = digest_null_init, +	.update = digest_null_update, +	.final = digest_null_final, +}; + +static int cipher_null_setkey ( void *ctx __unused, const void *key __unused, +				size_t keylen __unused ) { +	/* Do nothing */ +	return 0; +} + +static void cipher_null_setiv ( void *ctx __unused, +				const void *iv __unused ) { +	/* Do nothing */ +} + +static void cipher_null_encrypt ( void *ctx __unused, const void *src, +				  void *dst, size_t len ) { +	memcpy ( dst, src, len ); +} + +static void cipher_null_decrypt ( void *ctx __unused, const void *src, +				  void *dst, size_t len ) { +	memcpy ( dst, src, len ); +} + +struct cipher_algorithm cipher_null = { +	.name = "null", +	.ctxsize = 0, +	.blocksize = 1, +	.setkey = cipher_null_setkey, +	.setiv = cipher_null_setiv, +	.encrypt = cipher_null_encrypt, +	.decrypt = cipher_null_decrypt, +}; + +static int pubkey_null_init ( void *ctx __unused, const void *key __unused, +			      size_t key_len __unused ) { +	return 0; +} + +static size_t pubkey_null_max_len ( void *ctx __unused ) { +	return 0; +} + +static int pubkey_null_encrypt ( void *ctx __unused, +				 const void *plaintext __unused, +				 size_t plaintext_len __unused, +				 void *ciphertext __unused ) { +	return 0; +} + +static int pubkey_null_decrypt ( void *ctx __unused, +				 const void *ciphertext __unused, +				 size_t ciphertext_len __unused, +				 void *plaintext __unused ) { +	return 0; +} + +static int pubkey_null_sign ( void *ctx __unused, +			      struct digest_algorithm *digest __unused, +			      const void *value __unused, +			      void *signature __unused ) { +	return 0; +} + +static int pubkey_null_verify ( void *ctx __unused, +				struct digest_algorithm *digest __unused, +				const void *value __unused, +				const void *signature __unused , +				size_t signature_len __unused ) { +	return 0; +} + +static void pubkey_null_final ( void *ctx __unused ) { +	/* Do nothing */ +} + +struct pubkey_algorithm pubkey_null = { +	.name = "null", +	.ctxsize = 0, +	.init = pubkey_null_init, +	.max_len = pubkey_null_max_len, +	.encrypt = pubkey_null_encrypt, +	.decrypt = pubkey_null_decrypt, +	.sign = pubkey_null_sign, +	.verify = pubkey_null_verify, +	.final = pubkey_null_final, +}; diff --git a/roms/ipxe/src/crypto/deflate.c b/roms/ipxe/src/crypto/deflate.c new file mode 100644 index 00000000..91a48996 --- /dev/null +++ b/roms/ipxe/src/crypto/deflate.c @@ -0,0 +1,1045 @@ +/* + * Copyright (C) 2014 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 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 <string.h> +#include <strings.h> +#include <errno.h> +#include <assert.h> +#include <ctype.h> +#include <ipxe/uaccess.h> +#include <ipxe/deflate.h> + +/** @file + * + * DEFLATE decompression algorithm + * + * This file implements the decompression half of the DEFLATE + * algorithm specified in RFC 1951. + * + * Portions of this code are derived from wimboot's xca.c. + * + */ + +/** + * Byte reversal table + * + * For some insane reason, the DEFLATE format stores some values in + * bit-reversed order. + */ +static uint8_t deflate_reverse[256]; + +/** Literal/length base values + * + * We include entries only for literal/length codes 257-284.  Code 285 + * does not fit the pattern (it represents a length of 258; following + * the pattern from the earlier codes would give a length of 259), and + * has no extra bits.  Codes 286-287 are invalid, but can occur.  We + * treat any code greater than 284 as meaning "length 285, no extra + * bits". + */ +static uint8_t deflate_litlen_base[28]; + +/** Distance base values + * + * We include entries for all possible codes 0-31, avoiding the need + * to check for undefined codes 30 and 31 before performing the + * lookup.  Codes 30 and 31 are never initialised, and will therefore + * be treated as meaning "14 extra bits, base distance 0". + */ +static uint16_t deflate_distance_base[32]; + +/** Code length map */ +static uint8_t deflate_codelen_map[19] = { +	16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 +}; + +/** Static Huffman alphabet length patterns */ +static struct deflate_static_length_pattern deflate_static_length_patterns[] = { +	/* Literal/length code lengths */ +	{ 0x88, ( ( ( 143 -   0 ) + 1 ) / 2 ) }, +	{ 0x99, ( ( ( 255 - 144 ) + 1 ) / 2 ) }, +	{ 0x77, ( ( ( 279 - 256 ) + 1 ) / 2 ) }, +	{ 0x88, ( ( ( 287 - 280 ) + 1 ) / 2 ) }, +	/* Distance code lengths */ +	{ 0x55, ( ( (  31 -   0 ) + 1 ) / 2 ) }, +	/* End marker */ +	{ 0, 0 } +}; + +/** + * Transcribe binary value (for debugging) + * + * @v value		Value + * @v bits		Length of value (in bits) + * @ret string		Transcribed value + */ +static const char * deflate_bin ( unsigned long value, unsigned int bits ) { +	static char buf[ ( 8 * sizeof ( value ) ) + 1 /* NUL */ ]; +	char *out = buf; + +	/* Sanity check */ +	assert ( bits < sizeof ( buf ) ); + +	/* Transcribe value */ +	while ( bits-- ) +		*(out++) = ( ( value & ( 1 << bits ) ) ? '1' : '0' ); +	*out = '\0'; + +	return buf; +} + +/** + * Set Huffman symbol length + * + * @v deflate		Decompressor + * @v index		Index within lengths + * @v bits		Symbol length (in bits) + */ +static void deflate_set_length ( struct deflate *deflate, unsigned int index, +				 unsigned int bits ) { + +	deflate->lengths[ index / 2 ] |= ( bits << ( 4 * ( index % 2 ) ) ); +} + +/** + * Get Huffman symbol length + * + * @v deflate		Decompressor + * @v index		Index within lengths + * @ret bits		Symbol length (in bits) + */ +static unsigned int deflate_length ( struct deflate *deflate, +				     unsigned int index ) { + +	return ( ( deflate->lengths[ index / 2 ] >> ( 4 * ( index % 2 ) ) ) +		 & 0x0f ); +} + +/** + * Determine Huffman alphabet name (for debugging) + * + * @v deflate		Decompressor + * @v alphabet		Huffman alphabet + * @ret name		Alphabet name + */ +static const char * deflate_alphabet_name ( struct deflate *deflate, +					    struct deflate_alphabet *alphabet ){ + +	if ( alphabet == &deflate->litlen ) { +		return "litlen"; +	} else if ( alphabet == &deflate->distance_codelen ) { +		return "distance/codelen"; +	} else { +		return "<UNKNOWN>"; +	} +} + +/** + * Dump Huffman alphabet (for debugging) + * + * @v deflate		Decompressor + * @v alphabet		Huffman alphabet + */ +static void deflate_dump_alphabet ( struct deflate *deflate, +				    struct deflate_alphabet *alphabet ) { +	struct deflate_huf_symbols *huf_sym; +	unsigned int bits; +	unsigned int huf; +	unsigned int i; + +	/* Do nothing unless debugging is enabled */ +	if ( ! DBG_EXTRA ) +		return; + +	/* Dump symbol table for each utilised length */ +	for ( bits = 1 ; bits <= ( sizeof ( alphabet->huf ) / +				   sizeof ( alphabet->huf[0] ) ) ; bits++ ) { +		huf_sym = &alphabet->huf[ bits - 1 ]; +		if ( huf_sym->freq == 0 ) +			continue; +		huf = ( huf_sym->start >> huf_sym->shift ); +		DBGC2 ( alphabet, "DEFLATE %p \"%s\" length %d start \"%s\" " +			"freq %d:", deflate, +			deflate_alphabet_name ( deflate, alphabet ), bits, +			deflate_bin ( huf, huf_sym->bits ), huf_sym->freq ); +		for ( i = 0 ; i < huf_sym->freq ; i++ ) { +			DBGC2 ( alphabet, " %03x", +				huf_sym->raw[ huf + i ] ); +		} +		DBGC2 ( alphabet, "\n" ); +	} + +	/* Dump quick lookup table */ +	DBGC2 ( alphabet, "DEFLATE %p \"%s\" quick lookup:", deflate, +		deflate_alphabet_name ( deflate, alphabet ) ); +	for ( i = 0 ; i < ( sizeof ( alphabet->lookup ) / +			    sizeof ( alphabet->lookup[0] ) ) ; i++ ) { +		DBGC2 ( alphabet, " %d", ( alphabet->lookup[i] + 1 ) ); +	} +	DBGC2 ( alphabet, "\n" ); +} + +/** + * Construct Huffman alphabet + * + * @v deflate		Decompressor + * @v alphabet		Huffman alphabet + * @v count		Number of symbols + * @v offset		Starting offset within length table + * @ret rc		Return status code + */ +static int deflate_alphabet ( struct deflate *deflate, +			      struct deflate_alphabet *alphabet, +			      unsigned int count, unsigned int offset ) { +	struct deflate_huf_symbols *huf_sym; +	unsigned int huf; +	unsigned int cum_freq; +	unsigned int bits; +	unsigned int raw; +	unsigned int adjustment; +	unsigned int prefix; +	int complete; + +	/* Clear symbol table */ +	memset ( alphabet->huf, 0, sizeof ( alphabet->huf ) ); + +	/* Count number of symbols with each Huffman-coded length */ +	for ( raw = 0 ; raw < count ; raw++ ) { +		bits = deflate_length ( deflate, ( raw + offset ) ); +		if ( bits ) +			alphabet->huf[ bits - 1 ].freq++; +	} + +	/* Populate Huffman-coded symbol table */ +	huf = 0; +	cum_freq = 0; +	for ( bits = 1 ; bits <= ( sizeof ( alphabet->huf ) / +				   sizeof ( alphabet->huf[0] ) ) ; bits++ ) { +		huf_sym = &alphabet->huf[ bits - 1 ]; +		huf_sym->bits = bits; +		huf_sym->shift = ( 16 - bits ); +		huf_sym->start = ( huf << huf_sym->shift ); +		huf_sym->raw = &alphabet->raw[cum_freq]; +		huf += huf_sym->freq; +		if ( huf > ( 1U << bits ) ) { +			DBGC ( alphabet, "DEFLATE %p \"%s\" has too many " +			       "symbols with lengths <=%d\n", deflate, +			       deflate_alphabet_name ( deflate, alphabet ), +			       bits ); +			return -EINVAL; +		} +		huf <<= 1; +		cum_freq += huf_sym->freq; +	} +	complete = ( huf == ( 1U << bits ) ); + +	/* Populate raw symbol table */ +	for ( raw = 0 ; raw < count ; raw++ ) { +		bits = deflate_length ( deflate, ( raw + offset ) ); +		if ( bits ) { +			huf_sym = &alphabet->huf[ bits - 1 ]; +			*(huf_sym->raw++) = raw; +		} +	} + +	/* Adjust Huffman-coded symbol table raw pointers and populate +	 * quick lookup table. +	 */ +	for ( bits = 1 ; bits <= ( sizeof ( alphabet->huf ) / +				   sizeof ( alphabet->huf[0] ) ) ; bits++ ) { +		huf_sym = &alphabet->huf[ bits - 1 ]; + +		/* Adjust raw pointer */ +		huf_sym->raw -= huf_sym->freq; /* Reset to first symbol */ +		adjustment = ( huf_sym->start >> huf_sym->shift ); +		huf_sym->raw -= adjustment; /* Adjust for quick indexing */ + +		/* Populate quick lookup table */ +		for ( prefix = ( huf_sym->start >> DEFLATE_HUFFMAN_QL_SHIFT ) ; +		      prefix < ( 1 << DEFLATE_HUFFMAN_QL_BITS ) ; prefix++ ) { +			alphabet->lookup[prefix] = ( bits - 1 ); +		} +	} + +	/* Dump alphabet (for debugging) */ +	deflate_dump_alphabet ( deflate, alphabet ); + +	/* Check that there are no invalid codes */ +	if ( ! complete ) { +		DBGC ( alphabet, "DEFLATE %p \"%s\" is incomplete\n", deflate, +		       deflate_alphabet_name ( deflate, alphabet ) ); +		return -EINVAL; +	} + +	return 0; +} + +/** + * Attempt to accumulate bits from input stream + * + * @v deflate		Decompressor + * @v in		Compressed input data + * @v target		Number of bits to accumulate + * @ret excess		Number of excess bits accumulated (may be negative) + */ +static int deflate_accumulate ( struct deflate *deflate, +				struct deflate_chunk *in, +				unsigned int target ) { +	uint8_t byte; + +	while ( deflate->bits < target ) { + +		/* Check for end of input */ +		if ( in->offset >= in->len ) +			break; + +		/* Acquire byte from input */ +		copy_from_user ( &byte, in->data, in->offset++, +				 sizeof ( byte ) ); +		deflate->accumulator = ( deflate->accumulator | +					 ( byte << deflate->bits ) ); +		deflate->rotalumucca = ( deflate->rotalumucca | +					 ( deflate_reverse[byte] << +					   ( 24 - deflate->bits ) ) ); +		deflate->bits += 8; + +		/* Sanity check */ +		assert ( deflate->bits <= +			 ( 8 * sizeof ( deflate->accumulator ) ) ); +	} + +	return ( deflate->bits - target ); +} + +/** + * Consume accumulated bits from the input stream + * + * @v deflate		Decompressor + * @v count		Number of accumulated bits to consume + * @ret data		Consumed bits + */ +static int deflate_consume ( struct deflate *deflate, unsigned int count ) { +	int data; + +	/* Sanity check */ +	assert ( count <= deflate->bits ); + +	/* Extract data and consume bits */ +	data = ( deflate->accumulator & ( ( 1 << count ) - 1 ) ); +	deflate->accumulator >>= count; +	deflate->rotalumucca <<= count; +	deflate->bits -= count; + +	return data; +} + +/** + * Attempt to extract a fixed number of bits from input stream + * + * @v deflate		Decompressor + * @v in		Compressed input data + * @v target		Number of bits to extract + * @ret data		Extracted bits (or negative if not yet accumulated) + */ +static int deflate_extract ( struct deflate *deflate, struct deflate_chunk *in, +			     unsigned int target ) { +	int excess; +	int data; + +	/* Return immediately if we are attempting to extract zero bits */ +	if ( target == 0 ) +		return 0; + +	/* Attempt to accumulate bits */ +	excess = deflate_accumulate ( deflate, in, target ); +	if ( excess < 0 ) +		return excess; + +	/* Extract data and consume bits */ +	data = deflate_consume ( deflate, target ); +	DBGCP ( deflate, "DEFLATE %p extracted %s = %#x = %d\n", deflate, +		deflate_bin ( data, target ), data, data ); + +	return data; +} + +/** + * Attempt to decode a Huffman-coded symbol from input stream + * + * @v deflate		Decompressor + * @v in		Compressed input data + * @v alphabet		Huffman alphabet + * @ret code		Raw code (or negative if not yet accumulated) + */ +static int deflate_decode ( struct deflate *deflate, +			    struct deflate_chunk *in, +			    struct deflate_alphabet *alphabet ) { +	struct deflate_huf_symbols *huf_sym; +	uint16_t huf; +	unsigned int lookup_index; +	int excess; +	unsigned int raw; + +	/* Attempt to accumulate maximum required number of bits. +	 * There may be fewer bits than this remaining in the stream, +	 * even if the stream still contains some complete +	 * Huffman-coded symbols. +	 */ +	deflate_accumulate ( deflate, in, DEFLATE_HUFFMAN_BITS ); + +	/* Normalise the bit-reversed accumulated value to 16 bits */ +	huf = ( deflate->rotalumucca >> 16 ); + +	/* Find symbol set for this length */ +	lookup_index = ( huf >> DEFLATE_HUFFMAN_QL_SHIFT ); +	huf_sym = &alphabet->huf[ alphabet->lookup[ lookup_index ] ]; +	while ( huf < huf_sym->start ) +		huf_sym--; + +	/* Calculate number of excess bits, and return if not yet complete */ +	excess = ( deflate->bits - huf_sym->bits ); +	if ( excess < 0 ) +		return excess; + +	/* Consume bits */ +	deflate_consume ( deflate, huf_sym->bits ); + +	/* Look up raw symbol */ +	raw = huf_sym->raw[ huf >> huf_sym->shift ]; +	DBGCP ( deflate, "DEFLATE %p decoded %s = %#x = %d\n", deflate, +		deflate_bin ( ( huf >> huf_sym->shift ), huf_sym->bits ), +		raw, raw ); + +	return raw; +} + +/** + * Discard bits up to the next byte boundary + * + * @v deflate		Decompressor + */ +static void deflate_discard_to_byte ( struct deflate *deflate ) { + +	deflate_consume ( deflate, ( deflate->bits & 7 ) ); +} + +/** + * Copy data to output buffer (if available) + * + * @v out		Output data buffer + * @v start		Source data + * @v offset		Starting offset within source data + * @v len		Length to copy + */ +static void deflate_copy ( struct deflate_chunk *out, +			   userptr_t start, size_t offset, size_t len ) { +	size_t out_offset = out->offset; +	size_t copy_len; + +	/* Copy data one byte at a time, to allow for overlap */ +	if ( out_offset < out->len ) { +		copy_len = ( out->len - out_offset ); +		if ( copy_len > len ) +			copy_len = len; +		while ( copy_len-- ) { +			memcpy_user ( out->data, out_offset++, +				      start, offset++, 1 ); +		} +	} +	out->offset += len; +} + +/** + * Inflate compressed data + * + * @v deflate		Decompressor + * @v in		Compressed input data + * @v out		Output data buffer + * @ret rc		Return status code + * + * The caller can use deflate_finished() to determine whether a + * successful return indicates that the decompressor is merely waiting + * for more input. + * + * Data will not be written beyond the specified end of the output + * data buffer, but the offset within the output data buffer will be + * updated to reflect the amount that should have been written.  The + * caller can use this to find the length of the decompressed data + * before allocating the output data buffer. + */ +int deflate_inflate ( struct deflate *deflate, +		      struct deflate_chunk *in, +		      struct deflate_chunk *out ) { + +	/* This could be implemented more neatly if gcc offered a +	 * means for enforcing tail recursion. +	 */ +	if ( deflate->resume ) { +		goto *(deflate->resume); +	} else switch ( deflate->format ) { +		case DEFLATE_RAW:	goto block_header; +		case DEFLATE_ZLIB:	goto zlib_header; +		default:		assert ( 0 ); +	} + + zlib_header: { +		int header; +		int cm; + +		/* Extract header */ +		header = deflate_extract ( deflate, in, ZLIB_HEADER_BITS ); +		if ( header < 0 ) { +			deflate->resume = &&zlib_header; +			return 0; +		} + +		/* Parse header */ +		cm = ( ( header >> ZLIB_HEADER_CM_LSB ) & ZLIB_HEADER_CM_MASK ); +		if ( cm != ZLIB_HEADER_CM_DEFLATE ) { +			DBGC ( deflate, "DEFLATE %p unsupported ZLIB " +			       "compression method %d\n", deflate, cm ); +			return -ENOTSUP; +		} +		if ( header & ( 1 << ZLIB_HEADER_FDICT_BIT ) ) { +			DBGC ( deflate, "DEFLATE %p unsupported ZLIB preset " +			       "dictionary\n", deflate ); +			return -ENOTSUP; +		} + +		/* Process first block header */ +		goto block_header; +	} + + block_header: { +		int header; +		int bfinal; +		int btype; + +		/* Extract block header */ +		header = deflate_extract ( deflate, in, DEFLATE_HEADER_BITS ); +		if ( header < 0 ) { +			deflate->resume = &&block_header; +			return 0; +		} + +		/* Parse header */ +		deflate->header = header; +		bfinal = ( header & ( 1 << DEFLATE_HEADER_BFINAL_BIT ) ); +		btype = ( header >> DEFLATE_HEADER_BTYPE_LSB ); +		DBGC ( deflate, "DEFLATE %p found %sblock type %#x\n", +		       deflate, ( bfinal ? "final " : "" ), btype ); +		switch ( btype ) { +		case DEFLATE_HEADER_BTYPE_LITERAL: +			goto literal_block; +		case DEFLATE_HEADER_BTYPE_STATIC: +			goto static_block; +		case DEFLATE_HEADER_BTYPE_DYNAMIC: +			goto dynamic_block; +		default: +			DBGC ( deflate, "DEFLATE %p unsupported block type " +			       "%#x\n", deflate, btype ); +			return -ENOTSUP; +		} +	} + + literal_block: { + +		/* Discard any bits up to the next byte boundary */ +		deflate_discard_to_byte ( deflate ); +	} + + literal_len: { +		int len; + +		/* Extract LEN field */ +		len = deflate_extract ( deflate, in, DEFLATE_LITERAL_LEN_BITS ); +		if ( len < 0 ) { +			deflate->resume = &&literal_len; +			return 0; +		} + +		/* Record length of literal data */ +		deflate->remaining = len; +		DBGC2 ( deflate, "DEFLATE %p literal block length %#04zx\n", +			deflate, deflate->remaining ); +	} + + literal_nlen: { +		int nlen; + +		/* Extract NLEN field */ +		nlen = deflate_extract ( deflate, in, DEFLATE_LITERAL_LEN_BITS); +		if ( nlen < 0 ) { +			deflate->resume = &&literal_nlen; +			return 0; +		} + +		/* Verify NLEN */ +		if ( ( ( deflate->remaining ^ ~nlen ) & +		       ( ( 1 << DEFLATE_LITERAL_LEN_BITS ) - 1 ) ) != 0 ) { +			DBGC ( deflate, "DEFLATE %p invalid len/nlen " +			       "%#04zx/%#04x\n", deflate, +			       deflate->remaining, nlen ); +			return -EINVAL; +		} +	} + + literal_data: { +		size_t in_remaining; +		size_t len; + +		/* Calculate available amount of literal data */ +		in_remaining = ( in->len - in->offset ); +		len = deflate->remaining; +		if ( len > in_remaining ) +			len = in_remaining; + +		/* Copy data to output buffer */ +		deflate_copy ( out, in->data, in->offset, len ); + +		/* Consume data from input buffer */ +		in->offset += len; +		deflate->remaining -= len; + +		/* Finish processing if we are blocked */ +		if ( deflate->remaining ) { +			deflate->resume = &&literal_data; +			return 0; +		} + +		/* Otherwise, finish block */ +		goto block_done; +	} + + static_block: { +		struct deflate_static_length_pattern *pattern; +		uint8_t *lengths = deflate->lengths; + +		/* Construct static Huffman lengths as per RFC 1950 */ +		for ( pattern = deflate_static_length_patterns ; +		      pattern->count ; pattern++ ) { +			memset ( lengths, pattern->fill, pattern->count ); +			lengths += pattern->count; +		} +		deflate->litlen_count = 288; +		deflate->distance_count = 32; +		goto construct_alphabets; +	} + + dynamic_block: + + dynamic_header: { +		int header; +		unsigned int hlit; +		unsigned int hdist; +		unsigned int hclen; + +		/* Extract block header */ +		header = deflate_extract ( deflate, in, DEFLATE_DYNAMIC_BITS ); +		if ( header < 0 ) { +			deflate->resume = &&dynamic_header; +			return 0; +		} + +		/* Parse header */ +		hlit = ( ( header >> DEFLATE_DYNAMIC_HLIT_LSB ) & +			 DEFLATE_DYNAMIC_HLIT_MASK ); +		hdist = ( ( header >> DEFLATE_DYNAMIC_HDIST_LSB ) & +			  DEFLATE_DYNAMIC_HDIST_MASK ); +		hclen = ( ( header >> DEFLATE_DYNAMIC_HCLEN_LSB ) & +			  DEFLATE_DYNAMIC_HCLEN_MASK ); +		deflate->litlen_count = ( hlit + 257 ); +		deflate->distance_count = ( hdist + 1 ); +		deflate->length_index = 0; +		deflate->length_target = ( hclen + 4 ); +		DBGC2 ( deflate, "DEFLATE %p dynamic block %d codelen, %d " +			"litlen, %d distance\n", deflate, +			deflate->length_target, deflate->litlen_count, +			deflate->distance_count ); + +		/* Prepare for decoding code length code lengths */ +		memset ( &deflate->lengths, 0, sizeof ( deflate->lengths ) ); +	} + + dynamic_codelen: { +		int len; +		unsigned int index; +		int rc; + +		/* Extract all code lengths */ +		while ( deflate->length_index < deflate->length_target ) { + +			/* Extract code length length */ +			len = deflate_extract ( deflate, in, +						DEFLATE_CODELEN_BITS ); +			if ( len < 0 ) { +				deflate->resume = &&dynamic_codelen; +				return 0; +			} + +			/* Store code length */ +			index = deflate_codelen_map[deflate->length_index++]; +			deflate_set_length ( deflate, index, len ); +			DBGCP ( deflate, "DEFLATE %p codelen for %d is %d\n", +				deflate, index, len ); +		} + +		/* Generate code length alphabet */ +		if ( ( rc = deflate_alphabet ( deflate, +					       &deflate->distance_codelen, +					       ( DEFLATE_CODELEN_MAX_CODE + 1 ), +					       0 ) ) != 0 ) +			return rc; + +		/* Prepare for decoding literal/length/distance code lengths */ +		memset ( &deflate->lengths, 0, sizeof ( deflate->lengths ) ); +		deflate->length_index = 0; +		deflate->length_target = ( deflate->litlen_count + +					   deflate->distance_count ); +		deflate->length = 0; +	} + + dynamic_litlen_distance: { +		int len; +		int index; + +		/* Decode literal/length/distance code length */ +		len = deflate_decode ( deflate, in, &deflate->distance_codelen); +		if ( len < 0 ) { +			deflate->resume = &&dynamic_litlen_distance; +			return 0; +		} + +		/* Prepare for extra bits */ +		if ( len < 16 ) { +			deflate->length = len; +			deflate->extra_bits = 0; +			deflate->dup_len = 1; +		} else { +			static const uint8_t dup_len[3] = { 3, 3, 11 }; +			static const uint8_t extra_bits[3] = { 2, 3, 7 }; +			index = ( len - 16 ); +			deflate->dup_len = dup_len[index]; +			deflate->extra_bits = extra_bits[index]; +			if ( index ) +				deflate->length = 0; +		} +	} + + dynamic_litlen_distance_extra: { +		int extra; +		unsigned int dup_len; + +		/* Extract extra bits */ +		extra = deflate_extract ( deflate, in, deflate->extra_bits ); +		if ( extra < 0 ) { +			deflate->resume = &&dynamic_litlen_distance_extra; +			return 0; +		} + +		/* Store code lengths */ +		dup_len = ( deflate->dup_len + extra ); +		while ( ( deflate->length_index < deflate->length_target ) && +			dup_len-- ) { +			deflate_set_length ( deflate, deflate->length_index++, +					     deflate->length ); +		} + +		/* Process next literal/length or distance code +		 * length, if more are required. +		 */ +		if ( deflate->length_index < deflate->length_target ) +			goto dynamic_litlen_distance; + +		/* Construct alphabets */ +		goto construct_alphabets; +	} + + construct_alphabets: { +		unsigned int distance_offset = deflate->litlen_count; +		unsigned int distance_count = deflate->distance_count; +		int rc; + +		/* Generate literal/length alphabet */ +		if ( ( rc = deflate_alphabet ( deflate, &deflate->litlen, +					       deflate->litlen_count, 0 ) ) !=0) +			return rc; + +		/* Handle degenerate case of a single distance code +		 * (for which it is impossible to construct a valid, +		 * complete Huffman alphabet).  RFC 1951 states: +		 * +		 *   If only one distance code is used, it is encoded +		 *   using one bit, not zero bits; in this case there +		 *   is a single code length of one, with one unused +		 *   code.  One distance code of zero bits means that +		 *   there are no distance codes used at all (the data +		 *   is all literals). +		 * +		 * If we have only a single distance code, then we +		 * instead use two distance codes both with length 1. +		 * This results in a valid Huffman alphabet.  The code +		 * "0" will mean distance code 0 (which is either +		 * correct or irrelevant), and the code "1" will mean +		 * distance code 1 (which is always irrelevant). +		 */ +		if ( deflate->distance_count == 1 ) { + +			deflate->lengths[0] = 0x11; +			distance_offset = 0; +			distance_count = 2; +		} + +		/* Generate distance alphabet */ +		if ( ( rc = deflate_alphabet ( deflate, +					       &deflate->distance_codelen, +					       distance_count, +					       distance_offset ) ) != 0 ) +			return rc; +	} + + lzhuf_litlen: { +		int code; +		uint8_t byte; +		unsigned int extra; +		unsigned int bits; + +		/* Decode Huffman codes */ +		while ( 1 ) { + +			/* Decode Huffman code */ +			code = deflate_decode ( deflate, in, &deflate->litlen ); +			if ( code < 0 ) { +				deflate->resume = &&lzhuf_litlen; +				return 0; +			} + +			/* Handle according to code type */ +			if ( code < DEFLATE_LITLEN_END ) { + +				/* Literal value: copy to output buffer */ +				byte = code; +				DBGCP ( deflate, "DEFLATE %p literal %#02x " +					"('%c')\n", deflate, byte, +					( isprint ( byte ) ? byte : '.' ) ); +				deflate_copy ( out, virt_to_user ( &byte ), 0, +					       sizeof ( byte ) ); + +			} else if ( code == DEFLATE_LITLEN_END ) { + +				/* End of block */ +				goto block_done; + +			} else { + +				/* Length code: process extra bits */ +				extra = ( code - DEFLATE_LITLEN_END - 1 ); +				if ( extra < 28 ) { +					bits = ( extra / 4 ); +					if ( bits ) +						bits--; +					deflate->extra_bits = bits; +					deflate->dup_len = +						deflate_litlen_base[extra]; +				} else { +					deflate->extra_bits = 0; +					deflate->dup_len = 258; +				} +				goto lzhuf_litlen_extra; +			} +		} +	} + + lzhuf_litlen_extra: { +		int extra; + +		/* Extract extra bits */ +		extra = deflate_extract ( deflate, in, deflate->extra_bits ); +		if ( extra < 0 ) { +			deflate->resume = &&lzhuf_litlen_extra; +			return 0; +		} + +		/* Update duplicate length */ +		deflate->dup_len += extra; +	} + + lzhuf_distance: { +		int code; +		unsigned int extra; +		unsigned int bits; + +		/* Decode Huffman code */ +		code = deflate_decode ( deflate, in, +					&deflate->distance_codelen ); +		if ( code < 0 ) { +			deflate->resume = &&lzhuf_distance; +			return 0; +		} + +		/* Process extra bits */ +		extra = code; +		bits = ( extra / 2 ); +		if ( bits ) +			bits--; +		deflate->extra_bits = bits; +		deflate->dup_distance = deflate_distance_base[extra]; +	} + + lzhuf_distance_extra: { +		int extra; +		size_t dup_len; +		size_t dup_distance; + +		/* Extract extra bits */ +		extra = deflate_extract ( deflate, in, deflate->extra_bits ); +		if ( extra < 0 ) { +			deflate->resume = &&lzhuf_distance_extra; +			return 0; +		} + +		/* Update duplicate distance */ +		dup_distance = ( deflate->dup_distance + extra ); +		dup_len = deflate->dup_len; +		DBGCP ( deflate, "DEFLATE %p duplicate length %zd distance " +			"%zd\n", deflate, dup_len, dup_distance ); + +		/* Sanity check */ +		if ( dup_distance > out->offset ) { +			DBGC ( deflate, "DEFLATE %p bad distance %zd (max " +			       "%zd)\n", deflate, dup_distance, out->offset ); +			return -EINVAL; +		} + +		/* Copy data, allowing for overlap */ +		deflate_copy ( out, out->data, ( out->offset - dup_distance ), +			       dup_len ); + +		/* Process next literal/length symbol */ +		goto lzhuf_litlen; +	} + + block_done: { + +		DBGCP ( deflate, "DEFLATE %p end of block\n", deflate ); + +		/* If this was not the final block, process next block header */ +		if ( ! ( deflate->header & ( 1 << DEFLATE_HEADER_BFINAL_BIT ) )) +			goto block_header; + +		/* Otherwise, process footer (if any) */ +		switch ( deflate->format ) { +		case DEFLATE_RAW:	goto finished; +		case DEFLATE_ZLIB:	goto zlib_footer; +		default:		assert ( 0 ); +		} +	} + + zlib_footer: { + +		/* Discard any bits up to the next byte boundary */ +		deflate_discard_to_byte ( deflate ); +	} + + zlib_adler32: { +		int excess; + +		/* Accumulate the 32 bits of checksum.  We don't check +		 * the value, stop processing immediately afterwards, +		 * and so don't have to worry about the nasty corner +		 * cases involved in calling deflate_extract() to +		 * obtain a full 32 bits. +		 */ +		excess = deflate_accumulate ( deflate, in, ZLIB_ADLER32_BITS ); +		if ( excess < 0 ) { +			deflate->resume = &&zlib_adler32; +			return 0; +		} + +		/* Finish processing */ +		goto finished; +	} + + finished: { +		/* Mark as finished and terminate */ +		DBGCP ( deflate, "DEFLATE %p finished\n", deflate ); +		deflate->resume = NULL; +		return 0; +	} +} + +/** + * Initialise decompressor + * + * @v deflate		Decompressor + * @v format		Compression format code + */ +void deflate_init ( struct deflate *deflate, enum deflate_format format ) { +	static int global_init_done; +	uint8_t i; +	uint8_t bit; +	uint8_t byte; +	unsigned int base; +	unsigned int bits; + +	/* Perform global initialisation if required */ +	if ( ! global_init_done ) { + +		/* Initialise byte reversal table */ +		for ( i = 255 ; i ; i-- ) { +			for ( bit = 1, byte = 0 ; bit ; bit <<= 1 ) { +				byte <<= 1; +				if ( i & bit ) +					byte |= 1; +			} +			deflate_reverse[i] = byte; +		} + +		/* Initialise literal/length extra bits table */ +		base = 3; +		for ( i = 0 ; i < 28 ; i++ ) { +			bits = ( i / 4 ); +			if ( bits ) +				bits--; +			deflate_litlen_base[i] = base; +			base += ( 1 << bits ); +		} +		assert ( base == 259 ); /* sic */ + +		/* Initialise distance extra bits table */ +		base = 1; +		for ( i = 0 ; i < 30 ; i++ ) { +			bits = ( i / 2 ); +			if ( bits ) +				bits--; +			deflate_distance_base[i] = base; +			base += ( 1 << bits ); +		} +		assert ( base == 32769 ); + +		/* Record global initialisation as complete */ +		global_init_done = 1; +	} + +	/* Initialise structure */ +	memset ( deflate, 0, sizeof ( *deflate ) ); +	deflate->format = format; +} diff --git a/roms/ipxe/src/crypto/drbg.c b/roms/ipxe/src/crypto/drbg.c new file mode 100644 index 00000000..9e0175d2 --- /dev/null +++ b/roms/ipxe/src/crypto/drbg.c @@ -0,0 +1,427 @@ +/* + * 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 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 ); + +/** @file + * + * DRBG mechanism + * + * This mechanism is designed to comply with ANS X9.82 Part 3-2007 + * Section 9.  This standard is not freely available, but most of the + * text appears to be shared with NIST SP 800-90, which can be + * downloaded from + * + *     http://csrc.nist.gov/publications/nistpubs/800-90/SP800-90revised_March2007.pdf + * + * Where possible, references are given to both documents.  In the + * case of any disagreement, ANS X9.82 takes priority over NIST SP + * 800-90.  (In particular, note that some algorithms that are + * Approved by NIST SP 800-90 are not Approved by ANS X9.82.) + */ + +#include <stdint.h> +#include <string.h> +#include <errno.h> +#include <assert.h> +#include <ipxe/entropy.h> +#include <ipxe/drbg.h> + +/** + * Instantiate DRBG + * + * @v state		Algorithm state to be initialised + * @v personal		Personalisation string + * @v personal_len	Length of personalisation string + * @ret rc		Return status code + * + * This is the Instantiate_function defined in ANS X9.82 Part 3-2007 + * Section 9.2 (NIST SP 800-90 Section 9.1). + * + * Only a single security strength is supported, and prediction + * resistance is always enabled.  The nonce is accounted for by + * increasing the entropy input, as per ANS X9.82 Part 3-2007 Section + * 8.4.2 (NIST SP 800-90 Section 8.6.7). + */ +int drbg_instantiate ( struct drbg_state *state, const void *personal, +		       size_t personal_len ) { +	unsigned int entropy_bits = ( ( 3 * DRBG_SECURITY_STRENGTH + 1 ) / 2 ); +	size_t min_len = DRBG_MIN_ENTROPY_LEN_BYTES; +	size_t max_len = DRBG_MAX_ENTROPY_LEN_BYTES; +	uint8_t data[max_len]; +	int len; +	int rc; + +	DBGC ( state, "DRBG %p instantiate\n", state ); + +	/* Sanity checks */ +	assert ( state != NULL ); + +	/* 1.  If requested_instantiation_security_strength > +	 *     highest_supported_security_strength, then return an +	 *     ERROR_FLAG +	 */ +	if ( DRBG_SECURITY_STRENGTH > DRBG_MAX_SECURITY_STRENGTH ) { +		DBGC ( state, "DRBG %p cannot support security strength %d\n", +		       state, DRBG_SECURITY_STRENGTH ); +		return -ENOTSUP; +	} + +	/* 2.  If prediction_resistance_flag is set, and prediction +	 *     resistance is not supported, then return an ERROR_FLAG +	 * +	 * (Nothing to do since prediction resistance is always +	 * supported.) +	 */ + +	/* 3.  If the length of the personalization_string > +	 *     max_personalization_string_length, return an ERROR_FLAG +	 */ +	if ( personal_len > DRBG_MAX_PERSONAL_LEN_BYTES ) { +		DBGC ( state, "DRBG %p personalisation string too long (%zd " +		       "bytes)\n", state, personal_len ); +		return -ERANGE; +	} + +	/* 4.  Set security_strength to the nearest security strength +	 *     greater than or equal to +	 *     requested_instantiation_security_strength. +	 * +	 * (Nothing to do since we support only a single security +	 * strength.) +	 */ + +	/* 5.  Using the security_strength, select appropriate DRBG +	 *     mechanism parameters. +	 * +	 * (Nothing to do since we support only a single security +	 * strength.) +	 */ + +	/* 6.  ( status, entropy_input ) = Get_entropy_input ( +	 *     security_strength, min_length, max_length, +	 *     prediction_resistance_request ) +	 * 7.  If an ERROR is returned in step 6, return a +	 *     CATASTROPHIC_ERROR_FLAG. +	 * 8.  Obtain a nonce. +	 */ +	len = get_entropy_input ( entropy_bits, data, min_len, +				  sizeof ( data ) ); +	if ( len < 0 ) { +		rc = len; +		DBGC ( state, "DRBG %p could not get entropy input: %s\n", +		       state, strerror ( rc ) ); +		return rc; +	} +	assert ( len >= ( int ) min_len ); +	assert ( len <= ( int ) sizeof ( data ) ); + +	/* 9.  initial_working_state = Instantiate_algorithm ( +	 *     entropy_input, nonce, personalization_string ). +	 */ +	drbg_instantiate_algorithm ( state, data, len, personal, personal_len ); + +	/* 10.  Get a state_handle for a currently empty state.  If an +	 *      empty internal state cannot be found, return an +	 *      ERROR_FLAG. +	 * 11.  Set the internal state indicated by state_handle to +	 *      the initial values for the internal state (i.e. set +	 *      the working_state to the values returned as +	 *      initial_working_state in step 9 and any other values +	 *      required for the working_state, and set the +	 *      administrative information to the appropriate values. +	 * +	 * (Almost nothing to do since the memory to hold the state +	 * was passed in by the caller and has already been updated +	 * in-situ.) +	 */ +	state->reseed_required = 0; +	state->valid = 1; + +	/* 12.  Return SUCCESS and state_handle. */ +	return 0; +} + +/** + * Reseed DRBG + * + * @v state		Algorithm state + * @v additional	Additional input + * @v additional_len	Length of additional input + * @ret rc		Return status code + * + * This is the Reseed_function defined in ANS X9.82 Part 3-2007 + * Section 9.3 (NIST SP 800-90 Section 9.2). + * + * Prediction resistance is always enabled. + */ +int drbg_reseed ( struct drbg_state *state, const void *additional, +		  size_t additional_len ) { +	unsigned int entropy_bits = DRBG_SECURITY_STRENGTH; +	size_t min_len = DRBG_MIN_ENTROPY_LEN_BYTES; +	size_t max_len = DRBG_MAX_ENTROPY_LEN_BYTES; +	uint8_t data[max_len]; +	int len; +	int rc; + +	DBGC ( state, "DRBG %p reseed\n", state ); + +	/* Sanity checks */ +	assert ( state != NULL ); + +	/* 1.  Using state_handle, obtain the current internal state. +	 *     If state_handle indicates an invalid or empty internal +	 *     state, return an ERROR_FLAG. +	 * +	 * (Almost nothing to do since the memory holding the internal +	 * state was passed in by the caller.) +	 */ +	if ( ! state->valid ) { +		DBGC ( state, "DRBG %p not valid\n", state ); +		return -EINVAL; +	} + +	/* 2.  If prediction_resistance_request is set, and +	 *     prediction_resistance_flag is not set, then return an +	 *     ERROR_FLAG. +	 * +	 * (Nothing to do since prediction resistance is always +	 * supported.) +	 */ + +	/* 3.  If the length of the additional_input > +	 *     max_additional_input_length, return an ERROR_FLAG. +	 */ +	if ( additional_len > DRBG_MAX_ADDITIONAL_LEN_BYTES ) { +		DBGC ( state, "DRBG %p additional input too long (%zd bytes)\n", +		       state, additional_len ); +		return -ERANGE; +	} + +	/* 4.  ( status, entropy_input ) = Get_entropy_input ( +	 *     security_strength, min_length, max_length, +	 *     prediction_resistance_request ). +	 * +	 * 5.  If an ERROR is returned in step 4, return a +	 *     CATASTROPHIC_ERROR_FLAG. +	 */ +	len = get_entropy_input ( entropy_bits, data, min_len, +				  sizeof ( data ) ); +	if ( len < 0 ) { +		rc = len; +		DBGC ( state, "DRBG %p could not get entropy input: %s\n", +		       state, strerror ( rc ) ); +		return rc; +	} + +	/* 6.  new_working_state = Reseed_algorithm ( working_state, +	 *     entropy_input, additional_input ). +	 */ +	drbg_reseed_algorithm ( state, data, len, additional, additional_len ); + +	/* 7.  Replace the working_state in the internal state +	 *     indicated by state_handle with the values of +	 *     new_working_state obtained in step 6. +	 * +	 * (Nothing to do since the state has already been updated in-situ.) +	 */ + +	/* 8.  Return SUCCESS. */ +	return 0; +} + +/** + * Generate pseudorandom bits using DRBG + * + * @v state		Algorithm state + * @v additional	Additional input + * @v additional_len	Length of additional input + * @v prediction_resist	Prediction resistance is required + * @v data		Output buffer + * @v len		Length of output buffer + * @ret rc		Return status code + * + * This is the Generate_function defined in ANS X9.82 Part 3-2007 + * Section 9.4 (NIST SP 800-90 Section 9.3). + * + * Requests must be for an integral number of bytes.  Only a single + * security strength is supported.  Prediction resistance is supported + * if requested. + */ +int drbg_generate ( struct drbg_state *state, const void *additional, +		    size_t additional_len, int prediction_resist, +		    void *data, size_t len ) { +	int rc; + +	DBGC ( state, "DRBG %p generate\n", state ); + +	/* Sanity checks */ +	assert ( state != NULL ); +	assert ( data != NULL ); + +	/* 1.  Using state_handle, obtain the current internal state +	 *     for the instantiation.  If state_handle indicates an +	 *     invalid or empty internal state, then return an ERROR_FLAG. +	 * +	 * (Almost nothing to do since the memory holding the internal +	 * state was passed in by the caller.) +	 */ +	if ( ! state->valid ) { +		DBGC ( state, "DRBG %p not valid\n", state ); +		return -EINVAL; +	} + +	/* 2.  If requested_number_of_bits > +	 *     max_number_of_bits_per_request, then return an +	 *     ERROR_FLAG. +	 */ +	if ( len > DRBG_MAX_GENERATED_LEN_BYTES ) { +		DBGC ( state, "DRBG %p request too long (%zd bytes)\n", +		       state, len ); +		return -ERANGE; +	} + +	/* 3.  If requested_security_strength > the security_strength +	 *     indicated in the internal state, then return an +	 *     ERROR_FLAG. +	 * +	 * (Nothing to do since only a single security strength is +	 * supported.) +	 */ + +	/* 4.  If the length of the additional_input > +	 *     max_additional_input_length, then return an ERROR_FLAG. +	 */ +	if ( additional_len > DRBG_MAX_ADDITIONAL_LEN_BYTES ) { +		DBGC ( state, "DRBG %p additional input too long (%zd bytes)\n", +		       state, additional_len ); +		return -ERANGE; +	} + +	/* 5.  If prediction_resistance_request is set, and +	 *     prediction_resistance_flag is not set, then return an +	 *     ERROR_FLAG. +	 * +	 * (Nothing to do since prediction resistance is always +	 * supported.) +	 */ + +	/* 6.  Clear the reseed_required_flag. */ +	state->reseed_required = 0; + + step_7: +	/* 7.  If reseed_required_flag is set, or if +	 *     prediction_resistance_request is set, then +	 */ +	if ( state->reseed_required || prediction_resist ) { + +		/* 7.1  status = Reseed_function ( state_handle, +		 *      prediction_resistance_request, +		 *      additional_input ) +		 * 7.2  If status indicates an ERROR, then return +		 *      status. +		 */ +		if ( ( rc = drbg_reseed ( state, additional, +					  additional_len ) ) != 0 ) { +			DBGC ( state, "DRBG %p could not reseed: %s\n", +			       state, strerror ( rc ) ); +			return rc; +		} + +		/* 7.3  Using state_handle, obtain the new internal +		 *      state. +		 * +		 * (Nothing to do since the internal state has been +		 * updated in-situ.) +		 */ + +		/* 7.4  additional_input = the Null string. */ +		additional = NULL; +		additional_len = 0; + +		/* 7.5  Clear the reseed_required_flag. */ +		state->reseed_required = 0; +	} + +	/* 8.  ( status, pseudorandom_bits, new_working_state ) = +	 *     Generate_algorithm ( working_state, +	 *     requested_number_of_bits, additional_input ). +	 */ +	rc = drbg_generate_algorithm ( state, additional, additional_len, +				       data, len ); + +	/* 9.  If status indicates that a reseed is required before +	 *     the requested bits can be generated, then +	 */ +	if ( rc != 0 ) { + +		/* 9.1  Set the reseed_required_flag. */ +		state->reseed_required = 1; + +		/* 9.2  If the prediction_resistance_flag is set, then +		 *      set the prediction_resistance_request +		 *      indication. +		 */ +		prediction_resist = 1; + +		/* 9.3  Go to step 7. */ +		goto step_7; +	} + +	/* 10.  Replace the old working_state in the internal state +	 *      indicated by state_handle with the values of +	 *      new_working_state. +	 * +	 * (Nothing to do since the working state has already been +	 * updated in-situ.) +	 */ + +	/* 11.  Return SUCCESS and pseudorandom_bits. */ +	return 0; +} + +/** + * Uninstantiate DRBG + * + * @v state		Algorithm state + * + * This is the Uninstantiate_function defined in ANS X9.82 Part 3-2007 + * Section 9.5 (NIST SP 800-90 Section 9.4). + */ +void drbg_uninstantiate ( struct drbg_state *state ) { + +	DBGC ( state, "DRBG %p uninstantiate\n", state ); + +	/* Sanity checks */ +	assert ( state != NULL ); + +	/* 1.  If state_handle indicates an invalid state, then return +	 *     an ERROR_FLAG. +	 * +	 * (Nothing to do since the memory holding the internal state +	 * was passed in by the caller.) +	 */ + +	/* 2.  Erase the contents of the internal state indicated by +	 *     state_handle. +	 */ +	memset ( state, 0, sizeof ( *state ) ); + +	/* 3.  Return SUCCESS. */ +} diff --git a/roms/ipxe/src/crypto/entropy.c b/roms/ipxe/src/crypto/entropy.c new file mode 100644 index 00000000..c7045840 --- /dev/null +++ b/roms/ipxe/src/crypto/entropy.c @@ -0,0 +1,479 @@ +/* + * 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 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 ); + +/** @file + * + * Entropy source + * + * This algorithm is designed to comply with ANS X9.82 Part 4 (April + * 2011 Draft) Section 13.3.  This standard is unfortunately not + * freely available. + */ + +#include <stdint.h> +#include <assert.h> +#include <string.h> +#include <errno.h> +#include <ipxe/crypto.h> +#include <ipxe/hash_df.h> +#include <ipxe/entropy.h> + +/* Disambiguate the various error causes */ +#define EPIPE_REPETITION_COUNT_TEST \ +	__einfo_error ( EINFO_EPIPE_REPETITION_COUNT_TEST ) +#define EINFO_EPIPE_REPETITION_COUNT_TEST \ +	__einfo_uniqify ( EINFO_EPIPE, 0x01, "Repetition count test failed" ) +#define EPIPE_ADAPTIVE_PROPORTION_TEST \ +	__einfo_error ( EINFO_EPIPE_ADAPTIVE_PROPORTION_TEST ) +#define EINFO_EPIPE_ADAPTIVE_PROPORTION_TEST \ +	__einfo_uniqify ( EINFO_EPIPE, 0x02, "Adaptive proportion test failed" ) + +/** + * Calculate cutoff value for the repetition count test + * + * @ret cutoff		Cutoff value + * + * This is the cutoff value for the Repetition Count Test defined in + * ANS X9.82 Part 2 (October 2011 Draft) Section 8.5.2.1.2. + */ +static inline __attribute__ (( always_inline )) unsigned int +repetition_count_cutoff ( void ) { +	double max_repetitions; +	unsigned int cutoff; + +	/* The cutoff formula for the repetition test is: +	 * +	 *   C = ( 1 + ( -log2(W) / H_min ) ) +	 * +	 * where W is set at 2^(-30) (in ANS X9.82 Part 2 (October +	 * 2011 Draft) Section 8.5.2.1.3.1). +	 */ +	max_repetitions = ( 1 + ( 30 / min_entropy_per_sample() ) ); + +	/* Round up to a whole number of repetitions.  We don't have +	 * the ceil() function available, so do the rounding by hand. +	 */ +	cutoff = max_repetitions; +	if ( cutoff < max_repetitions ) +		cutoff++; +	linker_assert ( ( cutoff >= max_repetitions ), rounding_error ); + +	/* Floating-point operations are not allowed in iPXE since we +	 * never set up a suitable environment.  Abort the build +	 * unless the calculated number of repetitions is a +	 * compile-time constant. +	 */ +	linker_assert ( __builtin_constant_p ( cutoff ), +			repetition_count_cutoff_not_constant ); + +	return cutoff; +} + +/** + * Perform repetition count test + * + * @v sample		Noise sample + * @ret rc		Return status code + * + * This is the Repetition Count Test defined in ANS X9.82 Part 2 + * (October 2011 Draft) Section 8.5.2.1.2. + */ +static int repetition_count_test ( noise_sample_t sample ) { +	static noise_sample_t most_recent_sample; +	static unsigned int repetition_count = 0; + +	/* A = the most recently seen sample value +	 * B = the number of times that value A has been seen in a row +	 * C = the cutoff value above which the repetition test should fail +	 */ + +	/* 1.  For each new sample processed: +	 * +	 * (Note that the test for "repetition_count > 0" ensures that +	 * the initial value of most_recent_sample is treated as being +	 * undefined.) +	 */ +	if ( ( sample == most_recent_sample ) && ( repetition_count > 0 ) ) { + +		/* a) If the new sample = A, then B is incremented by one. */ +		repetition_count++; + +		/*    i.  If B >= C, then an error condition is raised +		 *        due to a failure of the test +		 */ +		if ( repetition_count >= repetition_count_cutoff() ) +			return -EPIPE_REPETITION_COUNT_TEST; + +	} else { + +		/* b) Else: +		 *    i.  A = new sample +		 */ +		most_recent_sample = sample; + +		/*    ii. B = 1 */ +		repetition_count = 1; +	} + +	return 0; +} + +/** + * Window size for the adaptive proportion test + * + * ANS X9.82 Part 2 (October 2011 Draft) Section 8.5.2.1.3.1.1 allows + * five possible window sizes: 16, 64, 256, 4096 and 65536. + * + * We expect to generate relatively few (<256) entropy samples during + * a typical iPXE run; the use of a large window size would mean that + * the test would never complete a single cycle.  We use a window size + * of 64, which is the smallest window size that permits values of + * H_min down to one bit per sample. + */ +#define ADAPTIVE_PROPORTION_WINDOW_SIZE 64 + +/** + * Combine adaptive proportion test window size and min-entropy + * + * @v n			N (window size) + * @v h			H (min-entropy) + * @ret n_h		(N,H) combined value + */ +#define APC_N_H( n, h ) ( ( (n) << 8 ) | (h) ) + +/** + * Define a row of the adaptive proportion cutoff table + * + * @v h			H (min-entropy) + * @v c16		Cutoff for N=16 + * @v c64		Cutoff for N=64 + * @v c256		Cutoff for N=256 + * @v c4096		Cutoff for N=4096 + * @v c65536		Cutoff for N=65536 + */ +#define APC_TABLE_ROW( h, c16, c64, c256, c4096, c65536)	   \ +	case APC_N_H ( 16, h ) :	return c16;		   \ +	case APC_N_H ( 64, h ) :	return c64;   		   \ +	case APC_N_H ( 256, h ) :	return c256;		   \ +	case APC_N_H ( 4096, h ) :	return c4096;		   \ +	case APC_N_H ( 65536, h ) :	return c65536; + +/** Value used to represent "N/A" in adaptive proportion cutoff table */ +#define APC_NA 0 + +/** + * Look up value in adaptive proportion test cutoff table + * + * @v n			N (window size) + * @v h			H (min-entropy) + * @ret cutoff		Cutoff + * + * This is the table of cutoff values defined in ANS X9.82 Part 2 + * (October 2011 Draft) Section 8.5.2.1.3.1.2. + */ +static inline __attribute__ (( always_inline )) unsigned int +adaptive_proportion_cutoff_lookup ( unsigned int n, unsigned int h ) { +	switch ( APC_N_H ( n, h ) ) { +		APC_TABLE_ROW (  1, APC_NA,     51,    168,   2240,  33537 ); +		APC_TABLE_ROW (  2, APC_NA,     35,    100,   1193,  17053 ); +		APC_TABLE_ROW (  3,     10,     24,     61,    643,   8705 ); +		APC_TABLE_ROW (  4,      8,     16,     38,    354,   4473 ); +		APC_TABLE_ROW (  5,      6,     12,     25,    200,   2321 ); +		APC_TABLE_ROW (  6,      5,      9,     17,    117,   1220 ); +		APC_TABLE_ROW (  7,      4,      7,     15,     71,    653 ); +		APC_TABLE_ROW (  8,      4,      5,      9,     45,    358 ); +		APC_TABLE_ROW (  9,      3,      4,      7,     30,    202 ); +		APC_TABLE_ROW ( 10,      3,      4,      5,     21,    118 ); +		APC_TABLE_ROW ( 11,      2,      3,      4,     15,     71 ); +		APC_TABLE_ROW ( 12,      2,      3,      4,     11,     45 ); +		APC_TABLE_ROW ( 13,      2,      2,      3,      9,     30 ); +		APC_TABLE_ROW ( 14,      2,      2,      3,      7,     21 ); +		APC_TABLE_ROW ( 15,      1,      2,      2,      6,     15 ); +		APC_TABLE_ROW ( 16,      1,      2,      2,      5,     11 ); +		APC_TABLE_ROW ( 17,      1,      1,      2,      4,      9 ); +		APC_TABLE_ROW ( 18,      1,      1,      2,      4,      7 ); +		APC_TABLE_ROW ( 19,      1,      1,      1,      3,      6 ); +		APC_TABLE_ROW ( 20,      1,      1,      1,      3,      5 ); +	default: +		return APC_NA; +	} +} + +/** + * Calculate cutoff value for the adaptive proportion test + * + * @ret cutoff		Cutoff value + * + * This is the cutoff value for the Adaptive Proportion Test defined + * in ANS X9.82 Part 2 (October 2011 Draft) Section 8.5.2.1.3.1.2. + */ +static inline __attribute__ (( always_inline )) unsigned int +adaptive_proportion_cutoff ( void ) { +	unsigned int h; +	unsigned int n; +	unsigned int cutoff; + +	/* Look up cutoff value in cutoff table */ +	n = ADAPTIVE_PROPORTION_WINDOW_SIZE; +	h = min_entropy_per_sample(); +	cutoff = adaptive_proportion_cutoff_lookup ( n, h ); + +	/* Fail unless cutoff value is a build-time constant */ +	linker_assert ( __builtin_constant_p ( cutoff ), +			adaptive_proportion_cutoff_not_constant ); + +	/* Fail if cutoff value is N/A */ +	linker_assert ( ( cutoff != APC_NA ), +			adaptive_proportion_cutoff_not_applicable ); + +	return cutoff; +} + +/** + * Perform adaptive proportion test + * + * @v sample		Noise sample + * @ret rc		Return status code + * + * This is the Adaptive Proportion Test for the Most Common Value + * defined in ANS X9.82 Part 2 (October 2011 Draft) Section 8.5.2.1.3. + */ +static int adaptive_proportion_test ( noise_sample_t sample ) { +	static noise_sample_t current_counted_sample; +	static unsigned int sample_count = ADAPTIVE_PROPORTION_WINDOW_SIZE; +	static unsigned int repetition_count; + +	/* A = the sample value currently being counted +	 * B = the number of samples examined in this run of the test so far +	 * N = the total number of samples that must be observed in +	 *     one run of the test, also known as the "window size" of +	 *     the test +	 * B = the current number of times that S (sic) has been seen +	 *     in the W (sic) samples examined so far +	 * C = the cutoff value above which the repetition test should fail +	 * W = the probability of a false positive: 2^-30 +	 */ + +	/* 1.  The entropy source draws the current sample from the +	 *     noise source. +	 * +	 * (Nothing to do; we already have the current sample.) +	 */ + +	/* 2.  If S = N, then a new run of the test begins: */ +	if ( sample_count == ADAPTIVE_PROPORTION_WINDOW_SIZE ) { + +		/* a.  A = the current sample */ +		current_counted_sample = sample; + +		/* b.  S = 0 */ +		sample_count = 0; + +		/* c. B = 0 */ +		repetition_count = 0; + +	} else { + +		/* Else: (the test is already running) +		 * a.  S = S + 1 +		 */ +		sample_count++; + +		/* b.  If A = the current sample, then: */ +		if ( sample == current_counted_sample ) { + +			/* i.   B = B + 1 */ +			repetition_count++; + +			/* ii.  If S (sic) > C then raise an error +			 *      condition, because the test has +			 *      detected a failure +			 */ +			if ( repetition_count > adaptive_proportion_cutoff() ) +				return -EPIPE_ADAPTIVE_PROPORTION_TEST; + +		} +	} + +	return 0; +} + +/** + * Get entropy sample + * + * @ret entropy		Entropy sample + * @ret rc		Return status code + * + * This is the GetEntropy function defined in ANS X9.82 Part 2 + * (October 2011 Draft) Section 6.5.1. + */ +static int get_entropy ( entropy_sample_t *entropy ) { +	static int rc = 0; +	noise_sample_t noise; + +	/* Any failure is permanent */ +	if ( rc != 0 ) +		return rc; + +	/* Get noise sample */ +	if ( ( rc = get_noise ( &noise ) ) != 0 ) +		return rc; + +	/* Perform Repetition Count Test and Adaptive Proportion Test +	 * as mandated by ANS X9.82 Part 2 (October 2011 Draft) +	 * Section 8.5.2.1.1. +	 */ +	if ( ( rc = repetition_count_test ( noise ) ) != 0 ) +		return rc; +	if ( ( rc = adaptive_proportion_test ( noise ) ) != 0 ) +		return rc; + +	/* We do not use any optional conditioning component */ +	*entropy = noise; + +	return 0; +} + +/** + * Calculate number of samples required for startup tests + * + * @ret num_samples	Number of samples required + * + * ANS X9.82 Part 2 (October 2011 Draft) Section 8.5.2.1.5 requires + * that at least one full cycle of the continuous tests must be + * performed at start-up. + */ +static inline __attribute__ (( always_inline )) unsigned int +startup_test_count ( void ) { +	unsigned int num_samples; + +	/* At least max(N,C) samples shall be generated by the noise +	 * source for start-up testing. +	 */ +	num_samples = repetition_count_cutoff(); +	if ( num_samples < adaptive_proportion_cutoff() ) +		num_samples = adaptive_proportion_cutoff(); +	linker_assert ( __builtin_constant_p ( num_samples ), +			startup_test_count_not_constant ); + +	return num_samples; +} + +/** + * Create next nonce value + * + * @ret nonce		Nonce + * + * This is the MakeNextNonce function defined in ANS X9.82 Part 4 + * (April 2011 Draft) Section 13.3.4.2. + */ +static uint32_t make_next_nonce ( void ) { +	static uint32_t nonce; + +	/* The simplest implementation of a nonce uses a large counter */ +	nonce++; + +	return nonce; +} + +/** + * Obtain entropy input temporary buffer + * + * @v num_samples	Number of entropy samples + * @v tmp		Temporary buffer + * @v tmp_len		Length of temporary buffer + * @ret rc		Return status code + * + * This is (part of) the implementation of the Get_entropy_input + * function (using an entropy source as the source of entropy input + * and condensing each entropy source output after each GetEntropy + * call) as defined in ANS X9.82 Part 4 (April 2011 Draft) Section + * 13.3.4.2. + * + * To minimise code size, the number of samples required is calculated + * at compilation time. + */ +int get_entropy_input_tmp ( unsigned int num_samples, uint8_t *tmp, +			    size_t tmp_len ) { +	static unsigned int startup_tested = 0; +	struct { +		uint32_t nonce; +		entropy_sample_t sample; +	} __attribute__ (( packed )) data;; +	uint8_t df_buf[tmp_len]; +	unsigned int i; +	int rc; + +	/* Enable entropy gathering */ +	if ( ( rc = entropy_enable() ) != 0 ) +		return rc; + +	/* Perform mandatory startup tests, if not yet performed */ +	for ( ; startup_tested < startup_test_count() ; startup_tested++ ) { +		if ( ( rc = get_entropy ( &data.sample ) ) != 0 ) +			goto err_get_entropy; +	} + +	/* 3.  entropy_total = 0 +	 * +	 * (Nothing to do; the number of entropy samples required has +	 * already been precalculated.) +	 */ + +	/* 4.  tmp = a fixed n-bit value, such as 0^n */ +	memset ( tmp, 0, tmp_len ); + +	/* 5.  While ( entropy_total < min_entropy ) */ +	while ( num_samples-- ) { +		/* 5.1.  ( status, entropy_bitstring, assessed_entropy ) +		 *       = GetEntropy() +		 * 5.2.  If status indicates an error, return ( status, Null ) +		 */ +		if ( ( rc = get_entropy ( &data.sample ) ) != 0 ) +			goto err_get_entropy; + +		/* 5.3.  nonce = MakeNextNonce() */ +		data.nonce = make_next_nonce(); + +		/* 5.4.  tmp = tmp XOR +		 *             df ( ( nonce || entropy_bitstring ), n ) +		 */ +		hash_df ( &entropy_hash_df_algorithm, &data, sizeof ( data ), +			  df_buf, sizeof ( df_buf ) ); +		for ( i = 0 ; i < tmp_len ; i++ ) +			tmp[i] ^= df_buf[i]; + +		/* 5.5.  entropy_total = entropy_total + assessed_entropy +		 * +		 * (Nothing to do; the number of entropy samples +		 * required has already been precalculated.) +		 */ +	} + +	/* Disable entropy gathering */ +	entropy_disable(); + +	return 0; + + err_get_entropy: +	entropy_disable(); +	return rc; +} diff --git a/roms/ipxe/src/crypto/hash_df.c b/roms/ipxe/src/crypto/hash_df.c new file mode 100644 index 00000000..adf1d87e --- /dev/null +++ b/roms/ipxe/src/crypto/hash_df.c @@ -0,0 +1,138 @@ +/* + * 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 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 ); + +/** @file + * + * Hash-based derivation function (Hash_df) + * + * This algorithm is designed to comply with ANS X9.82 Part 3-2007 + * Section 10.5.2.  This standard is not freely available, but most of + * the text appears to be shared with NIST SP 800-90, which can be + * downloaded from + * + *     http://csrc.nist.gov/publications/nistpubs/800-90/SP800-90revised_March2007.pdf + * + * Where possible, references are given to both documents.  In the + * case of any disagreement, ANS X9.82 takes priority over NIST SP + * 800-90.  (In particular, note that some algorithms that are + * Approved by NIST SP 800-90 are not Approved by ANS X9.82.) + */ + +#include <stdint.h> +#include <string.h> +#include <assert.h> +#include <byteswap.h> +#include <ipxe/crypto.h> +#include <ipxe/hash_df.h> + +/** + * Distribute entropy throughout a buffer + * + * @v hash		Underlying hash algorithm + * @v input		Input data + * @v input_len		Length of input data, in bytes + * @v output		Output buffer + * @v output_len	Length of output buffer, in bytes + * + * This is the Hash_df function defined in ANS X9.82 Part 3-2007 + * Section 10.5.2 (NIST SP 800-90 Section 10.4.1). + * + * The number of bits requested is implicit in the length of the + * output buffer.  Requests must be for an integral number of bytes. + * + * The output buffer is filled incrementally with each iteration of + * the central loop, rather than constructing an overall "temp" and + * then taking the leftmost(no_of_bits_to_return) bits. + * + * There is no way for the Hash_df function to fail.  The returned + * status SUCCESS is implicit. + */ +void hash_df ( struct digest_algorithm *hash, const void *input, +	       size_t input_len, void *output, size_t output_len ) { +	uint8_t context[hash->ctxsize]; +	uint8_t digest[hash->digestsize]; +	size_t frag_len; +	struct { +		uint8_t pad[3]; +		uint8_t counter; +		uint32_t no_of_bits_to_return; +	} __attribute__ (( packed )) prefix; +	void *temp; +	size_t remaining; + +	DBGC ( &hash_df, "HASH_DF input:\n" ); +	DBGC_HDA ( &hash_df, 0, input, input_len ); + +	/* Sanity checks */ +	assert ( input != NULL ); +	assert ( output != NULL ); + +	/* 1.  temp = the Null string +	 * 2.  len = ceil ( no_of_bits_to_return / outlen ) +	 * +	 * (Nothing to do.  We fill the output buffer incrementally, +	 * rather than constructing the complete "temp" in-memory. +	 * "len" is implicit in the number of iterations required to +	 * fill the output buffer, and so is not calculated +	 * explicitly.) +	 */ + +	/* 3.  counter = an 8-bit binary value representing the integer "1" */ +	prefix.counter = 1; + +	/* 4.  For i = 1 to len do */ +	for ( temp = output, remaining = output_len ; remaining > 0 ; ) { + +		/* Comment: in step 5.1 (sic), no_of_bits_to_return is +		 * used as a 32-bit string. +		 * +		 * 4.1  temp = temp || Hash ( counter || no_of_bits_to_return +		 *                            || input_string ) +		 */ +		prefix.no_of_bits_to_return = htonl ( output_len * 8 ); +		digest_init ( hash, context ); +		digest_update ( hash, context, &prefix.counter, +				( sizeof ( prefix ) - +				  offsetof ( typeof ( prefix ), counter ) ) ); +		digest_update ( hash, context, input, input_len ); +		digest_final ( hash, context, digest ); + +		/* 4.2  counter = counter + 1 */ +		prefix.counter++; + +		/* 5.    requested_bits = Leftmost ( no_of_bits_to_return ) +		 *       of temp +		 * +		 * (We fill the output buffer incrementally.) +		 */ +		frag_len = sizeof ( digest ); +		if ( frag_len > remaining ) +			frag_len = remaining; +		memcpy ( temp, digest, frag_len ); +		temp += frag_len; +		remaining -= frag_len; +	} + +	/* 6.  Return SUCCESS and requested_bits */ +	DBGC ( &hash_df, "HASH_DF output:\n" ); +	DBGC_HDA ( &hash_df, 0, output, output_len ); +	return; +} diff --git a/roms/ipxe/src/crypto/hmac.c b/roms/ipxe/src/crypto/hmac.c new file mode 100644 index 00000000..e9459198 --- /dev/null +++ b/roms/ipxe/src/crypto/hmac.c @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2007 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 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 ); + +/** + * @file + * + * Keyed-Hashing for Message Authentication + */ + +#include <string.h> +#include <assert.h> +#include <ipxe/crypto.h> +#include <ipxe/hmac.h> + +/** + * Reduce HMAC key length + * + * @v digest		Digest algorithm to use + * @v digest_ctx	Digest context + * @v key		Key + * @v key_len		Length of key + */ +static void hmac_reduce_key ( struct digest_algorithm *digest, +			      void *key, size_t *key_len ) { +	uint8_t digest_ctx[digest->ctxsize]; + +	digest_init ( digest, digest_ctx ); +	digest_update ( digest, digest_ctx, key, *key_len ); +	digest_final ( digest, digest_ctx, key ); +	*key_len = digest->digestsize; +} + +/** + * Initialise HMAC + * + * @v digest		Digest algorithm to use + * @v digest_ctx	Digest context + * @v key		Key + * @v key_len		Length of key + * + * The length of the key should be less than the block size of the + * digest algorithm being used.  (If the key length is greater, it + * will be replaced with its own digest, and key_len will be updated + * accordingly). + */ +void hmac_init ( struct digest_algorithm *digest, void *digest_ctx, +		 void *key, size_t *key_len ) { +	unsigned char k_ipad[digest->blocksize]; +	unsigned int i; + +	/* Reduce key if necessary */ +	if ( *key_len > sizeof ( k_ipad ) ) +		hmac_reduce_key ( digest, key, key_len ); + +	/* Construct input pad */ +	memset ( k_ipad, 0, sizeof ( k_ipad ) ); +	memcpy ( k_ipad, key, *key_len ); +	for ( i = 0 ; i < sizeof ( k_ipad ) ; i++ ) { +		k_ipad[i] ^= 0x36; +	} +	 +	/* Start inner hash */ +	digest_init ( digest, digest_ctx ); +	digest_update ( digest, digest_ctx, k_ipad, sizeof ( k_ipad ) ); +} + +/** + * Finalise HMAC + * + * @v digest		Digest algorithm to use + * @v digest_ctx	Digest context + * @v key		Key + * @v key_len		Length of key + * @v hmac		HMAC digest to fill in + * + * The length of the key should be less than the block size of the + * digest algorithm being used.  (If the key length is greater, it + * will be replaced with its own digest, and key_len will be updated + * accordingly). + */ +void hmac_final ( struct digest_algorithm *digest, void *digest_ctx, +		  void *key, size_t *key_len, void *hmac ) { +	unsigned char k_opad[digest->blocksize]; +	unsigned int i; + +	/* Reduce key if necessary */ +	if ( *key_len > sizeof ( k_opad ) ) +		hmac_reduce_key ( digest, key, key_len ); + +	/* Construct output pad */ +	memset ( k_opad, 0, sizeof ( k_opad ) ); +	memcpy ( k_opad, key, *key_len ); +	for ( i = 0 ; i < sizeof ( k_opad ) ; i++ ) { +		k_opad[i] ^= 0x5c; +	} +	 +	/* Finish inner hash */ +	digest_final ( digest, digest_ctx, hmac ); + +	/* Perform outer hash */ +	digest_init ( digest, digest_ctx ); +	digest_update ( digest, digest_ctx, k_opad, sizeof ( k_opad ) ); +	digest_update ( digest, digest_ctx, hmac, digest->digestsize ); +	digest_final ( digest, digest_ctx, hmac ); +} diff --git a/roms/ipxe/src/crypto/hmac_drbg.c b/roms/ipxe/src/crypto/hmac_drbg.c new file mode 100644 index 00000000..1e5f732e --- /dev/null +++ b/roms/ipxe/src/crypto/hmac_drbg.c @@ -0,0 +1,359 @@ +/* + * 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 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 ); + +/** @file + * + * HMAC_DRBG algorithm + * + * This algorithm is designed to comply with ANS X9.82 Part 3-2007 + * Section 10.2.2.2.  This standard is not freely available, but most + * of the text appears to be shared with NIST SP 800-90, which can be + * downloaded from + * + *     http://csrc.nist.gov/publications/nistpubs/800-90/SP800-90revised_March2007.pdf + * + * Where possible, references are given to both documents.  In the + * case of any disagreement, ANS X9.82 takes priority over NIST SP + * 800-90.  (In particular, note that some algorithms that are + * Approved by NIST SP 800-90 are not Approved by ANS X9.82.) + */ + +#include <stdint.h> +#include <string.h> +#include <errno.h> +#include <assert.h> +#include <ipxe/crypto.h> +#include <ipxe/hmac.h> +#include <ipxe/hmac_drbg.h> + +/** + * Update the HMAC_DRBG key + * + * @v hash		Underlying hash algorithm + * @v state		HMAC_DRBG internal state + * @v data		Provided data + * @v len		Length of provided data + * @v single		Single byte used in concatenation + * + * This function carries out the operation + * + *     K = HMAC ( K, V || single || provided_data ) + * + * as used by hmac_drbg_update() + */ +static void hmac_drbg_update_key ( struct digest_algorithm *hash, +				   struct hmac_drbg_state *state, +				   const void *data, size_t len, +				   const uint8_t single ) { +	uint8_t context[ hash->ctxsize ]; +	size_t out_len = hash->digestsize; + +	DBGC ( state, "HMAC_DRBG_%s %p provided data :\n", hash->name, state ); +	DBGC_HDA ( state, 0, data, len ); + +	/* Sanity checks */ +	assert ( hash != NULL ); +	assert ( state != NULL ); +	assert ( ( data != NULL ) || ( len == 0 ) ); +	assert ( ( single == 0x00 ) || ( single == 0x01 ) ); + +	/* K = HMAC ( K, V || single || provided_data ) */ +	hmac_init ( hash, context, state->key, &out_len ); +	assert ( out_len == hash->digestsize ); +	hmac_update ( hash, context, state->value, out_len ); +	hmac_update ( hash, context, &single, sizeof ( single ) ); +	hmac_update ( hash, context, data, len ); +	hmac_final ( hash, context, state->key, &out_len, state->key ); +	assert ( out_len == hash->digestsize ); + +	DBGC ( state, "HMAC_DRBG_%s %p K = HMAC ( K, V || %#02x || " +	       "provided_data ) :\n", hash->name, state, single ); +	DBGC_HDA ( state, 0, state->key, out_len ); +} + +/** + * Update the HMAC_DRBG value + * + * @v hash		Underlying hash algorithm + * @v state		HMAC_DRBG internal state + * @v data		Provided data + * @v len		Length of provided data + * @v single		Single byte used in concatenation + * + * This function carries out the operation + * + *     V = HMAC ( K, V ) + * + * as used by hmac_drbg_update() and hmac_drbg_generate() + */ +static void hmac_drbg_update_value ( struct digest_algorithm *hash, +				     struct hmac_drbg_state *state ) { +	uint8_t context[ hash->ctxsize ]; +	size_t out_len = hash->digestsize; + +	/* Sanity checks */ +	assert ( hash != NULL ); +	assert ( state != NULL ); + +	/* V = HMAC ( K, V ) */ +	hmac_init ( hash, context, state->key, &out_len ); +	assert ( out_len == hash->digestsize ); +	hmac_update ( hash, context, state->value, out_len ); +	hmac_final ( hash, context, state->key, &out_len, state->value ); +	assert ( out_len == hash->digestsize ); + +	DBGC ( state, "HMAC_DRBG_%s %p V = HMAC ( K, V ) :\n", +	       hash->name, state ); +	DBGC_HDA ( state, 0, state->value, out_len ); +} + +/** + * Update HMAC_DRBG internal state + * + * @v hash		Underlying hash algorithm + * @v state		HMAC_DRBG internal state + * @v data		Provided data + * @v len		Length of provided data + * + * This is the HMAC_DRBG_Update function defined in ANS X9.82 Part + * 3-2007 Section 10.2.2.2.2 (NIST SP 800-90 Section 10.1.2.2). + * + * The key and value are updated in-place within the HMAC_DRBG + * internal state. + */ +static void hmac_drbg_update ( struct digest_algorithm *hash, +			       struct hmac_drbg_state *state, +			       const void *data, size_t len ) { + +	DBGC ( state, "HMAC_DRBG_%s %p update\n", hash->name, state ); + +	/* Sanity checks */ +	assert ( hash != NULL ); +	assert ( state != NULL ); +	assert ( ( data != NULL ) || ( len == 0 ) ); + +	/* 1.  K = HMAC ( K, V || 0x00 || provided_data ) */ +	hmac_drbg_update_key ( hash, state, data, len, 0x00 ); + +	/* 2.  V = HMAC ( K, V ) */ +	hmac_drbg_update_value ( hash, state ); + +	/* 3.  If ( provided_data = Null ), then return K and V */ +	if ( ! len ) +		return; + +	/* 4.  K = HMAC ( K, V || 0x01 || provided_data ) */ +	hmac_drbg_update_key ( hash, state, data, len, 0x01 ); + +	/* 5.  V = HMAC ( K, V ) */ +	hmac_drbg_update_value ( hash, state ); + +	/* 6.  Return K and V */ +} + +/** + * Instantiate HMAC_DRBG + * + * @v hash		Underlying hash algorithm + * @v state		HMAC_DRBG internal state to be initialised + * @v entropy		Entropy input + * @v entropy_len	Length of entropy input + * @v personal		Personalisation string + * @v personal_len	Length of personalisation string + * + * This is the HMAC_DRBG_Instantiate_algorithm function defined in ANS + * X9.82 Part 3-2007 Section 10.2.2.2.3 (NIST SP 800-90 Section + * 10.1.2.3). + * + * The nonce must be included within the entropy input (i.e. the + * entropy input must contain at least 3/2 * security_strength bits of + * entropy, as per ANS X9.82 Part 3-2007 Section 8.4.2 (NIST SP 800-90 + * Section 8.6.7). + * + * The key, value and reseed counter are updated in-place within the + * HMAC_DRBG internal state. + */ +void hmac_drbg_instantiate ( struct digest_algorithm *hash, +			     struct hmac_drbg_state *state, +			     const void *entropy, size_t entropy_len, +			     const void *personal, size_t personal_len ){ +	size_t out_len = hash->digestsize; + +	DBGC ( state, "HMAC_DRBG_%s %p instantiate\n", hash->name, state ); + +	/* Sanity checks */ +	assert ( hash != NULL ); +	assert ( state != NULL ); +	assert ( entropy != NULL ); +	assert ( ( personal != NULL ) || ( personal_len == 0 ) ); + +	/* 1.  seed_material = entropy_input || nonce || +	 *     personalisation_string +	 */ + +	/* 2.  Key = 0x00 00..00 */ +	memset ( state->key, 0x00, out_len ); + +	/* 3.  V = 0x01 01...01 */ +	memset ( state->value, 0x01, out_len ); + +	/* 4.  ( Key, V ) = HMAC_DBRG_Update ( seed_material, Key, V ) +	 * 5.  reseed_counter = 1 +	 * 6.  Return V, Key and reseed_counter as the +	 *     initial_working_state +	 */ +	hmac_drbg_reseed ( hash, state, entropy, entropy_len, +			   personal, personal_len ); +} + +/** + * Reseed HMAC_DRBG + * + * @v hash		Underlying hash algorithm + * @v state		HMAC_DRBG internal state + * @v entropy		Entropy input + * @v entropy_len	Length of entropy input + * @v additional	Additional input + * @v additional_len	Length of additional input + * + * This is the HMAC_DRBG_Reseed_algorithm function defined in ANS X9.82 + * Part 3-2007 Section 10.2.2.2.4 (NIST SP 800-90 Section 10.1.2.4). + * + * The key, value and reseed counter are updated in-place within the + * HMAC_DRBG internal state. + */ +void hmac_drbg_reseed ( struct digest_algorithm *hash, +			struct hmac_drbg_state *state, +			const void *entropy, size_t entropy_len, +			const void *additional, size_t additional_len ) { +	uint8_t seed_material[ entropy_len + additional_len ]; + +	DBGC ( state, "HMAC_DRBG_%s %p (re)seed\n", hash->name, state ); + +	/* Sanity checks */ +	assert ( hash != NULL ); +	assert ( state != NULL ); +	assert ( entropy != NULL ); +	assert ( ( additional != NULL ) || ( additional_len == 0 ) ); + +	/* 1.  seed_material = entropy_input || additional_input */ +	memcpy ( seed_material, entropy, entropy_len ); +	memcpy ( ( seed_material + entropy_len ), additional, additional_len ); +	DBGC ( state, "HMAC_DRBG_%s %p seed material :\n", hash->name, state ); +	DBGC_HDA ( state, 0, seed_material, sizeof ( seed_material ) ); + +	/* 2.  ( Key, V ) = HMAC_DBRG_Update ( seed_material, Key, V ) */ +	hmac_drbg_update ( hash, state, seed_material, +			   sizeof ( seed_material ) ); + +	/* 3.  reseed_counter = 1 */ +	state->reseed_counter = 1; + +	/* 4.  Return V, Key and reseed_counter as the new_working_state */ +} + +/** + * Generate pseudorandom bits using HMAC_DRBG + * + * @v hash		Underlying hash algorithm + * @v state		HMAC_DRBG internal state + * @v additional	Additional input + * @v additional_len	Length of additional input + * @v data		Output buffer + * @v len		Length of output buffer + * @ret rc		Return status code + * + * This is the HMAC_DRBG_Generate_algorithm function defined in ANS X9.82 + * Part 3-2007 Section 10.2.2.2.5 (NIST SP 800-90 Section 10.1.2.5). + * + * Requests must be for an integral number of bytes. + * + * The key, value and reseed counter are updated in-place within the + * HMAC_DRBG internal state. + * + * Note that the only permitted error is "reseed required". + */ +int hmac_drbg_generate ( struct digest_algorithm *hash, +			 struct hmac_drbg_state *state, +			 const void *additional, size_t additional_len, +			 void *data, size_t len ) { +	size_t out_len = hash->digestsize; +	void *orig_data = data; +	size_t orig_len = len; +	size_t frag_len; + +	DBGC ( state, "HMAC_DRBG_%s %p generate\n", hash->name, state ); + +	/* Sanity checks */ +	assert ( hash != NULL ); +	assert ( state != NULL ); +	assert ( data != NULL ); +	assert ( ( additional != NULL ) || ( additional_len == 0 ) ); + +	/* 1.  If reseed_counter > reseed_interval, then return an +	 *     indication that a reseed is required +	 */ +	if ( state->reseed_counter > HMAC_DRBG_RESEED_INTERVAL ) { +		DBGC ( state, "HMAC_DRBG_%s %p reseed interval exceeded\n", +		       hash->name, state ); +		return -ESTALE; +	} + +	/* 2.  If additional_input != Null, then +	 *     ( Key, V ) = HMAC_DRBG_Update ( additional_input, Key, V ) +	 */ +	if ( additional_len ) +		hmac_drbg_update ( hash, state, additional, additional_len ); + +	/* 3.  temp = Null +	 * 4.  While ( len ( temp ) < requested_number_of_bits ) do: +	 */ +	while ( len ) { + +		/* 4.1  V = HMAC ( Key, V ) */ +		hmac_drbg_update_value ( hash, state ); + +		/* 4.2.  temp = temp || V +		 * 5.    returned_bits = Leftmost requested_number_of_bits +		 *       of temp +		 */ +		frag_len = len; +		if ( frag_len > out_len ) +			frag_len = out_len; +		memcpy ( data, state->value, frag_len ); +		data += frag_len; +		len -= frag_len; +	} + +	/* 6.  ( Key, V ) = HMAC_DRBG_Update ( additional_input, Key, V ) */ +	hmac_drbg_update ( hash, state, additional, additional_len ); + +	/* 7.  reseed_counter = reseed_counter + 1 */ +	state->reseed_counter++; + +	DBGC ( state, "HMAC_DRBG_%s %p generated :\n", hash->name, state ); +	DBGC_HDA ( state, 0, orig_data, orig_len ); + +	/* 8.  Return SUCCESS, returned_bits, and the new values of +	 *     Key, V and reseed_counter as the new_working_state +	 */ +	return 0; +} diff --git a/roms/ipxe/src/crypto/md5.c b/roms/ipxe/src/crypto/md5.c new file mode 100644 index 00000000..122c7d59 --- /dev/null +++ b/roms/ipxe/src/crypto/md5.c @@ -0,0 +1,298 @@ +/* + * 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 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 ); + +/** @file + * + * MD5 algorithm + * + */ + +#include <stdint.h> +#include <string.h> +#include <byteswap.h> +#include <assert.h> +#include <ipxe/rotate.h> +#include <ipxe/crypto.h> +#include <ipxe/asn1.h> +#include <ipxe/md5.h> + +/** MD5 variables */ +struct md5_variables { +	/* This layout matches that of struct md5_digest_data, +	 * allowing for efficient endianness-conversion, +	 */ +	uint32_t a; +	uint32_t b; +	uint32_t c; +	uint32_t d; +	uint32_t w[16]; +} __attribute__ (( packed )); + +/** MD5 constants */ +static const uint32_t k[64] = { +	0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a, +	0xa8304613, 0xfd469501, 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, +	0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, 0xf61e2562, 0xc040b340, +	0x265e5a51, 0xe9b6c7aa, 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8, +	0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 0xa9e3e905, 0xfcefa3f8, +	0x676f02d9, 0x8d2a4c8a, 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, +	0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, 0x289b7ec6, 0xeaa127fa, +	0xd4ef3085, 0x04881d05, 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, +	0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92, +	0xffeff47d, 0x85845dd1, 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, +	0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391 +}; + +/** MD5 shift amounts */ +static const uint8_t r[64] = { +	7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, +	5,  9, 14, 20, 5,  9, 14, 20, 5,  9, 14, 20, 5,  9, 14, 20, +	4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, +	6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21 +}; + +/** + * f(b,c,d) for steps 0 to 15 + * + * @v v		MD5 variables + * @ret f	f(b,c,d) + */ +static uint32_t md5_f_0_15 ( struct md5_variables *v ) { +	return ( v->d ^ ( v->b & ( v->c ^ v->d ) ) ); +} + +/** + * f(b,c,d) for steps 16 to 31 + * + * @v v		MD5 variables + * @ret f	f(b,c,d) + */ +static uint32_t md5_f_16_31 ( struct md5_variables *v ) { +	return ( v->c ^ ( v->d & ( v->b ^ v->c ) ) ); +} + +/** + * f(b,c,d) for steps 32 to 47 + * + * @v v		MD5 variables + * @ret f	f(b,c,d) + */ +static uint32_t md5_f_32_47 ( struct md5_variables *v ) { +	return ( v->b ^ v->c ^ v->d ); +} + +/** + * f(b,c,d) for steps 48 to 63 + * + * @v v		MD5 variables + * @ret f	f(b,c,d) + */ +static uint32_t md5_f_48_63 ( struct md5_variables *v ) { +	return ( v->c ^ ( v->b | (~v->d) ) ); +} + +/** An MD5 step function */ +struct md5_step { +	/** +	 * Calculate f(b,c,d) +	 * +	 * @v v		MD5 variables +	 * @ret f	f(b,c,d) +	 */ +	uint32_t ( * f ) ( struct md5_variables *v ); +	/** Coefficient of i in g=ni+m */ +	uint8_t coefficient; +	/** Constant term in g=ni+m */ +	uint8_t constant; +}; + +/** MD5 steps */ +static struct md5_step md5_steps[4] = { +	/** 0 to 15 */ +	{ .f = md5_f_0_15,	.coefficient = 1,	.constant = 0 }, +	/** 16 to 31 */ +	{ .f = md5_f_16_31,	.coefficient = 5,	.constant = 1 }, +	/** 32 to 47 */ +	{ .f = md5_f_32_47,	.coefficient = 3,	.constant = 5 }, +	/** 48 to 63 */ +	{ .f = md5_f_48_63,	.coefficient = 7,	.constant = 0 }, +}; + +/** + * Initialise MD5 algorithm + * + * @v ctx		MD5 context + */ +static void md5_init ( void *ctx ) { +	struct md5_context *context = ctx; + +	context->ddd.dd.digest.h[0] = cpu_to_le32 ( 0x67452301 ); +	context->ddd.dd.digest.h[1] = cpu_to_le32 ( 0xefcdab89 ); +	context->ddd.dd.digest.h[2] = cpu_to_le32 ( 0x98badcfe ); +	context->ddd.dd.digest.h[3] = cpu_to_le32 ( 0x10325476 ); +	context->len = 0; +} + +/** + * Calculate MD5 digest of accumulated data + * + * @v context		MD5 context + */ +static void md5_digest ( struct md5_context *context ) { +        union { +		union md5_digest_data_dwords ddd; +		struct md5_variables v; +	} u; +	uint32_t *a = &u.v.a; +	uint32_t *b = &u.v.b; +	uint32_t *c = &u.v.c; +	uint32_t *d = &u.v.d; +	uint32_t *w = u.v.w; +	uint32_t f; +	uint32_t g; +	uint32_t temp; +	struct md5_step *step; +	unsigned int i; + +	/* Sanity checks */ +	assert ( ( context->len % sizeof ( context->ddd.dd.data ) ) == 0 ); +	linker_assert ( &u.ddd.dd.digest.h[0] == a, md5_bad_layout ); +	linker_assert ( &u.ddd.dd.digest.h[1] == b, md5_bad_layout ); +	linker_assert ( &u.ddd.dd.digest.h[2] == c, md5_bad_layout ); +	linker_assert ( &u.ddd.dd.digest.h[3] == d, md5_bad_layout ); +	linker_assert ( &u.ddd.dd.data.dword[0] == w, md5_bad_layout ); + +	DBGC ( context, "MD5 digesting:\n" ); +	DBGC_HDA ( context, 0, &context->ddd.dd.digest, +		   sizeof ( context->ddd.dd.digest ) ); +	DBGC_HDA ( context, context->len, &context->ddd.dd.data, +		   sizeof ( context->ddd.dd.data ) ); + +	/* Convert h[0..3] to host-endian, and initialise a, b, c, d, +	 * and w[0..15] +	 */ +	for ( i = 0 ; i < ( sizeof ( u.ddd.dword ) / +			    sizeof ( u.ddd.dword[0] ) ) ; i++ ) { +		le32_to_cpus ( &context->ddd.dword[i] ); +		u.ddd.dword[i] = context->ddd.dword[i]; +	} + +	/* Main loop */ +	for ( i = 0 ; i < 64 ; i++ ) { +		step = &md5_steps[ i / 16 ]; +		f = step->f ( &u.v ); +		g = ( ( ( step->coefficient * i ) + step->constant ) % 16 ); +		temp = *d; +		*d = *c; +		*c = *b; +		*b = ( *b + rol32 ( ( *a + f + k[i] + w[g] ), r[i] ) ); +		*a = temp; +		DBGC2 ( context, "%2d : %08x %08x %08x %08x\n", +			i, *a, *b, *c, *d ); +	} + +	/* Add chunk to hash and convert back to big-endian */ +	for ( i = 0 ; i < 4 ; i++ ) { +		context->ddd.dd.digest.h[i] = +			cpu_to_le32 ( context->ddd.dd.digest.h[i] + +				      u.ddd.dd.digest.h[i] ); +	} + +	DBGC ( context, "MD5 digested:\n" ); +	DBGC_HDA ( context, 0, &context->ddd.dd.digest, +		   sizeof ( context->ddd.dd.digest ) ); +} + +/** + * Accumulate data with MD5 algorithm + * + * @v ctx		MD5 context + * @v data		Data + * @v len		Length of data + */ +static void md5_update ( void *ctx, const void *data, size_t len ) { +	struct md5_context *context = ctx; +	const uint8_t *byte = data; +	size_t offset; + +	/* Accumulate data a byte at a time, performing the digest +	 * whenever we fill the data buffer +	 */ +	while ( len-- ) { +		offset = ( context->len % sizeof ( context->ddd.dd.data ) ); +		context->ddd.dd.data.byte[offset] = *(byte++); +		context->len++; +		if ( ( context->len % sizeof ( context->ddd.dd.data ) ) == 0 ) +			md5_digest ( context ); +	} +} + +/** + * Generate MD5 digest + * + * @v ctx		MD5 context + * @v out		Output buffer + */ +static void md5_final ( void *ctx, void *out ) { +	struct md5_context *context = ctx; +	uint64_t len_bits; +	uint8_t pad; + +	/* Record length before pre-processing */ +	len_bits = cpu_to_le64 ( ( ( uint64_t ) context->len ) * 8 ); + +	/* Pad with a single "1" bit followed by as many "0" bits as required */ +	pad = 0x80; +	do { +		md5_update ( ctx, &pad, sizeof ( pad ) ); +		pad = 0x00; +	} while ( ( context->len % sizeof ( context->ddd.dd.data ) ) != +		  offsetof ( typeof ( context->ddd.dd.data ), final.len ) ); + +	/* Append length (in bits) */ +	md5_update ( ctx, &len_bits, sizeof ( len_bits ) ); +	assert ( ( context->len % sizeof ( context->ddd.dd.data ) ) == 0 ); + +	/* Copy out final digest */ +	memcpy ( out, &context->ddd.dd.digest, +		 sizeof ( context->ddd.dd.digest ) ); +} + +/** MD5 algorithm */ +struct digest_algorithm md5_algorithm = { +	.name		= "md5", +	.ctxsize	= sizeof ( struct md5_context ), +	.blocksize	= sizeof ( union md5_block ), +	.digestsize	= sizeof ( struct md5_digest ), +	.init		= md5_init, +	.update		= md5_update, +	.final		= md5_final, +}; + +/** "md5" object identifier */ +static uint8_t oid_md5[] = { ASN1_OID_MD5 }; + +/** "md5" OID-identified algorithm */ +struct asn1_algorithm oid_md5_algorithm __asn1_algorithm = { +	.name = "md5", +	.digest = &md5_algorithm, +	.oid = ASN1_OID_CURSOR ( oid_md5 ), +}; diff --git a/roms/ipxe/src/crypto/null_entropy.c b/roms/ipxe/src/crypto/null_entropy.c new file mode 100644 index 00000000..c56d5e76 --- /dev/null +++ b/roms/ipxe/src/crypto/null_entropy.c @@ -0,0 +1,36 @@ +/* + * 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 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 ); + +/** @file + * + * Nonexistent entropy source + * + * + * This source provides no entropy and must NOT be used in a + * security-sensitive environment. + */ + +#include <ipxe/entropy.h> + +PROVIDE_ENTROPY_INLINE ( null, min_entropy_per_sample ); +PROVIDE_ENTROPY_INLINE ( null, entropy_enable ); +PROVIDE_ENTROPY_INLINE ( null, entropy_disable ); +PROVIDE_ENTROPY_INLINE ( null, get_noise ); diff --git a/roms/ipxe/src/crypto/ocsp.c b/roms/ipxe/src/crypto/ocsp.c new file mode 100644 index 00000000..66e47c57 --- /dev/null +++ b/roms/ipxe/src/crypto/ocsp.c @@ -0,0 +1,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; +} diff --git a/roms/ipxe/src/crypto/privkey.c b/roms/ipxe/src/crypto/privkey.c new file mode 100644 index 00000000..e010649c --- /dev/null +++ b/roms/ipxe/src/crypto/privkey.c @@ -0,0 +1,118 @@ +/* + * 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 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 <string.h> +#include <ipxe/dhcp.h> +#include <ipxe/settings.h> +#include <ipxe/x509.h> +#include <ipxe/privkey.h> + +/** @file + * + * Private key + * + * Life would in theory be easier if we could use a single file to + * hold both the certificate and corresponding private key. + * Unfortunately, the only common format which supports this is + * PKCS#12 (aka PFX), which is too ugly to be allowed anywhere near my + * codebase.  See, for reference and amusement: + * + *    http://www.cs.auckland.ac.nz/~pgut001/pubs/pfx.html + */ + +/* Allow private key to be overridden if not explicitly specified */ +#ifdef PRIVATE_KEY +#define ALLOW_KEY_OVERRIDE 0 +#else +#define ALLOW_KEY_OVERRIDE 1 +#endif + +/* Raw private key data */ +extern char private_key_data[]; +extern char private_key_len[]; +__asm__ ( ".section \".rodata\", \"a\", @progbits\n\t" +	  "\nprivate_key_data:\n\t" +#ifdef PRIVATE_KEY +	  ".incbin \"" PRIVATE_KEY "\"\n\t" +#endif /* PRIVATE_KEY */ +	  ".size private_key_data, ( . - private_key_data )\n\t" +	  ".equ private_key_len, ( . - private_key_data )\n\t" +	  ".previous\n\t" ); + +/** Private key */ +struct asn1_cursor private_key = { +	.data = private_key_data, +	.len = ( ( size_t ) private_key_len ), +}; + +/** Private key setting */ +static struct setting privkey_setting __setting ( SETTING_CRYPTO, privkey ) = { +	.name = "privkey", +	.description = "Private key", +	.tag = DHCP_EB_KEY, +	.type = &setting_type_hex, +}; + +/** + * Apply private key configuration settings + * + * @ret rc		Return status code + */ +static int privkey_apply_settings ( void ) { +	static void *key_data = NULL; +	int len; + +	/* Allow private key to be overridden only if not explicitly +	 * specified at build time. +	 */ +	if ( ALLOW_KEY_OVERRIDE ) { + +		/* Restore default private key */ +		private_key.data = private_key_data; +		private_key.len = ( ( size_t ) private_key_len ); + +		/* Fetch new private key, if any */ +		free ( key_data ); +		if ( ( len = fetch_raw_setting_copy ( NULL, &privkey_setting, +						      &key_data ) ) >= 0 ) { +			private_key.data = key_data; +			private_key.len = len; +		} +	} + +	/* Debug */ +	if ( private_key.len ) { +		DBGC ( &private_key, "PRIVKEY using %s private key:\n", +		       ( key_data ? "external" : "built-in" ) ); +		DBGC_HDA ( &private_key, 0, private_key.data, private_key.len ); +	} else { +		DBGC ( &private_key, "PRIVKEY has no private key\n" ); +	} + +	return 0; +} + +/** Private key settings applicator */ +struct settings_applicator privkey_applicator __settings_applicator = { +	.apply = privkey_apply_settings, +}; diff --git a/roms/ipxe/src/crypto/random_nz.c b/roms/ipxe/src/crypto/random_nz.c new file mode 100644 index 00000000..f1d2e187 --- /dev/null +++ b/roms/ipxe/src/crypto/random_nz.c @@ -0,0 +1,76 @@ +/* + * 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 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 ); + +/** @file + * + * Random non-zero bytes + * + * The RSA algorithm requires the generation of random non-zero bytes, + * i.e. bytes in the range [0x01,0xff]. + * + * This algorithm is designed to comply with ANS X9.82 Part 1-2006 + * Section 9.2.1.  This standard is not freely available, but most of + * the text appears to be shared with NIST SP 800-90, which can be + * downloaded from + * + *     http://csrc.nist.gov/publications/nistpubs/800-90/SP800-90revised_March2007.pdf + * + * Where possible, references are given to both documents.  In the + * case of any disagreement, ANS X9.82 takes priority over NIST SP + * 800-90.  (In particular, note that some algorithms that are + * Approved by NIST SP 800-90 are not Approved by ANS X9.82.) + */ + +#include <stddef.h> +#include <stdint.h> +#include <ipxe/rbg.h> +#include <ipxe/random_nz.h> + +/** + * Get random non-zero bytes + * + * @v data		Output buffer + * @v len		Length of output buffer + * @ret rc		Return status code + * + * This algorithm is designed to be isomorphic to the Simple Discard + * Method described in ANS X9.82 Part 1-2006 Section 9.2.1 (NIST SP + * 800-90 Section B.5.1.1). + */ +int get_random_nz ( void *data, size_t len ) { +	uint8_t *bytes = data; +	int rc; + +	while ( len ) { + +		/* Generate random byte */ +		if ( ( rc = rbg_generate ( NULL, 0, 0, bytes, 1 ) ) != 0 ) +			return rc; + +		/* Move to next byte if this byte is acceptable */ +		if ( *bytes != 0 ) { +			bytes++; +			len--; +		} +	} + +	return 0; +} diff --git a/roms/ipxe/src/crypto/rbg.c b/roms/ipxe/src/crypto/rbg.c new file mode 100644 index 00000000..e2d06978 --- /dev/null +++ b/roms/ipxe/src/crypto/rbg.c @@ -0,0 +1,115 @@ +/* + * 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 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 ); + +/** @file + * + * RBG mechanism + * + * This mechanism is designed to comply with ANS X9.82 Part 4 (April + * 2011 Draft) Section 10.  This standard is unfortunately not freely + * available. + * + * The chosen RBG design is that of a DRBG with a live entropy source + * with no conditioning function.  Only a single security strength is + * supported.  No seedfile is used since there may be no non-volatile + * storage available.  The system UUID is used as the personalisation + * string. + */ + +#include <stdint.h> +#include <string.h> +#include <ipxe/init.h> +#include <ipxe/settings.h> +#include <ipxe/uuid.h> +#include <ipxe/crypto.h> +#include <ipxe/drbg.h> +#include <ipxe/rbg.h> + +/** The RBG */ +struct random_bit_generator rbg; + +/** + * Start up RBG + * + * @ret rc		Return status code + * + * This is the RBG_Startup function defined in ANS X9.82 Part 4 (April + * 2011 Draft) Section 9.1.2.2. + */ +static int rbg_startup ( void ) { +	union uuid uuid; +	int len; +	int rc; + +	/* Try to obtain system UUID for use as personalisation +	 * string, in accordance with ANS X9.82 Part 3-2007 Section +	 * 8.5.2.  If no UUID is available, proceed without a +	 * personalisation string. +	 */ +	if ( ( len = fetch_uuid_setting ( NULL, &uuid_setting, &uuid ) ) < 0 ) { +		rc = len; +		DBGC ( &rbg, "RBG could not fetch personalisation string: " +		       "%s\n", strerror ( rc ) ); +		len = 0; +	} + +	/* Instantiate DRBG */ +	if ( ( rc = drbg_instantiate ( &rbg.state, &uuid, len ) ) != 0 ) { +		DBGC ( &rbg, "RBG could not instantiate DRBG: %s\n", +		       strerror ( rc ) ); +		return rc; +	} + +	return 0; +} + +/** + * Shut down RBG + * + */ +static void rbg_shutdown ( void ) { + +	/* Uninstantiate DRBG */ +	drbg_uninstantiate ( &rbg.state ); +} + +/** RBG startup function */ +static void rbg_startup_fn ( void ) { + +	/* Start up RBG.  There is no way to report an error at this +	 * stage, but a failed startup will result in an invalid DRBG +	 * that refuses to generate bits. +	 */ +	rbg_startup(); +} + +/** RBG shutdown function */ +static void rbg_shutdown_fn ( int booting __unused ) { + +	/* Shut down RBG */ +	rbg_shutdown(); +} + +/** RBG startup table entry */ +struct startup_fn startup_rbg __startup_fn ( STARTUP_NORMAL ) = { +	.startup = rbg_startup_fn, +	.shutdown = rbg_shutdown_fn, +}; diff --git a/roms/ipxe/src/crypto/rootcert.c b/roms/ipxe/src/crypto/rootcert.c new file mode 100644 index 00000000..ae28905a --- /dev/null +++ b/roms/ipxe/src/crypto/rootcert.c @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2007 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 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 <stdlib.h> +#include <ipxe/crypto.h> +#include <ipxe/sha256.h> +#include <ipxe/x509.h> +#include <ipxe/settings.h> +#include <ipxe/dhcp.h> +#include <ipxe/init.h> +#include <ipxe/rootcert.h> + +/** @file + * + * Root certificate store + * + */ + +/** Length of a root certificate fingerprint */ +#define FINGERPRINT_LEN SHA256_DIGEST_SIZE + +/* Allow trusted certificates to be overridden if not explicitly specified */ +#ifdef TRUSTED +#define ALLOW_TRUST_OVERRIDE 0 +#else +#define ALLOW_TRUST_OVERRIDE 1 +#endif + +/* Use iPXE root CA if no trusted certificates are explicitly specified */ +#ifndef TRUSTED +#define TRUSTED								\ +	/* iPXE root CA */						\ +	0x9f, 0xaf, 0x71, 0x7b, 0x7f, 0x8c, 0xa2, 0xf9, 0x3c, 0x25,	\ +	0x6c, 0x79, 0xf8, 0xac, 0x55, 0x91, 0x89, 0x5d, 0x66, 0xd1,	\ +	0xff, 0x3b, 0xee, 0x63, 0x97, 0xa7, 0x0d, 0x29, 0xc6, 0x5e,	\ +	0xed, 0x1a, +#endif + +/** Root certificate fingerprints */ +static const uint8_t fingerprints[] = { TRUSTED }; + +/** Root certificate fingerprint setting */ +static struct setting trust_setting __setting ( SETTING_CRYPTO, trust ) = { +	.name = "trust", +	.description = "Trusted root certificate fingerprints", +	.tag = DHCP_EB_TRUST, +	.type = &setting_type_hex, +}; + +/** Root certificates */ +struct x509_root root_certificates = { +	.digest = &sha256_algorithm, +	.count = ( sizeof ( fingerprints ) / FINGERPRINT_LEN ), +	.fingerprints = fingerprints, +}; + +/** + * Initialise root certificate + * + * The list of trusted root certificates can be specified at build + * time using the TRUST= build parameter.  If no certificates are + * specified, then the default iPXE root CA certificate is trusted. + * + * If no certificates were explicitly specified, then we allow the + * list of trusted root certificate fingerprints to be overridden + * using the "trust" setting, but only at the point of iPXE + * initialisation.  This prevents untrusted sources of settings + * (e.g. DHCP) from subverting the chain of trust, while allowing + * trustworthy sources (e.g. VMware GuestInfo or non-volatile stored + * options) to specify the trusted root certificate without requiring + * a rebuild. + */ +static void rootcert_init ( void ) { +	void *external = NULL; +	int len; + +	/* Allow trusted root certificates to be overridden only if +	 * not explicitly specified at build time. +	 */ +	if ( ALLOW_TRUST_OVERRIDE ) { + +		/* Fetch copy of "trust" setting, if it exists.  This +		 * memory will never be freed. +		 */ +		if ( ( len = fetch_raw_setting_copy ( NULL, &trust_setting, +						      &external ) ) >= 0 ) { +			root_certificates.fingerprints = external; +			root_certificates.count = ( len / FINGERPRINT_LEN ); +		} +	} + +	DBGC ( &root_certificates, "ROOTCERT using %d %s certificate(s):\n", +	       root_certificates.count, ( external ? "external" : "built-in" )); +	DBGC_HDA ( &root_certificates, 0, root_certificates.fingerprints, +		   ( root_certificates.count * FINGERPRINT_LEN ) ); +} + +/** Root certificate initialiser */ +struct init_fn rootcert_init_fn __init_fn ( INIT_LATE ) = { +	.initialise = rootcert_init, +}; diff --git a/roms/ipxe/src/crypto/rsa.c b/roms/ipxe/src/crypto/rsa.c new file mode 100644 index 00000000..0ab7b2ad --- /dev/null +++ b/roms/ipxe/src/crypto/rsa.c @@ -0,0 +1,705 @@ +/* + * 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 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 <stdarg.h> +#include <string.h> +#include <errno.h> +#include <ipxe/asn1.h> +#include <ipxe/crypto.h> +#include <ipxe/bigint.h> +#include <ipxe/random_nz.h> +#include <ipxe/md5.h> +#include <ipxe/sha1.h> +#include <ipxe/sha256.h> +#include <ipxe/rsa.h> + +/** @file + * + * RSA public-key cryptography + * + * RSA is documented in RFC 3447. + */ + +/* Disambiguate the various error causes */ +#define EACCES_VERIFY \ +	__einfo_error ( EINFO_EACCES_VERIFY ) +#define EINFO_EACCES_VERIFY \ +	__einfo_uniqify ( EINFO_EACCES, 0x01, "RSA signature incorrect" ) + +/** "rsaEncryption" object identifier */ +static uint8_t oid_rsa_encryption[] = { ASN1_OID_RSAENCRYPTION }; + +/** "md5WithRSAEncryption" object identifier */ +static uint8_t oid_md5_with_rsa_encryption[] = +	{ ASN1_OID_MD5WITHRSAENCRYPTION }; + +/** "sha1WithRSAEncryption" object identifier */ +static uint8_t oid_sha1_with_rsa_encryption[] = +	{ ASN1_OID_SHA1WITHRSAENCRYPTION }; + +/** "sha256WithRSAEncryption" object identifier */ +static uint8_t oid_sha256_with_rsa_encryption[] = +	{ ASN1_OID_SHA256WITHRSAENCRYPTION }; + +/** "rsaEncryption" OID-identified algorithm */ +struct asn1_algorithm rsa_encryption_algorithm __asn1_algorithm = { +	.name = "rsaEncryption", +	.pubkey = &rsa_algorithm, +	.digest = NULL, +	.oid = ASN1_OID_CURSOR ( oid_rsa_encryption ), +}; + +/** "md5WithRSAEncryption" OID-identified algorithm */ +struct asn1_algorithm md5_with_rsa_encryption_algorithm __asn1_algorithm = { +	.name = "md5WithRSAEncryption", +	.pubkey = &rsa_algorithm, +	.digest = &md5_algorithm, +	.oid = ASN1_OID_CURSOR ( oid_md5_with_rsa_encryption ), +}; + +/** "sha1WithRSAEncryption" OID-identified algorithm */ +struct asn1_algorithm sha1_with_rsa_encryption_algorithm __asn1_algorithm = { +	.name = "sha1WithRSAEncryption", +	.pubkey = &rsa_algorithm, +	.digest = &sha1_algorithm, +	.oid = ASN1_OID_CURSOR ( oid_sha1_with_rsa_encryption ), +}; + +/** "sha256WithRSAEncryption" OID-identified algorithm */ +struct asn1_algorithm sha256_with_rsa_encryption_algorithm __asn1_algorithm = { +	.name = "sha256WithRSAEncryption", +	.pubkey = &rsa_algorithm, +	.digest = &sha256_algorithm, +	.oid = ASN1_OID_CURSOR ( oid_sha256_with_rsa_encryption ), +}; + +/** MD5 digestInfo prefix */ +static const uint8_t rsa_md5_prefix_data[] = +	{ RSA_DIGESTINFO_PREFIX ( MD5_DIGEST_SIZE, ASN1_OID_MD5 ) }; + +/** SHA-1 digestInfo prefix */ +static const uint8_t rsa_sha1_prefix_data[] = +	{ RSA_DIGESTINFO_PREFIX ( SHA1_DIGEST_SIZE, ASN1_OID_SHA1 ) }; + +/** SHA-256 digestInfo prefix */ +static const uint8_t rsa_sha256_prefix_data[] = +	{ RSA_DIGESTINFO_PREFIX ( SHA256_DIGEST_SIZE, ASN1_OID_SHA256 ) }; + +/** MD5 digestInfo prefix */ +struct rsa_digestinfo_prefix rsa_md5_prefix __rsa_digestinfo_prefix = { +	.digest = &md5_algorithm, +	.data = rsa_md5_prefix_data, +	.len = sizeof ( rsa_md5_prefix_data ), +}; + +/** SHA-1 digestInfo prefix */ +struct rsa_digestinfo_prefix rsa_sha1_prefix __rsa_digestinfo_prefix = { +	.digest = &sha1_algorithm, +	.data = rsa_sha1_prefix_data, +	.len = sizeof ( rsa_sha1_prefix_data ), +}; + +/** SHA-256 digestInfo prefix */ +struct rsa_digestinfo_prefix rsa_sha256_prefix __rsa_digestinfo_prefix = { +	.digest = &sha256_algorithm, +	.data = rsa_sha256_prefix_data, +	.len = sizeof ( rsa_sha256_prefix_data ), +}; + +/** + * Identify RSA prefix + * + * @v digest		Digest algorithm + * @ret prefix		RSA prefix, or NULL + */ +static struct rsa_digestinfo_prefix * +rsa_find_prefix ( struct digest_algorithm *digest ) { +	struct rsa_digestinfo_prefix *prefix; + +	for_each_table_entry ( prefix, RSA_DIGESTINFO_PREFIXES ) { +		if ( prefix->digest == digest ) +			return prefix; +	} +	return NULL; +} + +/** + * Free RSA dynamic storage + * + * @v context		RSA context + */ +static void rsa_free ( struct rsa_context *context ) { + +	free ( context->dynamic ); +	context->dynamic = NULL; +} + +/** + * Allocate RSA dynamic storage + * + * @v context		RSA context + * @v modulus_len	Modulus length + * @v exponent_len	Exponent length + * @ret rc		Return status code + */ +static int rsa_alloc ( struct rsa_context *context, size_t modulus_len, +		       size_t exponent_len ) { +	unsigned int size = bigint_required_size ( modulus_len ); +	unsigned int exponent_size = bigint_required_size ( exponent_len ); +	bigint_t ( size ) *modulus; +	bigint_t ( exponent_size ) *exponent; +	size_t tmp_len = bigint_mod_exp_tmp_len ( modulus, exponent ); +	struct { +		bigint_t ( size ) modulus; +		bigint_t ( exponent_size ) exponent; +		bigint_t ( size ) input; +		bigint_t ( size ) output; +		uint8_t tmp[tmp_len]; +	} __attribute__ (( packed )) *dynamic; + +	/* Free any existing dynamic storage */ +	rsa_free ( context ); + +	/* Allocate dynamic storage */ +	dynamic = malloc ( sizeof ( *dynamic ) ); +	if ( ! dynamic ) +		return -ENOMEM; + +	/* Assign dynamic storage */ +	context->dynamic = dynamic; +	context->modulus0 = &dynamic->modulus.element[0]; +	context->size = size; +	context->max_len = modulus_len; +	context->exponent0 = &dynamic->exponent.element[0]; +	context->exponent_size = exponent_size; +	context->input0 = &dynamic->input.element[0]; +	context->output0 = &dynamic->output.element[0]; +	context->tmp = &dynamic->tmp; + +	return 0; +} + +/** + * Parse RSA integer + * + * @v integer		Integer to fill in + * @v raw		ASN.1 cursor + * @ret rc		Return status code + */ +static int rsa_parse_integer ( struct asn1_cursor *integer, +			       const struct asn1_cursor *raw ) { + +	/* Enter integer */ +	memcpy ( integer, raw, sizeof ( *integer ) ); +	asn1_enter ( integer, ASN1_INTEGER ); + +	/* Skip initial sign byte if applicable */ +	if ( ( integer->len > 1 ) && +	     ( *( ( uint8_t * ) integer->data ) == 0x00 ) ) { +		integer->data++; +		integer->len--; +	} + +	/* Fail if cursor or integer are invalid */ +	if ( ! integer->len ) +		return -EINVAL; + +	return 0; +} + +/** + * Parse RSA modulus and exponent + * + * @v modulus		Modulus to fill in + * @v exponent		Exponent to fill in + * @v raw		ASN.1 cursor + * @ret rc		Return status code + */ +static int rsa_parse_mod_exp ( struct asn1_cursor *modulus, +			       struct asn1_cursor *exponent, +			       const struct asn1_cursor *raw ) { +	struct asn1_bit_string bits; +	struct asn1_cursor cursor; +	int is_private; +	int rc; + +	/* Enter subjectPublicKeyInfo/RSAPrivateKey */ +	memcpy ( &cursor, raw, sizeof ( cursor ) ); +	asn1_enter ( &cursor, ASN1_SEQUENCE ); + +	/* Determine key format */ +	if ( asn1_type ( &cursor ) == ASN1_INTEGER ) { + +		/* Private key */ +		is_private = 1; + +		/* Skip version */ +		asn1_skip_any ( &cursor ); + +	} else { + +		/* Public key */ +		is_private = 0; + +		/* Skip algorithm */ +		asn1_skip ( &cursor, ASN1_SEQUENCE ); + +		/* Enter subjectPublicKey */ +		if ( ( rc = asn1_integral_bit_string ( &cursor, &bits ) ) != 0 ) +			return rc; +		cursor.data = bits.data; +		cursor.len = bits.len; + +		/* Enter RSAPublicKey */ +		asn1_enter ( &cursor, ASN1_SEQUENCE ); +	} + +	/* Extract modulus */ +	if ( ( rc = rsa_parse_integer ( modulus, &cursor ) ) != 0 ) +		return rc; +	asn1_skip_any ( &cursor ); + +	/* Skip public exponent, if applicable */ +	if ( is_private ) +		asn1_skip ( &cursor, ASN1_INTEGER ); + +	/* Extract publicExponent/privateExponent */ +	if ( ( rc = rsa_parse_integer ( exponent, &cursor ) ) != 0 ) +		return rc; + +	return 0; +} + +/** + * Initialise RSA cipher + * + * @v ctx		RSA context + * @v key		Key + * @v key_len		Length of key + * @ret rc		Return status code + */ +static int rsa_init ( void *ctx, const void *key, size_t key_len ) { +	struct rsa_context *context = ctx; +	struct asn1_cursor modulus; +	struct asn1_cursor exponent; +	struct asn1_cursor cursor; +	int rc; + +	/* Initialise context */ +	memset ( context, 0, sizeof ( *context ) ); + +	/* Initialise cursor */ +	cursor.data = key; +	cursor.len = key_len; + +	/* Parse modulus and exponent */ +	if ( ( rc = rsa_parse_mod_exp ( &modulus, &exponent, &cursor ) ) != 0 ){ +		DBGC ( context, "RSA %p invalid modulus/exponent:\n", context ); +		DBGC_HDA ( context, 0, cursor.data, cursor.len ); +		goto err_parse; +	} + +	DBGC ( context, "RSA %p modulus:\n", context ); +	DBGC_HDA ( context, 0, modulus.data, modulus.len ); +	DBGC ( context, "RSA %p exponent:\n", context ); +	DBGC_HDA ( context, 0, exponent.data, exponent.len ); + +	/* Allocate dynamic storage */ +	if ( ( rc = rsa_alloc ( context, modulus.len, exponent.len ) ) != 0 ) +		goto err_alloc; + +	/* Construct big integers */ +	bigint_init ( ( ( bigint_t ( context->size ) * ) context->modulus0 ), +		      modulus.data, modulus.len ); +	bigint_init ( ( ( bigint_t ( context->exponent_size ) * ) +			context->exponent0 ), exponent.data, exponent.len ); + +	return 0; + +	rsa_free ( context ); + err_alloc: + err_parse: +	return rc; +} + +/** + * Calculate RSA maximum output length + * + * @v ctx		RSA context + * @ret max_len		Maximum output length + */ +static size_t rsa_max_len ( void *ctx ) { +	struct rsa_context *context = ctx; + +	return context->max_len; +} + +/** + * Perform RSA cipher operation + * + * @v context		RSA context + * @v in		Input buffer + * @v out		Output buffer + */ +static void rsa_cipher ( struct rsa_context *context, +			 const void *in, void *out ) { +	bigint_t ( context->size ) *input = ( ( void * ) context->input0 ); +	bigint_t ( context->size ) *output = ( ( void * ) context->output0 ); +	bigint_t ( context->size ) *modulus = ( ( void * ) context->modulus0 ); +	bigint_t ( context->exponent_size ) *exponent = +		( ( void * ) context->exponent0 ); + +	/* Initialise big integer */ +	bigint_init ( input, in, context->max_len ); + +	/* Perform modular exponentiation */ +	bigint_mod_exp ( input, modulus, exponent, output, context->tmp ); + +	/* Copy out result */ +	bigint_done ( output, out, context->max_len ); +} + +/** + * Encrypt using RSA + * + * @v ctx		RSA context + * @v plaintext		Plaintext + * @v plaintext_len	Length of plaintext + * @v ciphertext	Ciphertext + * @ret ciphertext_len	Length of ciphertext, or negative error + */ +static int rsa_encrypt ( void *ctx, const void *plaintext, +			 size_t plaintext_len, void *ciphertext ) { +	struct rsa_context *context = ctx; +	void *temp; +	uint8_t *encoded; +	size_t max_len = ( context->max_len - 11 ); +	size_t random_nz_len = ( max_len - plaintext_len + 8 ); +	int rc; + +	/* Sanity check */ +	if ( plaintext_len > max_len ) { +		DBGC ( context, "RSA %p plaintext too long (%zd bytes, max " +		       "%zd)\n", context, plaintext_len, max_len ); +		return -ERANGE; +	} +	DBGC ( context, "RSA %p encrypting:\n", context ); +	DBGC_HDA ( context, 0, plaintext, plaintext_len ); + +	/* Construct encoded message (using the big integer output +	 * buffer as temporary storage) +	 */ +	temp = context->output0; +	encoded = temp; +	encoded[0] = 0x00; +	encoded[1] = 0x02; +	if ( ( rc = get_random_nz ( &encoded[2], random_nz_len ) ) != 0 ) { +		DBGC ( context, "RSA %p could not generate random data: %s\n", +		       context, strerror ( rc ) ); +		return rc; +	} +	encoded[ 2 + random_nz_len ] = 0x00; +	memcpy ( &encoded[ context->max_len - plaintext_len ], +		 plaintext, plaintext_len ); + +	/* Encipher the encoded message */ +	rsa_cipher ( context, encoded, ciphertext ); +	DBGC ( context, "RSA %p encrypted:\n", context ); +	DBGC_HDA ( context, 0, ciphertext, context->max_len ); + +	return context->max_len; +} + +/** + * Decrypt using RSA + * + * @v ctx		RSA context + * @v ciphertext	Ciphertext + * @v ciphertext_len	Ciphertext length + * @v plaintext		Plaintext + * @ret plaintext_len	Plaintext length, or negative error + */ +static int rsa_decrypt ( void *ctx, const void *ciphertext, +			 size_t ciphertext_len, void *plaintext ) { +	struct rsa_context *context = ctx; +	void *temp; +	uint8_t *encoded; +	uint8_t *end; +	uint8_t *zero; +	uint8_t *start; +	size_t plaintext_len; + +	/* Sanity check */ +	if ( ciphertext_len != context->max_len ) { +		DBGC ( context, "RSA %p ciphertext incorrect length (%zd " +		       "bytes, should be %zd)\n", +		       context, ciphertext_len, context->max_len ); +		return -ERANGE; +	} +	DBGC ( context, "RSA %p decrypting:\n", context ); +	DBGC_HDA ( context, 0, ciphertext, ciphertext_len ); + +	/* Decipher the message (using the big integer input buffer as +	 * temporary storage) +	 */ +	temp = context->input0; +	encoded = temp; +	rsa_cipher ( context, ciphertext, encoded ); + +	/* Parse the message */ +	end = ( encoded + context->max_len ); +	if ( ( encoded[0] != 0x00 ) || ( encoded[1] != 0x02 ) ) +		goto invalid; +	zero = memchr ( &encoded[2], 0, ( end - &encoded[2] ) ); +	if ( ! zero ) +		goto invalid; +	start = ( zero + 1 ); +	plaintext_len = ( end - start ); + +	/* Copy out message */ +	memcpy ( plaintext, start, plaintext_len ); +	DBGC ( context, "RSA %p decrypted:\n", context ); +	DBGC_HDA ( context, 0, plaintext, plaintext_len ); + +	return plaintext_len; + + invalid: +	DBGC ( context, "RSA %p invalid decrypted message:\n", context ); +	DBGC_HDA ( context, 0, encoded, context->max_len ); +	return -EINVAL; +} + +/** + * Encode RSA digest + * + * @v context		RSA context + * @v digest		Digest algorithm + * @v value		Digest value + * @v encoded		Encoded digest + * @ret rc		Return status code + */ +static int rsa_encode_digest ( struct rsa_context *context, +			       struct digest_algorithm *digest, +			       const void *value, void *encoded ) { +	struct rsa_digestinfo_prefix *prefix; +	size_t digest_len = digest->digestsize; +	uint8_t *temp = encoded; +	size_t digestinfo_len; +	size_t max_len; +	size_t pad_len; + +	/* Identify prefix */ +	prefix = rsa_find_prefix ( digest ); +	if ( ! prefix ) { +		DBGC ( context, "RSA %p has no prefix for %s\n", +		       context, digest->name ); +		return -ENOTSUP; +	} +	digestinfo_len = ( prefix->len + digest_len ); + +	/* Sanity check */ +	max_len = ( context->max_len - 11 ); +	if ( digestinfo_len > max_len ) { +		DBGC ( context, "RSA %p %s digestInfo too long (%zd bytes, max" +		       "%zd)\n", +		       context, digest->name, digestinfo_len, max_len ); +		return -ERANGE; +	} +	DBGC ( context, "RSA %p encoding %s digest:\n", +	       context, digest->name ); +	DBGC_HDA ( context, 0, value, digest_len ); + +	/* Construct encoded message */ +	*(temp++) = 0x00; +	*(temp++) = 0x01; +	pad_len = ( max_len - digestinfo_len + 8 ); +	memset ( temp, 0xff, pad_len ); +	temp += pad_len; +	*(temp++) = 0x00; +	memcpy ( temp, prefix->data, prefix->len ); +	temp += prefix->len; +	memcpy ( temp, value, digest_len ); +	temp += digest_len; +	assert ( temp == ( encoded + context->max_len ) ); +	DBGC ( context, "RSA %p encoded %s digest:\n", context, digest->name ); +	DBGC_HDA ( context, 0, encoded, context->max_len ); + +	return 0; +} + +/** + * Sign digest value using RSA + * + * @v ctx		RSA context + * @v digest		Digest algorithm + * @v value		Digest value + * @v signature		Signature + * @ret signature_len	Signature length, or negative error + */ +static int rsa_sign ( void *ctx, struct digest_algorithm *digest, +		      const void *value, void *signature ) { +	struct rsa_context *context = ctx; +	void *temp; +	int rc; + +	DBGC ( context, "RSA %p signing %s digest:\n", context, digest->name ); +	DBGC_HDA ( context, 0, value, digest->digestsize ); + +	/* Encode digest (using the big integer output buffer as +	 * temporary storage) +	 */ +	temp = context->output0; +	if ( ( rc = rsa_encode_digest ( context, digest, value, temp ) ) != 0 ) +		return rc; + +	/* Encipher the encoded digest */ +	rsa_cipher ( context, temp, signature ); +	DBGC ( context, "RSA %p signed %s digest:\n", context, digest->name ); +	DBGC_HDA ( context, 0, signature, context->max_len ); + +	return context->max_len; +} + +/** + * Verify signed digest value using RSA + * + * @v ctx		RSA context + * @v digest		Digest algorithm + * @v value		Digest value + * @v signature		Signature + * @v signature_len	Signature length + * @ret rc		Return status code + */ +static int rsa_verify ( void *ctx, struct digest_algorithm *digest, +			const void *value, const void *signature, +			size_t signature_len ) { +	struct rsa_context *context = ctx; +	void *temp; +	void *expected; +	void *actual; +	int rc; + +	/* Sanity check */ +	if ( signature_len != context->max_len ) { +		DBGC ( context, "RSA %p signature incorrect length (%zd " +		       "bytes, should be %zd)\n", +		       context, signature_len, context->max_len ); +		return -ERANGE; +	} +	DBGC ( context, "RSA %p verifying %s digest:\n", +	       context, digest->name ); +	DBGC_HDA ( context, 0, value, digest->digestsize ); +	DBGC_HDA ( context, 0, signature, signature_len ); + +	/* Decipher the signature (using the big integer input buffer +	 * as temporary storage) +	 */ +	temp = context->input0; +	expected = temp; +	rsa_cipher ( context, signature, expected ); +	DBGC ( context, "RSA %p deciphered signature:\n", context ); +	DBGC_HDA ( context, 0, expected, context->max_len ); + +	/* Encode digest (using the big integer output buffer as +	 * temporary storage) +	 */ +	temp = context->output0; +	actual = temp; +	if ( ( rc = rsa_encode_digest ( context, digest, value, actual ) ) !=0 ) +		return rc; + +	/* Verify the signature */ +	if ( memcmp ( actual, expected, context->max_len ) != 0 ) { +		DBGC ( context, "RSA %p signature verification failed\n", +		       context ); +		return -EACCES_VERIFY; +	} + +	DBGC ( context, "RSA %p signature verified successfully\n", context ); +	return 0; +} + +/** + * Finalise RSA cipher + * + * @v ctx		RSA context + */ +static void rsa_final ( void *ctx ) { +	struct rsa_context *context = ctx; + +	rsa_free ( context ); +} + +/** + * Check for matching RSA public/private key pair + * + * @v private_key	Private key + * @v private_key_len	Private key length + * @v public_key	Public key + * @v public_key_len	Public key length + * @ret rc		Return status code + */ +static int rsa_match ( const void *private_key, size_t private_key_len, +		       const void *public_key, size_t public_key_len ) { +	struct asn1_cursor private_modulus; +	struct asn1_cursor private_exponent; +	struct asn1_cursor private_cursor; +	struct asn1_cursor public_modulus; +	struct asn1_cursor public_exponent; +	struct asn1_cursor public_cursor; +	int rc; + +	/* Initialise cursors */ +	private_cursor.data = private_key; +	private_cursor.len = private_key_len; +	public_cursor.data = public_key; +	public_cursor.len = public_key_len; + +	/* Parse moduli and exponents */ +	if ( ( rc = rsa_parse_mod_exp ( &private_modulus, &private_exponent, +					&private_cursor ) ) != 0 ) +		return rc; +	if ( ( rc = rsa_parse_mod_exp ( &public_modulus, &public_exponent, +					&public_cursor ) ) != 0 ) +		return rc; + +	/* Compare moduli */ +	if ( asn1_compare ( &private_modulus, &public_modulus ) != 0 ) +		return -ENOTTY; + +	return 0; +} + +/** RSA public-key algorithm */ +struct pubkey_algorithm rsa_algorithm = { +	.name		= "rsa", +	.ctxsize	= sizeof ( struct rsa_context ), +	.init		= rsa_init, +	.max_len	= rsa_max_len, +	.encrypt	= rsa_encrypt, +	.decrypt	= rsa_decrypt, +	.sign		= rsa_sign, +	.verify		= rsa_verify, +	.final		= rsa_final, +	.match		= rsa_match, +}; diff --git a/roms/ipxe/src/crypto/sha1.c b/roms/ipxe/src/crypto/sha1.c new file mode 100644 index 00000000..e1bef669 --- /dev/null +++ b/roms/ipxe/src/crypto/sha1.c @@ -0,0 +1,272 @@ +/* + * 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 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 ); + +/** @file + * + * SHA-1 algorithm + * + */ + +#include <stdint.h> +#include <string.h> +#include <byteswap.h> +#include <assert.h> +#include <ipxe/rotate.h> +#include <ipxe/crypto.h> +#include <ipxe/asn1.h> +#include <ipxe/sha1.h> + +/** SHA-1 variables */ +struct sha1_variables { +	/* This layout matches that of struct sha1_digest_data, +	 * allowing for efficient endianness-conversion, +	 */ +	uint32_t a; +	uint32_t b; +	uint32_t c; +	uint32_t d; +	uint32_t e; +	uint32_t w[80]; +} __attribute__ (( packed )); + +/** + * f(a,b,c,d) for steps 0 to 19 + * + * @v v		SHA-1 variables + * @ret f	f(a,b,c,d) + */ +static uint32_t sha1_f_0_19 ( struct sha1_variables *v ) { +	return ( ( v->b & v->c ) | ( (~v->b) & v->d ) ); +} + +/** + * f(a,b,c,d) for steps 20 to 39 and 60 to 79 + * + * @v v		SHA-1 variables + * @ret f	f(a,b,c,d) + */ +static uint32_t sha1_f_20_39_60_79 ( struct sha1_variables *v ) { +	return ( v->b ^ v->c ^ v->d ); +} + +/** + * f(a,b,c,d) for steps 40 to 59 + * + * @v v		SHA-1 variables + * @ret f	f(a,b,c,d) + */ +static uint32_t sha1_f_40_59 ( struct sha1_variables *v ) { +	return ( ( v->b & v->c ) | ( v->b & v->d ) | ( v->c & v->d ) ); +} + +/** An SHA-1 step function */ +struct sha1_step { +	/** +	 * Calculate f(a,b,c,d) +	 * +	 * @v v		SHA-1 variables +	 * @ret f	f(a,b,c,d) +	 */ +	uint32_t ( * f ) ( struct sha1_variables *v ); +	/** Constant k */ +	uint32_t k; +}; + +/** SHA-1 steps */ +static struct sha1_step sha1_steps[4] = { +	/** 0 to 19 */ +	{ .f = sha1_f_0_19,		.k = 0x5a827999 }, +	/** 20 to 39 */ +	{ .f = sha1_f_20_39_60_79,	.k = 0x6ed9eba1 }, +	/** 40 to 59 */ +	{ .f = sha1_f_40_59,		.k = 0x8f1bbcdc }, +	/** 60 to 79 */ +	{ .f = sha1_f_20_39_60_79,	.k = 0xca62c1d6 }, +}; + +/** + * Initialise SHA-1 algorithm + * + * @v ctx		SHA-1 context + */ +static void sha1_init ( void *ctx ) { +	struct sha1_context *context = ctx; + +	context->ddd.dd.digest.h[0] = cpu_to_be32 ( 0x67452301 ); +	context->ddd.dd.digest.h[1] = cpu_to_be32 ( 0xefcdab89 ); +	context->ddd.dd.digest.h[2] = cpu_to_be32 ( 0x98badcfe ); +	context->ddd.dd.digest.h[3] = cpu_to_be32 ( 0x10325476 ); +	context->ddd.dd.digest.h[4] = cpu_to_be32 ( 0xc3d2e1f0 ); +	context->len = 0; +} + +/** + * Calculate SHA-1 digest of accumulated data + * + * @v context		SHA-1 context + */ +static void sha1_digest ( struct sha1_context *context ) { +        union { +		union sha1_digest_data_dwords ddd; +		struct sha1_variables v; +	} u; +	uint32_t *a = &u.v.a; +	uint32_t *b = &u.v.b; +	uint32_t *c = &u.v.c; +	uint32_t *d = &u.v.d; +	uint32_t *e = &u.v.e; +	uint32_t *w = u.v.w; +	uint32_t f; +	uint32_t k; +	uint32_t temp; +	struct sha1_step *step; +	unsigned int i; + +	/* Sanity checks */ +	assert ( ( context->len % sizeof ( context->ddd.dd.data ) ) == 0 ); +	linker_assert ( &u.ddd.dd.digest.h[0] == a, sha1_bad_layout ); +	linker_assert ( &u.ddd.dd.digest.h[1] == b, sha1_bad_layout ); +	linker_assert ( &u.ddd.dd.digest.h[2] == c, sha1_bad_layout ); +	linker_assert ( &u.ddd.dd.digest.h[3] == d, sha1_bad_layout ); +	linker_assert ( &u.ddd.dd.digest.h[4] == e, sha1_bad_layout ); +	linker_assert ( &u.ddd.dd.data.dword[0] == w, sha1_bad_layout ); + +	DBGC ( context, "SHA1 digesting:\n" ); +	DBGC_HDA ( context, 0, &context->ddd.dd.digest, +		   sizeof ( context->ddd.dd.digest ) ); +	DBGC_HDA ( context, context->len, &context->ddd.dd.data, +		   sizeof ( context->ddd.dd.data ) ); + +	/* Convert h[0..4] to host-endian, and initialise a, b, c, d, +	 * e, and w[0..15] +	 */ +	for ( i = 0 ; i < ( sizeof ( u.ddd.dword ) / +			    sizeof ( u.ddd.dword[0] ) ) ; i++ ) { +		be32_to_cpus ( &context->ddd.dword[i] ); +		u.ddd.dword[i] = context->ddd.dword[i]; +	} + +	/* Initialise w[16..79] */ +	for ( i = 16 ; i < 80 ; i++ ) +		w[i] = rol32 ( ( w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16] ), 1 ); + +	/* Main loop */ +	for ( i = 0 ; i < 80 ; i++ ) { +		step = &sha1_steps[ i / 20 ]; +		f = step->f ( &u.v ); +		k = step->k; +		temp = ( rol32 ( *a, 5 ) + f + *e + k + w[i] ); +		*e = *d; +		*d = *c; +		*c = rol32 ( *b, 30 ); +		*b = *a; +		*a = temp; +		DBGC2 ( context, "%2d : %08x %08x %08x %08x %08x\n", +			i, *a, *b, *c, *d, *e ); +	} + +	/* Add chunk to hash and convert back to big-endian */ +	for ( i = 0 ; i < 5 ; i++ ) { +		context->ddd.dd.digest.h[i] = +			cpu_to_be32 ( context->ddd.dd.digest.h[i] + +				      u.ddd.dd.digest.h[i] ); +	} + +	DBGC ( context, "SHA1 digested:\n" ); +	DBGC_HDA ( context, 0, &context->ddd.dd.digest, +		   sizeof ( context->ddd.dd.digest ) ); +} + +/** + * Accumulate data with SHA-1 algorithm + * + * @v ctx		SHA-1 context + * @v data		Data + * @v len		Length of data + */ +static void sha1_update ( void *ctx, const void *data, size_t len ) { +	struct sha1_context *context = ctx; +	const uint8_t *byte = data; +	size_t offset; + +	/* Accumulate data a byte at a time, performing the digest +	 * whenever we fill the data buffer +	 */ +	while ( len-- ) { +		offset = ( context->len % sizeof ( context->ddd.dd.data ) ); +		context->ddd.dd.data.byte[offset] = *(byte++); +		context->len++; +		if ( ( context->len % sizeof ( context->ddd.dd.data ) ) == 0 ) +			sha1_digest ( context ); +	} +} + +/** + * Generate SHA-1 digest + * + * @v ctx		SHA-1 context + * @v out		Output buffer + */ +static void sha1_final ( void *ctx, void *out ) { +	struct sha1_context *context = ctx; +	uint64_t len_bits; +	uint8_t pad; + +	/* Record length before pre-processing */ +	len_bits = cpu_to_be64 ( ( ( uint64_t ) context->len ) * 8 ); + +	/* Pad with a single "1" bit followed by as many "0" bits as required */ +	pad = 0x80; +	do { +		sha1_update ( ctx, &pad, sizeof ( pad ) ); +		pad = 0x00; +	} while ( ( context->len % sizeof ( context->ddd.dd.data ) ) != +		  offsetof ( typeof ( context->ddd.dd.data ), final.len ) ); + +	/* Append length (in bits) */ +	sha1_update ( ctx, &len_bits, sizeof ( len_bits ) ); +	assert ( ( context->len % sizeof ( context->ddd.dd.data ) ) == 0 ); + +	/* Copy out final digest */ +	memcpy ( out, &context->ddd.dd.digest, +		 sizeof ( context->ddd.dd.digest ) ); +} + +/** SHA-1 algorithm */ +struct digest_algorithm sha1_algorithm = { +	.name		= "sha1", +	.ctxsize	= sizeof ( struct sha1_context ), +	.blocksize	= sizeof ( union sha1_block ), +	.digestsize	= sizeof ( struct sha1_digest ), +	.init		= sha1_init, +	.update		= sha1_update, +	.final		= sha1_final, +}; + +/** "sha1" object identifier */ +static uint8_t oid_sha1[] = { ASN1_OID_SHA1 }; + +/** "sha1" OID-identified algorithm */ +struct asn1_algorithm oid_sha1_algorithm __asn1_algorithm = { +	.name = "sha1", +	.digest = &sha1_algorithm, +	.oid = ASN1_OID_CURSOR ( oid_sha1 ), +}; diff --git a/roms/ipxe/src/crypto/sha1extra.c b/roms/ipxe/src/crypto/sha1extra.c new file mode 100644 index 00000000..cec0d35e --- /dev/null +++ b/roms/ipxe/src/crypto/sha1extra.c @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2009 Joshua Oreman <oremanj@rwcr.net>. + * + * 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 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 <string.h> +#include <ipxe/crypto.h> +#include <ipxe/sha1.h> +#include <ipxe/hmac.h> +#include <stdint.h> +#include <byteswap.h> + +/** + * SHA1 pseudorandom function for creating derived keys + * + * @v key	Master key with which this call is associated + * @v key_len	Length of key + * @v label	NUL-terminated ASCII string describing purpose of PRF data + * @v data	Further data that should be included in the PRF + * @v data_len	Length of further PRF data + * @v prf_len	Bytes of PRF to generate + * @ret prf	Pseudorandom function bytes + * + * This is the PRF variant used by 802.11, defined in IEEE 802.11-2007 + * 8.5.5.1. EAP-FAST uses a different SHA1-based PRF, and TLS uses an + * MD5-based PRF. + */ +void prf_sha1 ( const void *key, size_t key_len, const char *label, +		const void *data, size_t data_len, void *prf, size_t prf_len ) +{ +	u32 blk; +	u8 keym[key_len];	/* modifiable copy of key */ +	u8 in[strlen ( label ) + 1 + data_len + 1]; /* message to HMAC */ +	u8 *in_blknr;		/* pointer to last byte of in, block number */ +	u8 out[SHA1_DIGEST_SIZE]; /* HMAC-SHA1 result */ +	u8 sha1_ctx[SHA1_CTX_SIZE]; /* SHA1 context */ +	const size_t label_len = strlen ( label ); + +	/* The HMAC-SHA-1 is calculated using the given key on the +	   message text `label', followed by a NUL, followed by one +	   byte indicating the block number (0 for first). */ + +	memcpy ( keym, key, key_len ); + +	memcpy ( in, label, strlen ( label ) + 1 ); +	memcpy ( in + label_len + 1, data, data_len ); +	in_blknr = in + label_len + 1 + data_len; + +	for ( blk = 0 ;; blk++ ) { +		*in_blknr = blk; + +		hmac_init ( &sha1_algorithm, sha1_ctx, keym, &key_len ); +		hmac_update ( &sha1_algorithm, sha1_ctx, in, sizeof ( in ) ); +		hmac_final ( &sha1_algorithm, sha1_ctx, keym, &key_len, out ); + +		if ( prf_len <= sizeof ( out ) ) { +			memcpy ( prf, out, prf_len ); +			break; +		} + +		memcpy ( prf, out, sizeof ( out ) ); +		prf_len -= sizeof ( out ); +		prf += sizeof ( out ); +	} +} + +/** + * PBKDF2 key derivation function inner block operation + * + * @v passphrase	Passphrase from which to derive key + * @v pass_len		Length of passphrase + * @v salt		Salt to include in key + * @v salt_len		Length of salt + * @v iterations	Number of iterations of SHA1 to perform + * @v blocknr		Index of this block, starting at 1 + * @ret block		SHA1_SIZE bytes of PBKDF2 data + * + * The operation of this function is described in RFC 2898. + */ +static void pbkdf2_sha1_f ( const void *passphrase, size_t pass_len, +			    const void *salt, size_t salt_len, +			    int iterations, u32 blocknr, u8 *block ) +{ +	u8 pass[pass_len];	/* modifiable passphrase */ +	u8 in[salt_len + 4];	/* input buffer to first round */ +	u8 last[SHA1_DIGEST_SIZE]; /* output of round N, input of N+1 */ +	u8 sha1_ctx[SHA1_CTX_SIZE]; +	u8 *next_in = in;	/* changed to `last' after first round */ +	int next_size = sizeof ( in ); +	int i; +	unsigned int j; + +	blocknr = htonl ( blocknr ); + +	memcpy ( pass, passphrase, pass_len ); +	memcpy ( in, salt, salt_len ); +	memcpy ( in + salt_len, &blocknr, 4 ); +	memset ( block, 0, sizeof ( last ) ); + +	for ( i = 0; i < iterations; i++ ) { +		hmac_init ( &sha1_algorithm, sha1_ctx, pass, &pass_len ); +		hmac_update ( &sha1_algorithm, sha1_ctx, next_in, next_size ); +		hmac_final ( &sha1_algorithm, sha1_ctx, pass, &pass_len, last ); + +		for ( j = 0; j < sizeof ( last ); j++ ) { +			block[j] ^= last[j]; +		} + +		next_in = last; +		next_size = sizeof ( last ); +	} +} + +/** + * PBKDF2 key derivation function using SHA1 + * + * @v passphrase	Passphrase from which to derive key + * @v pass_len		Length of passphrase + * @v salt		Salt to include in key + * @v salt_len		Length of salt + * @v iterations	Number of iterations of SHA1 to perform + * @v key_len		Length of key to generate + * @ret key		Generated key bytes + * + * This is used most notably in 802.11 WPA passphrase hashing, in + * which case the salt is the SSID, 4096 iterations are used, and a + * 32-byte key is generated that serves as the Pairwise Master Key for + * EAPOL authentication. + * + * The operation of this function is further described in RFC 2898. + */ +void pbkdf2_sha1 ( const void *passphrase, size_t pass_len, +		   const void *salt, size_t salt_len, +		   int iterations, void *key, size_t key_len ) +{ +	u32 blocks = ( key_len + SHA1_DIGEST_SIZE - 1 ) / SHA1_DIGEST_SIZE; +	u32 blk; +	u8 buf[SHA1_DIGEST_SIZE]; + +	for ( blk = 1; blk <= blocks; blk++ ) { +		pbkdf2_sha1_f ( passphrase, pass_len, salt, salt_len, +				iterations, blk, buf ); +		if ( key_len <= sizeof ( buf ) ) { +			memcpy ( key, buf, key_len ); +			break; +		} + +		memcpy ( key, buf, sizeof ( buf ) ); +		key_len -= sizeof ( buf ); +		key += sizeof ( buf ); +	} +} diff --git a/roms/ipxe/src/crypto/sha256.c b/roms/ipxe/src/crypto/sha256.c new file mode 100644 index 00000000..36e02b3c --- /dev/null +++ b/roms/ipxe/src/crypto/sha256.c @@ -0,0 +1,256 @@ +/* + * 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 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 ); + +/** @file + * + * SHA-256 algorithm + * + */ + +#include <stdint.h> +#include <string.h> +#include <byteswap.h> +#include <assert.h> +#include <ipxe/rotate.h> +#include <ipxe/crypto.h> +#include <ipxe/asn1.h> +#include <ipxe/sha256.h> + +/** SHA-256 variables */ +struct sha256_variables { +	/* This layout matches that of struct sha256_digest_data, +	 * allowing for efficient endianness-conversion, +	 */ +	uint32_t a; +	uint32_t b; +	uint32_t c; +	uint32_t d; +	uint32_t e; +	uint32_t f; +	uint32_t g; +	uint32_t h; +	uint32_t w[64]; +} __attribute__ (( packed )); + +/** SHA-256 constants */ +static const uint32_t k[64] = { +	0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, +	0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, +	0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, +	0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, +	0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, +	0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, +	0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, +	0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, +	0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, +	0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, +	0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 +}; + +/** + * Initialise SHA-256 algorithm + * + * @v ctx		SHA-256 context + */ +static void sha256_init ( void *ctx ) { +	struct sha256_context *context = ctx; + +	context->ddd.dd.digest.h[0] = cpu_to_be32 ( 0x6a09e667 ); +	context->ddd.dd.digest.h[1] = cpu_to_be32 ( 0xbb67ae85 ); +	context->ddd.dd.digest.h[2] = cpu_to_be32 ( 0x3c6ef372 ); +	context->ddd.dd.digest.h[3] = cpu_to_be32 ( 0xa54ff53a ); +	context->ddd.dd.digest.h[4] = cpu_to_be32 ( 0x510e527f ); +	context->ddd.dd.digest.h[5] = cpu_to_be32 ( 0x9b05688c ); +	context->ddd.dd.digest.h[6] = cpu_to_be32 ( 0x1f83d9ab ); +	context->ddd.dd.digest.h[7] = cpu_to_be32 ( 0x5be0cd19 ); +	context->len = 0; +} + +/** + * Calculate SHA-256 digest of accumulated data + * + * @v context		SHA-256 context + */ +static void sha256_digest ( struct sha256_context *context ) { +        union { +		union sha256_digest_data_dwords ddd; +		struct sha256_variables v; +	} u; +	uint32_t *a = &u.v.a; +	uint32_t *b = &u.v.b; +	uint32_t *c = &u.v.c; +	uint32_t *d = &u.v.d; +	uint32_t *e = &u.v.e; +	uint32_t *f = &u.v.f; +	uint32_t *g = &u.v.g; +	uint32_t *h = &u.v.h; +	uint32_t *w = u.v.w; +	uint32_t s0; +	uint32_t s1; +	uint32_t maj; +	uint32_t t1; +	uint32_t t2; +	uint32_t ch; +	unsigned int i; + +	/* Sanity checks */ +	assert ( ( context->len % sizeof ( context->ddd.dd.data ) ) == 0 ); +	linker_assert ( &u.ddd.dd.digest.h[0] == a, sha256_bad_layout ); +	linker_assert ( &u.ddd.dd.digest.h[1] == b, sha256_bad_layout ); +	linker_assert ( &u.ddd.dd.digest.h[2] == c, sha256_bad_layout ); +	linker_assert ( &u.ddd.dd.digest.h[3] == d, sha256_bad_layout ); +	linker_assert ( &u.ddd.dd.digest.h[4] == e, sha256_bad_layout ); +	linker_assert ( &u.ddd.dd.digest.h[5] == f, sha256_bad_layout ); +	linker_assert ( &u.ddd.dd.digest.h[6] == g, sha256_bad_layout ); +	linker_assert ( &u.ddd.dd.digest.h[7] == h, sha256_bad_layout ); +	linker_assert ( &u.ddd.dd.data.dword[0] == w, sha256_bad_layout ); + +	DBGC ( context, "SHA256 digesting:\n" ); +	DBGC_HDA ( context, 0, &context->ddd.dd.digest, +		   sizeof ( context->ddd.dd.digest ) ); +	DBGC_HDA ( context, context->len, &context->ddd.dd.data, +		   sizeof ( context->ddd.dd.data ) ); + +	/* Convert h[0..7] to host-endian, and initialise a, b, c, d, +	 * e, f, g, h, and w[0..15] +	 */ +	for ( i = 0 ; i < ( sizeof ( u.ddd.dword ) / +			    sizeof ( u.ddd.dword[0] ) ) ; i++ ) { +		be32_to_cpus ( &context->ddd.dword[i] ); +		u.ddd.dword[i] = context->ddd.dword[i]; +	} + +	/* Initialise w[16..63] */ +	for ( i = 16 ; i < 64 ; i++ ) { +		s0 = ( ror32 ( w[i-15], 7 ) ^ ror32 ( w[i-15], 18 ) ^ +		       ( w[i-15] >> 3 ) ); +		s1 = ( ror32 ( w[i-2], 17 ) ^ ror32 ( w[i-2], 19 ) ^ +		       ( w[i-2] >> 10 ) ); +		w[i] = ( w[i-16] + s0 + w[i-7] + s1 ); +	} + +	/* Main loop */ +	for ( i = 0 ; i < 64 ; i++ ) { +		s0 = ( ror32 ( *a, 2 ) ^ ror32 ( *a, 13 ) ^ ror32 ( *a, 22 ) ); +		maj = ( ( *a & *b ) ^ ( *a & *c ) ^ ( *b & *c ) ); +		t2 = ( s0 + maj ); +		s1 = ( ror32 ( *e, 6 ) ^ ror32 ( *e, 11 ) ^ ror32 ( *e, 25 ) ); +		ch = ( ( *e & *f ) ^ ( (~*e) & *g ) ); +		t1 = ( *h + s1 + ch + k[i] + w[i] ); +		*h = *g; +		*g = *f; +		*f = *e; +		*e = ( *d + t1 ); +		*d = *c; +		*c = *b; +		*b = *a; +		*a = ( t1 + t2 ); +		DBGC2 ( context, "%2d : %08x %08x %08x %08x %08x %08x %08x " +			"%08x\n", i, *a, *b, *c, *d, *e, *f, *g, *h ); +	} + +	/* Add chunk to hash and convert back to big-endian */ +	for ( i = 0 ; i < 8 ; i++ ) { +		context->ddd.dd.digest.h[i] = +			cpu_to_be32 ( context->ddd.dd.digest.h[i] + +				      u.ddd.dd.digest.h[i] ); +	} + +	DBGC ( context, "SHA256 digested:\n" ); +	DBGC_HDA ( context, 0, &context->ddd.dd.digest, +		   sizeof ( context->ddd.dd.digest ) ); +} + +/** + * Accumulate data with SHA-256 algorithm + * + * @v ctx		SHA-256 context + * @v data		Data + * @v len		Length of data + */ +static void sha256_update ( void *ctx, const void *data, size_t len ) { +	struct sha256_context *context = ctx; +	const uint8_t *byte = data; +	size_t offset; + +	/* Accumulate data a byte at a time, performing the digest +	 * whenever we fill the data buffer +	 */ +	while ( len-- ) { +		offset = ( context->len % sizeof ( context->ddd.dd.data ) ); +		context->ddd.dd.data.byte[offset] = *(byte++); +		context->len++; +		if ( ( context->len % sizeof ( context->ddd.dd.data ) ) == 0 ) +			sha256_digest ( context ); +	} +} + +/** + * Generate SHA-256 digest + * + * @v ctx		SHA-256 context + * @v out		Output buffer + */ +static void sha256_final ( void *ctx, void *out ) { +	struct sha256_context *context = ctx; +	uint64_t len_bits; +	uint8_t pad; + +	/* Record length before pre-processing */ +	len_bits = cpu_to_be64 ( ( ( uint64_t ) context->len ) * 8 ); + +	/* Pad with a single "1" bit followed by as many "0" bits as required */ +	pad = 0x80; +	do { +		sha256_update ( ctx, &pad, sizeof ( pad ) ); +		pad = 0x00; +	} while ( ( context->len % sizeof ( context->ddd.dd.data ) ) != +		  offsetof ( typeof ( context->ddd.dd.data ), final.len ) ); + +	/* Append length (in bits) */ +	sha256_update ( ctx, &len_bits, sizeof ( len_bits ) ); +	assert ( ( context->len % sizeof ( context->ddd.dd.data ) ) == 0 ); + +	/* Copy out final digest */ +	memcpy ( out, &context->ddd.dd.digest, +		 sizeof ( context->ddd.dd.digest ) ); +} + +/** SHA-256 algorithm */ +struct digest_algorithm sha256_algorithm = { +	.name		= "sha256", +	.ctxsize	= sizeof ( struct sha256_context ), +	.blocksize	= sizeof ( union sha256_block ), +	.digestsize	= sizeof ( struct sha256_digest ), +	.init		= sha256_init, +	.update		= sha256_update, +	.final		= sha256_final, +}; + +/** "sha256" object identifier */ +static uint8_t oid_sha256[] = { ASN1_OID_SHA256 }; + +/** "sha256" OID-identified algorithm */ +struct asn1_algorithm oid_sha256_algorithm __asn1_algorithm = { +	.name = "sha256", +	.digest = &sha256_algorithm, +	.oid = ASN1_OID_CURSOR ( oid_sha256 ), +}; diff --git a/roms/ipxe/src/crypto/x509.c b/roms/ipxe/src/crypto/x509.c new file mode 100644 index 00000000..4a02dad1 --- /dev/null +++ b/roms/ipxe/src/crypto/x509.c @@ -0,0 +1,1765 @@ +/* + * Copyright (C) 2007 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 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 <stdlib.h> +#include <string.h> +#include <errno.h> +#include <assert.h> +#include <ipxe/list.h> +#include <ipxe/base16.h> +#include <ipxe/asn1.h> +#include <ipxe/crypto.h> +#include <ipxe/md5.h> +#include <ipxe/sha1.h> +#include <ipxe/sha256.h> +#include <ipxe/rsa.h> +#include <ipxe/rootcert.h> +#include <ipxe/certstore.h> +#include <ipxe/socket.h> +#include <ipxe/in.h> +#include <ipxe/x509.h> +#include <config/crypto.h> + +/** @file + * + * X.509 certificates + * + * The structure of X.509v3 certificates is documented in RFC 5280 + * section 4.1. + */ + +/* Disambiguate the various error causes */ +#define ENOTSUP_ALGORITHM \ +	__einfo_error ( EINFO_ENOTSUP_ALGORITHM ) +#define EINFO_ENOTSUP_ALGORITHM \ +	__einfo_uniqify ( EINFO_ENOTSUP, 0x01, "Unsupported algorithm" ) +#define ENOTSUP_EXTENSION \ +	__einfo_error ( EINFO_ENOTSUP_EXTENSION ) +#define EINFO_ENOTSUP_EXTENSION \ +	__einfo_uniqify ( EINFO_ENOTSUP, 0x02, "Unsupported extension" ) +#define EINVAL_ALGORITHM \ +	__einfo_error ( EINFO_EINVAL_ALGORITHM ) +#define EINFO_EINVAL_ALGORITHM \ +	__einfo_uniqify ( EINFO_EINVAL, 0x01, "Invalid algorithm type" ) +#define EINVAL_ALGORITHM_MISMATCH \ +	__einfo_error ( EINFO_EINVAL_ALGORITHM_MISMATCH ) +#define EINFO_EINVAL_ALGORITHM_MISMATCH \ +	__einfo_uniqify ( EINFO_EINVAL, 0x04, "Signature algorithm mismatch" ) +#define EINVAL_PATH_LEN \ +	__einfo_error ( EINFO_EINVAL_PATH_LEN ) +#define EINFO_EINVAL_PATH_LEN \ +	__einfo_uniqify ( EINFO_EINVAL, 0x05, "Invalid pathLenConstraint" ) +#define EINVAL_VERSION \ +	__einfo_error ( EINFO_EINVAL_VERSION ) +#define EINFO_EINVAL_VERSION \ +	__einfo_uniqify ( EINFO_EINVAL, 0x06, "Invalid version" ) +#define EACCES_WRONG_ISSUER \ +	__einfo_error ( EINFO_EACCES_WRONG_ISSUER ) +#define EINFO_EACCES_WRONG_ISSUER \ +	__einfo_uniqify ( EINFO_EACCES, 0x01, "Wrong issuer" ) +#define EACCES_NOT_CA \ +	__einfo_error ( EINFO_EACCES_NOT_CA ) +#define EINFO_EACCES_NOT_CA \ +	__einfo_uniqify ( EINFO_EACCES, 0x02, "Not a CA certificate" ) +#define EACCES_KEY_USAGE \ +	__einfo_error ( EINFO_EACCES_KEY_USAGE ) +#define EINFO_EACCES_KEY_USAGE \ +	__einfo_uniqify ( EINFO_EACCES, 0x03, "Incorrect key usage" ) +#define EACCES_EXPIRED \ +	__einfo_error ( EINFO_EACCES_EXPIRED ) +#define EINFO_EACCES_EXPIRED \ +	__einfo_uniqify ( EINFO_EACCES, 0x04, "Expired (or not yet valid)" ) +#define EACCES_PATH_LEN \ +	__einfo_error ( EINFO_EACCES_PATH_LEN ) +#define EINFO_EACCES_PATH_LEN \ +	__einfo_uniqify ( EINFO_EACCES, 0x05, "Maximum path length exceeded" ) +#define EACCES_UNTRUSTED \ +	__einfo_error ( EINFO_EACCES_UNTRUSTED ) +#define EINFO_EACCES_UNTRUSTED \ +	__einfo_uniqify ( EINFO_EACCES, 0x06, "Untrusted root certificate" ) +#define EACCES_OUT_OF_ORDER \ +	__einfo_error ( EINFO_EACCES_OUT_OF_ORDER ) +#define EINFO_EACCES_OUT_OF_ORDER \ +	__einfo_uniqify ( EINFO_EACCES, 0x07, "Validation out of order" ) +#define EACCES_EMPTY \ +	__einfo_error ( EINFO_EACCES_EMPTY ) +#define EINFO_EACCES_EMPTY \ +	__einfo_uniqify ( EINFO_EACCES, 0x08, "Empty certificate chain" ) +#define EACCES_OCSP_REQUIRED \ +	__einfo_error ( EINFO_EACCES_OCSP_REQUIRED ) +#define EINFO_EACCES_OCSP_REQUIRED \ +	__einfo_uniqify ( EINFO_EACCES, 0x09, "OCSP check required" ) +#define EACCES_WRONG_NAME \ +	__einfo_error ( EINFO_EACCES_WRONG_NAME ) +#define EINFO_EACCES_WRONG_NAME \ +	__einfo_uniqify ( EINFO_EACCES, 0x0a, "Incorrect certificate name" ) +#define EACCES_USELESS \ +	__einfo_error ( EINFO_EACCES_USELESS ) +#define EINFO_EACCES_USELESS \ +	__einfo_uniqify ( EINFO_EACCES, 0x0b, "No usable certificates" ) + +/** + * Get X.509 certificate name (for debugging) + * + * @v cert		X.509 certificate + * @ret name		Name (for debugging) + */ +const char * x509_name ( struct x509_certificate *cert ) { +	struct asn1_cursor *common_name = &cert->subject.common_name; +	struct digest_algorithm *digest = &sha1_algorithm; +	static char buf[64]; +	uint8_t fingerprint[ digest->digestsize ]; +	size_t len; + +	len = common_name->len; +	if ( len ) { +		/* Certificate has a commonName: use that */ +		if ( len > ( sizeof ( buf ) - 1 /* NUL */ ) ) +			len = ( sizeof ( buf ) - 1 /* NUL */ ); +		memcpy ( buf, common_name->data, len ); +		buf[len] = '\0'; +	} else { +		/* Certificate has no commonName: use SHA-1 fingerprint */ +		x509_fingerprint ( cert, digest, fingerprint ); +		base16_encode ( fingerprint, sizeof ( fingerprint ), buf ); +	} +	return buf; +} + +/** "commonName" object identifier */ +static uint8_t oid_common_name[] = { ASN1_OID_COMMON_NAME }; + +/** "commonName" object identifier cursor */ +static struct asn1_cursor oid_common_name_cursor = +	ASN1_OID_CURSOR ( oid_common_name ); + +/** + * Parse X.509 certificate version + * + * @v cert		X.509 certificate + * @v raw		ASN.1 cursor + * @ret rc		Return status code + */ +static int x509_parse_version ( struct x509_certificate *cert, +				const struct asn1_cursor *raw ) { +	struct asn1_cursor cursor; +	int version; +	int rc; + +	/* Enter version */ +	memcpy ( &cursor, raw, sizeof ( cursor ) ); +	asn1_enter ( &cursor, ASN1_EXPLICIT_TAG ( 0 ) ); + +	/* Parse integer */ +	if ( ( rc = asn1_integer ( &cursor, &version ) ) != 0 ) { +		DBGC ( cert, "X509 %p cannot parse version: %s\n", +		       cert, strerror ( rc ) ); +		DBGC_HDA ( cert, 0, raw->data, raw->len ); +		return rc; +	} + +	/* Sanity check */ +	if ( version < 0 ) { +		DBGC ( cert, "X509 %p invalid version %d\n", cert, version ); +		DBGC_HDA ( cert, 0, raw->data, raw->len ); +		return -EINVAL_VERSION; +	} + +	/* Record version */ +	cert->version = version; +	DBGC2 ( cert, "X509 %p is a version %d certificate\n", +		cert, ( cert->version + 1 ) ); + +	return 0; +} + +/** + * Parse X.509 certificate serial number + * + * @v cert		X.509 certificate + * @v raw		ASN.1 cursor + * @ret rc		Return status code + */ +static int x509_parse_serial ( struct x509_certificate *cert, +			       const struct asn1_cursor *raw ) { +	struct x509_serial *serial = &cert->serial; +	int rc; + +	/* Record raw serial number */ +	memcpy ( &serial->raw, raw, sizeof ( serial->raw ) ); +	if ( ( rc = asn1_shrink ( &serial->raw, ASN1_INTEGER ) ) != 0 ) { +		DBGC ( cert, "X509 %p cannot shrink serialNumber: %s\n", +		       cert, strerror ( rc ) ); +		return rc; +	} +	DBGC2 ( cert, "X509 %p issuer is:\n", cert ); +	DBGC2_HDA ( cert, 0, serial->raw.data, serial->raw.len ); + +	return 0; +} + +/** + * Parse X.509 certificate issuer + * + * @v cert		X.509 certificate + * @v raw		ASN.1 cursor + * @ret rc		Return status code + */ +static int x509_parse_issuer ( struct x509_certificate *cert, +			       const struct asn1_cursor *raw ) { +	struct x509_issuer *issuer = &cert->issuer; +	int rc; + +	/* Record raw issuer */ +	memcpy ( &issuer->raw, raw, sizeof ( issuer->raw ) ); +	if ( ( rc = asn1_shrink ( &issuer->raw, ASN1_SEQUENCE ) ) != 0 ) { +		DBGC ( cert, "X509 %p cannot shrink issuer: %s\n", +		       cert, strerror ( rc ) ); +		return rc; +	} +	DBGC2 ( cert, "X509 %p issuer is:\n", cert ); +	DBGC2_HDA ( cert, 0, issuer->raw.data, issuer->raw.len ); + +	return 0; +} + +/** + * Parse X.509 certificate validity + * + * @v cert		X.509 certificate + * @v raw		ASN.1 cursor + * @ret rc		Return status code + */ +static int x509_parse_validity ( struct x509_certificate *cert, +				 const struct asn1_cursor *raw ) { +	struct x509_validity *validity = &cert->validity; +	struct x509_time *not_before = &validity->not_before; +	struct x509_time *not_after = &validity->not_after; +	struct asn1_cursor cursor; +	int rc; + +	/* Enter validity */ +	memcpy ( &cursor, raw, sizeof ( cursor ) ); +	asn1_enter ( &cursor, ASN1_SEQUENCE ); + +	/* Parse notBefore */ +	if ( ( rc = asn1_generalized_time ( &cursor, +					    ¬_before->time ) ) != 0 ) { +		DBGC ( cert, "X509 %p cannot parse notBefore: %s\n", +		       cert, strerror ( rc ) ); +		return rc; +	} +	DBGC2 ( cert, "X509 %p valid from time %lld\n", +		cert, not_before->time ); +	asn1_skip_any ( &cursor ); + +	/* Parse notAfter */ +	if ( ( rc = asn1_generalized_time ( &cursor, +					    ¬_after->time ) ) != 0 ) { +		DBGC ( cert, "X509 %p cannot parse notAfter: %s\n", +		       cert, strerror ( rc ) ); +		return rc; +	} +	DBGC2 ( cert, "X509 %p valid until time %lld\n", +		cert, not_after->time ); + +	return 0; +} + +/** + * Parse X.509 certificate common name + * + * @v cert		X.509 certificate + * @v raw		ASN.1 cursor + * @ret rc		Return status code + */ +static int x509_parse_common_name ( struct x509_certificate *cert, +				    const struct asn1_cursor *raw ) { +	struct asn1_cursor cursor; +	struct asn1_cursor oid_cursor; +	struct asn1_cursor name_cursor; +	int rc; + +	/* Enter name */ +	memcpy ( &cursor, raw, sizeof ( cursor ) ); +	asn1_enter ( &cursor, ASN1_SEQUENCE ); + +	/* Scan through name list */ +	for ( ; cursor.len ; asn1_skip_any ( &cursor ) ) { + +		/* Check for "commonName" OID */ +		memcpy ( &oid_cursor, &cursor, sizeof ( oid_cursor ) ); +		asn1_enter ( &oid_cursor, ASN1_SET ); +		asn1_enter ( &oid_cursor, ASN1_SEQUENCE ); +		memcpy ( &name_cursor, &oid_cursor, sizeof ( name_cursor ) ); +		asn1_enter ( &oid_cursor, ASN1_OID ); +		if ( asn1_compare ( &oid_common_name_cursor, &oid_cursor ) != 0) +			continue; +		asn1_skip_any ( &name_cursor ); +		if ( ( rc = asn1_enter_any ( &name_cursor ) ) != 0 ) { +			DBGC ( cert, "X509 %p cannot locate name:\n", cert ); +			DBGC_HDA ( cert, 0, raw->data, raw->len ); +			return rc; +		} + +		/* Record common name */ +		memcpy ( &cert->subject.common_name, &name_cursor, +			 sizeof ( cert->subject.common_name ) ); + +		return 0; +	} + +	/* Certificates may not have a commonName */ +	DBGC2 ( cert, "X509 %p no commonName found:\n", cert ); +	return 0; +} + +/** + * Parse X.509 certificate subject + * + * @v cert		X.509 certificate + * @v raw		ASN.1 cursor + * @ret rc		Return status code + */ +static int x509_parse_subject ( struct x509_certificate *cert, +				const struct asn1_cursor *raw ) { +	struct x509_subject *subject = &cert->subject; +	int rc; + +	/* Record raw subject */ +	memcpy ( &subject->raw, raw, sizeof ( subject->raw ) ); +	asn1_shrink_any ( &subject->raw ); +	DBGC2 ( cert, "X509 %p subject is:\n", cert ); +	DBGC2_HDA ( cert, 0, subject->raw.data, subject->raw.len ); + +	/* Parse common name */ +	if ( ( rc = x509_parse_common_name ( cert, raw ) ) != 0 ) +		return rc; +	DBGC2 ( cert, "X509 %p common name is \"%s\":\n", cert, +		x509_name ( cert ) ); + +	return 0; +} + +/** + * Parse X.509 certificate public key information + * + * @v cert		X.509 certificate + * @v raw		ASN.1 cursor + * @ret rc		Return status code + */ +static int x509_parse_public_key ( struct x509_certificate *cert, +				   const struct asn1_cursor *raw ) { +	struct x509_public_key *public_key = &cert->subject.public_key; +	struct asn1_algorithm **algorithm = &public_key->algorithm; +	struct asn1_bit_string *raw_bits = &public_key->raw_bits; +	struct asn1_cursor cursor; +	int rc; + +	/* Record raw subjectPublicKeyInfo */ +	memcpy ( &cursor, raw, sizeof ( cursor ) ); +	asn1_shrink_any ( &cursor ); +	memcpy ( &public_key->raw, &cursor, sizeof ( public_key->raw ) ); +	DBGC2 ( cert, "X509 %p public key is:\n", cert ); +	DBGC2_HDA ( cert, 0, public_key->raw.data, public_key->raw.len ); + +	/* Enter subjectPublicKeyInfo */ +	asn1_enter ( &cursor, ASN1_SEQUENCE ); + +	/* Parse algorithm */ +	if ( ( rc = asn1_pubkey_algorithm ( &cursor, algorithm ) ) != 0 ) { +		DBGC ( cert, "X509 %p could not parse public key algorithm: " +		       "%s\n", cert, strerror ( rc ) ); +		return rc; +	} +	DBGC2 ( cert, "X509 %p public key algorithm is %s\n", +		cert, (*algorithm)->name ); +	asn1_skip_any ( &cursor ); + +	/* Parse bit string */ +	if ( ( rc = asn1_bit_string ( &cursor, raw_bits ) ) != 0 ) { +		DBGC ( cert, "X509 %p could not parse public key bits: %s\n", +		       cert, strerror ( rc ) ); +		return rc; +	} + +	return 0; +} + +/** + * Parse X.509 certificate basic constraints + * + * @v cert		X.509 certificate + * @v raw		ASN.1 cursor + * @ret rc		Return status code + */ +static int x509_parse_basic_constraints ( struct x509_certificate *cert, +					  const struct asn1_cursor *raw ) { +	struct x509_basic_constraints *basic = &cert->extensions.basic; +	struct asn1_cursor cursor; +	int ca = 0; +	int path_len; +	int rc; + +	/* Enter basicConstraints */ +	memcpy ( &cursor, raw, sizeof ( cursor ) ); +	asn1_enter ( &cursor, ASN1_SEQUENCE ); + +	/* Parse "cA", if present */ +	if ( asn1_type ( &cursor ) == ASN1_BOOLEAN ) { +		ca = asn1_boolean ( &cursor ); +		if ( ca < 0 ) { +			rc = ca; +			DBGC ( cert, "X509 %p cannot parse cA: %s\n", +			       cert, strerror ( rc ) ); +			DBGC_HDA ( cert, 0, raw->data, raw->len ); +			return rc; +		} +		asn1_skip_any ( &cursor ); +	} +	basic->ca = ca; +	DBGC2 ( cert, "X509 %p is %sa CA certificate\n", +		cert, ( basic->ca ? "" : "not " ) ); + +	/* Ignore everything else unless "cA" is true */ +	if ( ! ca ) +		return 0; + +	/* Parse "pathLenConstraint", if present and applicable */ +	basic->path_len = X509_PATH_LEN_UNLIMITED; +	if ( asn1_type ( &cursor ) == ASN1_INTEGER ) { +		if ( ( rc = asn1_integer ( &cursor, &path_len ) ) != 0 ) { +			DBGC ( cert, "X509 %p cannot parse pathLenConstraint: " +			       "%s\n", cert, strerror ( rc ) ); +			DBGC_HDA ( cert, 0, raw->data, raw->len ); +			return rc; +		} +		if ( path_len < 0 ) { +			DBGC ( cert, "X509 %p invalid pathLenConstraint %d\n", +			       cert, path_len ); +			DBGC_HDA ( cert, 0, raw->data, raw->len ); +			return -EINVAL; +		} +		basic->path_len = path_len; +		DBGC2 ( cert, "X509 %p path length constraint is %d\n", +			cert, basic->path_len ); +	} + +	return 0; +} + +/** + * Parse X.509 certificate key usage + * + * @v cert		X.509 certificate + * @v raw		ASN.1 cursor + * @ret rc		Return status code + */ +static int x509_parse_key_usage ( struct x509_certificate *cert, +				  const struct asn1_cursor *raw ) { +	struct x509_key_usage *usage = &cert->extensions.usage; +	struct asn1_bit_string bit_string; +	const uint8_t *bytes; +	size_t len; +	unsigned int i; +	int rc; + +	/* Mark extension as present */ +	usage->present = 1; + +	/* Parse bit string */ +	if ( ( rc = asn1_bit_string ( raw, &bit_string ) ) != 0 ) { +		DBGC ( cert, "X509 %p could not parse key usage: %s\n", +		       cert, strerror ( rc ) ); +		return rc; +	} + +	/* Parse key usage bits */ +	bytes = bit_string.data; +	len = bit_string.len; +	if ( len > sizeof ( usage->bits ) ) +		len = sizeof ( usage->bits ); +	for ( i = 0 ; i < len ; i++ ) { +		usage->bits |= ( *(bytes++) << ( 8 * i ) ); +	} +	DBGC2 ( cert, "X509 %p key usage is %08x\n", cert, usage->bits ); + +	return 0; +} + +/** "id-kp-codeSigning" object identifier */ +static uint8_t oid_code_signing[] = { ASN1_OID_CODESIGNING }; + +/** "id-kp-OCSPSigning" object identifier */ +static uint8_t oid_ocsp_signing[] = { ASN1_OID_OCSPSIGNING }; + +/** Supported key purposes */ +static struct x509_key_purpose x509_key_purposes[] = { +	{ +		.name = "codeSigning", +		.bits = X509_CODE_SIGNING, +		.oid = ASN1_OID_CURSOR ( oid_code_signing ), +	}, +	{ +		.name = "ocspSigning", +		.bits = X509_OCSP_SIGNING, +		.oid = ASN1_OID_CURSOR ( oid_ocsp_signing ), +	}, +}; + +/** + * Parse X.509 certificate key purpose identifier + * + * @v cert		X.509 certificate + * @v raw		ASN.1 cursor + * @ret rc		Return status code + */ +static int x509_parse_key_purpose ( struct x509_certificate *cert, +				    const struct asn1_cursor *raw ) { +	struct x509_extended_key_usage *ext_usage = &cert->extensions.ext_usage; +	struct x509_key_purpose *purpose; +	struct asn1_cursor cursor; +	unsigned int i; +	int rc; + +	/* Enter keyPurposeId */ +	memcpy ( &cursor, raw, sizeof ( cursor ) ); +	if ( ( rc = asn1_enter ( &cursor, ASN1_OID ) ) != 0 ) { +		DBGC ( cert, "X509 %p invalid keyPurposeId:\n", cert ); +		DBGC_HDA ( cert, 0, raw->data, raw->len ); +		return rc; +	} + +	/* Identify key purpose */ +	for ( i = 0 ; i < ( sizeof ( x509_key_purposes ) / +			    sizeof ( x509_key_purposes[0] ) ) ; i++ ) { +		purpose = &x509_key_purposes[i]; +		if ( asn1_compare ( &cursor, &purpose->oid ) == 0 ) { +			DBGC2 ( cert, "X509 %p has key purpose %s\n", +				cert, purpose->name ); +			ext_usage->bits |= purpose->bits; +			return 0; +		} +	} + +	/* Ignore unrecognised key purposes */ +	return 0; +} + +/** + * Parse X.509 certificate extended key usage + * + * @v cert		X.509 certificate + * @v raw		ASN.1 cursor + * @ret rc		Return status code + */ +static int x509_parse_extended_key_usage ( struct x509_certificate *cert, +					   const struct asn1_cursor *raw ) { +	struct asn1_cursor cursor; +	int rc; + +	/* Enter extKeyUsage */ +	memcpy ( &cursor, raw, sizeof ( cursor ) ); +	asn1_enter ( &cursor, ASN1_SEQUENCE ); + +	/* Parse each extended key usage in turn */ +	while ( cursor.len ) { +		if ( ( rc = x509_parse_key_purpose ( cert, &cursor ) ) != 0 ) +			return rc; +		asn1_skip_any ( &cursor ); +	} + +	return 0; +} + +/** + * Parse X.509 certificate OCSP access method + * + * @v cert		X.509 certificate + * @v raw		ASN.1 cursor + * @ret rc		Return status code + */ +static int x509_parse_ocsp ( struct x509_certificate *cert, +			     const struct asn1_cursor *raw ) { +	struct x509_ocsp_responder *ocsp = &cert->extensions.auth_info.ocsp; +	struct asn1_cursor *uri = &ocsp->uri; +	int rc; + +	/* Enter accessLocation */ +	memcpy ( uri, raw, sizeof ( *uri ) ); +	if ( ( rc = asn1_enter ( uri, X509_GENERAL_NAME_URI ) ) != 0 ) { +		DBGC ( cert, "X509 %p OCSP does not contain " +		       "uniformResourceIdentifier:\n", cert ); +		DBGC_HDA ( cert, 0, raw->data, raw->len ); +		return rc; +	} +	DBGC2 ( cert, "X509 %p OCSP URI is:\n", cert ); +	DBGC2_HDA ( cert, 0, uri->data, uri->len ); + +	return 0; +} + +/** "id-ad-ocsp" object identifier */ +static uint8_t oid_ad_ocsp[] = { ASN1_OID_OCSP }; + +/** Supported access methods */ +static struct x509_access_method x509_access_methods[] = { +	{ +		.name = "OCSP", +		.oid = ASN1_OID_CURSOR ( oid_ad_ocsp ), +		.parse = x509_parse_ocsp, +	}, +}; + +/** + * Identify X.509 access method by OID + * + * @v oid		OID + * @ret method		Access method, or NULL + */ +static struct x509_access_method * +x509_find_access_method ( const struct asn1_cursor *oid ) { +	struct x509_access_method *method; +	unsigned int i; + +	for ( i = 0 ; i < ( sizeof ( x509_access_methods ) / +			    sizeof ( x509_access_methods[0] ) ) ; i++ ) { +		method = &x509_access_methods[i]; +		if ( asn1_compare ( &method->oid, oid ) == 0 ) +			return method; +	} + +	return NULL; +} + +/** + * Parse X.509 certificate access description + * + * @v cert		X.509 certificate + * @v raw		ASN.1 cursor + * @ret rc		Return status code + */ +static int x509_parse_access_description ( struct x509_certificate *cert, +					   const struct asn1_cursor *raw ) { +	struct asn1_cursor cursor; +	struct asn1_cursor subcursor; +	struct x509_access_method *method; +	int rc; + +	/* Enter keyPurposeId */ +	memcpy ( &cursor, raw, sizeof ( cursor ) ); +	asn1_enter ( &cursor, ASN1_SEQUENCE ); + +	/* Try to identify access method */ +	memcpy ( &subcursor, &cursor, sizeof ( subcursor ) ); +	asn1_enter ( &subcursor, ASN1_OID ); +	method = x509_find_access_method ( &subcursor ); +	asn1_skip_any ( &cursor ); +	DBGC2 ( cert, "X509 %p found access method %s\n", +		cert, ( method ? method->name : "<unknown>" ) ); + +	/* Parse access location, if applicable */ +	if ( method && ( ( rc = method->parse ( cert, &cursor ) ) != 0 ) ) +		return rc; + +	return 0; +} + +/** + * Parse X.509 certificate authority information access + * + * @v cert		X.509 certificate + * @v raw		ASN.1 cursor + * @ret rc		Return status code + */ +static int x509_parse_authority_info_access ( struct x509_certificate *cert, +					      const struct asn1_cursor *raw ) { +	struct asn1_cursor cursor; +	int rc; + +	/* Enter authorityInfoAccess */ +	memcpy ( &cursor, raw, sizeof ( cursor ) ); +	asn1_enter ( &cursor, ASN1_SEQUENCE ); + +	/* Parse each access description in turn */ +	while ( cursor.len ) { +		if ( ( rc = x509_parse_access_description ( cert, +							    &cursor ) ) != 0 ) +			return rc; +		asn1_skip_any ( &cursor ); +	} + +	return 0; +} + +/** + * Parse X.509 certificate subject alternative name + * + * @v cert		X.509 certificate + * @v raw		ASN.1 cursor + * @ret rc		Return status code + */ +static int x509_parse_subject_alt_name ( struct x509_certificate *cert, +					 const struct asn1_cursor *raw ) { +	struct x509_subject_alt_name *alt_name = &cert->extensions.alt_name; +	struct asn1_cursor *names = &alt_name->names; +	int rc; + +	/* Enter subjectAltName */ +	memcpy ( names, raw, sizeof ( *names ) ); +	if ( ( rc = asn1_enter ( names, ASN1_SEQUENCE ) ) != 0 ) { +		DBGC ( cert, "X509 %p invalid subjectAltName: %s\n", +		       cert, strerror ( rc ) ); +		DBGC_HDA ( cert, 0, raw->data, raw->len ); +		return rc; +	} +	DBGC2 ( cert, "X509 %p has subjectAltName:\n", cert ); +	DBGC2_HDA ( cert, 0, names->data, names->len ); + +	return 0; +} + +/** "id-ce-basicConstraints" object identifier */ +static uint8_t oid_ce_basic_constraints[] = +	{ ASN1_OID_BASICCONSTRAINTS }; + +/** "id-ce-keyUsage" object identifier */ +static uint8_t oid_ce_key_usage[] = +	{ ASN1_OID_KEYUSAGE }; + +/** "id-ce-extKeyUsage" object identifier */ +static uint8_t oid_ce_ext_key_usage[] = +	{ ASN1_OID_EXTKEYUSAGE }; + +/** "id-pe-authorityInfoAccess" object identifier */ +static uint8_t oid_pe_authority_info_access[] = +	{ ASN1_OID_AUTHORITYINFOACCESS }; + +/** "id-ce-subjectAltName" object identifier */ +static uint8_t oid_ce_subject_alt_name[] = +	{ ASN1_OID_SUBJECTALTNAME }; + +/** Supported certificate extensions */ +static struct x509_extension x509_extensions[] = { +	{ +		.name = "basicConstraints", +		.oid = ASN1_OID_CURSOR ( oid_ce_basic_constraints ), +		.parse = x509_parse_basic_constraints, +	}, +	{ +		.name = "keyUsage", +		.oid = ASN1_OID_CURSOR ( oid_ce_key_usage ), +		.parse = x509_parse_key_usage, +	}, +	{ +		.name = "extKeyUsage", +		.oid = ASN1_OID_CURSOR ( oid_ce_ext_key_usage ), +		.parse = x509_parse_extended_key_usage, +	}, +	{ +		.name = "authorityInfoAccess", +		.oid = ASN1_OID_CURSOR ( oid_pe_authority_info_access ), +		.parse = x509_parse_authority_info_access, +	}, +	{ +		.name = "subjectAltName", +		.oid = ASN1_OID_CURSOR ( oid_ce_subject_alt_name ), +		.parse = x509_parse_subject_alt_name, +	}, +}; + +/** + * Identify X.509 extension by OID + * + * @v oid		OID + * @ret extension	Extension, or NULL + */ +static struct x509_extension * +x509_find_extension ( const struct asn1_cursor *oid ) { +	struct x509_extension *extension; +	unsigned int i; + +	for ( i = 0 ; i < ( sizeof ( x509_extensions ) / +			    sizeof ( x509_extensions[0] ) ) ; i++ ) { +		extension = &x509_extensions[i]; +		if ( asn1_compare ( &extension->oid, oid ) == 0 ) +			return extension; +	} + +	return NULL; +} + +/** + * Parse X.509 certificate extension + * + * @v cert		X.509 certificate + * @v raw		ASN.1 cursor + * @ret rc		Return status code + */ +static int x509_parse_extension ( struct x509_certificate *cert, +				  const struct asn1_cursor *raw ) { +	struct asn1_cursor cursor; +	struct asn1_cursor subcursor; +	struct x509_extension *extension; +	int is_critical = 0; +	int rc; + +	/* Enter extension */ +	memcpy ( &cursor, raw, sizeof ( cursor ) ); +	asn1_enter ( &cursor, ASN1_SEQUENCE ); + +	/* Try to identify extension */ +	memcpy ( &subcursor, &cursor, sizeof ( subcursor ) ); +	asn1_enter ( &subcursor, ASN1_OID ); +	extension = x509_find_extension ( &subcursor ); +	asn1_skip_any ( &cursor ); +	DBGC2 ( cert, "X509 %p found extension %s\n", +		cert, ( extension ? extension->name : "<unknown>" ) ); + +	/* Identify criticality */ +	if ( asn1_type ( &cursor ) == ASN1_BOOLEAN ) { +		is_critical = asn1_boolean ( &cursor ); +		if ( is_critical < 0 ) { +			rc = is_critical; +			DBGC ( cert, "X509 %p cannot parse extension " +			       "criticality: %s\n", cert, strerror ( rc ) ); +			DBGC_HDA ( cert, 0, raw->data, raw->len ); +			return rc; +		} +		asn1_skip_any ( &cursor ); +	} + +	/* Handle unknown extensions */ +	if ( ! extension ) { +		if ( is_critical ) { +			/* Fail if we cannot handle a critical extension */ +			DBGC ( cert, "X509 %p cannot handle critical " +			       "extension:\n", cert ); +			DBGC_HDA ( cert, 0, raw->data, raw->len ); +			return -ENOTSUP_EXTENSION; +		} else { +			/* Ignore unknown non-critical extensions */ +			return 0; +		} +	}; + +	/* Extract extnValue */ +	if ( ( rc = asn1_enter ( &cursor, ASN1_OCTET_STRING ) ) != 0 ) { +		DBGC ( cert, "X509 %p extension missing extnValue:\n", cert ); +		DBGC_HDA ( cert, 0, raw->data, raw->len ); +		return rc; +	} + +	/* Parse extension */ +	if ( ( rc = extension->parse ( cert, &cursor ) ) != 0 ) +		return rc; + +	return 0; +} + +/** + * Parse X.509 certificate extensions, if present + * + * @v cert		X.509 certificate + * @v raw		ASN.1 cursor + * @ret rc		Return status code + */ +static int x509_parse_extensions ( struct x509_certificate *cert, +				   const struct asn1_cursor *raw ) { +	struct asn1_cursor cursor; +	int rc; + +	/* Enter extensions, if present */ +	memcpy ( &cursor, raw, sizeof ( cursor ) ); +	asn1_enter ( &cursor, ASN1_EXPLICIT_TAG ( 3 ) ); +	asn1_enter ( &cursor, ASN1_SEQUENCE ); + +	/* Parse each extension in turn */ +	while ( cursor.len ) { +		if ( ( rc = x509_parse_extension ( cert, &cursor ) ) != 0 ) +			return rc; +		asn1_skip_any ( &cursor ); +	} + +	return 0; +} + +/** + * Parse X.509 certificate tbsCertificate + * + * @v cert		X.509 certificate + * @v raw		ASN.1 cursor + * @ret rc		Return status code + */ +static int x509_parse_tbscertificate ( struct x509_certificate *cert, +				       const struct asn1_cursor *raw ) { +	struct asn1_algorithm **algorithm = &cert->signature_algorithm; +	struct asn1_cursor cursor; +	int rc; + +	/* Record raw tbsCertificate */ +	memcpy ( &cursor, raw, sizeof ( cursor ) ); +	asn1_shrink_any ( &cursor ); +	memcpy ( &cert->tbs, &cursor, sizeof ( cert->tbs ) ); + +	/* Enter tbsCertificate */ +	asn1_enter ( &cursor, ASN1_SEQUENCE ); + +	/* Parse version, if present */ +	if ( asn1_type ( &cursor ) == ASN1_EXPLICIT_TAG ( 0 ) ) { +		if ( ( rc = x509_parse_version ( cert, &cursor ) ) != 0 ) +			return rc; +		asn1_skip_any ( &cursor ); +	} + +	/* Parse serialNumber */ +	if ( ( rc = x509_parse_serial ( cert, &cursor ) ) != 0 ) +		return rc; +	asn1_skip_any ( &cursor ); + +	/* Parse signature */ +	if ( ( rc = asn1_signature_algorithm ( &cursor, algorithm ) ) != 0 ) { +		DBGC ( cert, "X509 %p could not parse signature algorithm: " +		       "%s\n", cert, strerror ( rc ) ); +		return rc; +	} +	DBGC2 ( cert, "X509 %p tbsCertificate signature algorithm is %s\n", +		cert, (*algorithm)->name ); +	asn1_skip_any ( &cursor ); + +	/* Parse issuer */ +	if ( ( rc = x509_parse_issuer ( cert, &cursor ) ) != 0 ) +		return rc; +	asn1_skip_any ( &cursor ); + +	/* Parse validity */ +	if ( ( rc = x509_parse_validity ( cert, &cursor ) ) != 0 ) +		return rc; +	asn1_skip_any ( &cursor ); + +	/* Parse subject */ +	if ( ( rc = x509_parse_subject ( cert, &cursor ) ) != 0 ) +		return rc; +	asn1_skip_any ( &cursor ); + +	/* Parse subjectPublicKeyInfo */ +	if ( ( rc = x509_parse_public_key ( cert, &cursor ) ) != 0 ) +		return rc; +	asn1_skip_any ( &cursor ); + +	/* Parse extensions, if present */ +	if ( ( rc = x509_parse_extensions ( cert, &cursor ) ) != 0 ) +		return rc; + +	return 0; +} + +/** + * Parse X.509 certificate from ASN.1 data + * + * @v cert		X.509 certificate + * @v raw		ASN.1 cursor + * @ret rc		Return status code + */ +int x509_parse ( struct x509_certificate *cert, +		 const struct asn1_cursor *raw ) { +	struct x509_signature *signature = &cert->signature; +	struct asn1_algorithm **signature_algorithm = &signature->algorithm; +	struct asn1_bit_string *signature_value = &signature->value; +	struct asn1_cursor cursor; +	int rc; + +	/* Record raw certificate */ +	memcpy ( &cursor, raw, sizeof ( cursor ) ); +	memcpy ( &cert->raw, &cursor, sizeof ( cert->raw ) ); + +	/* Enter certificate */ +	asn1_enter ( &cursor, ASN1_SEQUENCE ); + +	/* Parse tbsCertificate */ +	if ( ( rc = x509_parse_tbscertificate ( cert, &cursor ) ) != 0 ) +		return rc; +	asn1_skip_any ( &cursor ); + +	/* Parse signatureAlgorithm */ +	if ( ( rc = asn1_signature_algorithm ( &cursor, +					       signature_algorithm ) ) != 0 ) { +		DBGC ( cert, "X509 %p could not parse signature algorithm: " +		       "%s\n", cert, strerror ( rc ) ); +		return rc; +	} +	DBGC2 ( cert, "X509 %p signatureAlgorithm is %s\n", +		cert, (*signature_algorithm)->name ); +	asn1_skip_any ( &cursor ); + +	/* Parse signatureValue */ +	if ( ( rc = asn1_integral_bit_string ( &cursor, +					       signature_value ) ) != 0 ) { +		DBGC ( cert, "X509 %p could not parse signature value: %s\n", +		       cert, strerror ( rc ) ); +		return rc; +	} +	DBGC2 ( cert, "X509 %p signatureValue is:\n", cert ); +	DBGC2_HDA ( cert, 0, signature_value->data, signature_value->len ); + +	/* Check that algorithm in tbsCertificate matches algorithm in +	 * signature +	 */ +	if ( signature->algorithm != (*signature_algorithm) ) { +		DBGC ( cert, "X509 %p signature algorithm %s does not match " +		       "signatureAlgorithm %s\n", +		       cert, signature->algorithm->name, +		       (*signature_algorithm)->name ); +		return -EINVAL_ALGORITHM_MISMATCH; +	} + +	return 0; +} + +/** + * Create X.509 certificate + * + * @v data		Raw certificate data + * @v len		Length of raw data + * @ret cert		X.509 certificate + * @ret rc		Return status code + * + * On success, the caller holds a reference to the X.509 certificate, + * and is responsible for ultimately calling x509_put(). + */ +int x509_certificate ( const void *data, size_t len, +		       struct x509_certificate **cert ) { +	struct asn1_cursor cursor; +	void *raw; +	int rc; + +	/* Initialise cursor */ +	cursor.data = data; +	cursor.len = len; +	asn1_shrink_any ( &cursor ); + +	/* Return stored certificate, if present */ +	if ( ( *cert = certstore_find ( &cursor ) ) != NULL ) { + +		/* Add caller's reference */ +		x509_get ( *cert ); +		return 0; +	} + +	/* Allocate and initialise certificate */ +	*cert = zalloc ( sizeof ( **cert ) + cursor.len ); +	if ( ! *cert ) +		return -ENOMEM; +	ref_init ( &(*cert)->refcnt, NULL ); +	raw = ( *cert + 1 ); + +	/* Copy raw data */ +	memcpy ( raw, cursor.data, cursor.len ); +	cursor.data = raw; + +	/* Parse certificate */ +	if ( ( rc = x509_parse ( *cert, &cursor ) ) != 0 ) { +		x509_put ( *cert ); +		*cert = NULL; +		return rc; +	} + +	/* Add certificate to store */ +	certstore_add ( *cert ); + +	return 0; +} + +/** + * Check X.509 certificate signature + * + * @v cert		X.509 certificate + * @v public_key	X.509 public key + * @ret rc		Return status code + */ +static int x509_check_signature ( struct x509_certificate *cert, +				  struct x509_public_key *public_key ) { +	struct x509_signature *signature = &cert->signature; +	struct asn1_algorithm *algorithm = signature->algorithm; +	struct digest_algorithm *digest = algorithm->digest; +	struct pubkey_algorithm *pubkey = algorithm->pubkey; +	uint8_t digest_ctx[ digest->ctxsize ]; +	uint8_t digest_out[ digest->digestsize ]; +	uint8_t pubkey_ctx[ pubkey->ctxsize ]; +	int rc; + +	/* Sanity check */ +	assert ( cert->signature_algorithm == cert->signature.algorithm ); + +	/* Calculate certificate digest */ +	digest_init ( digest, digest_ctx ); +	digest_update ( digest, digest_ctx, cert->tbs.data, cert->tbs.len ); +	digest_final ( digest, digest_ctx, digest_out ); +	DBGC2 ( cert, "X509 %p \"%s\" digest:\n", cert, x509_name ( cert ) ); +	DBGC2_HDA ( cert, 0, digest_out, sizeof ( digest_out ) ); + +	/* Check that signature public key algorithm matches signer */ +	if ( public_key->algorithm->pubkey != pubkey ) { +		DBGC ( cert, "X509 %p \"%s\" signature algorithm %s does not " +		       "match signer's algorithm %s\n", +		       cert, x509_name ( cert ), algorithm->name, +		       public_key->algorithm->name ); +		rc = -EINVAL_ALGORITHM_MISMATCH; +		goto err_mismatch; +	} + +	/* Verify signature using signer's public key */ +	if ( ( rc = pubkey_init ( pubkey, pubkey_ctx, public_key->raw.data, +				  public_key->raw.len ) ) != 0 ) { +		DBGC ( cert, "X509 %p \"%s\" cannot initialise public key: " +		       "%s\n", cert, x509_name ( cert ), strerror ( rc ) ); +		goto err_pubkey_init; +	} +	if ( ( rc = pubkey_verify ( pubkey, pubkey_ctx, digest, digest_out, +				    signature->value.data, +				    signature->value.len ) ) != 0 ) { +		DBGC ( cert, "X509 %p \"%s\" signature verification failed: " +		       "%s\n", cert, x509_name ( cert ), strerror ( rc ) ); +		goto err_pubkey_verify; +	} + +	/* Success */ +	rc = 0; + + err_pubkey_verify: +	pubkey_final ( pubkey, pubkey_ctx ); + err_pubkey_init: + err_mismatch: +	return rc; +} + +/** + * Check X.509 certificate against issuer certificate + * + * @v cert		X.509 certificate + * @v issuer		X.509 issuer certificate + * @ret rc		Return status code + */ +int x509_check_issuer ( struct x509_certificate *cert, +			struct x509_certificate *issuer ) { +	struct x509_public_key *public_key = &issuer->subject.public_key; +	int rc; + +	/* Check issuer.  In theory, this should be a full X.500 DN +	 * comparison, which would require support for a plethora of +	 * abominations such as TeletexString (which allows the +	 * character set to be changed mid-string using escape codes). +	 * In practice, we assume that anyone who deliberately changes +	 * the encoding of the issuer DN is probably a masochist who +	 * will rather enjoy the process of figuring out exactly why +	 * their certificate doesn't work. +	 * +	 * See http://www.cs.auckland.ac.nz/~pgut001/pubs/x509guide.txt +	 * for some enjoyable ranting on this subject. +	 */ +	if ( asn1_compare ( &cert->issuer.raw, &issuer->subject.raw ) != 0 ) { +		DBGC ( cert, "X509 %p \"%s\" issuer does not match ", +		       cert, x509_name ( cert ) ); +		DBGC ( cert, "X509 %p \"%s\" subject\n", +		       issuer, x509_name ( issuer ) ); +		DBGC_HDA ( cert, 0, cert->issuer.raw.data, +			   cert->issuer.raw.len ); +		DBGC_HDA ( issuer, 0, issuer->subject.raw.data, +			   issuer->subject.raw.len ); +		return -EACCES_WRONG_ISSUER; +	} + +	/* Check that issuer is allowed to sign certificates */ +	if ( ! issuer->extensions.basic.ca ) { +		DBGC ( issuer, "X509 %p \"%s\" cannot sign ", +		       issuer, x509_name ( issuer ) ); +		DBGC ( issuer, "X509 %p \"%s\": not a CA certificate\n", +		       cert, x509_name ( cert ) ); +		return -EACCES_NOT_CA; +	} +	if ( issuer->extensions.usage.present && +	     ( ! ( issuer->extensions.usage.bits & X509_KEY_CERT_SIGN ) ) ) { +		DBGC ( issuer, "X509 %p \"%s\" cannot sign ", +		       issuer, x509_name ( issuer ) ); +		DBGC ( issuer, "X509 %p \"%s\": no keyCertSign usage\n", +		       cert, x509_name ( cert ) ); +		return -EACCES_KEY_USAGE; +	} + +	/* Check signature */ +	if ( ( rc = x509_check_signature ( cert, public_key ) ) != 0 ) +		return rc; + +	return 0; +} + +/** + * Calculate X.509 certificate fingerprint + * + * @v cert		X.509 certificate + * @v digest		Digest algorithm + * @v fingerprint	Fingerprint buffer + */ +void x509_fingerprint ( struct x509_certificate *cert, +			struct digest_algorithm *digest, +			void *fingerprint ) { +	uint8_t ctx[ digest->ctxsize ]; + +	/* Calculate fingerprint */ +	digest_init ( digest, ctx ); +	digest_update ( digest, ctx, cert->raw.data, cert->raw.len ); +	digest_final ( digest, ctx, fingerprint ); +} + +/** + * Check X.509 root certificate + * + * @v cert		X.509 certificate + * @v root		X.509 root certificate list + * @ret rc		Return status code + */ +int x509_check_root ( struct x509_certificate *cert, struct x509_root *root ) { +	struct digest_algorithm *digest = root->digest; +	uint8_t fingerprint[ digest->digestsize ]; +	const uint8_t *root_fingerprint = root->fingerprints; +	unsigned int i; + +	/* Calculate certificate fingerprint */ +	x509_fingerprint ( cert, digest, fingerprint ); + +	/* Check fingerprint against all root certificates */ +	for ( i = 0 ; i < root->count ; i++ ) { +		if ( memcmp ( fingerprint, root_fingerprint, +			      sizeof ( fingerprint ) ) == 0 ) { +			DBGC ( cert, "X509 %p \"%s\" is a root certificate\n", +			       cert, x509_name ( cert ) ); +			return 0; +		} +		root_fingerprint += sizeof ( fingerprint ); +	} + +	DBGC2 ( cert, "X509 %p \"%s\" is not a root certificate\n", +		cert, x509_name ( cert ) ); +	return -ENOENT; +} + +/** + * Check X.509 certificate validity period + * + * @v cert		X.509 certificate + * @v time		Time at which to check certificate + * @ret rc		Return status code + */ +int x509_check_time ( struct x509_certificate *cert, time_t time ) { +	struct x509_validity *validity = &cert->validity; + +	/* Check validity period */ +	if ( validity->not_before.time > ( time + TIMESTAMP_ERROR_MARGIN ) ) { +		DBGC ( cert, "X509 %p \"%s\" is not yet valid (at time %lld)\n", +		       cert, x509_name ( cert ), time ); +		return -EACCES_EXPIRED; +	} +	if ( validity->not_after.time < ( time - TIMESTAMP_ERROR_MARGIN ) ) { +		DBGC ( cert, "X509 %p \"%s\" has expired (at time %lld)\n", +		       cert, x509_name ( cert ), time ); +		return -EACCES_EXPIRED; +	} + +	DBGC2 ( cert, "X509 %p \"%s\" is valid (at time %lld)\n", +		cert, x509_name ( cert ), time ); +	return 0; +} + +/** + * Validate X.509 certificate + * + * @v cert		X.509 certificate + * @v issuer		Issuing X.509 certificate (or NULL) + * @v time		Time at which to validate certificate + * @v root		Root certificate list, or NULL to use default + * @ret rc		Return status code + * + * The issuing certificate must have already been validated. + * + * Validation results are cached: if a certificate has already been + * successfully validated then @c issuer, @c time, and @c root will be + * ignored. + */ +int x509_validate ( struct x509_certificate *cert, +		    struct x509_certificate *issuer, +		    time_t time, struct x509_root *root ) { +	unsigned int max_path_remaining; +	int rc; + +	/* Use default root certificate store if none specified */ +	if ( ! root ) +		root = &root_certificates; + +	/* Return success if certificate has already been validated */ +	if ( cert->valid ) +		return 0; + +	/* Fail if certificate is invalid at specified time */ +	if ( ( rc = x509_check_time ( cert, time ) ) != 0 ) +		return rc; + +	/* Succeed if certificate is a trusted root certificate */ +	if ( x509_check_root ( cert, root ) == 0 ) { +		cert->valid = 1; +		cert->path_remaining = ( cert->extensions.basic.path_len + 1 ); +		return 0; +	} + +	/* Fail unless we have an issuer */ +	if ( ! issuer ) { +		DBGC2 ( cert, "X509 %p \"%s\" has no issuer\n", +			cert, x509_name ( cert ) ); +		return -EACCES_UNTRUSTED; +	} + +	/* Fail unless issuer has already been validated */ +	if ( ! issuer->valid ) { +		DBGC ( cert, "X509 %p \"%s\" ", cert, x509_name ( cert ) ); +		DBGC ( cert, "issuer %p \"%s\" has not yet been validated\n", +		       issuer, x509_name ( issuer ) ); +		return -EACCES_OUT_OF_ORDER; +	} + +	/* Fail if issuing certificate cannot validate this certificate */ +	if ( ( rc = x509_check_issuer ( cert, issuer ) ) != 0 ) +		return rc; + +	/* Fail if path length constraint is violated */ +	if ( issuer->path_remaining == 0 ) { +		DBGC ( cert, "X509 %p \"%s\" ", cert, x509_name ( cert ) ); +		DBGC ( cert, "issuer %p \"%s\" path length exceeded\n", +		       issuer, x509_name ( issuer ) ); +		return -EACCES_PATH_LEN; +	} + +	/* Fail if OCSP is required */ +	if ( cert->extensions.auth_info.ocsp.uri.len && +	     ( ! cert->extensions.auth_info.ocsp.good ) ) { +		DBGC ( cert, "X509 %p \"%s\" requires an OCSP check\n", +		       cert, x509_name ( cert ) ); +		return -EACCES_OCSP_REQUIRED; +	} + +	/* Calculate effective path length */ +	cert->path_remaining = ( issuer->path_remaining - 1 ); +	max_path_remaining = ( cert->extensions.basic.path_len + 1 ); +	if ( cert->path_remaining > max_path_remaining ) +		cert->path_remaining = max_path_remaining; + +	/* Mark certificate as valid */ +	cert->valid = 1; + +	DBGC ( cert, "X509 %p \"%s\" successfully validated using ", +	       cert, x509_name ( cert ) ); +	DBGC ( cert, "issuer %p \"%s\"\n", issuer, x509_name ( issuer ) ); +	return 0; +} + +/** + * Check X.509 certificate alternative dNSName + * + * @v cert		X.509 certificate + * @v raw		ASN.1 cursor + * @v name		Name + * @ret rc		Return status code + */ +static int x509_check_dnsname ( struct x509_certificate *cert, +				const struct asn1_cursor *raw, +				const char *name ) { +	const char *fullname = name; +	const char *dnsname = raw->data; +	size_t len = raw->len; + +	/* Check for wildcards */ +	if ( ( len >= 2 ) && ( dnsname[0] == '*' ) && ( dnsname[1] == '.' ) ) { + +		/* Skip initial "*." */ +		dnsname += 2; +		len -= 2; + +		/* Skip initial portion of name to be tested */ +		name = strchr ( name, '.' ); +		if ( ! name ) +			return -ENOENT; +		name++; +	} + +	/* Compare names */ +	if ( ! ( ( strlen ( name ) == len ) && +		 ( memcmp ( name, dnsname, len ) == 0 ) ) ) +		return -ENOENT; + +	if ( name != fullname ) { +		DBGC2 ( cert, "X509 %p \"%s\" found wildcard match for " +			"\"*.%s\"\n", cert, x509_name ( cert ), name ); +	} +	return 0; +} + +/** + * Check X.509 certificate alternative iPAddress + * + * @v cert		X.509 certificate + * @v raw		ASN.1 cursor + * @v name		Name + * @ret rc		Return status code + */ +static int x509_check_ipaddress ( struct x509_certificate *cert, +				  const struct asn1_cursor *raw, +				  const char *name ) { +	struct sockaddr sa; +	sa_family_t family; +	const void *address; +	int rc; + +	/* Determine address family */ +	if ( raw->len == sizeof ( struct in_addr ) ) { +		struct sockaddr_in *sin = ( ( struct sockaddr_in * ) &sa ); +		family = AF_INET; +		address = &sin->sin_addr; +	} else if ( raw->len == sizeof ( struct in6_addr ) ) { +		struct sockaddr_in6 *sin6 = ( ( struct sockaddr_in6 * ) &sa ); +		family = AF_INET6; +		address = &sin6->sin6_addr; +	} else { +		DBGC ( cert, "X509 %p \"%s\" has iPAddress with unexpected " +		       "length %zd\n", cert, x509_name ( cert ), raw->len ); +		DBGC_HDA ( cert, 0, raw->data, raw->len ); +		return -EINVAL; +	} + +	/* Attempt to convert name to a socket address */ +	if ( ( rc = sock_aton ( name, &sa ) ) != 0 ) { +		DBGC2 ( cert, "X509 %p \"%s\" cannot parse \"%s\" as " +			"iPAddress: %s\n", cert, x509_name ( cert ), name, +			strerror ( rc ) ); +		return rc; +	} +	if ( sa.sa_family != family ) +		return -ENOENT; + +	/* Compare addresses */ +	if ( memcmp ( address, raw->data, raw->len ) != 0 ) +		return -ENOENT; + +	DBGC2 ( cert, "X509 %p \"%s\" found iPAddress match for \"%s\"\n", +		cert, x509_name ( cert ), sock_ntoa ( &sa ) ); +	return 0; +} + +/** + * Check X.509 certificate alternative name + * + * @v cert		X.509 certificate + * @v raw		ASN.1 cursor + * @v name		Name + * @ret rc		Return status code + */ +static int x509_check_alt_name ( struct x509_certificate *cert, +				 const struct asn1_cursor *raw, +				 const char *name ) { +	struct asn1_cursor alt_name; +	unsigned int type; + +	/* Enter generalName */ +	memcpy ( &alt_name, raw, sizeof ( alt_name ) ); +	type = asn1_type ( &alt_name ); +	asn1_enter_any ( &alt_name ); + +	/* Check this name */ +	switch ( type ) { +	case X509_GENERAL_NAME_DNS : +		return x509_check_dnsname ( cert, &alt_name, name ); +	case X509_GENERAL_NAME_IP : +		return x509_check_ipaddress ( cert, &alt_name, name ); +	default: +		DBGC2 ( cert, "X509 %p \"%s\" unknown name of type %#02x:\n", +			cert, x509_name ( cert ), type ); +		DBGC2_HDA ( cert, 0, alt_name.data, alt_name.len ); +		return -ENOTSUP; +	} +} + +/** + * Check X.509 certificate name + * + * @v cert		X.509 certificate + * @v name		Name + * @ret rc		Return status code + */ +int x509_check_name ( struct x509_certificate *cert, const char *name ) { +	struct asn1_cursor *common_name = &cert->subject.common_name; +	struct asn1_cursor alt_name; +	int rc; + +	/* Check commonName */ +	if ( x509_check_dnsname ( cert, common_name, name ) == 0 ) { +		DBGC2 ( cert, "X509 %p \"%s\" commonName matches \"%s\"\n", +			cert, x509_name ( cert ), name ); +		return 0; +	} + +	/* Check any subjectAlternativeNames */ +	memcpy ( &alt_name, &cert->extensions.alt_name.names, +		 sizeof ( alt_name ) ); +	for ( ; alt_name.len ; asn1_skip_any ( &alt_name ) ) { +		if ( ( rc = x509_check_alt_name ( cert, &alt_name, +						  name ) ) == 0 ) { +			DBGC2 ( cert, "X509 %p \"%s\" subjectAltName matches " +				"\"%s\"\n", cert, x509_name ( cert ), name ); +			return 0; +		} +	} + +	DBGC ( cert, "X509 %p \"%s\" does not match name \"%s\"\n", +	       cert, x509_name ( cert ), name ); +	return -EACCES_WRONG_NAME; +} + +/** + * Free X.509 certificate chain + * + * @v refcnt		Reference count + */ +static void x509_free_chain ( struct refcnt *refcnt ) { +	struct x509_chain *chain = +		container_of ( refcnt, struct x509_chain, refcnt ); +	struct x509_link *link; +	struct x509_link *tmp; + +	DBGC2 ( chain, "X509 chain %p freed\n", chain ); + +	/* Free each link in the chain */ +	list_for_each_entry_safe ( link, tmp, &chain->links, list ) { +		x509_put ( link->cert ); +		list_del ( &link->list ); +		free ( link ); +	} + +	/* Free chain */ +	free ( chain ); +} + +/** + * Allocate X.509 certificate chain + * + * @ret chain		X.509 certificate chain, or NULL + */ +struct x509_chain * x509_alloc_chain ( void ) { +	struct x509_chain *chain; + +	/* Allocate chain */ +	chain = zalloc ( sizeof ( *chain ) ); +	if ( ! chain ) +		return NULL; + +	/* Initialise chain */ +	ref_init ( &chain->refcnt, x509_free_chain ); +	INIT_LIST_HEAD ( &chain->links ); + +	DBGC2 ( chain, "X509 chain %p allocated\n", chain ); +	return chain; +} + +/** + * Append X.509 certificate to X.509 certificate chain + * + * @v chain		X.509 certificate chain + * @v cert		X.509 certificate + * @ret rc		Return status code + */ +int x509_append ( struct x509_chain *chain, struct x509_certificate *cert ) { +	struct x509_link *link; + +	/* Allocate link */ +	link = zalloc ( sizeof ( *link ) ); +	if ( ! link ) +		return -ENOMEM; + +	/* Add link to chain */ +	link->cert = x509_get ( cert ); +	list_add_tail ( &link->list, &chain->links ); +	DBGC ( chain, "X509 chain %p added X509 %p \"%s\"\n", +	       chain, cert, x509_name ( cert ) ); + +	return 0; +} + +/** + * Append X.509 certificate to X.509 certificate chain + * + * @v chain		X.509 certificate chain + * @v data		Raw certificate data + * @v len		Length of raw data + * @ret rc		Return status code + */ +int x509_append_raw ( struct x509_chain *chain, const void *data, +		      size_t len ) { +	struct x509_certificate *cert; +	int rc; + +	/* Parse certificate */ +	if ( ( rc = x509_certificate ( data, len, &cert ) ) != 0 ) +		goto err_parse; + +	/* Append certificate to chain */ +	if ( ( rc = x509_append ( chain, cert ) ) != 0 ) +		goto err_append; + +	/* Drop reference to certificate */ +	x509_put ( cert ); + +	return 0; + + err_append: +	x509_put ( cert ); + err_parse: +	return rc; +} + +/** + * Identify X.509 certificate by subject + * + * @v certs		X.509 certificate list + * @v subject		Subject + * @ret cert		X.509 certificate, or NULL if not found + */ +static struct x509_certificate * +x509_find_subject ( struct x509_chain *certs, +		    const struct asn1_cursor *subject ) { +	struct x509_link *link; +	struct x509_certificate *cert; + +	/* Scan through certificate list */ +	list_for_each_entry ( link, &certs->links, list ) { + +		/* Check subject */ +		cert = link->cert; +		if ( asn1_compare ( subject, &cert->subject.raw ) == 0 ) +			return cert; +	} + +	return NULL; +} + +/** + * Append X.509 certificates to X.509 certificate chain + * + * @v chain		X.509 certificate chain + * @v certs		X.509 certificate list + * @ret rc		Return status code + * + * Certificates will be automatically appended to the chain based upon + * the subject and issuer names. + */ +int x509_auto_append ( struct x509_chain *chain, struct x509_chain *certs ) { +	struct x509_certificate *cert; +	struct x509_certificate *previous; +	int rc; + +	/* Get current certificate */ +	cert = x509_last ( chain ); +	if ( ! cert ) { +		DBGC ( chain, "X509 chain %p has no certificates\n", chain ); +		return -EACCES_EMPTY; +	} + +	/* Append certificates, in order */ +	while ( 1 ) { + +		/* Find issuing certificate */ +		previous = cert; +		cert = x509_find_subject ( certs, &cert->issuer.raw ); +		if ( ! cert ) +			break; +		if ( cert == previous ) +			break; + +		/* Append certificate to chain */ +		if ( ( rc = x509_append ( chain, cert ) ) != 0 ) +			return rc; +	} + +	return 0; +} + +/** + * Validate X.509 certificate chain + * + * @v chain		X.509 certificate chain + * @v time		Time at which to validate certificates + * @v store		Certificate store, or NULL to use default + * @v root		Root certificate list, or NULL to use default + * @ret rc		Return status code + */ +int x509_validate_chain ( struct x509_chain *chain, time_t time, +			  struct x509_chain *store, struct x509_root *root ) { +	struct x509_certificate *issuer = NULL; +	struct x509_link *link; +	int rc; + +	/* Use default certificate store if none specified */ +	if ( ! store ) +		store = &certstore; + +	/* Append any applicable certificates from the certificate store */ +	if ( ( rc = x509_auto_append ( chain, store ) ) != 0 ) +		return rc; + +	/* Find first certificate that can be validated as a +	 * standalone (i.e.  is already valid, or can be validated as +	 * a trusted root certificate). +	 */ +	list_for_each_entry ( link, &chain->links, list ) { + +		/* Try validating this certificate as a standalone */ +		if ( ( rc = x509_validate ( link->cert, NULL, time, +					    root ) ) != 0 ) +			continue; + +		/* Work back up to start of chain, performing pairwise +		 * validation. +		 */ +		issuer = link->cert; +		list_for_each_entry_continue_reverse ( link, &chain->links, +						       list ) { + +			/* Validate this certificate against its issuer */ +			if ( ( rc = x509_validate ( link->cert, issuer, time, +						    root ) ) != 0 ) +				return rc; +			issuer = link->cert; +		} + +		return 0; +	} + +	DBGC ( chain, "X509 chain %p found no usable certificates\n", chain ); +	return -EACCES_USELESS; +} + +/* Drag in certificate store */ +REQUIRE_OBJECT ( certstore ); | 
