/* Copyright 2013 The ChromiumOS Authors
 * 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.
 */

#include <string.h>

#include "2common.h"
#include "2rsa.h"
#include "2sysincludes.h"
#include "host_common.h"
#include "host_key21.h"

struct vb2_fw_preamble *vb2_create_fw_preamble(
	uint32_t firmware_version,
	const struct vb2_packed_key *kernel_subkey,
	const struct vb2_signature *body_signature,
	const struct vb2_private_key *signing_key,
	uint32_t flags)
{
	uint32_t signed_size = (sizeof(struct vb2_fw_preamble) +
				kernel_subkey->key_size +
				body_signature->sig_size);
	uint32_t block_size = signed_size +
		vb2_rsa_sig_size(signing_key->sig_alg);

	/* Allocate keyblock */
	struct vb2_fw_preamble *h =
		(struct vb2_fw_preamble *)calloc(block_size, 1);
	if (!h)
		return NULL;

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

	h->header_version_major = VB2_FIRMWARE_PREAMBLE_HEADER_VERSION_MAJOR;
	h->header_version_minor = VB2_FIRMWARE_PREAMBLE_HEADER_VERSION_MINOR;
	h->preamble_size = block_size;
	h->firmware_version = firmware_version;
	h->flags = flags;

	/* Copy data key */
	vb2_init_packed_key(&h->kernel_subkey, kernel_subkey_dest,
			    kernel_subkey->key_size);
	if (VB2_SUCCESS !=
	    vb2_copy_packed_key(&h->kernel_subkey, kernel_subkey)) {
		free(h);
		return NULL;
	}

	/* Copy body signature */
	vb2_init_signature(&h->body_signature,
			   body_sig_dest, body_signature->sig_size, 0);
	if (VB2_SUCCESS !=
	    vb2_copy_signature(&h->body_signature, body_signature)) {
		free(h);
		return NULL;
	}

	/* Set up signature struct so we can calculate the signature */
	vb2_init_signature(&h->preamble_signature, block_sig_dest,
			   vb2_rsa_sig_size(signing_key->sig_alg), signed_size);

	/* Calculate signature */
	struct vb2_signature *sig =
		vb2_calculate_signature((uint8_t *)h, signed_size, signing_key);
	vb2_copy_signature(&h->preamble_signature, sig);
	free(sig);

	/* Return the header */
	return h;
}

struct vb2_kernel_preamble *vb2_create_kernel_preamble(
	uint32_t kernel_version,
	uint64_t body_load_address,
	uint64_t bootloader_address,
	uint32_t bootloader_size,
	const struct vb2_signature *body_signature,
	uint64_t vmlinuz_header_address,
	uint32_t vmlinuz_header_size,
	uint32_t flags,
	uint32_t desired_size,
	const struct vb2_private_key *signing_key)
{
	uint64_t signed_size = (sizeof(struct vb2_kernel_preamble) +
				body_signature->sig_size);
	uint32_t sig_size = vb2_rsa_sig_size(signing_key->sig_alg);
	uint32_t block_size = signed_size + sig_size;

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

	/* Allocate keyblock */
	struct vb2_kernel_preamble *h =
		(struct vb2_kernel_preamble *)calloc(block_size, 1);
	if (!h)
		return NULL;

	uint8_t *body_sig_dest = (uint8_t *)(h + 1);
	uint8_t *block_sig_dest = body_sig_dest + body_signature->sig_size;

	h->header_version_major = VB2_KERNEL_PREAMBLE_HEADER_VERSION_MAJOR;
	h->header_version_minor = VB2_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;
	h->flags = flags;

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

	/* Set up signature struct so we can calculate the signature */
	vb2_init_signature(&h->preamble_signature, block_sig_dest,
			   sig_size, signed_size);

	/* Calculate signature */
	struct vb2_signature *sigtmp =
		vb2_calculate_signature((uint8_t *)h, signed_size, signing_key);
	vb2_copy_signature(&h->preamble_signature, sigtmp);
	free(sigtmp);

	/* Return the header */
	return h;
}

void vb2_kernel_get_vmlinuz_header(const struct vb2_kernel_preamble *preamble,
				   uint64_t *vmlinuz_header_address,
				   uint32_t *vmlinuz_header_size)
{
	if (preamble->header_version_minor < 1) {
		*vmlinuz_header_address = 0;
		*vmlinuz_header_size = 0;
	} else {
		/*
		 * Set header and size only if the preamble header version is >
		 * 2.1 as they don't exist in version 2.0 (Note that we don't
		 * need to check header_version_major; if that's not 2 then
		 * vb2_verify_kernel_preamble() would have already failed.
		 */
		*vmlinuz_header_address = preamble->vmlinuz_header_address;
		*vmlinuz_header_size = preamble->vmlinuz_header_size;
	}
}
