diff options
author | Yang Zhang <yang.z.zhang@intel.com> | 2015-08-28 09:58:54 +0800 |
---|---|---|
committer | Yang Zhang <yang.z.zhang@intel.com> | 2015-09-01 12:44:00 +0800 |
commit | e44e3482bdb4d0ebde2d8b41830ac2cdb07948fb (patch) | |
tree | 66b09f592c55df2878107a468a91d21506104d3f /qemu/roms/ipxe/src/crypto | |
parent | 9ca8dbcc65cfc63d6f5ef3312a33184e1d726e00 (diff) |
Add qemu 2.4.0
Change-Id: Ic99cbad4b61f8b127b7dc74d04576c0bcbaaf4f5
Signed-off-by: Yang Zhang <yang.z.zhang@intel.com>
Diffstat (limited to 'qemu/roms/ipxe/src/crypto')
35 files changed, 11245 insertions, 0 deletions
diff --git a/qemu/roms/ipxe/src/crypto/aes_wrap.c b/qemu/roms/ipxe/src/crypto/aes_wrap.c new file mode 100644 index 000000000..c09480e5a --- /dev/null +++ b/qemu/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/qemu/roms/ipxe/src/crypto/arc4.c b/qemu/roms/ipxe/src/crypto/arc4.c new file mode 100644 index 000000000..91a732019 --- /dev/null +++ b/qemu/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/qemu/roms/ipxe/src/crypto/asn1.c b/qemu/roms/ipxe/src/crypto/asn1.c new file mode 100644 index 000000000..6d880704f --- /dev/null +++ b/qemu/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/qemu/roms/ipxe/src/crypto/axtls/aes.c b/qemu/roms/ipxe/src/crypto/axtls/aes.c new file mode 100644 index 000000000..bd99a7097 --- /dev/null +++ b/qemu/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/qemu/roms/ipxe/src/crypto/axtls/bigint.h b/qemu/roms/ipxe/src/crypto/axtls/bigint.h new file mode 100644 index 000000000..1f38c53d6 --- /dev/null +++ b/qemu/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/qemu/roms/ipxe/src/crypto/axtls/bigint_impl.h b/qemu/roms/ipxe/src/crypto/axtls/bigint_impl.h new file mode 100644 index 000000000..09d8550ea --- /dev/null +++ b/qemu/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/qemu/roms/ipxe/src/crypto/axtls/config.h b/qemu/roms/ipxe/src/crypto/axtls/config.h new file mode 100644 index 000000000..32fa3bf03 --- /dev/null +++ b/qemu/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/qemu/roms/ipxe/src/crypto/axtls/crypto.h b/qemu/roms/ipxe/src/crypto/axtls/crypto.h new file mode 100644 index 000000000..2c4cda4de --- /dev/null +++ b/qemu/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/qemu/roms/ipxe/src/crypto/axtls/os_port.h b/qemu/roms/ipxe/src/crypto/axtls/os_port.h new file mode 100644 index 000000000..76313e204 --- /dev/null +++ b/qemu/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/qemu/roms/ipxe/src/crypto/axtls_aes.c b/qemu/roms/ipxe/src/crypto/axtls_aes.c new file mode 100644 index 000000000..7f93c0ed7 --- /dev/null +++ b/qemu/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/qemu/roms/ipxe/src/crypto/bigint.c b/qemu/roms/ipxe/src/crypto/bigint.c new file mode 100644 index 000000000..340128e2f --- /dev/null +++ b/qemu/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/qemu/roms/ipxe/src/crypto/cbc.c b/qemu/roms/ipxe/src/crypto/cbc.c new file mode 100644 index 000000000..9bf0e8b49 --- /dev/null +++ b/qemu/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/qemu/roms/ipxe/src/crypto/certstore.c b/qemu/roms/ipxe/src/crypto/certstore.c new file mode 100644 index 000000000..77cf6ebb6 --- /dev/null +++ b/qemu/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/qemu/roms/ipxe/src/crypto/chap.c b/qemu/roms/ipxe/src/crypto/chap.c new file mode 100644 index 000000000..db64371c7 --- /dev/null +++ b/qemu/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/qemu/roms/ipxe/src/crypto/cms.c b/qemu/roms/ipxe/src/crypto/cms.c new file mode 100644 index 000000000..b4a41de6c --- /dev/null +++ b/qemu/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/qemu/roms/ipxe/src/crypto/crc32.c b/qemu/roms/ipxe/src/crypto/crc32.c new file mode 100644 index 000000000..cfef68c02 --- /dev/null +++ b/qemu/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/qemu/roms/ipxe/src/crypto/crypto_null.c b/qemu/roms/ipxe/src/crypto/crypto_null.c new file mode 100644 index 000000000..ba05f7269 --- /dev/null +++ b/qemu/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/qemu/roms/ipxe/src/crypto/deflate.c b/qemu/roms/ipxe/src/crypto/deflate.c new file mode 100644 index 000000000..91a489961 --- /dev/null +++ b/qemu/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/qemu/roms/ipxe/src/crypto/drbg.c b/qemu/roms/ipxe/src/crypto/drbg.c new file mode 100644 index 000000000..9e0175d25 --- /dev/null +++ b/qemu/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/qemu/roms/ipxe/src/crypto/entropy.c b/qemu/roms/ipxe/src/crypto/entropy.c new file mode 100644 index 000000000..c7045840e --- /dev/null +++ b/qemu/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/qemu/roms/ipxe/src/crypto/hash_df.c b/qemu/roms/ipxe/src/crypto/hash_df.c new file mode 100644 index 000000000..adf1d87e4 --- /dev/null +++ b/qemu/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/qemu/roms/ipxe/src/crypto/hmac.c b/qemu/roms/ipxe/src/crypto/hmac.c new file mode 100644 index 000000000..e9459198c --- /dev/null +++ b/qemu/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/qemu/roms/ipxe/src/crypto/hmac_drbg.c b/qemu/roms/ipxe/src/crypto/hmac_drbg.c new file mode 100644 index 000000000..1e5f732e2 --- /dev/null +++ b/qemu/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/qemu/roms/ipxe/src/crypto/md5.c b/qemu/roms/ipxe/src/crypto/md5.c new file mode 100644 index 000000000..122c7d59e --- /dev/null +++ b/qemu/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/qemu/roms/ipxe/src/crypto/null_entropy.c b/qemu/roms/ipxe/src/crypto/null_entropy.c new file mode 100644 index 000000000..c56d5e76f --- /dev/null +++ b/qemu/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/qemu/roms/ipxe/src/crypto/ocsp.c b/qemu/roms/ipxe/src/crypto/ocsp.c new file mode 100644 index 000000000..66e47c57e --- /dev/null +++ b/qemu/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/qemu/roms/ipxe/src/crypto/privkey.c b/qemu/roms/ipxe/src/crypto/privkey.c new file mode 100644 index 000000000..e010649c0 --- /dev/null +++ b/qemu/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/qemu/roms/ipxe/src/crypto/random_nz.c b/qemu/roms/ipxe/src/crypto/random_nz.c new file mode 100644 index 000000000..f1d2e187d --- /dev/null +++ b/qemu/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/qemu/roms/ipxe/src/crypto/rbg.c b/qemu/roms/ipxe/src/crypto/rbg.c new file mode 100644 index 000000000..e2d06978c --- /dev/null +++ b/qemu/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/qemu/roms/ipxe/src/crypto/rootcert.c b/qemu/roms/ipxe/src/crypto/rootcert.c new file mode 100644 index 000000000..ae28905ac --- /dev/null +++ b/qemu/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/qemu/roms/ipxe/src/crypto/rsa.c b/qemu/roms/ipxe/src/crypto/rsa.c new file mode 100644 index 000000000..0ab7b2ad3 --- /dev/null +++ b/qemu/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/qemu/roms/ipxe/src/crypto/sha1.c b/qemu/roms/ipxe/src/crypto/sha1.c new file mode 100644 index 000000000..e1bef669e --- /dev/null +++ b/qemu/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/qemu/roms/ipxe/src/crypto/sha1extra.c b/qemu/roms/ipxe/src/crypto/sha1extra.c new file mode 100644 index 000000000..cec0d35e5 --- /dev/null +++ b/qemu/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/qemu/roms/ipxe/src/crypto/sha256.c b/qemu/roms/ipxe/src/crypto/sha256.c new file mode 100644 index 000000000..36e02b3c2 --- /dev/null +++ b/qemu/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/qemu/roms/ipxe/src/crypto/x509.c b/qemu/roms/ipxe/src/crypto/x509.c new file mode 100644 index 000000000..4a02dad14 --- /dev/null +++ b/qemu/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 ); |