diff options
author | Tom Hughes <tomhughes@chromium.org> | 2020-06-17 09:29:02 -0700 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2020-06-17 22:20:07 +0000 |
commit | f2fed78c16c99689b6deaca5bd7e57ebe9f00de0 (patch) | |
tree | 968dd5c669962d21dcc3b59075e828d5e195a2c1 | |
parent | 07b05acbc4f6b23fe820c2bea7041b379742bc06 (diff) | |
download | chrome-ec-f2fed78c16c99689b6deaca5bd7e57ebe9f00de0.tar.gz |
docs: Design doc for Detachable Base Verified Boot
Original document: http://go/detachable-base-vboot
Cleanup document: https://docs.google.com/document/d/1Gm2LXaoQeyo-eppss62sQdKkxd_qD2FdBPDOHQId3qI/
BRANCH=none
BUG=b:145815809
TEST=view in gitiles
Signed-off-by: Tom Hughes <tomhughes@chromium.org>
Change-Id: Ic27848f2a86234a8f757262ecaba784f19e0ba38
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/2249462
Commit-Queue: Nicolas Boichat <drinkcat@chromium.org>
Reviewed-by: Nicolas Boichat <drinkcat@chromium.org>
-rw-r--r-- | docs/detachable_base_verified_boot.md | 432 | ||||
-rw-r--r-- | docs/sitemap.md | 4 |
2 files changed, 436 insertions, 0 deletions
diff --git a/docs/detachable_base_verified_boot.md b/docs/detachable_base_verified_boot.md new file mode 100644 index 0000000000..c1eec3fc0b --- /dev/null +++ b/docs/detachable_base_verified_boot.md @@ -0,0 +1,432 @@ +# Detachable Base Verified Boot + +Authors: rspangler@google.com, drinkcat@google.com + +Last Updated: 2016-11-16 + +Original: http://go/detachable-base-vboot + +[TOC] + +## Introduction + +### What's a Base? + +Detachable Chromebooks such as `Poppy` have a tablet-like `Lid` and a detachable +keyboard `Base`. Effectively, the `Base` is a USB keyboard+trackpad which plugs +into the `Lid`. + +The `Lid` contains most of the components, including: + +* AP +* ECDisplay +* Storage +* Battery + +The `Base` connects to the `Lid` via USB pogo pins, and contains: + +* EC ([STM32F072]). To minimize confusion with the main EC in the `Lid`, this + will always be called the `BaseEC`. +* Matrixed keyboard +* Touchpad + +The `Base` always gets its power from the `Lid` USB port. This means that +attaching the base always triggers a power-on reset. + +### Verified Boot Requirements + +The `BaseEC` will be responsible for handling user input from the keyboard and +touchpad. This means that a compromised `BaseEC` could implement a keylogger. To +prevent this, we will use verified boot to protect the `BaseEC` firmware. + +We need a way to securely update the `BaseEC` firmware from the AP. We cannot +use EC Software Sync as implemented on existing Chromebooks (and as still used +in the `Lid`) because the `Base` cannot trust that it is talking to an official +`Lid` firmware/OS. All the Base knows is that _something_ on the other end of +USB is trying to send it an update. So the BaseEC will need to do its own public +key verification of the firmware update. This includes rollback protection. + +Updating the `BaseEC` firmware should not require rebooting the lid. This means +the update will take place after the OS has already booted on the lid. Ideally, +it should also not require the user to detach/reattach the base during the +update process. If the update takes longer than a few seconds, we should tell +the user, because the keyboard and trackpad will be unavailable during the +update. + +The solution should also have low (or no) BOM cost, and minimal flash size +requirement. + +## Proposal + +`BaseEC` RO region includes a public key, whose private counterpart is kept +safely on our signers. On boot, RO checks RW signature (RW image is signed by +our signers), and will only jump to RW if the signature is valid. + +We also include a rollback region (RB) to implement rollback protection (and +prevent rollback to a correctly signed, but compromised, RW). This region can +only be updated by RO. + +We also devise a scheme to update RW firmware (the details are documented in +[EC Update over USB]). + +Note: This proposal is very specific to the STM32 flash architecture. Other ECs +(particularly ones with external SPI flash) may need additional external logic +and/or a I2C EEPROM to hold the rollback info block. + +### Flash + +STM32F072 has 128KB flash, with 2KB erase sectors and 4KB protection blocks. + +We will divide flash into three sections: + +* `BaseEC`-RO firmware. + * Not updatable in production. + * Only capable of USB update, not keyboard/trackpad. + * Contains public key to verify RW image (RSA-3072). +* `BaseEC`-RW firmware. + * Fully functional. + * Updatable from AP. + * Signature (SHA-256 + RSA-3072). +* `BaseEC`-RB: Rollback info block (4KB). + * Contains minimum RW version that RO will accept to jump to. + * Updatable from RO. + +Each of those sections can be locked independently: In production, RO is always +locked, and only RO can write to RB (RO will always make sure to lock RB before +jumping to RW). + +Flash protection is a little entertaining on STM32: + +* The flash protection bits for the \*next\* boot are stored in a non-volatile + `WRPx` register (in EC code, this is abstracted as + `EC_FLASH_PROTECT_[REGION]_AT_BOOT` flags).. +* On chip reset, `WRPx` is copied into a read-only `FLASH_WRPR` register; that + controls which blocks are protected for this boot. This is abstracted as + `EC_FLASH_PROTECT_[REGION]_NOW` in the EC code. + +### Rollback Info Block + +The Rollback Info Block (aka "RB") is a 4KB block of flash. + +It has two 2KB erase sectors. We will ping-pong writes to those sectors, so that +interrupting power during an erase-write cannot cause data loss. If both sectors +are valid, the stricter (i.e. the highest value) of the 2 sectors is used. + +We will use the RB to hold the following: + +* Minimum **RW rollback firmware** version: a 32-bit integer. Used for + rollback protection. This number is independent of the actual EC version, + and is stored a 32-bit integer as part of the `BaseEC`-RW region (see + [CL:452815] for a possible implementation) +* A magic signature that indicates that the RB section is valid. + +### RO Verified Boot Flow + +#### Write-Protect RO think test before this handles corrupt RW. + +Write protect of RO firmware works the same way it does now: + +* Early RO code looks at a write protect (WP) GPIO and a global PSTATE + variable (part of the RO image itself). When we switch to RO that contains + the MP key, we set the PSTATE to locked. +* If both of those are set: + * RO code sets `EC_FLASH_PROTECT_RO_AT_BOOT` to protect itself. This + ensures RO code is never writable past this point. + * If `_AT_BOOT` flags protects more than the current write protect range + (`_NOW` flags), RO reboots so that changes take effect. +* Otherwise, someone has physically disconnected WP. Set `WRPx=0` to unprotect + all flash and reboot. + +#### Check if AP Wants To Update RW + +Next, RO needs to find out if the AP wants to update RW. RO initializes USB and +starts a 1 second timer to give the AP an opportunity to send a command before +RO jumps to RW. This delay gives us a way to regain control of the base, if the +previous RW firmware is properly signed but bad/nonfunctional. + +That command can be: + +* `STOP_IN_RO`: Yes, I might want to update you. Stick around. + * `UNLOCK_RW`: Tells EC to unlock RW region, if it is currently locked, so + that it can be reprogrammed. This also locks RB region. EC reboots if + needed. +* `JUMP_TO_RW`: No, I don't want to update you. Go ahead and jump to your RW + code if it verifies. + +RO will start verifying RW while it waits for the AP to send it a command or for +the timeout. If a command is received, RO will stop the 1-second timer, and wait +for more commands from the AP. This allows the AP to update RW. + +Verifying RW will take ~200 ms, and the AP should be able to send a command to +the base within ~100 ms of it appearing on USB, so this check should not cause +any delay to the base's boot process. + +#### Verify RW + +RO calculates the hash of RW. + +* Use the public key stored in RO to check if the hash matches the RSA-signed + RW signature. On failure, go back to waiting for an update from the AP. +* Check the RW rollback version against the stored minimum version in RB. If + the RW version is too low, fail. Go back to waiting for an update from the + AP. +* If RO is protected, then also set `EC_FLASH_PROTECT_RW_AT_BOOT` so that RW + will be protected on the next boot, the reboot. + +#### Roll Forward + +If `EC_FLASH_PROTECT_ROLLBACK_NOW` is set (RB is protected), do not attempt to +roll forward. We know RW firmware is properly signed, but not if it's +functional. + +If `EC_FLASH_PROTECT_ROLLBACK_NOW` is not set (RB is unprotected), \_and\_ the +RW signature is correct, then update RB: + +* Erase/write the older sector of RB. +* Set the stored minimum version to the RW rollback version. +* If RO is protected, then also set `EC_FLASH_PROTECT_ROLLBACK_NOW` so that RB + will be protected on the next boot. + +#### Jump to RW + +If the 1-second timer for the AP to send a command to RO has not expired, RO +waits for it to expire or the AP to send a command, whichever happens first. + +If RB or RW is unprotected (`EC_FLASH_PROTECT_RW/ROLLBACK_NOW` are not set), +protect it and reboot (we never want RW to be able to update RB on its own). + +Otherwise, jump to RW firmware. + +### RW Verified Boot Flow + +RW firmware provides the keyboard and trackpad functionality. + +#### AP Wants To Update RW + +At some point the AP may want to update RW. To do so, it sends `UNLOCK_RW` +command, to ask RW to unlock itself and reboot, then follow the update steps +above. + +#### AP Wants to Roll Forward RW + +After the update, the base boots to the new RW firmware. At that point, the AP +knows the new RW firmware is good enough to talk to, so it tells RW to prepare +for roll forward. + +* `UNLOCK_ROLLBACK` command: RW unprotects RB. +* On next boot (not necessarily urgent, but can be forced), RO will update RB + according to the steps above. + +### Write Protect GPIO + +The `BaseEC` needs a write protect (WP) GPIO signal to decide whether to keep RO +firmware protected or not. This is the same requirement as on existing ECs. + +In an assembled base, the WP signal will be physically asserted. De-asserting +the signal requires disassembling the base and disconnecting something. + +Typically, the `BaseEC` will apply a weak pull-up to the WP GPIO; the presence +of the WP screw/flex will short the pin to ground. + +#### RO Updates During Development + +If RO is unprotected (i.e. during development), RW can also update it. + +If the key is \_not\_ the same (dev->premp, premp->mp updates) we can't update +RW first (it won't verify). These steps should work though, if current RW is +recent enough and stable enough to update RO: + +* Make sure RW is active +* Update RO, reboot +* Update RW from RO + +If the key is the same, we can update RW first. + +### Signer, image format, and verification process + +Memory map: + +RO | RB | RW +------------------------------------------------- | --- | --- +`...` \| `Public key` \| `...` \| `FMAP` \| `...` | | `EC code and data` \| `Blank (0xff)` \| `Signature` + +* RO contains an embedded RSA public key (`vb21_packed_key` format), at a + variable location. +* RW contains a signature (`vb21_signature`), packed at the end of the RW + region. + * The signature also contains the actual length of the EC code and image + (ignoring 0xff padding) + * RO validates signature against the provided length, then checks that the + rest of the RW region (up to the signature itself) is filled with ones + (padding). + * This speeds up verification significantly, as SHA-256 is an + expensive process. +* RO contains an FMAP that allows futility to find the RO key, RW region, and + RW signature location. + +For re-signing, `futility` (rwsig type) does this: + +* Look for FMAP to find RO public key RW region, and RW signature locations. +* Resign RW region, using the length provided in existing RW signature. +* Replace RO public key with the one used for signing. + +`vb21_packed_key` (public key) has a field for key version, that we can use to +increment from dev keys, to premp, and final mp keys. BaseEC will need to report +the key version, to avoid incorrect updates. + +## Example Boot / Update Flows + +The base starts in the following state: + +* Powered off +* WP GPIO is asserted +* PSTATE is set to protect RO firmware +* RW firmware is valid, and currently version M +* `EC_FLASH_PROTECT_[REGION]_AT_BOOT/_NOW` protects RO+RW+RB (that is, + everything) + +All AP operations are done from the `Lid` OS. + +Base updates will interrupt keyboard/trackpad functionality, so the user should +be informed when an update is taking place. + +Reboots of the `Base` do not cause or require reboots of the `Lid`, do not +require action on the part of the user, and will not be visible to the user +(other than the previously noted lack of functionality). + +### Power On, No Update + +Step | RW | RB contents | `_AT_BOOT` | `_NOW` +--------------------------------------------------------------------------------------------- | --- | ----------- | ---------- | ------ +(initial state) | M | 1/blank | RO/RW/RB | RO/RW/RB +1. RO waits 1 second for an update request from AP | | | | +2. RO verifies RW signature => RW is good | | | | +3. RO notes that `_AT_BOOT` and `_NOW` already protect everything, so no reboot is necessary. | | | | +4. RO jumps to RW | | | | + +### Updating RW + +Assume AP now has a new `BaseEC`-RW, version N>M. The base is already running RW +version M. In this card, the rollback version in both version is identical +("1"), so RB does not require an update. + +Step | RW | RB contents | `_AT_BOOT` | `_NOW` +------------------------------------------------------- | --- | ----------- | -------------- | ------ +RW is running | M | 1/blank | RO/RW/RB | RO/RW/RB +AP tells RW to prepare for an update (UNLOCK_RW) | | | | +RW unsets `EC_FLASH_PROTECT_RW_AT_BOOT` to unprotect RW | | | **RO/\_\_/RB** | +RW reboots to update `EC_FLASH_PROTECT_RW_NOW` | | | | **RO/\_\_/RB** + +The next base boot is where the update takes place: + +Step | RW | RB contents | `_AT_BOOT` | `_NOW` +---------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | ----------- | ------------ | ------ +RO waits 2 seconds for an update request from the AP | M | 1/blank | RO/\_\_/RB | RO/\_\_/RB +AP tells RO an update is coming (`STOP_IN_RO`) | | | | +AP tells the user that a base update is taking place. UI should say: "Please don't be surprised that your keyboard and trackpad won't work for a few seconds..." | | | | +AP writes RW version N | **N** | | | +AP tells RO to reboot (`IMMEDIATE_RESET`) | | | | +RO reboots, verifies RW signature => RW is good | | | | +RO checks RW rollback version N (1) and sees it's greater or equal than RB rollback version 1. So, RW is good. | | | | +RO sets `RW_AT_BOOT` to protect RW on the next boot. | | | **RO/RW/RB** | +RO reboots | | | | **RO/RW/RB** + +The next base boot is where we first run the new RW firmware. + +### Roll forward + +Now let's assume we followed the steps above, and we now have a RW version O +that has rollback version 2. + +Step | RW | RB contents | `_AT_BOOT` | `_NOW` +-------------------------------------------------------------------------------------------------------------- | ----- | ----------- | -------------- | ------ +RO verifies RW signature => RW is good | **O** | 1/blank | RO/RW/RB | RO/RW/RB +RO checks RW rollback version O (2) and sees it's greater or equal than RB rollback version 1. So, RW is good. | | | | +RO jumps to RW | | | | +AP is satisfied that the base works, so it tells RW to prepare for a | | | | +roll-forward (`UNLOCK_ROLLBACK`) | | | | +RW unsets `ROLLBACK_AT_BOOT` | | | **RO/RW/\_\_** | +RW may reboot (or just wait for next reattach) | | | | **RO/RW/\_\_** + +On next boot, RB will be updated: + +Step | RW | RB contents | `_AT_BOOT` | `_NOW` +----------------------------------------------------------------------------------------------------------------------------------------- | --- | ----------- | ------------ | ------ +RO verifies RW signature => RW is good | O | 1/blank | RO/RW/\_\_ | RO/RW/\_\_ +RO sees that RB is unprotected, and sees RW rollback version O (2) and sees is greater than RB rollback version 1. So RB needs an update. | | | | +RO updates RB's second block | O | **1/2** | | +RO sets `ROLLBACK_AT_BOOT` to protect RB on the next boot. | | | **RO/RW/RB** | +RO reboots. | | | | **RO/RW/RB** + +## Details + +### STM32 Flash Protection + +At a high level, flash protection works on the STM32F072 chip works in the +following manner: + +* 128KB flash total flash, organized as 32 independently protectable 4KB + blocks. Each block has 2 independently erasable 2KB sectors. +* `FLASH_WRPR` is the register controlling flash write protect of these + blocks. It is not directly writable. In EC common code, these bits are + abstracted as `EC_FLASH_PROTECT_[REGION]_NOW`. +* Instead, there is a non-volatile register called `WRPx`, which is stored in + a separate information block of flash. This is always writable. In EC common + code, these bits are abstracted as `EC_FLASH_PROTECT_[REGION]_AT_BOOT`. On + chip reset, `WRPx` is copied to the `FLASH_WRPR` register. + +Here's the interesting part. The only way to change read-only firmware is to +change `WRPx` and then reset the chip, so that `WRPx` is copied into +`FLASH_WRPR`. At that point, read-only firmware could be writable. But that same +reset also transfers control back to the read-only firmware. If the read-only +firmware doesn't want to be writable, all it has to do is change `WRPx` back to +protect itself, and then reboot again. We do that already on all devices which +use the STM32 chips. + +Flash protection works similarly on other STM32F chips, if we need to move to a +larger or more capable EC for the base to support a more complex base. + +### Flash Contents + +The 128KB `BaseEC` flash will be divided into three parts. + +* Read-only firmware (`Base`-EC-RO, or just "RO" in this document) + * ~40KB + * Minimal functionality, so it can be small. + * Verifies the rewritable firmware. + * Updates the rewritable firmware over USB. + * Does NOT have keyboard or trackpad support. + * Includes the `Base-EC` root key. +* Rewritable firmware (`Base-EC`-RW, or just "RW" in this document) + * ~84KB + * Supports keyboard and trackpad. + * Trackpad drivers may be non-trivial in size. + * Future bases may include type-C ports, sensors, or batteries, all of + which will increase RW size. + * As with the main EC, it is unlikely we will have space for multiple + copies of RW (so, no RW-A and RW-B). + * Updates the read-only firmware over USB (pre-production devices only). +* Rollback block (`Base-EC`-RB, or just "RB" in this document) + * 4KB (one protection block) + * Contains rollback version information for RW + * Only writable by RO. + * Updates alternate between the 2 2KB erase sectors. We only erase one of + them at a time, so an interrupted erase/write will not cause data loss. + +Adding the RB will decrease the total amount of flash available for RO and RW, +but doesn't require any additional external components. This is acceptable +because RO will be smaller (since it only has update/verify functionality). + +### Verification Speed + +On a STM32F072 chip running at 48 MHz, + +* SHA-256 of a 64KB RW image takes 200 ms (~3 ms/KB) + * Reducing RW image size reduces verification time almost proportionally + (even if we need to check that the rest of the image is erased). +* RSA-2048 (exponent 3) signature verification takes ~50 ms +* RSA-3072 (exponent 3) signature verification takes ~100 ms + +[STM32F072]: http://www.st.com/content/ccc/resource/technical/document/reference_manual/c2/f8/8a/f2/18/e6/43/96/DM00031936.pdf/files/DM00031936.pdf +[EC Update over USB]: ./usb_updater.md +[CL:452815]: https://chromium-review.googlesource.com/c/452815/2 diff --git a/docs/sitemap.md b/docs/sitemap.md index 82e5914a0f..c6ce28618a 100644 --- a/docs/sitemap.md +++ b/docs/sitemap.md @@ -41,6 +41,10 @@ * [USB-A and USB-C Policies for Sourcing Power](./usb_power.md) * [USB-C Power Delivery TCPMv2](./usb-tcpmv2.md) +## Verified Boot + +* [Detachable Base Verified Boot](./detachable_base_verified_boot.md) + ## EC-3PO * [EC-3PO overview](./ec-3po.md) |