blob: 4f9fbe38f9847ee58f043ac7a240926ad128fa4c [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/base/chip.h"
#include "sw/device/silicon_creator/lib/drivers/lifecycle.h"
#include "sw/device/silicon_creator/lib/epmp_state.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/sigverify/rsa_key.h"
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
/**
* Usage constraints.
*
* This struct is used to constrain a boot stage image to a set of devices based
* on their device IDs, creator and/or owner manufacturing states, and life
* cycle states. Bits of `selector_bits` determine which fields (or individual
* words of a field as in the case of `device_id`) must be read from the
* hardware during verification. Unselected fields must be set to
* `MANIFEST_USAGE_CONSTRAINT_UNSELECTED_WORD_VAL` to be able to generate a
* consistent value during verification.
*/
typedef struct manifest_usage_constraints {
/**
* Usage constraint selector bits.
*
* The bits of this field are mapped to the remaining fields as follows:
* - Bits 0-7: `device_id[0-7]`
* - Bit 8 : `manuf_state_creator`
* - Bit 9 : `manuf_state_owner`
* - Bit 10 : `life_cycle_state`
*/
uint32_t selector_bits;
/**
* Device identifier value which is compared against the `DEVICE_ID` value
* stored in the `HW_CFG` partition in OTP.
*
* Mapped to bits 0-7 of `selector_bits`.
*/
lifecycle_device_id_t device_id;
/**
* Device Silicon Creator manufacting status compared against the
* `CREATOR_SW_MANUF_STATUS` value stored in the `CREATOR_SW_CFG` partition in
* OTP.
*
* Mapped to bit 8 of `selector_bits`.
*/
uint32_t manuf_state_creator;
/**
* Device Silicon Owner manufacturing status compared against the
* `OWNER_SW_MANUF_STATUS` value stored in the `OWNER_SW_CFG` partition in
* OTP.
*
* Mapped to bit 9 of `selector_bits`.
*/
uint32_t manuf_state_owner;
/**
* Device life cycle status compared against the status reported by the life
* cycle controller.
*
* Mapped to bit 10 of `selector_bits`.
*/
uint32_t life_cycle_state;
} manifest_usage_constraints_t;
/**
* Value to use for unselected usage constraint words.
*/
#define MANIFEST_USAGE_CONSTRAINT_UNSELECTED_WORD_VAL 0xA5A5A5A5
/**
* `selector_bits` bit indices for usage constraints fields.
*/
enum {
/**
* Bits mapped to the `device_id` field.
*/
kManifestSelectorBitDeviceIdFirst = 0,
kManifestSelectorBitDeviceIdLast = 7,
/**
* Bit mapped to the `manuf_state_creator` field.
*/
kManifestSelectorBitManufStateCreator = 8,
/**
* Bit mapped to the `manuf_state_owner` field.
*/
kManifestSelectorBitManufStateOwner = 9,
/**
* Bit mapped to the `life_cycle_state` field.
*/
kManifestSelectorBitLifeCycleState = 10,
};
/**
* 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 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.
*
* On-target verification should also integrate usage constraints comparison
* to signature verification to harden it against potential attacks. During
* verification, the digest of an image should be computed by first reading
* the usage constraints from the hardware and then concatenating the rest of
* the image:
*
* digest = SHA256(usage_constraints_from_hw || rest_of_the_image)
*
* The start and the length of the region that should be concatenated to the
* usage constraints read from the hardware can be obtained using
* `manifest_digest_region_get()`.
*/
sigverify_rsa_buffer_t signature;
/**
* Usage constraints.
*/
manifest_usage_constraints_t usage_constraints;
/**
* Modulus of the signer's 3072-bit RSA public key.
*/
sigverify_rsa_buffer_t modulus;
/**
* Address translation (hardened boolean).
*/
uint32_t address_translation;
/**
* 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, usage_constraints, 384);
OT_ASSERT_MEMBER_OFFSET(manifest_t, modulus, 432);
OT_ASSERT_MEMBER_OFFSET(manifest_t, address_translation, 816);
OT_ASSERT_MEMBER_OFFSET(manifest_t, identifier, 820);
OT_ASSERT_MEMBER_OFFSET(manifest_t, length, 824);
OT_ASSERT_MEMBER_OFFSET(manifest_t, version_major, 828);
OT_ASSERT_MEMBER_OFFSET(manifest_t, version_minor, 832);
OT_ASSERT_MEMBER_OFFSET(manifest_t, security_version, 836);
OT_ASSERT_MEMBER_OFFSET(manifest_t, timestamp, 840);
OT_ASSERT_MEMBER_OFFSET(manifest_t, binding_value, 848);
OT_ASSERT_MEMBER_OFFSET(manifest_t, max_key_version, 880);
OT_ASSERT_MEMBER_OFFSET(manifest_t, code_start, 884);
OT_ASSERT_MEMBER_OFFSET(manifest_t, code_end, 888);
OT_ASSERT_MEMBER_OFFSET(manifest_t, entry_point, 892);
OT_ASSERT_SIZE(manifest_t, CHIP_MANIFEST_SIZE);
/**
* Region of an image that should be included in the digest computation.
*/
typedef struct manifest_digest_region {
/**
* Start of the region.
*/
const void *start;
/**
* Length of the region in bytes.
*/
size_t length;
} manifest_digest_region_t;
#if defined(OT_PLATFORM_RV32) || defined(MANIFEST_UNIT_TEST_)
/**
* Checks the fields of a manifest.
*
* This function performs several basic checks to ensure that
* - 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) {
// 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 region of the image that should be included in the digest
* computation.
*
* Digest region of an image starts immediately after the `usage_constraints`
* field of its manifest and ends at the end of the image.
*
* @param manifest A manifest.
* return digest_region Region of the image that should be included in the
* digest computation.
*/
inline manifest_digest_region_t manifest_digest_region_get(
const manifest_t *manifest) {
enum {
kDigestRegionOffset =
sizeof(manifest->signature) + sizeof(manifest->usage_constraints),
};
return (manifest_digest_region_t){
.start = (const char *)manifest + kDigestRegionOffset,
.length = manifest->length - kDigestRegionOffset,
};
}
/**
* 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 // defined(OT_PLATFORM_RV32) || defined(MANIFEST_UNIT_TEST_)
/**
* Declarations for the functions above that should be defined in tests.
*/
rom_error_t manifest_check(const manifest_t *manifest);
manifest_digest_region_t manifest_digest_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 // defined(OT_PLATFORM_RV32) || defined(MANIFEST_UNIT_TEST_)
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
#endif // OPENTITAN_SW_DEVICE_SILICON_CREATOR_LIB_MANIFEST_H_