This document specifies the functionality of the OpenTitan key manager.
The key manager implements the hardware component of the identities and root keys strategy of OpenTitan.
It enables the system to shield critical assets from software directly and provides a simple model for software to use derived key and identity outputs.
Key manager behavior can be summarized by the functional model below.
In the diagram, the red boxes represent the working state and the associated internal key, the black ovals represent derivation functions, the green squares represent software inputs, and the remaining green / purple shapes represent outputs to both software and hardware.
In OpenTitan, the derivation method selected is [KMAC]({{< relref “hw/ip/kmac/doc” >}}). Each valid operation involves a KMAC invocation using the key manager working state as the “key” and other HW / SW supplied inputs as data. While KMAC can generate outputs of arbitrary length, this design fixes the size to 256b.
Effectively, the key manager behavior is divided into 3 classes of functions
Key manager state advancement
Output key generation
Identity / seed generation
In general, the key generation and seed generation functions are identical. They differ only in how software chooses to deploy the outputs.
The key manager working state (red boxes in the functional model) represents both the current state of the key manager as well as its related secret material. Each valid state (Initialized
/ CreatorRootKey
/ OwnerIntermediateKey
/ OwnerRootKey
), supplies its secret material as the “key” input to a KMAC operation. Invalid states, such as Reset / Disabled
on the other hand, either do not honor operation requests, or supplies random data when invoked.
The data input is dependent on each state, see below.
The key manager working state is not directly reset to any value. This ensures there is no deterministic hamming delta upon reset. Instead at reset time, the state value is simply unknown - which is expected to be some biased value the registers settle to based on silicon corner and environment conditions.
To begin operation, the state must first transition to Initialize. The advancement from Reset
to Initialized
is irreversible during the current power cycle. Until the initialize command is invoked, the key manager rejects all other software commands.
When transitioning from Reset
to Initialized
, random values obtained from the entropy source are used to populate the working state. This ensures that the hamming delta from both the previous value and the next value are both non-deterministic. The advancement from Initialized
to CreatorRootKey
is irreversible during the current power cycle.
CreatorRootKey
is the first operational state of the key manager. When transitioning from Initialized
to this state, a KMAC operation is invoked using the RootKey
as the key (from OTP), and the remaining inputs as data.
See below:
DiversificationKey
: Secret seed from flashHealthMeasurement
: Current life cycle stateDeviceIdentifier
: Unique device identification.HardwareRevisionSecret
: A global design time constant.Other than the DiversificationKey
, none of the values above are considered secret.
Once the CreatorRootKey
is reached, software can request key manager to advance state, generate output key or generate output identity. The key used for all 3 functions is the CreatorRootKey
.
The advancement from this state to the next is irreversible during the current power cycle.
This is the second operational state of the key manager. This state is reached through another invocation of the KMAC operation using the previous working state as the key, and other inputs as data. The output of the KMAC operation replaces the previous value of the working state, and the new value becomes the OwnerIntermediateKey
.
The relevant data inputs are:
OwnerRootSecret
: Secret seed from flash.SoftwareBinding
: A software programmed value representing the first owner code to be run.Once the OwnerIntermediateKey
is created, software can request key manager to advance state, generate output key or generate output identity. The key used for all 3 functions is the OwnerIntermediateKey
.
The advancement from this state to the next is irreversible during the current power cycle.
This is the last operational state of the key manager. This state is reached through another invocation of the KMAC operation using the previous working state as the key, and other inputs as data. The output of the KMAC operation replaces the previous value of the working state, and the new value becomes the OwnerRootKey
.
The relevant inputs are:
SoftwareBinding
- A software programmed value representing the owner kernel code.Once the OwnerRootKey
is created, software can request key manager to advance state, generate output key or generate output identity. An advance command invoked from OwnerRootKey
state simply moves the state to Disabled
.
The generate output and generate identity functions use OwnerRootKey
as the KMAC key. The advancement from this state to the next is irreversible during the current power cycle.
Disabled
is a state where the key manager is no longer operational. Upon Disabled
entry, the working state is updated with KMAC computed random values; however, sideload keys are preserved. This allows the software to keep the last valid sideload keys while preventing the system from further advancing the valid key.
When advance and generate calls are invoked from this state, the outputs and keys are indiscriminately updated with randomly computed values.
Invalid
state is entered whenever key manager is disabled through the life cycle connection. Upon Invalid
entry, both the working state and the sideload keys are wiped with entropy directly. Note, this is different from Disabled
state entry, which updates with KMAC outputs.
The function of the key manager is directly tied to the life cycle controller. During specific life cycle states, the key manager is explicitly invalidated.
When invalidated, the following key manager behavior applies:
Disabled
), it immediately wipes its key contents with entropy (working state, sideload keys and software keys) and transitions to Invalid
.Invalid
state, the behavior is consistent with Disabled
state.During each state, there are 3 valid commands software can issue:
The software is able to select a command and trigger the key manager FSM to process one of the commands. If a command is valid during the current working state, it is processed and acknowledged when complete.
If a command is invalid, the behavior depends on the current state. If the current state is Reset
, the invalid command is immediately rejected as the key manager FSM has not yet been initialized. If the current state is any other state, the key manager FSM processes with random, dummy data, but does not update working state or relevant output registers. For each valid command, a set of inputs are selected and sequenced to the KMAC module.
During Disable
and Invalid
states, working state and output registers are updated based on the input commands as with normal states. There are however a few differences:
The generate output command is composed of 2 options
generate-output-sw
generate-output-hw
The hardware option is meant specifically for symmetric side load use cases. When this option is issued, the output of the KMAC invocation is not stored in software visible registers, but instead in hardware registers that directly output to symmetric primitives such as AES, HMAC and KMAC.
All invoked KMAC operations expect the key in two shares. This means the working states, even though functionally 256b, are maintained as 512b.
For advance-state and generate-output
commands, the KMAC emitted output are also in 2-shares. Software is responsible for determining if the key should be preserved in shares or combined, depending on the use case.
An error code register is maintained {{< regref ERR_CODE >}} to check issues that might rise while using the key manager. There are two categories of errors
Hardware fault errors - These errors indicate something fundamental has gone wrong and are errors that could not have been caused by software.
Software operation errors - These errors could have been caused by user errors and is a sign that software should examine its usage of key manager.
generate
while in Reset) was invoked.Two separate alerts are generated, one corresponding to each category above.
In addition to the error code register, there is a separate {{< regref FAULT_STATUS >}} that captures the sources that caused Invalid states
to assert.
When these errors occur, a fault alert is generated.
When these errors occur, an operation alert is generated What is considered invalid input depends on the current state and the operation called.
When an advance operation is invoked:
Initialized
state, creator seed, device ID and health state data is checked for all 0‘s and all 1’s.CreatorRootKey
state, the owner seed is checked for all 0‘s and all 1’s.When a generate output key operation is invoked:
When a generate output identity is invoked:
When these errors occur, an operation alert is generated.
The table below enumerates the legal operations in a given state. When an illegal operation is supplied, the error code is updated and the operation is flagged as done with error
.
Current State | Legal Operations |
---|---|
Reset | Advance |
Initialized | Disable / Advance |
CreatorRootKey | Disable / Advance / Generate |
OwnerIntKey | Disable / Advance / Generate |
OwnerRootKey | Disable / Advance / Generate |
Invalid/Disabled | None |
Invalid
and Disabled
states lead to invalid operation error.In addition to alerts and interrupts, key manager may also update the working state key and relevant outputs based on current state. See the tables below for an enumeration.
Current State | Invalid Command | Invalid Output | Invalid Input | Invalid Operation |
---|---|---|---|---|
Reset | Not Possible | Not Possible | Not possible | Not updated |
Initialized | Updated | Updated | Not updated | Not updated |
CreatorRootKey | Updated | Updated | Not updated | Not possible |
OwnerIntKey | Updated | Updated | Not updated | Not possible |
OwnerRootKey | Updated | Updated | Not updated | Not possible |
Invalid/Disabled | Updated | Updated | Updated | Updated |
Reset
state, the KMAC module is never invoked, thus certain errors are not possible.Initialized
, CreatorRootKey
, OwnerIntermediateKey
and OwnerRootKey
states, a fault error causes the relevant key / outputs to be updated; however an operational error does not.Invalid
and Disabled
states, the relevant key / outputs are updated regardless of the error.The key manager supports DICE open profile. Specifically, the open profile has two compound device identifiers.
The attestation CDI is used to attest hardware and software configuration and is thus expected to change between updates. The sealing CDI on the other hand, is used to attest the authority of the hardware and softawre configuration. The sealing version is thus expected to remain stable across software updates.
To support these features, the key manager maintains two versions of the working state and associated internal key. There is one version for attestation and one version for sealing.
The main difference between the two CDIs is the different usage of SW_BINDING
. For the Sealing CDI, the {{< regref “SEALING_SW_BINDING” >}} is used, all other inputs are the same. For the Attestation CDI, the {{< regref “ATTEST_SW_BINDING” >}} is used, all other inputs are the same.
When invoking an advance operation, both versions are advanced, one after the other. There are thus two kmac transactions. The first trasnaction uses the Sealing CDI internal key, {{< regref “SEALING_SW_BINDING” >}} and other common inputs. The second transaction uses the Attestation CDI internal key, {{< regref “ATTEST_SW_BINDING” >}} and other common inputs.
When invoking a generate operation, the software must specify which CDI to use as the source key. This is done through {{< regref “CONTROL.CDI_SEL” >}}. Unlike the advance operation, there is only 1 transaction since we pick a specific CDI to operate.
When disabling, both versions are disabled together.
The following is a high level block diagram of the key manager.
Key manager is primarily composed of two components:
The key manager control block manages the working state, sideload key updates, as well as what commands are valid in each state. It also handles the life cycle keymgr_en
input, which helps disable the entire key manager function in the event of an escalation.
The KMAC interface control represents the bulk of key manager logic. Based on input from key manager control, this module selects the inputs for each given command and sequences the data to KMAC.
The KMAC interafce works on a simple valid / ready
protocol. When there is data to send, the KMAC interface sends out a valid
and keeps it active. When the destination accepts the transaction, the ready
is asserted. Note just like with any bus interface, the ready
may already be asserted when valid
asserts, or it may assert some time later, there are no restrictions. Since the data to be sent is always pre-buffered in key manager, the valid once asserts, never de-asserts until the entire transaction is complete.
The data interface itself is 64b wide. However, there may not always be 64b multiple aligned data to be sent. In these situations, the last transfer beat sent to KMAC has a byte mask / strobe attached. The byte mask indicates on the last beat which bytes are actually valid, and which are not. Not beats prior to the last always have fully asserted byte masks.
Once KMAC receives all the required data and the last indication, it begins processing the data into a digest. This process may take an arbitrary number of cycles. When this process is complete, a done
indication pulse is sent back with the digest. Note, the acceptance of done
has no back-pressure and keymgr
must accept it within one cycle.
See diagram below for an example transfer:
{{< wavejson >}} {signal: [ {name: ‘kmac_data_o.valid’, wave: ‘01...........|....0..’}, {name: ‘kmac_data_i.ready’, wave: ‘1...0..101...|.......’}, {name: ‘kmac_data_o.data’, wave: ‘x2222...2.222|2222x..’}, {name: ‘kmac_data_o.last’, wave: ‘0................10..’}, {name: ‘kmac_data_o.strb’, wave: ‘x2...............2x..’}, {name: ‘kmac_data_i.done’, wave: ‘0..................10’}, {name: ‘kmac_data_i.digest*’, wave: ‘x..................3x’}, ], } {{< /wavejson >}}
There are three sideload keys. One for AES, one for HMAC, and one for KMAC. When a sideload key is generated successfully through the generate-output-hw
command, the derived data is loaded into key storage registers. There is a set of storage registers for each destination.
The KMAC key however is further overloaded as it is the main derivation mechanism for key manager internal stage. The KMAC key thus has two possible outputs, one is the sideload key, and the other is internal state key.
When a valid operation is called, the internal state key is sent over the KMAC key. During all other times, the sideloaded value is presented. Note, there may not be a valid key in the sideload register if it has been cleared or never generated. The sideload key can be overwritten with another generate command, or cleared with entropy through {{< regref SIDELOAD_CLEAR >}}.
The clearing can be done one slot at a time, or all at once.
The following diagram illustrates an example when there is no valid key in the KMAC sideload registers and an operation is called. During the duration of the operation, the key is valid and shows the internal key state. Once the operation is complete, it falls back to the sideload key state, which is invalid in this case.
{{< wavejson >}} {signal: [ {name: ‘u_sideload_ctrl.u_kmac_key.key_o.valid’, wave: ‘0................’}, {name: ‘u_sideload_ctrl.u_kmac_key.key_o.key_share’, wave: ‘x................’}, {name: ‘u_ctrl.key_o.valid’, wave: ‘0................’}, {name: ‘u_ctrl.key_o.key_share’, wave: ‘x................’}, {name: ‘u_ctrl.op_start_i’, wave: ‘0....1.....0.....’}, {name: ‘kmac_key_o.valid’, wave: ‘0....1.....0.....’}, {name: ‘kmac_key_o.key_share*’, wave: ‘x....3.....x.....’}, ], } {{< /wavejson >}}
The following diagram illustrates an example when there is a valid key in the KMAC sideload registers and an operation is called. During the duration of the operation, the key is valid and shows the internal key state. Once the operation is complete, it falls back to the sideload key state, which is valid and contains a different value.
{{< wavejson >}} {signal: [ {name: ‘u_sideload_ctrl.u_kmac_key.key_o.valid’, wave: ‘01...............’}, {name: ‘u_sideload_ctrl.u_kmac_key.key_o.key_share’, wave: ‘x4...............’}, {name: ‘u_ctrl.key_o.valid’, wave: ‘0....1.....0.....’}, {name: ‘u_ctrl.key_o.key_share’, wave: ‘x................’}, {name: ‘u_ctrl.op_start_i’, wave: ‘0....1.....0.....’}, {name: ‘kmac_key_o.valid’, wave: ‘01...............’}, {name: ‘kmac_key_o.key_share*’, wave: ‘x4...3.....4.....’}, ], } {{< /wavejson >}}
The identities flow employs an idea called software binding to ensure that a particular key derivation scheme is only reproducible for a given software configuration. The binding is created through the secure boot flow, where each stage sets the binding used for the next verified stage before advancing to it. The software binding is used during the following state transitions only:
Initialized
to CreatorRootKey
CreatorRootKey
to OwnerIntermedaiteKey
OwnerIntermediateKey
to OwnerRootKey
In order to save on storage and not have a duplicate copy per stage, the software binding registers {{< regref SOFTWARE_BINDING >}} are shared between key manager stages.
Software sets the appropriate values and locks it by clearing {{< regref SOFT_BINDING_EN >}}. When later a successful advance
call is made, the key manager then unlocks by setting {{< regref SOFT_BINDING_EN >}} to 1. An unsuccessful advance call (errors) does not unlock the binding. This allows the next stage of software to re-use the binding registers.
{{< incGenFromIpDesc “../data/keymgr.hjson” “hwcfg” >}}
Software selects a command and triggers a “start”. If the command is valid and successful, key manager indicates done and no errors. If the command is invalid or unsuccessful, key manager indicates done with error. Regardless of the validity of the command, the hardware sequences are triggered to avoid leaking timing information.
The software is able to read the current state of key manager, however it never has access to the associated internal key.
When issuing the generate-output-hw
command, software must select a destination primitive (aes, hmac or kmac). At the conclusion of the command, key and valid signals are forwarded by the key manager to the selected destination primitive. The key and valid signals remain asserted to the selected destination until software explicitly disables the output via another command, or issues another generate-output-hw
command with a different destination primitive.
More details to come.
{{< incGenFromIpDesc “../data/keymgr.hjson” “registers” >}}