/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 *
 * Host functions for verified boot.
 *
 * TODO: change all 'return 0', 'return 1' into meaningful return codes.
 */

#include <string.h>

#include "host_common.h"
#include "cryptolib.h"
#include "utility.h"
#include "vboot_common.h"

VbFirmwarePreambleHeader *CreateFirmwarePreamble(
	uint64_t firmware_version,
	const VbPublicKey *kernel_subkey,
	const VbSignature *body_signature,
	const VbPrivateKey *signing_key,
	uint32_t flags)
{
	VbFirmwarePreambleHeader *h;
	uint64_t signed_size = (sizeof(VbFirmwarePreambleHeader) +
				kernel_subkey->key_size +
				body_signature->sig_size);
	uint64_t block_size = signed_size + siglen_map[signing_key->algorithm];
	uint8_t *kernel_subkey_dest;
	uint8_t *body_sig_dest;
	uint8_t *block_sig_dest;
	VbSignature *sigtmp;

	/* Allocate key block */
	h = (VbFirmwarePreambleHeader *)malloc(block_size);
	if (!h)
		return NULL;

	Memset(h, 0, block_size);
	kernel_subkey_dest = (uint8_t *)(h + 1);
	body_sig_dest = kernel_subkey_dest + kernel_subkey->key_size;
	block_sig_dest = body_sig_dest + body_signature->sig_size;

	h->header_version_major = FIRMWARE_PREAMBLE_HEADER_VERSION_MAJOR;
	h->header_version_minor = FIRMWARE_PREAMBLE_HEADER_VERSION_MINOR;
	h->preamble_size = block_size;
	h->firmware_version = firmware_version;
	h->flags = flags;

	/* Copy data key */
	PublicKeyInit(&h->kernel_subkey, kernel_subkey_dest,
		      kernel_subkey->key_size);
	PublicKeyCopy(&h->kernel_subkey, kernel_subkey);

	/* Copy body signature */
	SignatureInit(&h->body_signature, body_sig_dest,
		      body_signature->sig_size, 0);
	SignatureCopy(&h->body_signature, body_signature);

	/* Set up signature struct so we can calculate the signature */
	SignatureInit(&h->preamble_signature, block_sig_dest,
		      siglen_map[signing_key->algorithm], signed_size);

	/* Calculate signature */
	sigtmp = CalculateSignature((uint8_t *)h, signed_size, signing_key);
	SignatureCopy(&h->preamble_signature, sigtmp);
	free(sigtmp);

	/* Return the header */
	return h;
}

VbKernelPreambleHeader *CreateKernelPreamble(
	uint64_t kernel_version,
	uint64_t body_load_address,
	uint64_t bootloader_address,
	uint64_t bootloader_size,
	const VbSignature *body_signature,
	uint64_t vmlinuz_header_address,
	uint64_t vmlinuz_header_size,
	uint64_t desired_size,
	const VbPrivateKey *signing_key)
{
	VbKernelPreambleHeader *h;
	uint64_t signed_size = (sizeof(VbKernelPreambleHeader) +
				body_signature->sig_size);
	uint64_t block_size = signed_size + siglen_map[signing_key->algorithm];
	uint8_t *body_sig_dest;
	uint8_t *block_sig_dest;
	VbSignature *sigtmp;

	/* If the block size is smaller than the desired size, pad it */
	if (block_size < desired_size)
		block_size = desired_size;

	/* Allocate key block */
	h = (VbKernelPreambleHeader *)malloc(block_size);
	if (!h)
		return NULL;

	Memset(h, 0, block_size);
	body_sig_dest = (uint8_t *)(h + 1);
	block_sig_dest = body_sig_dest + body_signature->sig_size;

	h->header_version_major = KERNEL_PREAMBLE_HEADER_VERSION_MAJOR;
	h->header_version_minor = KERNEL_PREAMBLE_HEADER_VERSION_MINOR;
	h->preamble_size = block_size;
	h->kernel_version = kernel_version;
	h->body_load_address = body_load_address;
	h->bootloader_address = bootloader_address;
	h->bootloader_size = bootloader_size;
	h->vmlinuz_header_address = vmlinuz_header_address;
	h->vmlinuz_header_size = vmlinuz_header_size;

	/* Copy body signature */
	SignatureInit(&h->body_signature, body_sig_dest,
		      body_signature->sig_size, 0);
	SignatureCopy(&h->body_signature, body_signature);

	/* Set up signature struct so we can calculate the signature */
	SignatureInit(&h->preamble_signature, block_sig_dest,
		      siglen_map[signing_key->algorithm], signed_size);

	/* Calculate signature */
	sigtmp = CalculateSignature((uint8_t *)h, signed_size, signing_key);
	SignatureCopy(&h->preamble_signature, sigtmp);
	free(sigtmp);

	/* Return the header */
	return h;
}
