This document describes the OpenTitan provisioning flow which is divided into two stages:
The security scope is derived from the threats against the assets handled by the provisioning infrastructure.
The wafer foundry and OSAT are untrusted to maintain the secrecy of the following assets:
The security model of the provisioning infrastructure is based on the following requirements:
OpenTitan provides a set of lock/unlock tokens to control the state of the device in early manufacturing stages. See device lifecycle specification for more details.
RAW_UNLOCK
TEST_UNLOCK
TEST_EXIT
RMA_UNLOCK
Test unlock tokens are used as part of the process to logically disable the devices so that they can be safely transported between manufacturing stages. Unlock operations do not require CPU intervention. A test exit token is used to gate the device transition from TEST state into one of the final operational states (DEV
, PROD_END
, PROD
).
The following steps illustrate the set of operations required prior to personalization.
RAW_UNLOCK
: Unlock TEST mode by providing unlock token via TAP interface.
ANALOG TRIM & TEST (EDS)
: Regular analog test, scan, calibration, trimming and functional testing (EDS stage).
SET DEVICE_ID
: Program the device identifier fuses. DEVICE_ID
export may be required depending on the provisioning flow.
LOCK DEVICE
: (Optional) Program the TEST_UNLOCK (optional) and/or TEST_EXIT tokens depending on the test flow configuration.
Optional: If required by the manufacturing flow, lock the device for safe transport into the next test stage.
TEST_UNLOCK
: Unlock TEST mode by providing TEST_UNLOCK token via TAP interface.
ANALOG TRIM & TEST (OSAT)
: Regular analog test, scan, calibration, trimming and functional testing (EDS stage).
LOG DEVICE_ID
: Record DEVICE_ID and test results.
LOCK DEVICE
: Program the TEST_UNLOCK (optional) and/or TEST_EXIT tokens depending on test configuration.
Optional: If required by the manufacturing flow, lock the device for safe transport into the next test stage.
TEST_EXIT
: Unlock device and transition from TEST to one of the following operational states: DEV, PROD_END, or PROD.SKU SETTINGS
: SKU dependent fuse and info flash configuration.PERSONALIZATION
: See Personalization section for moreLOAD FW IMAGE
: Load factory image payload.The following diagram shows the physical connections between test components.
Device Under Test: An OpenTitan device being tested as part of its manufacturing process.
ATE: Automatic Test Equipment (ATE), used to perform tests on the devices as part of the manufacturing process.
Provisioning Appliance: A network connected local server with an attached HSM. The server implements an OpenTitan compliant secure boot implementation, and runs signed applications used to communicate with ATE and cloud services.
Provisioning Service: Cloud service used to authenticate and initialize provisioning appliances.
The provisioning service is used to provide initialization data to the provisioning appliance once it has been authenticated.
Registry Service: A cloud service used to host a device registry containing certificates and Certificate Revocations Lists (CRLs).
ATE - OpenTitan (DUT)
RST_N
: OpenTitan reset pin.STRAPS
: Pins used to control hardware and software test functionality. On the hardware side, strap pins are used to configure TEST modes and select TAP interfaces. On the software side, straps used to enable the SPI flash bootstrap mode in ROM, as well as manufacturing test modes. Some strap functionality is only available before entering DEV or PROD states. There is also additional functionality that is disabled when the device reaches the end of manufacturing.SPI
: Bidirectional interface used to exchange payloads and status with the device.Provisioning Appliance - ATE
Provisioning/Registry Services - Provisioning Appliance
The following diagram captures the life of a device throughout the manufacturing flow with emphasis on the personalization process.
Steps:
device_id
) and unlock tokens are programmed into the device's One Time Programmable (OTP) memory. The unlock tokens are delivered in cleartext form to each OpenTitan die.TEST_LOCKED
lifecycle state.appliance_secrets, and identifiers (device_ids)
in encrypted form.device_secrets
(in the injection case) and endorsement certificates. appliance_secrets
are used to enable signing on endorsement certificates with Silicon Creator intermediate CA keys.device_secrets
and the provisioning appliance is used to sign the endorsement certificate.Secure boot is always enforced by the ROM and cannot be disabled. Personalization and factory software payloads are signed, and boot verification is used to anchor the mechanism in which the device authenticates the provisioning appliance during personalization.
This section describes the personalization injection mode in detail.
P0. Get device identifier
The DEVICE is initially in LOCKED state before the start of production test. The TESTER gets the Device ID via TAP interface. The provisioning appliance returns unlock tokens associated with the Device ID.
Note 1: The Device ID read requirement applies only if unlock tokens are unique per device.
Note 2: The manufacturer must document the unlock token usage plan, including the proposed rotation plan.
P1. Unlock device and load personalization software
P1.1. The TESTER unlocks the DEVICE by sending the TEST_UNLOCK
and TEST_EXIT
tokens via TAP interface.
P1.2. The TESTER loads personalization software on the DEVICE using the SPI bootstrap interface. The DEVICE verifies the personalization software signature against one of the public keys stored in the ROM via secure boot.
P2. Device authentication and key exchange
P2.1 Authentication function
The authentication function relies on the following assumptions:
# Shared secret key between provisioning appliance and the DEVICE. # The parameters of the authentication function are outside the # classification scope of this document. key_auth = authentication_function()
P2.2. DEVICE sends authentication data to TESTER
The DEVICE will fail to generate an authentication payload if the device is not in PROD
, PROD_END
or DEV
state.
The DEVICE sends an authentication payload to the TESTER including:
device_id
).receiver_pub_key
) used to derive session keys[^1]; and,The DEVICE sends the payload to the TESTER via SPI, repeating the message continuously to simplify timing adjustments in the test sequence.
Payload generation:
// ECC key pair generation compliant to FIPS 186-4 appendix B.4. // Curves under consideration: NIST_P256 and NIST_P386. receiver_priv_key, receiver_pub_key = ECC_KDF(DRBG_context) key_auth = authentication_function() // data_size includes the size of the data + tag. data = "OTAU" || receiver_pub_key || device_id || data_size tag = MAC(key_auth, data) // Continuously broadcast via SPI device port. return data || tag
P2.3 TESTER verifies authentication data
The TESTER calls the provisioning appliance to verify the tag attached to the DEVICE authentication payload using the authentication key function from P2.1. The TESTER aborts personalization on failure.
Verification function:
key_auth = authentication_function() data || tag = authentication_data calculated_tag = MAC(key_auth, data) RETURN_ERROR_IF(calculated_tag != tag)
P3: Inject secrets
P3.1 Get wrapped personalization payload from provisioning appliance
The TESTER gets a personalization payload from the provisioning appliance. The provisioning appliance is in charge of generating the device secrets. The payload is associated with the Device ID and is wrapped with a key derived from the receiver_pub_key
extracted in step P2.
Transport wrapping. See ECIES Encryption for more details on the ECIES_ENC
input and output parameters. Some parameters are omitted for simplicity.
// OPEN: ctx_id in this case is set to a portion of the device_id. // The manufacturer shall incorporate a monotonically incrementing counter // into ctx_id. ephemeral_pub_key || tag || ctx_id || sender_pub_key || data_size || data_enc = ECIES_ENC(key=sender_priv_key, receiver_pub_key, data=(device_secrets || creator_certificate)) personalization_payload = "OTPL" || tag || ctx_id || sender_pub_key || data_size || data_enc
P3.2 TESTER sends personalization payload to DEVICE
The personalization payload is sent to the device via SPI in 1KB frame chunks. The payload is transferred from the DEVICE SPI RX FIFO into SRAM.
P4. DEVICE unwrap and install secrets
P4.1 DEVICE unwraps secrets and creator certificate
Unwrap process. See ECIES Decryption for more details on the ECIES_DEC
input and output parameters. Some parameters are omitted for simplicity.
// The following operation will verify the integrity of the encrypted // blob. The sender_pub_key is verified against a whitelist stored // in the personalization software. device_secrets, creator_cerficate = ECIES_DEC(key=receiver_priv_key, ephemeral_pub_key, sender_key_pub, data=enc_payload)
P4.2 Install secrets
Secrets are installed in OTP and in Flash. See Device Secrets for a breakdown of Silicon Creator level secrets.
P4.3 Report install status
Report install status to tester via SPI interface. The status code is repeated continuously on the SPI interface to simplify the test implementation.
P5. Install factory image
The factory image is the software loaded on the device before shipping. It is not expected to change frequently once the manufacturing flow is deployed for a given SKU configuration. At a minimum, the factory image will contain a ROM Extension (ROM_EXT
) component.
The TESTER programs the factory image on the DEVICE via SPI bootstrap interface The hash of the ROM_EXT
must match the value used to calculate the Creator Identity as described in the Identities and Root Keys document.
P6. Provisioning result
The DEVICE boots in identity UNOWNED state and in manufacturing mode. The conditions used to trigger manufacturing mode are TBD.
P6.1 Test Creator Identity
The ROM_EXT
uses the key manager to obtain the Creator Identity key. See the Asymmetric Keys section in the Attestation specification for more details.
The public portion of the Creator Identity is tested against the Creator Certificate. The result is reported to the TESTER via SPI.
This section describes the personalization self-generate mode in detail. In this mode, The device generates its own device secrets and the provisioning appliance is used to sign the endorsement certificate.
PS0. Get device identifier
The DEVICE is initially in LOCKED state. The TESTER gets the Device ID via TAP interface. The provisioning appliance returns unlock tokens associated with the Device ID.
PS1. Unlock device and load perso firmware
PS1.1. The TESTER unlocks the DEVICE by sending the TEST_UNLOCK
and TEST_EXIT
tokens via TAP interface.
PS1.2. The TESTER loads personalization software on the DEVICE using the SPI bootstrap interface. The DEVICE verifies the personalization software signature against one of the public keys stored in the ROM via secure boot.
PS2. Generate and install secrets
The device uses the available on-device entropy source to generate all the device secrets.
Secrets are installed in OTP and in Flash. Fuse locks are set as described in the OTP specification.
PS3. Export public key
The device exports the Creator Identity public key via SPI. The authentication function from P2.1 is used to generate an integrity digest over the exported data.
Payload with integrity tag:
// Shared between device and provisioning appliance. key_auth = authentication_function() // data_size includes the size of the data + tag. data = "OTAU" || data_size || device_id || creator_identity_pub_key tag = MAC(key_auth, data) // Continuously broadcast via SPI device port. return data || tag
PS4. Generate certificate
PS4.1. Verify received data
The provisioning appliance verifies the integrity of the received payload. The verification of the payload is used to authenticate the device, since the key used to calculate the digest tag is only known to the device and the provisioning appliance.
Verification function:
key_auth = authentication_function() data || tag = authentication_data calculated_tag = MAC(key_auth, data) RETURN_ERROR_IF(calculated_tag != tag)
PS4.2. Generate certificate
The provisioning appliance generates an endorsement certificate as defined in the Certificate Format section of the Attestation specification.
PS4.3. Send certificate
The certificate is sent to the device via SPI in 1KB frames. An integrity tag is added to the message using the same mechanism as in PS3.
Provisioning service to device certificate payload.
// Shared between device and provisioning appliance. key_auth = authentication_function() // data_size includes the size of the data + tag. data = "OTCI" || data_size || device_id || creator_identity_cert tag = MAC(key_auth, data) // Continuously broadcast via SPI device port. return data || tag
PS5. Install certificate
The device verifies the certificate payload sent by the provisioning appliance and installs it in a flash block.
PS6. Install factory image
The factory image is the software loaded on the device before shipping. It is not expected to change frequently once the manufacturing flow is deployed for a given SKU configuration. At a minimum, the factory image will contain a ROM Extension (ROM_EXT
) component.
The TESTER programs the factory image on the DEVICE via SPI bootstrap interface. The hash of the ROM_EXT
must match the value used to calculate the Creator Identity as described in the Identities and Root Keys document.
PS7. Provisioning result.
The ROM_EXT
uses the key manager to obtain the Creator Identity key. See the Asymmetric Keys section in the Attestation specification for more details.
The public portion of the Creator Identity is tested against the Creator Certificate. The result is reported to the TESTER via SPI.
OpenTitan provides a mechanism to enable provisioning of Silicon Owner secrets and endorsement certificates in manufacturing and post-manufacturing stages. Owners are encouraged to create an implementation plan to perform post-manufacturing provisioning to take full advantage of ownership transfer.
Provisioning post-ownership transfer assumes that the OpenTitan device is integrated into a system, and there is a HOST capable of communicating synchronously or asynchronously with the DEVICE (OpenTitan) and a remote registry and provisioning service.
The physical transport layer between the DEVICE and the HOST is use-case specific and managed at the OpenTitan SKU configuration level (e.g. different ROM_EXT
implementation per SKU).
The following diagram captures the Silicon Owner provisioning flow. Device attestation requires the device to have a valid Creator Identity endorsed by the Silicon Creator as described in the Personalization section.
The process can be implemented during manufacturing or post-manufacturing. If implemented during manufacturing, the ATE and provisioning appliance fulfill the role of the HOST.
Steps:
device_id)
and Silicon Creator Certificates are imported into the Silicon Owners internal device registry.RP0. Ownership transfer
The DEVICE is initially in UNOWNED state. The Silicon Owner triggers ownership transfer and loads new software. Ownership transfer details are covered in a separate document.
See Ownership Transfer document for more details.
RP1. Get attestation certificates
The HOST requests attestation certificates from the DEVICE. A device that has been transferred to a new owner has its attestation chain rooted in the Creator Identity. See the Attestation documentation for more information.
RP2. Get receiver_pub_key
The DEVICE generates a receiver
key pair and shares the receiver_pub_key
with the HOST.
Additional data required to generate a new owner certificate and/or device secrets is added to the payload. The payload is signed with the owner_identity_key
which can be verified against the DEVICE attestation chain obtained in RP1.
// ECC key pair generation compliant to FIPS 186-4 appendix B.4. // Curves under consideration: NIST_P256 and NIST_P386. receiver_priv_key, receiver_pub_key = ECC_KDF(DRBG_context) data = any additional data required to generate the owner cert. hdr = ctx_id || receiver_pub_key || data // owner_identity_key is an ECC key as described in the Asymmetric // Keys section of the Attestation document. signature = ASYM_SIGN(key=owner_identity_priv_key, data=hdr) payload = hdr || signature
RP3. Get wrapped secrets and owner certificate
RP3.1. The provisioning service verifies the receiver_pub_key
against the attestation chain obtained in RP1 and the internal device registry.
The provisioning service then generates a new owner_certificate
with the additional data provided by the DEVICE. The certificate is signed with a key managed by the Silicon Owner (e.g. owner_intermediate_ca_key
). The new owner_certificate
will be used as the root certificate in Attestation flows as described in the Attestation document.
RP3.2. The provisioning service performs ECIES encryption using the following parameters:
sender_priv_key
: Provisioning service private key. The public portion is known to the DEVICE firmware.receiver_pub_key
: DEVICE public key obtained in step RP2.ctx_id
: Context ID provided by the device in step RP2.owner_certificate
: New owner certificate generated in step RP3.1.device_secrets
: Additional secrets bound to the device. May be generated offline or as part of step RP3.1.payload = ECIES_ENC(key=sender_priv_key, pub_key=receiver_pub_key, data=(device_secrets | ctx_id)) | owner_certificate
RP3.3. The wrapped payload is sent to the DEVICE.
RP4. Unwrap and install secrets and owner certificate
Device performs ECIES decryption to unwrap the payload and install the new owner_certificate
and device_secrets
. The owner has access to info flash pages for storage of secrets.
The server_key_pub
and ephemeral_pub_key
values are sent with the wrapped payload. The public server key (server_key_pub
) is also known to the software running in the DEVICE.
device_secrets || owner_certificate = ECIES_DEC(key=receiver_priv_key, server_key_pub, ephemeral_pub_key, data=enc_payload)
RP5: Get attestation certificates
The HOST issues a new attestation command to the DEVICE. The DEVICE responds with an attestation chain rooted on the new owner_certificate
. An additional test command can be supported to test any functionality associated with the device_secrets
.
Key Establishment
Key exchange is based on NIST 800-56Ar3 6.2.1.2 Cofactor One-Pass Unified Model Scheme, employing one ephemeral key, two secret keys and ECC CDH. Both sender and receiver authentication are performed via public crypto, thus one secret key is associated with the receiver, while the other one is associated with the sender.
AEAD Modes of Operation
The following authenticated encryption schemes are supported by this architecture:
Inputs:
receiver_pub_key
: ECC public key provided by the receiver point (e.g. NIST_P256, NIST_P386sender_priv_key
: Sender ECC private key.ctx_id
: Context identifier provided by the device. Used to control forward secrecy of the protocol. For personalization this value is set to a portion of the device identifier (device_id
).data
, data_size
: Data to be encrypted and its sizeecies_key_length
: Target ECIES key length. Used as a configuration parameter.Outputs:
ephemeral_pub_key
: Ephemeral ECC shared key, required to perform decryption.tag
: MAC over payload.ctx_id
: Context identifier provided by the device.sender_pub_key
: Sender ECC public key.data_enc
, data_size
: Encrypted data and its size.Algorithm:
// NIST 800-56Ar3 6.2.1.2 One-Pass Unified Model. This requires // the creation of two shared secrets: shared_ephemeral and // shared_static. ephemeral_priv_key = GenerateEphemeralKey() shared_ephemeral = ECDH_compute_key(key_len, EC_POINT(receiver_pub_key), ephemeral_priv_key) shared_static = ECDH_compute_key(key_len, EC_POINT(receiver_pub_key), sender_priv_key) // Key derivation function used to calculate K and IV. K, IV = key_and_iv_generaration( ctx_id, data_size, shared_ephemeral, shared_static, receiver_pub_key, sender_pub_key) // The following authenticated encryption approach follows encrypt // then MAC approach. // See also Alternative Authenticated Encryption Scheme // Ke length should be one of 128, 192, 256. // Km length should be one of 256, 384, 512. // K should have an entropy with a security strength equivalent to // the one provided by Ke and Km when used with AES_CTR and MAC // respectively. [Ke || Km] = K data_enc = AES_CTR_ENC(Ke, IV, data) tag = MAC(Km, ctx_id || sender_pub_key || data_size || data_enc) return [ ephemeral_pub_key || tag || ctx_id || sender_pub_key || data_size || data_enc]
The following implementation uses AES in GCM mode to obtain the ciphertext and integrity tag.
// The following authenticated encryption approach removes the need // for separately calling a MAC function. In this case there is no // need to split the key K into Ke and Km components. // All the parameters are taken from the main encryption pseudo-code // block above. The following call replaces the AES_CTR and MAC // calls. data_enc, tag = AES_GCM_ENC(K, IV, ctx_id || sender_pub_key || data_size || data)
Inputs:
receiver_priv_key
: Receiver ECC private key. Generated by the device and associated with the context identifier (ctx_id
).ephemeral_pub_key
: Ephemeral ECC shared key generated by the sender.sender_pub_key
: Sender ECC public key. Embedded in payload sent to the device.ctx_id
: Context identifier provided by the device.data
, data_size
: Encrypted data and its size.ecies_key_length
: Target ECIES key length. Used as a configuration parameter.Outputs:
The algorithm implementation currently includes authentication and integrity checks, thus plaintext is the only documented output parameter.
plaintext
, size
: Plaintext data and its size.Algorithm:
[ephemeral_pub_key || tag || ctx_id || sender_pub_key || data_size || ciphertext ] = data // Check sender_pub_key against whitelist stored in device software. check_sender_key(sender_pub_key) // NIST 800-56Ar3 6.2.1.2 One-Pass Unified Model. This requires // the creation of two shared secrets: shared_ephemeral and // shared_static. ephemeral_key = GenerateEphemeralKey() shared_ephemeral = ECDH_compute_key(key_len, EC_POINT(ephemeral_pub_key), ephemeral_priv_key) shared_static = ECDH_compute_key(key_len, EC_POINT(sender_pub_key), ephemeral_priv_key) // Key derivation function used to calculate K and IV. K, IV = key_and_iv_generaration( ctx_id, data_size, shared_ephemeral, shared_static, receiver_pub_key, sender_pub_key) // The following authenticated encryption approach follows encrypt // then MAC approach. // See also // Ke length should be one of 128, 192, 256. // Km length should be one of 256, 384, 512. // K should have an entropy with a security strength equivalent to // the one provided by Ke and Km when used with AES_CTR and MAC // respectively. Ke || Km = K tag_expected = MAC(Ke, ctx_id || sender_pub_key || data_size || data_enc) RETURN_ERROR_IF(tag != tag_expected) RETURN_ERROR_IF(ctx_id != ctx_id_expected) plaintext = AES_CTR_DEC(Km, IV, data=ciphertext) return [plaintext]
Inputs:
shared_ephemeral
: Derived ephemeral key. See Encryption and Decryption algorithms for more details.shared_static
: Derived static key.ctx_id
: Context identifier provided by the device.data_size
: Size of input data.ecies_key_length
: Target ECIES key length. Used as a configuration parameter.Outputs:
K
: ECIES key of size equal to ecies_key_length
.IV
: IV
parameter derived to use as AES decrypt parameter.Algorithm:
shared_secret = shared_ephemeral || shared_static salt = "shared_tag" || ctx_id || 00's padding // KDF based on NIST 800-56C 2-step key derivation. The extract and // expand operations can be implemented via HMAC-SHA2. For SHA3 see // Alternative KDF 1-step KMAC. KDK = HKDF_extract(salt=salt, secret=shared_secret) L = ecies_key_length label = "ot_encrypt" context = receiver_pub_key || sender_pub_key fixed_info = label || context || STRING(L) K = HKDF_expand(KDK, L, fixed_info) # IV generation using additional HKDF_expand fixed_info_iv = "ot_iv" || context || STRING(12) IV = HKDF_expand(KDK, 12, fixed_info_iv) return K, IV
The following implementation uses a one-step key derivation function based on a KMAC implementation (e.g. KMAC256).
// KDF based on NIST 800-56C section 4.1 1-step key derivation. // Using KMAC256 as specified in section 4.1 option 3. L = ecies_key_length label = "ot_encrypt" context = receiver_pub_key || sender_pub_key fixed_info = label || context || STRING(L) salt = "shared_tag" || ctx_id || 00's padding up to key size other_input = salt || fixed_info K = HKDF(secret=shared_secret, L, other_intput) fixed_info_iv = "ot_iv" || context || STRING(12) IV = HKDF(secret=shared_secret, 12, other_input=salt || fixed_info_iv) return K, IV
[^1]: This is a static key within the context of a personalization run. The key is erased after personalization is complete.