/*
 * 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 );
FILE_SECBOOT ( PERMITTED );

#include <byteswap.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <strings.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 and updated in RFC 8017.
 */

/* 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" )

/** An RSA context */
struct rsa_context {
	/** Allocated memory */
	void *dynamic;
	/** Modulus */
	bigint_element_t *modulus0;
	/** Modulus size */
	unsigned int size;
	/** Modulus length */
	size_t max_len;
	/** Exponent */
	bigint_element_t *exponent0;
	/** Exponent size */
	unsigned int exponent_size;
	/** Input buffer */
	bigint_element_t *input0;
	/** Output buffer */
	bigint_element_t *output0;
	/** Temporary working space for modular exponentiation */
	void *tmp;
	/** Modulus MSB mask */
	uint8_t mask;
};

/**
 * Encode digest
 *
 * @v context		RSA context
 * @v digest		Digest algorithm
 * @v value		Digest value
 * @v reference		Reference encoded digest (or NULL)
 * @v encoded		Encoded digest
 * @ret rc		Return status code
 */
typedef int ( rsa_encode_t ) ( struct rsa_context *context,
			       struct digest_algorithm *digest,
			       const void *value, const void *reference,
			       void *encoded );

/** Generate random data */
int ( * rsa_get_random ) ( void *data, size_t len ) = get_random_nz;

/**
 * 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 inline void rsa_free ( struct rsa_context *context ) {

	free ( context->dynamic );
}

/**
 * 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;
	size_t tmp_len = bigint_mod_exp_tmp_len ( modulus );
	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;

	/* 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 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_cursor cursor;
	int is_private;
	int rc;

	/* Enter subjectPublicKeyInfo/privateKeyInfo/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 );

		/* Enter privateKey, if present */
		if ( asn1_check_algorithm ( &cursor, &rsa_encryption_algorithm,
					    NULL ) == 0 ) {

			/* Skip privateKeyAlgorithm */
			asn1_skip_any ( &cursor );

			/* Enter privateKey */
			asn1_enter ( &cursor, ASN1_OCTET_STRING );

			/* Enter RSAPrivateKey */
			asn1_enter ( &cursor, ASN1_SEQUENCE );

			/* Skip version */
			asn1_skip ( &cursor, ASN1_INTEGER );
		}

	} else {

		/* Public key */
		is_private = 0;

		/* Skip algorithm */
		asn1_skip ( &cursor, ASN1_SEQUENCE );

		/* Enter subjectPublicKey */
		asn1_enter_bits ( &cursor, NULL );

		/* Enter RSAPublicKey */
		asn1_enter ( &cursor, ASN1_SEQUENCE );
	}

	/* Extract modulus */
	memcpy ( modulus, &cursor, sizeof ( *modulus ) );
	if ( ( rc = asn1_enter_unsigned ( modulus ) ) != 0 )
		return rc;
	asn1_skip_any ( &cursor );

	/* Skip public exponent, if applicable */
	if ( is_private )
		asn1_skip ( &cursor, ASN1_INTEGER );

	/* Extract publicExponent/privateExponent */
	memcpy ( exponent, &cursor, sizeof ( *exponent ) );
	if ( ( rc = asn1_enter_unsigned ( exponent ) ) != 0 )
		return rc;

	return 0;
}

/**
 * Initialise RSA cipher
 *
 * @v context		RSA context
 * @v key		Key
 * @ret rc		Return status code
 */
static int rsa_init ( struct rsa_context *context,
		      const struct asn1_cursor *key ) {
	struct asn1_cursor modulus;
	struct asn1_cursor exponent;
	uint8_t msb;
	int rc;

	/* Initialise context */
	memset ( context, 0, sizeof ( *context ) );

	/* Parse modulus and exponent */
	if ( ( rc = rsa_parse_mod_exp ( &modulus, &exponent, key ) ) != 0 ){
		DBGC ( context, "RSA %p invalid modulus/exponent:\n", context );
		DBGC_HDA ( context, 0, key->data, key->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 );

	/* Construct MSB mask */
	msb = *( ( const uint8_t * ) modulus.data );
	if ( ! msb ) {
		DBGC ( context, "RSA %p invalid modulus MSB\n", context );
		rc = -EINVAL;
		goto err_msb;
	}
	context->mask = ( ( 1 << ( fls ( msb ) - 1 ) ) - 1 );

	/* 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_msb:
 err_parse:
	return rc;
}

/**
 * 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 PKCS#1
 *
 * @v key		Key
 * @v plaintext		Plaintext
 * @v ciphertext	Ciphertext
 * @ret ciphertext_len	Length of ciphertext, or negative error
 */
static int rsa_pkcs1_encrypt ( const struct asn1_cursor *key,
			       const struct asn1_cursor *plaintext,
			       struct asn1_builder *ciphertext ) {
	struct rsa_context context;
	void *temp;
	uint8_t *encoded;
	size_t max_len;
	size_t random_nz_len;
	int rc;

	DBGC ( &context, "RSA %p encrypting:\n", &context );
	DBGC_HDA ( &context, 0, plaintext->data, plaintext->len );

	/* Initialise context */
	if ( ( rc = rsa_init ( &context, key ) ) != 0 )
		goto err_init;

	/* Calculate lengths */
	max_len = ( context.max_len - 11 );
	random_nz_len = ( max_len - plaintext->len + 8 );

	/* Sanity check */
	if ( plaintext->len > max_len ) {
		DBGC ( &context, "RSA %p plaintext too long (%zd bytes, max "
		       "%zd)\n", &context, plaintext->len, max_len );
		rc = -ERANGE;
		goto err_sanity;
	}

	/* 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 = rsa_get_random ( &encoded[2], random_nz_len ) ) != 0 ) {
		DBGC ( &context, "RSA %p could not generate random data: %s\n",
		       &context, strerror ( rc ) );
		goto err_random;
	}
	encoded[ 2 + random_nz_len ] = 0x00;
	memcpy ( &encoded[ context.max_len - plaintext->len ],
		 plaintext->data, plaintext->len );
	DBGC ( &context, "RSA %p encoded:\n", &context );
	DBGC_HDA ( &context, 0, encoded, context.max_len );

	/* Create space for ciphertext */
	if ( ( rc = asn1_grow ( ciphertext, context.max_len ) ) != 0 )
		goto err_grow;

	/* Encipher the encoded message */
	rsa_cipher ( &context, encoded, ciphertext->data );
	DBGC ( &context, "RSA %p encrypted:\n", &context );
	DBGC_HDA ( &context, 0, ciphertext->data, context.max_len );

	/* Free context */
	rsa_free ( &context );

	return 0;

 err_grow:
 err_random:
 err_sanity:
	rsa_free ( &context );
 err_init:
	return rc;
}

/**
 * Decrypt using RSA PKCS#1
 *
 * @v key		Key
 * @v ciphertext	Ciphertext
 * @v plaintext		Plaintext
 * @ret rc		Return status code
 */
static int rsa_pkcs1_decrypt ( const struct asn1_cursor *key,
			       const struct asn1_cursor *ciphertext,
			       struct asn1_builder *plaintext ) {
	struct rsa_context context;
	void *temp;
	uint8_t *encoded;
	uint8_t *end;
	uint8_t *zero;
	uint8_t *start;
	size_t len;
	int rc;

	DBGC ( &context, "RSA %p decrypting:\n", &context );
	DBGC_HDA ( &context, 0, ciphertext->data, ciphertext->len );

	/* Initialise context */
	if ( ( rc = rsa_init ( &context, key ) ) != 0 )
		goto err_init;

	/* 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 );
		rc = -ERANGE;
		goto err_sanity;
	}

	/* Decipher the message (using the big integer input buffer as
	 * temporary storage)
	 */
	temp = context.input0;
	encoded = temp;
	rsa_cipher ( &context, ciphertext->data, encoded );
	DBGC ( &context, "RSA %p encoded:\n", &context );
	DBGC_HDA ( &context, 0, encoded, context.max_len );

	/* Parse the message */
	end = ( encoded + context.max_len );
	if ( ( encoded[0] != 0x00 ) || ( encoded[1] != 0x02 ) ) {
		rc = -EINVAL;
		goto err_invalid;
	}
	zero = memchr ( &encoded[2], 0, ( end - &encoded[2] ) );
	if ( ! zero ) {
		DBGC ( &context, "RSA %p invalid decrypted message:\n",
		       &context );
		DBGC_HDA ( &context, 0, encoded, context.max_len );
		rc = -EINVAL;
		goto err_invalid;
	}
	start = ( zero + 1 );
	len = ( end - start );

	/* Create space for plaintext */
	if ( ( rc = asn1_grow ( plaintext, len ) ) != 0 )
		goto err_grow;

	/* Copy out message */
	memcpy ( plaintext->data, start, len );
	DBGC ( &context, "RSA %p decrypted:\n", &context );
	DBGC_HDA ( &context, 0, plaintext->data, len );

	/* Free context */
	rsa_free ( &context );

	return 0;

 err_grow:
 err_invalid:
 err_sanity:
	rsa_free ( &context );
 err_init:
	return rc;
}

/**
 * Encode digest using RSA PKCS#1
 *
 * @v context		RSA context
 * @v digest		Digest algorithm
 * @v value		Digest value
 * @v reference		Reference encoded digest (or NULL)
 * @v encoded		Encoded digest
 * @ret rc		Return status code
 */
static int rsa_pkcs1_encode ( struct rsa_context *context,
			      struct digest_algorithm *digest,
			      const void *value,
			      const void *reference __unused,
			      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 using PKCS#1:\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 using PKCS#1:\n",
	       context, digest->name );
	DBGC_HDA ( context, 0, encoded, context->max_len );

	return 0;
}

/**
 * Apply RSA PSS mask generation function
 *
 * @v digest		Digest algorithm
 * @v ctx		Digest context buffer
 * @v out		Digest output buffer
 * @v seed		Mask seed
 * @v xor		XOR buffer
 * @v len		Length of XOR buffer
 */
static void rsa_xor_mask ( struct digest_algorithm *digest, void *ctx,
			   void *out, const void *seed, void *xor,
			   size_t len ) {
	size_t digest_len = digest->digestsize;
	const uint8_t *out_byte = out;
	uint8_t *xor_byte = xor;
	uint32_t counter = 0;
	unsigned int i;

	while ( len ) {

		/* Generate output */
		digest_init ( digest, ctx );
		digest_update ( digest, ctx, seed, digest_len );
		digest_update ( digest, ctx, &counter, sizeof ( counter ) );
		digest_final ( digest, ctx, out );

		/* XOR output into buffer */
		for ( i = 0 ; len && ( i < digest_len ) ; i++, len-- )
			*(xor_byte++) ^= out_byte[i];

		/* Increment counter */
		counter = htonl ( ntohl ( counter ) + 1 );
	}
}

/**
 * Encode digest using RSA PSS
 *
 * @v context		RSA context
 * @v digest		Digest algorithm
 * @v value		Digest value
 * @v reference		Reference encoded digest (or NULL)
 * @v encoded		Encoded digest
 * @ret rc		Return status code
 */
static int rsa_pss_encode ( struct rsa_context *context,
			    struct digest_algorithm *digest,
			    const void *value, const void *reference,
			    void *encoded ) {
	static uint8_t zero[8];
	uint8_t ctx[ digest->ctxsize ];
	uint8_t out[ digest->digestsize ];
	size_t digest_len = digest->digestsize;
	size_t mask_len;
	size_t pad_len;
	size_t min_len;
	void *hash;
	void *salt;
	uint8_t *msb;
	uint8_t *head;
	uint8_t *tail;
	int rc;

	/* Sanity check */
	min_len = ( sizeof ( *head ) + digest_len /* salt */ +
		    digest_len /* hash */ + sizeof ( *tail ) );
	if ( context->max_len < min_len ) {
		DBGC ( context, "RSA %p %s formatted digest value too long "
		       "(%zd bytes, max %zd)\n", context, digest->name,
		       min_len, context->max_len );
		return -ERANGE;
	}
	DBGC ( context, "RSA %p encoding %s digest using PSS:\n",
	       context, digest->name );
	DBGC_HDA ( context, 0, value, digest_len );

	/* Split message into component parts */
	pad_len = ( context->max_len - min_len );
	msb = encoded;
	head = ( msb + pad_len );
	salt = ( head + sizeof ( *head ) );
	hash = ( salt + digest_len );
	tail = ( hash + digest_len );
	mask_len = ( pad_len + sizeof ( *head ) + digest_len /* salt */ );
	assert ( tail == ( encoded + context->max_len - 1 ) );

	/* Generate or construct salt as applicable */
	if ( reference ) {
		memcpy ( encoded, reference, context->max_len );
		rsa_xor_mask ( digest, ctx, out, hash, encoded, mask_len );
	} else {
		if ( ( rc = rsa_get_random ( salt, digest_len ) ) != 0 ) {
			DBGC ( context, "RSA %p could not generate random "
			       "salt: %s\n", context, strerror ( rc ) );
			return rc;
		}
	}
	DBGC ( context, "RSA %p salt:\n", context );
	DBGC_HDA ( context, 0, salt, digest_len );

	/* Construct intermediate digest */
	digest_init ( digest, ctx );
	digest_update ( digest, ctx, zero, sizeof ( zero ) );
	digest_update ( digest, ctx, value, digest_len );
	digest_update ( digest, ctx, salt, digest_len );
	digest_final ( digest, ctx, hash );

	/* Construct message */
	memset ( encoded, 0, pad_len );
	*head = 0x01;
	rsa_xor_mask ( digest, ctx, out, hash, encoded, mask_len );
	*msb &= context->mask;
	*tail = 0xbc;
	DBGC ( context, "RSA %p encoded %s digest using PSS:\n",
	       context, digest->name );
	DBGC_HDA ( context, 0, encoded, context->max_len );

	return 0;
}

/**
 * Sign digest value using RSA
 *
 * @v key		Key
 * @v digest		Digest algorithm
 * @v value		Digest value
 * @v signature		Signature
 * @v encode		Encoding method
 * @ret rc		Return status code
 */
static int rsa_sign ( const struct asn1_cursor *key,
		      struct digest_algorithm *digest, const void *value,
		      struct asn1_builder *signature, rsa_encode_t *encode ) {
	struct rsa_context context;
	int rc;

	DBGC ( &context, "RSA %p signing %s digest:\n",
	       &context, digest->name );
	DBGC_HDA ( &context, 0, value, digest->digestsize );

	/* Initialise context */
	if ( ( rc = rsa_init ( &context, key ) ) != 0 )
		goto err_init;

	/* Create space for encoded digest and signature */
	if ( ( rc = asn1_grow ( signature, context.max_len ) ) != 0 )
		goto err_grow;

	/* Encode digest */
	if ( ( rc = encode ( &context, digest, value, NULL,
			     signature->data ) ) != 0 )
		goto err_encode;

	/* Encipher the encoded digest */
	rsa_cipher ( &context, signature->data, signature->data );
	DBGC ( &context, "RSA %p signed %s digest:\n", &context, digest->name );
	DBGC_HDA ( &context, 0, signature->data, signature->len );

	/* Free context */
	rsa_free ( &context );

	return 0;

 err_encode:
 err_grow:
	rsa_free ( &context );
 err_init:
	return rc;
}

/**
 * Verify signed digest value using RSA
 *
 * @v key		Key
 * @v digest		Digest algorithm
 * @v value		Digest value
 * @v signature		Signature
 * @v encoding		Encoding method
 * @ret rc		Return status code
 */
static int rsa_verify ( const struct asn1_cursor *key,
			struct digest_algorithm *digest, const void *value,
			const struct asn1_cursor *signature,
			rsa_encode_t *encode ) {
	struct rsa_context context;
	void *temp;
	void *expected;
	void *actual;
	int rc;

	DBGC ( &context, "RSA %p verifying %s digest:\n",
	       &context, digest->name );
	DBGC_HDA ( &context, 0, value, digest->digestsize );
	DBGC_HDA ( &context, 0, signature->data, signature->len );

	/* Initialise context */
	if ( ( rc = rsa_init ( &context, key ) ) != 0 )
		goto err_init;

	/* 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 );
		rc = -ERANGE;
		goto err_sanity;
	}

	/* Decipher the signature (using the big integer input buffer
	 * as temporary storage)
	 */
	temp = context.input0;
	expected = temp;
	rsa_cipher ( &context, signature->data, 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 = encode ( &context, digest, value, expected,
			     actual ) ) != 0 )
		goto err_encode;

	/* Verify the signature */
	if ( memcmp ( actual, expected, context.max_len ) != 0 ) {
		DBGC ( &context, "RSA %p signature verification failed\n",
		       &context );
		rc = -EACCES_VERIFY;
		goto err_verify;
	}

	/* Free context */
	rsa_free ( &context );

	DBGC ( &context, "RSA %p signature verified successfully\n", &context );
	return 0;

 err_verify:
 err_encode:
 err_sanity:
	rsa_free ( &context );
 err_init:
	return rc;
}

/**
 * Sign digest value using RSA PKCS#1
 *
 * @v key		Key
 * @v digest		Digest algorithm
 * @v value		Digest value
 * @v signature		Signature
 * @ret rc		Return status code
 */
static int rsa_pkcs1_sign ( const struct asn1_cursor *key,
			    struct digest_algorithm *digest, const void *value,
			    struct asn1_builder *signature ) {

	return rsa_sign ( key, digest, value, signature, rsa_pkcs1_encode );
}

/**
 * Verify signed digest value using RSA PKCS#1
 *
 * @v key		Key
 * @v digest		Digest algorithm
 * @v value		Digest value
 * @v signature		Signature
 * @ret rc		Return status code
 */
static int rsa_pkcs1_verify ( const struct asn1_cursor *key,
			      struct digest_algorithm *digest,
			      const void *value,
			      const struct asn1_cursor *signature ) {

	return rsa_verify ( key, digest, value, signature, rsa_pkcs1_encode );
}

/**
 * Sign digest value using RSA PSS
 *
 * @v key		Key
 * @v digest		Digest algorithm
 * @v value		Digest value
 * @v signature		Signature
 * @ret rc		Return status code
 */
static int rsa_pss_sign ( const struct asn1_cursor *key,
			  struct digest_algorithm *digest, const void *value,
			  struct asn1_builder *signature ) {

	return rsa_sign ( key, digest, value, signature, rsa_pss_encode );
}

/**
 * Verify signed digest value using RSA PSS
 *
 * @v key		Key
 * @v digest		Digest algorithm
 * @v value		Digest value
 * @v signature		Signature
 * @ret rc		Return status code
 */
static int rsa_pss_verify ( const struct asn1_cursor *key,
			    struct digest_algorithm *digest, const void *value,
			    const struct asn1_cursor *signature ) {

	return rsa_verify ( key, digest, value, signature, rsa_pss_encode );
}

/**
 * Check for matching RSA public/private key pair
 *
 * @v private_key	Private key
 * @v public_key	Public key
 * @ret rc		Return status code
 */
static int rsa_match ( const struct asn1_cursor *private_key,
		       const struct asn1_cursor *public_key ) {
	struct asn1_cursor private_modulus;
	struct asn1_cursor private_exponent;
	struct asn1_cursor public_modulus;
	struct asn1_cursor public_exponent;
	int rc;

	/* Parse moduli and exponents */
	if ( ( rc = rsa_parse_mod_exp ( &private_modulus, &private_exponent,
					private_key ) ) != 0 )
		return rc;
	if ( ( rc = rsa_parse_mod_exp ( &public_modulus, &public_exponent,
					public_key ) ) != 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",
	.encrypt	= rsa_pkcs1_encrypt,
	.decrypt	= rsa_pkcs1_decrypt,
	.sign		= rsa_pkcs1_sign,
	.verify		= rsa_pkcs1_verify,
	.match		= rsa_match,
};

/** RSA-PSS public-key algorithm */
struct pubkey_algorithm rsa_pss_algorithm = {
	.name		= "rsa_pss",
	.encrypt	= pubkey_null_encrypt,
	.decrypt	= pubkey_null_decrypt,
	.sign		= rsa_pss_sign,
	.verify		= rsa_pss_verify,
	.match		= rsa_match,
};

/* Drag in objects via rsa_algorithm */
REQUIRING_SYMBOL ( rsa_algorithm );

/* Drag in crypto configuration */
REQUIRE_OBJECT ( config_crypto );
