/* * Copyright (C) 2012 Michael Brown . * * 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 #include #include #include #include #include #include #include #include #include /** @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 }; /** "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 ), }; /** * 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, };