| /* |
| * 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. |
| * |
| * You can also choose to distribute this program under the terms of |
| * the Unmodified Binary Distribution Licence (as given in the file |
| * COPYING.UBDL), provided that you have satisfied its requirements. |
| */ |
| |
| FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); |
| |
| #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/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" ) |
| |
| /** |
| * 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 = RSA_CTX_SIZE, |
| .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, |
| }; |
| |
| /* Drag in objects via rsa_algorithm */ |
| REQUIRING_SYMBOL ( rsa_algorithm ); |
| |
| /* Drag in crypto configuration */ |
| REQUIRE_OBJECT ( config_crypto ); |