| # Pseudo-code for Mask ROM Secure Boot Process |
| |
| * [Secure Boot Process](#secure-boot-process) |
| * [Modules](#modules) |
| + [Boot Policy](#boot-policy) |
| - [Read Boot Policy](#read-boot-policy) |
| + [ROM_EXT Manifest](#rom-ext-manifest) |
| + [Bootstrap](#bootstrap) |
| - [Manufacturing boot-strapping intervention](#manufacturing-boot-strapping-intervention) |
| + [Keys and Signature](#keys-and-signature) |
| + [Chip-specific Startup](#chip-specific-startup) |
| + [Lockdown](#lockdown) |
| - [Locking Down Peripherals](#locking-down-peripherals) |
| + [Hardened Jump](#hardened-jump) |
| + [System State](#system-state) |
| - [Cleaning Device State](#cleaning-device-state) |
| + [CRT (C Runtime)](#crt--c-runtime-) |
| - [CRT Initialization](#crt-initialization) |
| * [Interface Data](#interface-data) |
| + [Key Management Data](#key-management-data) |
| + [Boot Policy Structure](#boot-policy-structure) |
| + [ROM_EXT Manifest Structure](#rom-ext-manifest-structure) |
| |
| This file should be read in conjunction with the secure boot specification. |
| References to that document are included. |
| |
| ## Secure Boot Process |
| |
| 1. Power on (entirely in hardware) |
| |
| **Open Q:** Whether SW has to configure PMP initial region. |
| |
| 2. Execution Begins with Mask ROM stage: |
| * CRT Startup Code (Written in Assembly) |
| |
| * Disable Interrupts and set well-defined exception handler. |
| This should keep initial execution deterministic. |
| |
| * Clean Device State Part 1. (See "Cleaning Device State" below) |
| This includes enabling SRAM Scrambling. |
| |
| * Setup structures for C execution (CRT: `.data`, `.bss` sections, stack). |
| |
| * Jump into C code |
| |
| * Main Mask ROM Secure Boot Software (Written in C) |
| * Orchestrated from `boot`: |
| |
| ```c |
| void boot(void) { |
| boot_reason = read_boot_reason(); // Boot Policy Module |
| |
| // Clean Device State Part 2. |
| // See "Cleaning Device State" Below. |
| clean_device_state_part_2(boot_reason); // Chip-Specific Startup Module |
| |
| // Enable Memory Protection |
| // - PMP Initial Region (if not done in power on) |
| enable_memory_protection(); // Lockdown Module |
| |
| // Chip-specific startup functionality |
| // **Open Q:** Proprietary Software Strategy. |
| // - Clocks |
| // - AST / Entropy. |
| // - Flash |
| // - Fuses |
| chip_specific_startup(); // Chip-Specific Startup Module |
| |
| // Manufacturing boot-strapping intervention. |
| // **Open Q:** How does the Mask ROM deal with chip recovery? |
| // **Open Q:** Pin Strapping Configuration. |
| manufacturing_boot_strap(boot_policy, boot_reason); // Bootstrap Module |
| |
| // Read Boot Policy for ROM_EXTs from flash (2.b) |
| boot_policy = read_boot_policy(boot_reason); // Boot Policy Module |
| |
| // Determine which ROM_EXT slot is prioritised (2.b, 2.c.i) |
| for ( current_rom_ext_manifest in rom_ext_manifests_to_try(boot_policy) ) { // Boot Policy Module |
| |
| // Sanity Check ROM_EXT Manifest (2.c.ii) |
| // **Open Q:** Integration with Secure Boot Hardware |
| // - Header Format (ROM_EXT Manifest Module) |
| // - Plausible Key (??) |
| // - Initial Digest Checks (Keys + Signature Module) |
| // - **Open Q**: ROM_EXT Anti-rollback (??) |
| if (!check_rom_ext_manifest(current_rom_ext_manifest)) { |
| // Manifest Failure (check Boot Policy) |
| if (try_next_on_manifest_failed(boot_policy)) // Boot Policy Module |
| continue |
| else |
| break |
| } |
| |
| // Find Public Key for ROM_EXT Image Signature (2.c.iii) |
| // **Open Q:** Key Selection method/mechanism. |
| rom_ext_pub_key = read_pub_key(current_rom_ext_manifest); // ROM_EXT Manifest Module |
| rom_ext_pub_key_id = calculate_key_id(rom_ext_pub_key); // Keys + Signature Module |
| if (!check_pub_key_id_valid(rom_ext_pub_key_id)) { // Keys + Signature Module |
| // Manifest failure (Check Boot Policy) |
| if (try_next_on_manifest_failed(boot_policy)) // Boot Policy Module |
| continue |
| else |
| break |
| } |
| |
| // Verify ROM_EXT Image Signature (2.c.iii) |
| // **Open Q:** Integration with Secure Boot Hardware, OTBN |
| if (!verify_rom_ext_signature(rom_ext_pub_key, |
| current_rom_ext_manifest)) { // Hardened Jump Module |
| // Manifest Failure (check Boot Policy) |
| // **Open Q:** Does this need different logic to the check after |
| // `check_rom_ext_manifest`? |
| if (try_next_on_manifest_failed(boot_policy)) // Boot Policy Module |
| continue |
| else |
| break |
| } |
| |
| // Update Boot Policy on Successful Signature |
| // **Open Q:** Does this ensure ROM_EXT Anti-rollback is updated? |
| update_next_boot_policy(boot_policy, current_rom_ext_manifest); // Boot Policy Module |
| |
| // Beyond this point, you know `current_rom_ext_manifest` is valid and the |
| // signature has been authenticated. |
| // |
| // The Mask ROM now has to do various things to prepare to execute the |
| // current ROM_EXT. Most of this is initializing identities, keys, and |
| // potentially locking down some hardware, before jumping to ROM_EXT. |
| // |
| // If any of these steps fail, we've probably left the hardware in an |
| // invalid state and should just reboot (because it may not be possible to |
| // undo the changes, for instance to write-enable bits). |
| |
| // System State Measurements (2.c.iv) |
| measurements = perform_system_state_measurements(); // System State Module |
| if (!boot_allowed_with_state(measurements)) { // System State Module |
| // Lifecycle failure (no policy check) |
| break |
| } |
| |
| // CreatorRootKey (2.c.iv) |
| // - This is only allowed to be done if we have verified the signature on |
| // the current ROM_EXT. |
| // **Open Q:** Does Mask ROM just lock down key manager inputs, and let the |
| // ROM_EXT load the key? |
| derive_and_lock_creator_root_key_inputs(measurements); // System State Module |
| |
| // Lock down Peripherals based on descriptors in ROM_EXT manifest. |
| // - This does not cover key-related lockdown, which is done in |
| // `derive_and_lock_creator_root_key_inputs`. |
| peripheral_lockdown(current_rom_ext_manifest); // Lockdown Module |
| |
| // PMP Region for ROM_EXT (2.c.v) |
| // **Open Q:** Integration with Secure Boot Hardware |
| // **Open Q:** Do we need to prevent access to Mask ROM after final jump? |
| pmp_unlock_rom_ext(); // Hardened Jump Module. |
| |
| // Transfer Execution to ROM_EXT (2.c.vi) |
| // **Open Q:** Integration with Secure Boot Hardware |
| // **Open Q:** Accumulated measurements to tie manifest to hardware config. |
| if (!final_jump_to_rom_ext(current_rom_ext_manifest)) { // Hardened Jump Module |
| // Boot Failure (no policy check) |
| // - Because we may have locked some write-enable bits that any following |
| // manifest cannot change if it needs to, we have to just reboot here. |
| boot_failed(boot_policy, current_rom_ext_manifest); // Boot Policy Module |
| } |
| } |
| |
| // Boot failed for all ROM_EXTs allowed by boot policy |
| boot_failed(boot_policy); // Boot Policy Module |
| } |
| ``` |
| |
| 3. Execution Enters ROM_EXT stage. |
| |
| Not covered by this document. Refer to Secure Boot document instead. |
| |
| ## Modules |
| |
| We've tried to divide the Mask ROM into self-contained modules that can be |
| tested separately, rather than having to test the whole boot system at once. |
| |
| The module descriptions below also include descriptions of: the DIFs that are |
| required for that module to be implemented fully; the functionality expected at |
| various milestones of the Mask ROM's development; and, optionally, pseudo-code |
| for any subroutines that make up their implementation. |
| |
| ### Boot Policy |
| |
| This manages reading the boot policy, updating the boot policy |
| if required, and making decisions based on that policy. Most Boot Policy |
| choices also depend on the Boot Reason, so reading and acting on that is |
| part of this module's responsibility too. |
| |
| DIFs Needed: |
| |
| * Flash Controller |
| * Reset Manager |
| |
| Milestone Expectations: |
| |
| * *v0.5:* Reading/Updating Boot Policy |
| * *v0.9:* Reading Boot Reason |
| |
| #### Read Boot Policy |
| |
| ```c |
| read_boot_policy() { |
| // Parameters: |
| // - initilized flash_ctrl DIF (for accessing flash info page) |
| // Returns: |
| // - Boot Policy Structure (see below) |
| |
| // 1. Uses dif_flash_ctrl to issue read of boot info page. |
| // 2. Pull this into a struct to return. |
| } |
| ``` |
| |
| ### ROM_EXT Manifest |
| |
| This manages reading and parsing ROM_EXT manifests. |
| |
| The manifest format is defined in [ROM_EXT Manifest Format](../rom_exts/manifest) |
| |
| DIFs Needed: |
| |
| * None. This is read out of flash using ibex loads/stores. |
| |
| Milestone Expectations: |
| |
| * *v0.5:* Initial Manifest Format, Initial Parser, Simple Tooling for |
| assembling ROM_EXT Slot A images. |
| * *v0.7:* Tooling to ensure ROM_EXT is loaded at boot. Tooling for assembling |
| Slot B images. |
| * v0.9: Nothing more (Bootstrap should work in v0.9). |
| |
| ### Bootstrap |
| |
| This manages boot-strapping, chip recovery, and manufacturer loading of ROM_EXT |
| images. |
| |
| DIFs Needed: |
| |
| * GPIO |
| * Pinmux |
| * Padctrl |
| * SPI Device / I2C Device |
| * Flash Controller |
| * Lifecycle Manager |
| |
| Milestone Expectations |
| |
| * *v0.5:* Nothing (images pre-loaded into Memory) |
| * *v0.9:* Full Strapping (ROM_EXT images loaded over SPI) |
| |
| #### Manufacturing boot-strapping intervention |
| |
| This is where, depending on lifecycle state, new flash images may be loaded onto |
| the device (usually during manufacturing). |
| |
| **Open Q:** Do we want hardware hardening for this? |
| There are suggestions around having the hardware return `nop` when reading |
| this memory if we're not in the correct lifecycle state. |
| |
| ```c |
| manufacturing_boot_strap() { |
| // 1. Read lifecycle state to establish whether bootstrapping is allowed. |
| // 2. Determine what kind of bootstrapping is being requested. |
| // 3. Clear Flash (Both Banks) and SRAM (Retention) |
| // **Open Q:** How much is cleared? Is the Creator Certificate cleared? |
| // 4. Load ROM_EXT image into flash if allowed. |
| // 5. Reboot device (causing loaded ROM_EXT to be verified then executed). |
| } |
| ``` |
| |
| ### Keys and Signature |
| |
| This manages public key selection (for ROM_EXT validation), and calculating the |
| digest and signature of the ROM_EXT image itself. |
| |
| DIFs Needed: |
| |
| * OTBN |
| * HMAC |
| * Key Manager |
| * Lifecycle Manager |
| |
| Milestone Expectations: |
| |
| * *v0.5:* HMAC Digests, Software Implementations of RSA Verify |
| * *v0.9:* OTBN Implementations of RSA Verify |
| |
| ### Chip-specific Startup |
| |
| This deals with how to initialize and clear any chip-specific hardware. |
| |
| DIFs Needed: |
| |
| * Flash |
| * OTP / Fuses |
| * AST? |
| * Entropy? |
| * Clocks |
| |
| ### Lockdown |
| |
| This is responsible for managing memory protection regions as well as |
| disallowing reconfiguration of peripherals. Some memory protection regions are |
| also handled by the Hardened Jump module. |
| |
| DIFs Needed: |
| |
| * "PMP" (Not actually a DIF, but something similar will be required) |
| * Flash Controller |
| * SRAM Scrambling Sequence (Not a DIF, but will need to be shared with DV) |
| * Pinmux / Padctrl / Alert Handler |
| |
| Milestone Expectations: |
| |
| * *v0.5:* PMP, Flash Controller |
| * *v0.9:* SRAM Scrambling, Lockdown Profiles Defined |
| |
| #### Locking Down Peripherals |
| |
| ```c |
| peripheral_lockdown() { |
| // Parameters: |
| // - ROM_EXT manifest |
| // - Handles for Peripheral DIFs |
| |
| // **Open Q:** When and How do we lock down peripheral configuration? |
| // - We configure based on ROM_EXT manifest (signed) |
| // - Only locks down these peripherals if info is provided. |
| |
| // What do we want to allow lockdown of? |
| // - Alert Manager |
| // - Pinmux / Padctrl |
| // - Entropy Configuration |
| // - Anything Else? |
| |
| // This is explicitly a separate step from locking the key manager inputs. In |
| // particular, this depends on the ROM_EXT's choices, whereas the key manager |
| // inputs getting locked is not something the ROM_EXT can choose. |
| } |
| ``` |
| |
| ### Hardened Jump |
| |
| This module is responsible for managing the state associated with the hardware |
| support for the jump into ROM_EXT. |
| |
| DIFs Needed: |
| |
| * Secure Boot (which implements Hardened Jump) |
| |
| Milestone Expectations: |
| |
| * *v0.5:* Unhardened Jump (entirely in SW) |
| * *v0.9:* HW-support Hardened Jump |
| |
| ### System State |
| |
| This deals with taking the system state measurements which are used to derive |
| the `CreatorRootKey`. Some of these measurements may cause boot to be halted. |
| |
| DIFs Needed: |
| |
| * Key Manager |
| * Lifecycle Manager |
| * Flash Controller |
| * OTP |
| * **Open Q:** ROM Integrity Measurement? |
| |
| Milestone Expectations: |
| |
| * *v0.5:* Software Binding Properties, OTP Bits. |
| * *v0.9:* Full `CreatorRootKey` derivation. |
| |
| #### Cleaning Device State |
| |
| Part of this process is done before we can execute any C code. In particular, we |
| have to clear all registers and all of the main RAM before we setup the CRT |
| (which allows us to execute any C code). This has to be done in assembly, as we |
| cannot execute C without setting up the CRT. |
| |
| We want to do as little as possible in the hand-written assembly, because it is |
| so hard to verify and to test. Additionally, we want to use the DIFs (which are |
| written in C) because we have verified them, rather than duplicating their |
| functionality in assembly, if at all possible. So this means we wait until we |
| are executing C to do the final parts of chip reset, especially parts that may |
| depend on reading or writing any device state. |
| |
| Unfortunately, it looks like we will have to enable SRAM scrambling (which |
| requires entropy and the AST) from assembly. |
| |
| **Open Q:** Can we zero all of SRAM before enabling scrambling? Should we? |
| We certainly cannot zero it all after we have enabled scrambling. |
| |
| ```c |
| // Will actually be implemented in assembly, as executed too early for C. |
| clean_device_state_part_1() { |
| |
| // - Zero all ISA Registers |
| |
| // Depending on boot reason: |
| // - Clear Main SRAM |
| // This can either be done by overwriting all of SRAM, or by enabling SRAM |
| // Scrambling. If we do both, we have to be careful about the order (see |
| // above). |
| // **Open Q:** Can we always do the same thing here, regardless of the boot |
| // reason? |
| } |
| |
| clean_device_state_part_2() { |
| // Parameters: |
| // - Boot Reason |
| |
| // Depending on boot reason: |
| // - Clear Power Manager Scratch Registers |
| // - Clear Retention SRAM |
| // These clears *must* be done by Mask ROM. |
| |
| // - Reset most devices |
| // **Open Q:** Which Devices are we leaving with state for ROM_EXT? |
| } |
| ``` |
| |
| ### CRT (C Runtime) |
| |
| This sets up execution so we can run C functions. We cannot execute any C code |
| until we have setup the CRT. |
| |
| DIFs Needed: None |
| |
| Milestone Expectations: |
| |
| * *v0.5:* Can Execute C functions. |
| * *v0.9:* SRAM Scrambling. |
| |
| #### CRT Initialization |
| |
| Setting up the CRT involves: loading the `.data` and `.bss` sections, and |
| setting up the stack and `gp` (`gp` may be used for referencing some data). |
| |
| ```c |
| // Will actually be written in assembly |
| crt_init() { |
| // - Load `.data` initial image into SRAM. |
| // - Zero `.bss` section. |
| // - Zero stack. |
| // - Load `sp`, `gp`. |
| // **Open Q:** These are used by exception and interrupt handlers if written |
| // in C. Should they be initialized as early as possible? We can turn off |
| // the use of `gp` fairly easily, `sp` is an ABI issue. |
| } |
| ``` |
| |
| ## Interface Data |
| |
| There is some data that is accessed by more than just the Mask ROM: |
| |
| * The Boot Policy structure, used to choose a ROM_EXT to boot. |
| * The ROM_EXT manifest, used to contain information about a specific ROM_EXT. |
| * The Key Management data, used to validate Root Keys. |
| |
| In order to keep the Mask ROM simple, a particular Mask ROM version will only |
| support one version each of the following structures. This means they must be |
| carefully designed to be extensible if the other systems accessing them may |
| require additional data in these formats. |
| |
| ### Key Management Data |
| |
| This is the storage for the IDs of keys that are used to sign a ROM_EXT. The |
| ROM_EXT image contains the full key, this stores only the key id (which can be |
| computed from the full key), and uses OTPs to decide whether a key is allowed to |
| be used to validate a ROM_EXT. |
| |
| Accessed by: |
| |
| * Mask ROM (to identify public key is Valid). |
| * ROM_EXT (to prevent use of public key). |
| |
| Needs to contain: |
| |
| * Read-only list of public key ids. |
| * OTP fuses to say whether a key id is revoked or not (keys start not revoked). |
| |
| Stored In: OTP and ROM |
| |
| Extensibility: None. A ROM_EXT can disable the use of a key, but cannot add new |
| valid root key IDs. There will be a fixed set of root key IDs as part of a given |
| Mask ROM version. |
| |
| ### Boot Policy Structure |
| |
| This is the in-flash structure that defines which ROM_EXT should be booted next, |
| whether it should fall back to the other ROM_EXT if it fails to boot, and/or |
| whether it should be marked as the primary ROM_EXT if it succeeds to validate. |
| |
| This structure merely controls which ROM_EXT is validated and executed -- it |
| does not contain any executable code. This means it does not need to be |
| protected as securely as the ROM_EXT images, which contain code and are signed |
| and validated before execution. |
| |
| Accessed by: |
| |
| * Mask ROM (to choose ROM_EXT, and during bootstrapping to bless new ROM_EXT). |
| * ROM_EXT (during firmware update). |
| |
| Needs to contain: |
| |
| * Identifier (so we know we're reading the right thing) |
| |
| This also acts like a version number, because the Mask ROM code that parses |
| the boot policy can never be updated. Conversely, any changes to this |
| structure require new Mask ROM parsing code, which should be denoted with a |
| new identifier. |
| |
| * Which ROM_EXT slot should be chosen first (2.b, 2.c.i). |
| * What to do if ROM_EXT does not validate (2.c.ii, 2.c.iii): |
| * Try Alternate ROM_EXT; or |
| * Fail to Boot |
| * What to do if ROM_EXT validates successfully, just before jumping to ROM_EXT: |
| * Do nothing; or |
| * Set current ROM_EXT as Primary if not already. |
| * Checksum (of everything else). |
| |
| Stored in: Flash (Info Partition) |
| |
| Extensibility: None. This info only controls the actions of the Mask ROM. You |
| cannot add functionality to the Mask ROM of a given chip, so there's no way to |
| add other information to this structure. |
| |
| The in-RAM representation also contains the boot reason (reset manager), because |
| it needs to be checked most times the boot policy is also read. |
| |
| ### ROM_EXT Manifest Structure |
| |
| Accessed by: |
| |
| * Mask ROM (to validate ROM_EXT) |
| * BL0 Kernel (during firmware update). |
| |
| Needs to contain: |
| |
| * Unsigned Area: |
| * Identifier (so we know we're reading the right thing) |
| This also acts as a ROM_EXT manifest version (ie, the version of the |
| layout of the header). |
| |
| * Signature |
| * Signed Area: |
| * Image Length |
| * Entry Point |
| **Open Q:** Is this fixed or programmable? Field not needed if fixed. |
| |
| * ROM_EXT PMP Region Information |
| **Open Q:** Is this fixed or programmable? Field not needed if fixed. |
| |
| * Key Selection Area |
| * Signature Algorithm Identifier |
| **Open Q:** Will the Mask ROM support more than one signature |
| scheme? |
| |
| * Public Key |
| |
| * Software Binding Properties: |
| * ROM_EXT Version |
| This is the version of the image contained in the ROM_EXT. |
| |
| * Usage Constraints |
| * Peripheral Lockdown Information |
| * Read-only Extension Area: |
| * Several words for ROM_EXT/BL0 use only. |
| (Interpretation governed by ROM_EXT/BL0 Version). |
| **Open Q:** Does this satisfy the previous usecases? |
| |
| This is likely to be a fixed number of `(offset, checksum)` pairs, |
| where the offset points to a structure somewhere within the ROM_EXT |
| image. This should help us avoid running out of space, while also |
| ensuring these structures do not become corrupted (note the image |
| and the read-only extension area are also signed). This approach |
| does allow the pointed-to structure to be variable-length, as it is |
| down to the ROM_EXT/BL0 to interpret the data and validate the |
| checksum. |
| |
| **Open Q:** Should we require these offsets to be word- or |
| page-aligned? |
| |
| For a given ROM_EXT manifest version, the number of these slots |
| cannot be changed, but different manifest versions can add to the |
| number of slots. Each slot in a manifest version should only be |
| allocated for a single use, and once allocated should not be |
| re-allocated. |
| |
| * ROM_EXT Code Image. |
| |
| Stored in: Flash |
| |
| Extensibility: |
| * Mask ROM: None |
| * ROM_EXT/BL0: |
| * Uses the "Extension Area" for additional read-only data if required. |
| This is not interpreted by the Mask ROM, but may be used by ROM_EXT or |
| BL0 if required. |