/*
 * Copyright 2020 Intel Corporation
 * SPDX-License-Identifier: Apache 2.0
 */

/*!
 * \file
 * \brief Abstraction layer for AES_GCM (authenticated- encryption) routines of
 * mbedtls library.
 */

#include "fdoCryptoHal.h"
#include "util.h"
#include "platform_utils.h"
#include "crypto_utils.h"
#include "safe_lib.h"
/**
 * fdo_crypto_aes_gcm_encrypt -  Perform Authenticated AES encryption on the
 * input plain text.
 *
 * @param plain_text
 *        input plain-text to modify.
 * @param plain_text_length
 *        plain-text size in bytes.
 * @param cipher_text
 *        Encrypted text(output).
 * @param cipher_text_length
 *	  Max length of Cipher Text
 * @param iv
 *        AES encryption IV.
 * @param iv_length
 *        AES encryption IV size in bytes.
 * @param key
 *        Key in Byte_array format used in encryption.
 * @param key_length
 *        Key size in Bytes. Only AES128 is supported
 * @param tag
 *        tag added during encryption (output).
 * @param tag_length
 *        tag size in Bytes.
 * @return ret
 *        return cipher_length in bytes during success and -1 during any error.
 */
int32_t fdo_crypto_aes_gcm_encrypt(const uint8_t *plain_text,
				   uint32_t plain_text_length,
				   uint8_t *cipher_text,
				   uint32_t cipher_text_length,
				   const uint8_t *iv, uint32_t iv_length,
				   const uint8_t *key, uint32_t key_length,
				   uint8_t *tag, uint32_t tag_length)
{
	int32_t retval = -1;
	static mbedtls_gcm_context ctx;

	if (NULL == plain_text || 0 == plain_text_length ||
	    NULL == cipher_text || NULL == iv || 0 == iv_length ||
	    NULL == key || key_length != PLATFORM_AES_KEY_DEFAULT_LEN ||
	    NULL == tag || tag_length != AES_GCM_TAG_LEN) {
		LOG(LOG_ERROR, "Invalid parameters!\n");
		return retval;
	}

	if (cipher_text_length < plain_text_length) {
		LOG(LOG_ERROR, "Output buffer is not sufficient!\n");
		return retval;
	}

	/* Initialise the context */
	mbedtls_gcm_init(&ctx);

	/* Initialise the GCM key */
	retval = mbedtls_gcm_setkey(&ctx, MBEDTLS_CIPHER_ID_AES,
				    (const unsigned char *)key, key_length * 8);
	if (retval != 0) {
		LOG(LOG_ERROR, "Key initialization failed!\n");
		retval = -1;
		goto end;
	}

	/* Do gcm crypt on data */
	retval = mbedtls_gcm_crypt_and_tag(
	    &ctx, MBEDTLS_GCM_ENCRYPT, plain_text_length,
	    (const unsigned char *)iv, iv_length, NULL, 0,
	    (const unsigned char *)plain_text, cipher_text, tag_length, tag);
	if (retval != 0) {
		LOG(LOG_ERROR, "AES GCM encrypt failed!\n");
		retval = -1;
		goto end;
	} else {
		retval = plain_text_length;
	}

end:
	/* Clean up and free allocated memory */
	mbedtls_gcm_free(&ctx);
	return retval;
}

/**
 * fdo_crypto_aes_gcm_decrypt -  Perform Authenticated AES decryption on the
 * input cipher text.
 *
 * @param clear_text
 *        output clear-text.
 * @param clear_text_length
 *        max plain-text buffer size in bytes.
 * @param cipher_text
 *        Encrypted text(input).
 * @param cipher_text_length
 *        Encrypted cipher-text size in Byte.
 * @param iv
 *        AES encryption IV.
 * @param iv_length
 *        AES encryption IV size in bytes.
 * @param key
 *        Key in Byte_array format used in encryption.
 * @param key_length
 *        Key size in Bytes. Only AES128 is supported
 * @param tag
 *        input authenticated tag which got added during encryption.
 * @param tag_length
 *        tag size in Bytes.
 * @return ret
 *        return clear_text_length in bytes during success and -1 during any
 * error.
 */
int32_t fdo_crypto_aes_gcm_decrypt(uint8_t *clear_text,
				   uint32_t clear_text_length,
				   const uint8_t *cipher_text,
				   uint32_t cipher_text_length,
				   const uint8_t *iv, uint32_t iv_length,
				   const uint8_t *key, uint32_t key_length,
				   uint8_t *tag, uint32_t tag_length)
{
	int32_t retval = -1;
	static mbedtls_gcm_context ctx;

	if (NULL == clear_text || NULL == cipher_text ||
	    0 == cipher_text_length || NULL == iv || 0 == iv_length ||
	    NULL == key || key_length != PLATFORM_AES_KEY_DEFAULT_LEN ||
	    NULL == tag || tag_length != AES_GCM_TAG_LEN) {
		LOG(LOG_ERROR, "Invalid parameters!\n");
		return retval;
	}

	if (clear_text_length < cipher_text_length) {
		LOG(LOG_ERROR, "Output buffer is not sufficient!\n");
		return retval;
	}

	/* Initialise the context */
	mbedtls_gcm_init(&ctx);

	/* Initialise the GCM key */
	retval = mbedtls_gcm_setkey(&ctx, MBEDTLS_CIPHER_ID_AES,
				    (const unsigned char *)key, key_length * 8);
	if (retval != 0) {
		LOG(LOG_ERROR, "Key initialization failed!\n");
		retval = -1;
		goto end;
	}

	/* Do gcm crypt on data */
	retval = mbedtls_gcm_auth_decrypt(
	    &ctx, cipher_text_length, (const unsigned char *)iv, iv_length,
	    NULL, 0, (const unsigned char *)tag, tag_length,
	    (const unsigned char *)cipher_text, clear_text);
	if (retval != 0) {
		LOG(LOG_ERROR, "AES GCM encrypt failed!\n");
		retval = -1;
		goto end;
	} else {
		retval = cipher_text_length;
	}

end:
	/* Clean up and free allocated memory */
	mbedtls_gcm_free(&ctx);
	return retval;
}
