Authors: [email protected], [email protected]
Last Updated: 2016-11-16
Original: http://go/detachable-base-vboot
[TOC]
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 theBaseEC
. - 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.
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.
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.
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 asEC_FLASH_PROTECT_[REGION]_AT_BOOT
flags).. - On chip reset,
WRPx
is copied into a read-onlyFLASH_WRPR
register; that controls which blocks are protected for this boot. This is abstracted asEC_FLASH_PROTECT_[REGION]_NOW
in the EC code.
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.
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.
- RO code sets
- Otherwise, someone has physically disconnected WP. Set
WRPx=0
to unprotect all flash and reboot.
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.
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.
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.
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 firmware provides the keyboard and trackpad functionality.
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.
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.
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.
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.
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.
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).
Step | RW | RB contents | _AT_BOOT |
_NOW |
---|---|---|---|---|
(initial state) | M | 1/blank | RO/RW/RB | RO/RW/RB |
- RO waits 1 second for an update request from AP | | | |
- RO verifies RW signature => RW is good | | | |
- RO notes that
_AT_BOOT
and_NOW
already protect everything, so no reboot is necessary. | | | | - RO jumps to 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.
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 |
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 asEC_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 asEC_FLASH_PROTECT_[REGION]_AT_BOOT
. On chip reset,WRPx
is copied to theFLASH_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.
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).
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