// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
/* Copyright 2020 IBM Corp. */
#ifndef pr_fmt
#define pr_fmt(fmt) "EDK2_COMPAT: " fmt
#endif

/*#include <opal.h>*/
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <stdint.h>
#include <ccan/endian/endian.h>
// #include <mbedtls/error.h>
/*#include <device.h>
#include <assert.h>*/
/*#include "libstb/crypto/pkcs7/pkcs7.h"*/
/*#include "edk2.h"*/
/*#include "../secvar.h"*/
#include "edk2-compat-process.h"

//nick stuff added 
//#include "external/extraMbedtls/include/pkcs7.h"
#include <stdlib.h>
#include "prlog.h"
#include "libstb/secvar/crypto/crypto.h"
#include "edk2.h"




bool setup_mode;

int update_variable_in_bank(struct secvar *update_var, const char *data,
			    const uint64_t dsize, struct list_head *bank)
{
	struct secvar *var;

	var = find_secvar(update_var->key, update_var->key_len, bank);
	if (!var)
		return OPAL_EMPTY;

        /* Reallocate the data memory, if there is change in data size */
	if (var->data_size < dsize)
		if (realloc_secvar(var, dsize))
			return OPAL_NO_MEM;

	if (dsize && data)
		memcpy(var->data, data, dsize);
	var->data_size = dsize;

        /* Clear the volatile bit only if updated with positive data size */
	if (dsize)
		var->flags &= ~SECVAR_FLAG_VOLATILE;
	else
		var->flags |= SECVAR_FLAG_VOLATILE;

	if (key_equals(update_var->key, "PK") || key_equals(update_var->key, "HWKH"))
		var->flags |= SECVAR_FLAG_PROTECTED;

	return 0;
}

/* Expand char to wide character size */
static char *char_to_wchar(const char *key, const size_t keylen)
{
	int i;
	char *str;

	str = zalloc(keylen * 2);
	if (!str)
		return NULL;

	for (i = 0; i < keylen*2; key++) {
		str[i++] = *key;
		str[i++] = '\0';
	}

	return str;
}

/* Returns the authority that can sign the given key update */
static void get_key_authority(const char *ret[3], const char *key)
{
	int i = 0;

	if (key_equals(key, "PK")) {
		ret[i++] = "PK";
	} else if (key_equals(key, "KEK")) {
		ret[i++] = "PK";
	} else if (key_equals(key, "db") || key_equals(key, "dbx")) {
		ret[i++] = "KEK";
		ret[i++] = "PK";
	}

	ret[i] = NULL;
}

EFI_SIGNATURE_LIST* get_esl_signature_list(const char *buf, size_t buflen)
{
	EFI_SIGNATURE_LIST *list = NULL;

	if (buflen < sizeof(EFI_SIGNATURE_LIST) || !buf) {
		prlog(PR_ERR, "ERROR: SigList does not have enough data to be valid\n");
		return NULL;
	}

	list = (EFI_SIGNATURE_LIST *)buf;

	return list;
}

/* Returns the size of the complete ESL. */
static int32_t get_esl_signature_list_size(const char *buf, const size_t buflen)
{
	EFI_SIGNATURE_LIST *list = get_esl_signature_list(buf, buflen);

	if (!list)
		return OPAL_PARAMETER;

	/*prlog(PR_DEBUG, "size of signature list size is %u\n",
			le32_to_cpu(list->SignatureListSize));*/ //NICK WAS HERE

	return le32_to_cpu(list->SignatureListSize);
}

/* 
 * Copies the certificate from the ESL into cert buffer and returns the size
 * of the certificate
 */
int get_esl_cert(const char *buf, const size_t buflen, char **cert)
{
	size_t sig_data_offset;
	size_t size;
	EFI_SIGNATURE_LIST *list = get_esl_signature_list(buf, buflen);

	if (!list)
		return OPAL_PARAMETER;

	assert(cert != NULL);

	size = le32_to_cpu(list->SignatureSize) - sizeof(uuid_t);

	/*prlog(PR_DEBUG,"size of signature list size is %u\n",
			le32_to_cpu(list->SignatureListSize));
	prlog(PR_DEBUG, "size of signature header size is %u\n",
			le32_to_cpu(list->SignatureHeaderSize));
	prlog(PR_DEBUG, "size of signature size is %u\n",
			le32_to_cpu(list->SignatureSize));*/ //NICK WAS HERE

	sig_data_offset = sizeof(EFI_SIGNATURE_LIST)
			  + le32_to_cpu(list->SignatureHeaderSize)
			  + 16 * sizeof(uint8_t);
	if (sig_data_offset > buflen)
		return OPAL_PARAMETER;

	*cert = zalloc(size);
	if (!(*cert))
		return OPAL_NO_MEM;

	/* Since buf can have more than one ESL, copy only the size calculated
	 * to return single ESL */
	memcpy(*cert, buf + sig_data_offset, size);

	return size;
}

/* 
 * Extracts size of the PKCS7 signed data embedded in the
 * struct Authentication 2 Descriptor Header.
 */
size_t get_pkcs7_len(const struct efi_variable_authentication_2 *auth)
{
	uint32_t dw_length;
	size_t size;

	assert(auth != NULL);

	dw_length = le32_to_cpu(auth->auth_info.hdr.dw_length);
	size = dw_length - (sizeof(auth->auth_info.hdr.dw_length)
			+ sizeof(auth->auth_info.hdr.w_revision)
			+ sizeof(auth->auth_info.hdr.w_certificate_type)
			+ sizeof(auth->auth_info.cert_type));

	return size;
}

int get_auth_descriptor2(const void *buf, const size_t buflen, void **auth_buffer)
{
	const struct efi_variable_authentication_2 *auth = buf;
	int auth_buffer_size;
	size_t len;

	assert(auth_buffer != NULL);
	if (buflen < sizeof(struct efi_variable_authentication_2)
	    || !buf)
			return OPAL_PARAMETER;

	len = get_pkcs7_len(auth);
	/* pkcs7 content length cannot be greater than buflen */ 
	if (len > buflen)
		return OPAL_PARAMETER;

	auth_buffer_size = sizeof(auth->timestamp) + sizeof(auth->auth_info.hdr)
			   + sizeof(auth->auth_info.cert_type) + len;

	*auth_buffer = zalloc(auth_buffer_size);
	if (!(*auth_buffer))
		return OPAL_NO_MEM;

	/*
	 * Data = auth descriptor + new ESL data.
	 * Extracts only the auth descriptor from data.
	 */
	memcpy(*auth_buffer, buf, auth_buffer_size);

	return auth_buffer_size;
}

static bool validate_cert(char *signing_cert, int signing_cert_size)
{
	//NICK CHILD removed direct mbedtls call, use general crypto
	// mbedtls_x509_crt x509;
	crypto_x509 *x509;
	char *x509_buf = NULL;
	int rc;

	// NICK CHILD removed direct mbedtls call, use general crypto
	// mbedtls_x509_crt_init(&x509);
	// rc = mbedtls_x509_crt_parse(&x509, (unsigned char *)signing_cert, signing_cert_size);
	/* If failure in parsing the certificate, exit */
	// if(rc) {
	// 	prlog(PR_ERR, "X509 certificate parsing failed %04x\n", rc);
	x509 = crypto_x509_parse_der((unsigned char *)signing_cert, signing_cert_size);
	if (!x509) {
		prlog(PR_ERR, "X509 certificate parsing failed\n");

		return false;
	}

	x509_buf = zalloc(CERT_BUFFER_SIZE);
	// NICK CHILD removed direct mbedtls call, use general crypto
	// rc = mbedtls_x509_crt_info(x509_buf, CERT_BUFFER_SIZE, "\tCRT:", &x509);
	rc = crypto_x509_get_long_desc(x509_buf, CERT_BUFFER_SIZE, "CRT:", x509);
	// NICK CHILD General Question: Should we be printing to debug here?
	// it seems wierd to parse to info string then not print
	// NICK CHILD removed direct mbedtls call, use general crypto
	// mbedtls_x509_crt_free(&x509);
	crypto_x509_free(x509);
	free(x509_buf);
	x509_buf = NULL;

	/* If failure in reading the certificate, exit */
	if (rc < 0)
		return false;

	return true;
}

static bool validate_hash(uuid_t type, int size)
{
        if (uuid_equals(&type, &EFI_CERT_SHA1_GUID) && (size == 20))
                return true;

        if (uuid_equals(&type, &EFI_CERT_SHA224_GUID) && (size == 28))
                return true;

        if (uuid_equals(&type, &EFI_CERT_SHA256_GUID) && (size == 32))
                return true;

        if (uuid_equals(&type, &EFI_CERT_SHA384_GUID) && (size == 48))
                return true;

        if (uuid_equals(&type, &EFI_CERT_SHA512_GUID) && (size == 64))
                return true;

        return false;
}

int validate_esl_list(const char *key, const char *esl, const size_t size)
{
	int count = 0;
	int dsize;
	char *data = NULL;
	int eslvarsize = size;
	int eslsize;
	int rc = OPAL_SUCCESS;
	int offset = 0;
	EFI_SIGNATURE_LIST *list = NULL;

	while (eslvarsize > 0) {
		prlog(PR_DEBUG, "esl var size size is %d offset is %d\n", eslvarsize, offset);
		if (eslvarsize < sizeof(EFI_SIGNATURE_LIST))
			break;

		/* Check Supported ESL Type */
		list = get_esl_signature_list(esl, eslvarsize);

		if (!list)
			return OPAL_PARAMETER;

		prlog(PR_DEBUG, "size of signature list size is %u\n",
				le32_to_cpu(list->SignatureListSize));

		/* Calculate the size of the ESL */
		eslsize = le32_to_cpu(list->SignatureListSize);

		/* If could not extract the size */
		if (eslsize <= 0) {
			prlog(PR_ERR, "Invalid size of the ESL\n");
			rc = OPAL_PARAMETER;
			break;
		}

		/* Extract the certificate from the ESL */
		dsize = get_esl_cert(esl, eslvarsize, &data);
		if (dsize < 0) {
			rc = dsize;
			break;
		}

		if (key_equals(key, "dbx")) {
			if (!validate_hash(list->SignatureType, dsize)) {
				prlog(PR_ERR, "No valid hash is found\n");
				rc = OPAL_PARAMETER;
				break;
			}
		} else {
		       if (!uuid_equals(&list->SignatureType, &EFI_CERT_X509_GUID)
			   || !validate_cert(data, dsize)) {
				prlog(PR_ERR, "No valid cert is found\n");
				rc = OPAL_PARAMETER;
				break;
		       }
		}

		count++;

		/* Look for the next ESL */
		offset = offset + eslsize;
		eslvarsize = eslvarsize - eslsize;
		free(data);
		/* Since we are going to allocate again in the next iteration */
		data = NULL;
	}

	if (rc == OPAL_SUCCESS) {
		if (key_equals(key, "PK") && (count > 1)) {
			prlog(PR_ERR, "PK can only be one\n");
			rc = OPAL_PARAMETER;
		} else {
			rc = count;
		}
	}

	free(data);

	prlog(PR_INFO, "Total ESLs are %d\n", rc);
	return rc;
}

/* Get the timestamp for the last update of the give key */
static struct efi_time *get_last_timestamp(const char *key, char *last_timestamp)
{
	struct efi_time *timestamp = (struct efi_time*)last_timestamp;

	if (!last_timestamp)
		return NULL;

	if (key_equals(key, "PK"))
		return &timestamp[0];
	else if (key_equals(key, "KEK"))
		return &timestamp[1];
	else if (key_equals(key, "db"))
		return &timestamp[2];
	else if (key_equals(key, "dbx"))
		return &timestamp[3];
	else
		return NULL;
}

int update_timestamp(const char *key, const struct efi_time *timestamp, char *last_timestamp)
{
	struct efi_time *prev;

	prev = get_last_timestamp(key, last_timestamp);
	if (prev == NULL)
		return OPAL_INTERNAL_ERROR;

	/* Update with new timestamp */
	memcpy(prev, timestamp, sizeof(struct efi_time));

	prlog(PR_DEBUG, "updated prev year is %d month %d day %d\n",
			le16_to_cpu(prev->year), prev->month, prev->day);

	return OPAL_SUCCESS;
}

static uint64_t unpack_timestamp(const struct efi_time *timestamp)
{
	uint64_t val = 0;
	uint16_t year = le32_to_cpu(timestamp->year);

	/* pad1, nanosecond, timezone, daylight and pad2 are meant to be zero */
	val |= ((uint64_t) timestamp->pad1 & 0xFF) << 0;
	val |= ((uint64_t) timestamp->second & 0xFF) << (1*8);
	val |= ((uint64_t) timestamp->minute & 0xFF) << (2*8);
	val |= ((uint64_t) timestamp->hour & 0xFF) << (3*8);
	val |= ((uint64_t) timestamp->day & 0xFF) << (4*8);
	val |= ((uint64_t) timestamp->month & 0xFF) << (5*8);
	val |= ((uint64_t) year) << (6*8);

	return val;
}

int check_timestamp(const char *key, const struct efi_time *timestamp,
		    char *last_timestamp)
{
	struct efi_time *prev;
	uint64_t new;
	uint64_t last;

	prev = get_last_timestamp(key, last_timestamp);
	if (prev == NULL)
		return OPAL_INTERNAL_ERROR;

	prlog(PR_DEBUG, "timestamp year is %d month %d day %d\n",
			le16_to_cpu(timestamp->year), timestamp->month,
			timestamp->day);
	prlog(PR_DEBUG, "prev year is %d month %d day %d\n",
			le16_to_cpu(prev->year), prev->month, prev->day);

	new = unpack_timestamp(timestamp);
	last = unpack_timestamp(prev);

	if (new > last)
		return OPAL_SUCCESS;

	return OPAL_PERMISSION;
}

/* Extract PKCS7 from the authentication header */
//NICK CHILD removed direct mbedtls call, use general crypto
// static mbedtls_pkcs7* get_pkcs7(const struct efi_variable_authentication_2 *auth)
static crypto_pkcs7* get_pkcs7(const struct efi_variable_authentication_2 *auth)
{
	char *checkpkcs7cert = NULL;
	size_t len;
	//NICK CHILD removed direct mbedtls call, use general crypto
	// mbedtls_pkcs7 *pkcs7 = NULL;
	crypto_pkcs7 *pkcs7 = NULL;

	int rc;

	len = get_pkcs7_len(auth);

	//NICK CHILD removed direct mbedtls call, use general crypto
	// pkcs7 = malloc(sizeof(struct mbedtls_pkcs7));
	// if (!pkcs7)
	// 	return NULL;

	// mbedtls_pkcs7_init(pkcs7);
	// rc = mbedtls_pkcs7_parse_der( auth->auth_info.cert_data, len, pkcs7);
	// if (rc <= 0) {
	// 	prlog(PR_ERR, "Parsing pkcs7 failed %04x\n", rc);
	pkcs7 = crypto_pkcs7_parse_der(auth->auth_info.cert_data, len);
	if (!pkcs7) {
		prlog(PR_ERR, "Parsing pkcs7 failed\n");
		goto out;
	}

	checkpkcs7cert = zalloc(CERT_BUFFER_SIZE);
	if (!checkpkcs7cert)
		goto out;

	//NICK CHILD removed direct mbedtls call, use general crypto
	// rc = mbedtls_x509_crt_info(checkpkcs7cert, CERT_BUFFER_SIZE, "CRT:",
	// 			   &(pkcs7->signed_data.certs));
	rc = crypto_x509_get_long_desc(checkpkcs7cert, CERT_BUFFER_SIZE, "CRT:",
				  crypto_pkcs7_get_signing_cert(pkcs7, 0));
	if (rc < 0) {
		prlog(PR_ERR, "Failed to parse the certificate in PKCS7 structure\n");
		free(checkpkcs7cert);
		goto out;
	}

	prlog(PR_DEBUG, "%s \n", checkpkcs7cert);
	free(checkpkcs7cert);
	return pkcs7;

out:
	//NICK CHILD removed direct mbedtls call, use general crypto
	//mbedtls_pkcs7_free(pkcs7);
	if (pkcs7) crypto_pkcs7_free(pkcs7);
	pkcs7 = NULL;
	return pkcs7;
}

/* Verify the PKCS7 signature on the signed data. */
static int verify_signature(const struct efi_variable_authentication_2 *auth,
			    const char *newcert, const size_t new_data_size,
			    const struct secvar *avar)
{
	//NICK CHILD removed direct mbedtls call, use general crypto
	//mbedtls_pkcs7 *pkcs7 = NULL;
	//mbedtls_x509_crt x509;
	crypto_pkcs7 *pkcs7 = NULL;
	crypto_x509 *x509 = NULL;

	char *signing_cert = NULL;
	char *x509_buf = NULL;
	int signing_cert_size;
	int rc = 0;
	char *errbuf;
	int eslvarsize;
	int eslsize;
	int offset = 0;

	if (!auth)
		return OPAL_PARAMETER;

	/* Extract the pkcs7 from the auth structure */
	pkcs7 = get_pkcs7(auth);
	/* Failure to parse pkcs7 implies bad input. */
	if (!pkcs7)
			return OPAL_PARAMETER;	

	prlog(PR_INFO, "Load the signing certificate from the keystore\n");

	eslvarsize = avar->data_size;

	/* Variable is not empty */
	while (eslvarsize > 0) {
		if (eslvarsize < sizeof(EFI_SIGNATURE_LIST))
			break;

		/* Calculate the size of the ESL */
		eslsize = get_esl_signature_list_size(avar->data + offset,
						      eslvarsize);
		/* If could not extract the size */
		if (eslsize <= 0) {
			rc = OPAL_PARAMETER;
			break;
		}

		/* Extract the certificate from the ESL */
		signing_cert_size = get_esl_cert(avar->data + offset,
						 eslvarsize, &signing_cert);
		if (signing_cert_size < 0) {
			rc = signing_cert_size;
			break;
		}

		// NICK CHILD removed direct mbedtls call, use general crypto
		// mbedtls_x509_crt_init(&x509);
		// rc = mbedtls_x509_crt_parse(&x509,
		// 			    (unsigned char *)signing_cert,
		// 			    signing_cert_size);
		// /* This should not happen, unless something corrupted in PNOR */
		// if(rc) {
		// 	prlog(PR_ERR, "X509 certificate parsing failed %04x\n", rc);
		x509 = crypto_x509_parse_der((unsigned char *)signing_cert, signing_cert_size);
		if (!x509) {
			prlog(PR_ERR, "X509 certificate parsing failed\n");

			rc = OPAL_INTERNAL_ERROR;
			break;
		}

		x509_buf = zalloc(CERT_BUFFER_SIZE);
		// NICK CHILD removed direct mbedtls call, use general crypto
		// rc = mbedtls_x509_crt_info(x509_buf,
		// 			   CERT_BUFFER_SIZE,
		// 			   "\tCRT:",
		// 			   &x509);	//NICK ADDED \t
		rc = crypto_x509_get_long_desc(x509_buf, CERT_BUFFER_SIZE, "\tCRT:", x509);
		/* This should not happen, unless something corrupted in PNOR */
		if (rc < 0) {
			free(x509_buf);
			rc = OPAL_INTERNAL_ERROR;
			break;
		}

		prlog(PR_INFO, "%s \n", x509_buf);
		free(x509_buf);
		x509_buf = NULL;
		//NICK CHILD removed direct mbedtls call, use general crypto
		// rc = mbedtls_pkcs7_signed_hash_verify(pkcs7, &x509, (unsigned char *)newcert, new_data_size);
		rc = crypto_pkcs7_signed_hash_verify(pkcs7, x509, (unsigned char *)newcert, new_data_size);
		/* If you find a signing certificate, you are done */
		if (rc == 0) {
			prlog(PR_INFO, "Signature Verification passed\n");
			//NICK CHILD removed direct mbedtls call, use general crypto
			// mbedtls_x509_crt_free(&x509);
			crypto_x509_free(x509);

			break;
		} else {
			//NICK CHILD removed direct mbedtls call, use general crypto
			// errbuf = zalloc(MBEDTLS_ERR_BUFFER_SIZE);
			// mbedtls_strerror(rc, errbuf, MBEDTLS_ERR_BUFFER_SIZE);
			errbuf = zalloc(CRYPTO_ERR_BUFFER_SIZE);
			crypto_strerror(rc, errbuf, CRYPTO_ERR_BUFFER_SIZE);

			prlog(PR_NOTICE, "Signature Verification failed %02x %s\n",
					rc, errbuf); //ADDED  BY NICK CHILD, WAS PR_ERR now PR_NOTICE
			free(errbuf);
			rc = OPAL_PERMISSION;
		}


		/* Look for the next ESL */
		offset = offset + eslsize;
		eslvarsize = eslvarsize - eslsize;
		//NICK CHILD removed direct mbedtls call, use general crypto
		// mbedtls_x509_crt_free(&x509);
		crypto_x509_free(x509);

		free(signing_cert);
		/* Since we are going to allocate again in the next iteration */
		signing_cert = NULL;

	}

	free(signing_cert);
	//NICK CHILD removed direct mbedtls call, use general crypto
	// mbedtls_pkcs7_free(pkcs7);
	// free(pkcs7);
	crypto_pkcs7_free(pkcs7);

	return rc;
}

/* 
 * Create the hash of the buffer
 * name || vendor guid || attributes || timestamp || newcontent
 * which is submitted as signed by the user.
 * Returns the sha256 hash, else negative error code.
 */
static char *get_hash_to_verify(const char *key, const char *new_data,
				const size_t new_data_size,
				const struct efi_time *timestamp)
{
	le32 attr = cpu_to_le32(SECVAR_ATTRIBUTES);
	size_t varlen;
	char *wkey;
	uuid_t guid;
	unsigned char *hash = NULL;
	//NICK CHILD removed direct mbedtls call, use general crypto
	// const mbedtls_md_info_t *md_info;
	// mbedtls_md_context_t ctx;
	crypto_md_ctx *ctx;

	int rc;

	//NICK CHILD removed direct mbedtls call, use general crypto
	// md_info = mbedtls_md_info_from_type( MBEDTLS_MD_SHA256 );
	// mbedtls_md_init(&ctx);

	// rc = mbedtls_md_setup(&ctx, md_info, 0);

	// if (rc)
	// 	goto out;

	// rc = mbedtls_md_starts(&ctx);
	rc = crypto_md_ctx_init(&ctx, CRYPTO_MD_SHA256);

	if (rc)
		goto out;

	if (key_equals(key, "PK")
	    || key_equals(key, "KEK"))
		guid = EFI_GLOBAL_VARIABLE_GUID;
	else if (key_equals(key, "db")
	    || key_equals(key, "dbx"))
		guid = EFI_IMAGE_SECURITY_DATABASE_GUID;
	else
		return NULL;

	/* Expand char name to wide character width */
	varlen = strlen(key) * 2;
	wkey = char_to_wchar(key, strlen(key));

	//NICK CHILD removed direct mbedtls call, use general crypt
	//rc = mbedtls_md_update(&ctx, (const unsigned char *)wkey, varlen);
	rc = crypto_md_update(ctx, (const unsigned char *)wkey, varlen);

	free(wkey);
	if (rc) 
		goto out;

	//NICK CHILD removed direct mbedtls call, use general crypt
	//rc = mbedtls_md_update(&ctx, (const unsigned char *)&guid, sizeof(guid));
	rc = crypto_md_update(ctx, (const unsigned char *)&guid, sizeof(guid));

	if (rc)
		goto out;
	//NICK CHILD removed direct mbedtls call, use general crypt
	//rc = mbedtls_md_update(&ctx, (const unsigned char *)&attr, sizeof(attr));
	rc = crypto_md_update(ctx, (const unsigned char *)&attr, sizeof(attr));

	if (rc)
		goto out;

	//NICK CHILD removed direct mbedtls call, use general crypt
	// rc = mbedtls_md_update(&ctx, (const unsigned char *)timestamp,
	//		       sizeof(struct efi_time));
	rc = crypto_md_update(ctx, (const unsigned char *)timestamp,
			       sizeof(struct efi_time));

	if (rc)
		goto out;

	//NICK CHILD removed direct mbedtls call, use general crypt	
	// rc = mbedtls_md_update(&ctx, (const unsigned char *)new_data, new_data_size);
	rc = crypto_md_update(ctx, (const unsigned char *)new_data, new_data_size);

	if (rc)
		goto out;

	hash = zalloc(32);
	if (!hash)
		return NULL;
	//NICK CHILD removed direct mbedtls call, use general crypt
	//rc = mbedtls_md_finish(&ctx, hash);
	rc = crypto_md_finish(ctx, hash);

	if (rc) {
		free(hash);
		hash = NULL;
	}

out:
	//NICK CHILD removed direct mbedtls call, use general crypt
	//mbedtls_md_free(&ctx);
	crypto_md_free(ctx);

	return (char *)hash;
}

bool is_pkcs7_sig_format(const void *data)
{
	const struct efi_variable_authentication_2 *auth = data;
	uuid_t pkcs7_guid = EFI_CERT_TYPE_PKCS7_GUID;

	return !memcmp(&auth->auth_info.cert_type, &pkcs7_guid, 16);
}

int process_update(const struct secvar *update, char **newesl,
		   int *new_data_size, struct efi_time *timestamp,
		   struct list_head *bank, char *last_timestamp)
{
	struct efi_variable_authentication_2 *auth = NULL;
	void *auth_buffer = NULL;
	int auth_buffer_size = 0;
	const char *key_authority[3];
	char *tbhbuffer = NULL;
	size_t tbhbuffersize = 0;
	struct secvar *avar = NULL;
	int rc = 0;
	int i;

	/* We need to split data into authentication descriptor and new ESL */
	auth_buffer_size = get_auth_descriptor2(update->data,
						update->data_size,
						&auth_buffer);
	if ((auth_buffer_size < 0)
	     || (update->data_size < auth_buffer_size)) {
		prlog(PR_ERR, "Invalid auth buffer size\n");
		rc = auth_buffer_size;
		goto out;
	}

	auth = auth_buffer;

	if (!timestamp) {
		rc = OPAL_INTERNAL_ERROR;
		goto out;
	}

	memcpy(timestamp, auth_buffer, sizeof(struct efi_time));

	rc = check_timestamp(update->key, timestamp, last_timestamp);
	/* Failure implies probably an older command being resubmitted */
	if (rc != OPAL_SUCCESS) {
		prlog(PR_ERR, "Timestamp verification failed for key %s\n", update->key);
		goto out;
	}

	/* Calculate the size of new ESL data */
	*new_data_size = update->data_size - auth_buffer_size;
	if (*new_data_size < 0) {
		prlog(PR_ERR, "Invalid new ESL (new data content) size\n");
		rc = OPAL_PARAMETER;
		goto out;
	}
	*newesl = zalloc(*new_data_size);
	if (!(*newesl)) {
		rc = OPAL_NO_MEM;
		goto out;
	}
	memcpy(*newesl, update->data + auth_buffer_size, *new_data_size);

	/* Validate the new ESL is in right format */
	rc = validate_esl_list(update->key, *newesl, *new_data_size);
	if (rc < 0) {
		prlog(PR_ERR, "ESL validation failed for key %s with error %04x\n",
		      update->key, rc);
		goto out;
	}

	if (setup_mode) {
		rc = OPAL_SUCCESS;
		goto out;
	}

	/* Prepare the data to be verified */
	tbhbuffer = get_hash_to_verify(update->key, *newesl, *new_data_size,
				timestamp);
	if (!tbhbuffer) {
		rc = OPAL_INTERNAL_ERROR;
		goto out;
	}

	/* Get the authority to verify the signature */
	get_key_authority(key_authority, update->key);

	/*
	 * Try for all the authorities that are allowed to sign.
	 * For eg. db/dbx can be signed by both PK or KEK
	 */
	for (i = 0; key_authority[i] != NULL; i++) {
		prlog(PR_DEBUG, "update key is %s\n", update->key); //changed by NICK for clarity
		prlog(PR_DEBUG, "key authority is %s\n", key_authority[i]);
		avar = find_secvar(key_authority[i],
				    strlen(key_authority[i]) + 1,
				    bank);
		if (!avar || !avar->data_size)
			continue;

		/* Verify the signature */
		rc = verify_signature(auth, tbhbuffer, tbhbuffersize,
				      avar);

		/* Break if signature verification is successful */
		if (rc == OPAL_SUCCESS) {
			printf("Update for %s is correctly signed by current %s\n", update->key, key_authority[i]); //changed by NICK for clarity
			break;
		}
	}

out:
	free(auth_buffer);
	free(tbhbuffer);

	return rc;
}
