blob: e0fdfb4fff7088d9223590db7ad7934e440c1ba2 [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
#ifndef OPENTITAN_SW_DEVICE_SILICON_CREATOR_LIB_MANIFEST_H_
#define OPENTITAN_SW_DEVICE_SILICON_CREATOR_LIB_MANIFEST_H_
#include <stddef.h>
#include "sw/device/lib/base/macros.h"
#include "sw/device/silicon_creator/lib/epmp.h"
#include "sw/device/silicon_creator/lib/error.h"
#include "sw/device/silicon_creator/lib/keymgr_binding_value.h"
#include "sw/device/silicon_creator/lib/manifest_size.h"
#include "sw/device/silicon_creator/lib/sigverify_rsa_key.h"
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
/**
* Manifest for boot stage images stored in flash.
*
* OpenTitan secure boot, at a minimum, consists of three boot stages: ROM,
* ROM_EXT, and the first owner boot stage, e.g. BL0. ROM is stored in the
* read-only Mask ROM while remaining stages are stored in flash. This structure
* must be placed at the start of ROM_EXT and first owner boot stage images so
* that ROM and ROM_EXT can verify the integrity and authenticity of the next
* stage and configure peripherals as needed before handing over execution.
*
* Use of this struct for stages following the first owner boot stage is
* optional.
*
* Note: The definitions in
* sw/host/rom_ext_image_tools/signer/image/src/manifest.rs must be updated if
* this struct is modified. Please see the instructions in that file.
*/
typedef struct manifest {
/**
* Image signature.
*
* RSASSA-PKCS1-v1_5 signature of the image generated using a 3072-bit RSA
* private key and the SHA-256 hash function. The signed region of an image
* starts immediately after this field and ends at the end of the image. The
* start and the length of the signed region can be obtained using
* `manifest_signed_region_get`.
*/
sigverify_rsa_buffer_t signature;
/**
* Modulus of the signer's 3072-bit RSA public key.
*/
sigverify_rsa_buffer_t modulus;
/**
* Exponent of the signer's RSA public key.
*/
uint32_t exponent;
/**
* Manifest identifier.
*/
uint32_t identifier;
/**
* Length of the image including the manifest in bytes.
*
* Note that the length includes the signature but the signature is excluded
* from the signed region.
*/
uint32_t length;
/**
* Image major version.
*/
uint32_t version_major;
/**
* Image minor version.
*/
uint32_t version_minor;
/**
* Security version of the image used for anti-rollback protection.
*/
uint32_t security_version;
/**
* Image timestamp.
*
* Unix timestamp that gives the creation time of the image, seconds since
* 00:00:00 on January 1, 1970 UTC (the Unix Epoch).
*/
uint64_t timestamp;
/**
* Binding value used by key manager to derive secret values.
*
* A change in this value changes the secret value of key manager, and
* consequently, the versioned keys and identity seeds generated at subsequent
* boot stages.
*/
keymgr_binding_value_t binding_value;
/**
* Maximum allowed version for keys generated at the next boot stage.
*/
uint32_t max_key_version;
/**
* Offset of the start of the executable region of the image from the start
* of the manifest in bytes.
*/
uint32_t code_start;
/**
* Offset of the end of the executable region (exclusive) of the image from
* the start of the manifest in bytes.
*/
uint32_t code_end;
/**
* Offset of the first instruction to execute in the image from the start of
* the manifest in bytes.
*/
uint32_t entry_point;
} manifest_t;
OT_ASSERT_MEMBER_OFFSET(manifest_t, signature, 0);
OT_ASSERT_MEMBER_OFFSET(manifest_t, modulus, 384);
OT_ASSERT_MEMBER_OFFSET(manifest_t, exponent, 768);
OT_ASSERT_MEMBER_OFFSET(manifest_t, identifier, 772);
OT_ASSERT_MEMBER_OFFSET(manifest_t, length, 776);
OT_ASSERT_MEMBER_OFFSET(manifest_t, version_major, 780);
OT_ASSERT_MEMBER_OFFSET(manifest_t, version_minor, 784);
OT_ASSERT_MEMBER_OFFSET(manifest_t, security_version, 788);
OT_ASSERT_MEMBER_OFFSET(manifest_t, timestamp, 792);
OT_ASSERT_MEMBER_OFFSET(manifest_t, binding_value, 800);
OT_ASSERT_MEMBER_OFFSET(manifest_t, max_key_version, 832);
OT_ASSERT_MEMBER_OFFSET(manifest_t, code_start, 836);
OT_ASSERT_MEMBER_OFFSET(manifest_t, code_end, 840);
OT_ASSERT_MEMBER_OFFSET(manifest_t, entry_point, 844);
OT_ASSERT_SIZE(manifest_t, MANIFEST_SIZE);
/**
* Signed region of an image.
*/
typedef struct manifest_signed_region {
/**
* Start of the signed region of an image.
*/
const void *start;
/**
* Length of the signed region of an image in bytes.
*/
size_t length;
} manifest_signed_region_t;
/**
* Allowed bounds for the `length` field.
*/
// FIXME: Update min value after we have a fairly representative ROM_EXT.
#define MANIFEST_LENGTH_FIELD_MIN MANIFEST_SIZE
#define MANIFEST_LENGTH_FIELD_MAX 65536
#ifndef OT_OFF_TARGET_TEST
/**
* Checks the fields of a manifest.
*
* This function performs several basic checks to ensure that
* - Length of the image is within a reasonable range,
* - Executable region is non-empty, inside the image, located after the
* manifest, and word aligned, and
* - Entry point is inside the executable region and word aligned.
*
* @param manfiest A manifest.
* @return Result of the operation.
*/
inline rom_error_t manifest_check(const manifest_t *manifest) {
// Length of the image must be within bounds.
if (manifest->length < MANIFEST_LENGTH_FIELD_MIN ||
manifest->length > MANIFEST_LENGTH_FIELD_MAX) {
return kErrorManifestBadLength;
}
// Executable region must be empty, inside the image, located after the
// manifest, and word aligned.
if (manifest->code_start >= manifest->code_end ||
manifest->code_start < sizeof(manifest_t) ||
manifest->code_end > manifest->length ||
(manifest->code_start & 0x3) != 0 || (manifest->code_end & 0x3) != 0) {
return kErrorManifestBadCodeRegion;
}
// Entry point must be inside the executable region and word aligned.
if (manifest->entry_point < manifest->code_start ||
manifest->entry_point >= manifest->code_end ||
(manifest->entry_point & 0x3) != 0) {
return kErrorManifestBadEntryPoint;
}
return kErrorOk;
}
/**
* Gets the signed region of an image.
*
* @param manifest A manifest
* return signed_region Signed region of an image.
*/
inline manifest_signed_region_t manifest_signed_region_get(
const manifest_t *manifest) {
return (manifest_signed_region_t){
.start = (const char *)manifest + sizeof(manifest->signature),
.length = manifest->length - sizeof(manifest->signature),
};
}
/**
* Gets the executable region of the image.
*
* @param manifest A manifest.
* return Executable region of the image.
*/
inline epmp_region_t manifest_code_region_get(const manifest_t *manifest) {
return (epmp_region_t){
.start = (uintptr_t)manifest + manifest->code_start,
.end = (uintptr_t)manifest + manifest->code_end,
};
}
/**
* Gets the entry point of an image.
*
* Entry point is the address that a boot stage jumps to transfer execution to
* the next stage in the boot chain. This function returns a `uintptr_t` instead
* of a function pointer to accommodate for entry points with different
* parameters and return types.
*
* @param manfiest A manifest.
* return Entry point address.
*/
inline uintptr_t manifest_entry_point_get(const manifest_t *manifest) {
return (uintptr_t)manifest + manifest->entry_point;
}
#else
/**
* Declarations for the functions above that should be defined in tests.
*/
rom_error_t manifest_check(const manifest_t *manifest);
manifest_signed_region_t manifest_signed_region_get(const manifest_t *manifest);
epmp_region_t manifest_code_region_get(const manifest_t *manifest);
uintptr_t manifest_entry_point_get(const manifest_t *manifest);
#endif
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
#endif // OPENTITAN_SW_DEVICE_SILICON_CREATOR_LIB_MANIFEST_H_