Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[provisioning] Encrypt the RMA unlock token #25304

Merged
merged 1 commit into from
Nov 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ CP_PROVISIONING_INPUTS = _DEVICE_ID_AND_TEST_TOKENS + """
FT_PROVISIONING_INPUTS = _DEVICE_ID_AND_TEST_TOKENS + """
--target-mission-mode-lc-state="prod"
--rma-unlock-token="0x01234567_89abcdef_01234567_89abcdef"
--token-encrypt-key-der-file="sw/device/silicon_creator/manuf/keys/fake/rma_unlock_enc_rsa3072.pub.der"
--rom-ext-measurement="0x11111111_11111111_11111111_11111111_11111111_11111111_11111111_11111111"
--owner-manifest-measurement="0x22222222_22222222_22222222_22222222_22222222_22222222_22222222_22222222"
--owner-measurement="0x33333333_33333333_33333333_33333333_33333333_33333333_33333333_33333333"
Expand Down
1 change: 1 addition & 0 deletions sw/device/silicon_creator/manuf/keys/fake/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ filegroup(
":ca_config.json",
":dice_ca.pem",
":ext_ca.pem",
":rma_unlock_enc_rsa3072.pub.der",
":sk.pkcs8.der",
],
)
19 changes: 19 additions & 0 deletions sw/device/silicon_creator/manuf/keys/fake/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,22 @@ $ openssl x509 -req -in ca.csr -signkey sk.pem -out ca.pem -days 3650 \
# Examine the generated certificate:
$ openssl x509 -in ca.pem -text
```

# Generating the RMA unlock token encryption keypair with OpenSSL

The RMA unlock token encryption keypair is an RSA-3072 key used to encrypt the
RMA unlock token generated during provisioning.

The fake keys (used for testing) in this subdirectory were generated with `openssl`.

```
### Generate the RSA keypair:
$ openssl genrsa -out rma_unlock_enc_rsa3072.pem 3072

### Extract the public key to a separate file:
$ openssl rsa -in rma_unlock_enc_rsa3072.pem -pubout -out rma_unlock_enc_rsa3072.pub.pem

### Convert the PEM files to DER files:
$ openssl rsa -in rma_unlock_enc_rsa3072.pem -outform der -out rma_unlock_enc_rsa3072..der
$ openssl rsa -pubin -in rma_unlock_enc_rsa3072.pub.pem -outform der -out rma_unlock_enc_rsa3072.pub.der
```
Binary file not shown.
Binary file not shown.
1 change: 1 addition & 0 deletions sw/host/provisioning/ft/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ package(default_visibility = ["//visibility:public"])
"@crate_index//:anyhow",
"@crate_index//:clap",
"@crate_index//:elliptic-curve",
"@crate_index//:hex",
"@crate_index//:humantime",
"@crate_index//:log",
"@crate_index//:p256",
Expand Down
25 changes: 21 additions & 4 deletions sw/host/provisioning/ft/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@ use opentitanlib::test_utils::init::InitializeTest;
use opentitanlib::test_utils::lc::read_lc_state;
use opentitanlib::test_utils::load_sram_program::SramProgramParams;
use ujson_lib::provisioning_data::{ManufCertgenInputs, ManufFtIndividualizeData};
use util_lib::{hex_string_to_u32_arrayvec, hex_string_to_u8_arrayvec};
use util_lib::{
encrypt_token, hex_string_to_u32_arrayvec, hex_string_to_u8_arrayvec, load_rsa_public_key,
random_token,
};

/// Provisioning data command-line parameters.
#[derive(Debug, Args, Clone)]
Expand All @@ -44,7 +47,7 @@ pub struct ManufFtProvisioningDataInput {

/// RMA unlock token; a 128-bit hex string.
#[arg(long)]
pub rma_unlock_token: String,
pub rma_unlock_token: Option<String>,

/// LC state to transition to from TEST_UNLOCKED*.
#[arg(long, value_parser = DifLcCtrlState::parse_lc_state_str)]
Expand All @@ -69,6 +72,10 @@ pub struct ManufFtProvisioningDataInput {
/// Security version the Owner image to be loaded onto the device.
#[arg(long, default_value = "0")]
pub owner_security_version: u32,

/// Token Encryption public key (RSA) DER file path.
#[arg(long)]
token_encrypt_key_der_file: PathBuf,
}

#[derive(Debug, Parser)]
Expand Down Expand Up @@ -116,8 +123,18 @@ fn main() -> Result<()> {
hex_string_to_u32_arrayvec::<4>(opts.provisioning_data.test_unlock_token.as_str())?;
let _test_exit_token =
hex_string_to_u32_arrayvec::<4>(opts.provisioning_data.test_exit_token.as_str())?;
let rma_unlock_token =
hex_string_to_u32_arrayvec::<4>(opts.provisioning_data.rma_unlock_token.as_str())?;
let rma_unlock_token = if let Some(token) = &opts.provisioning_data.rma_unlock_token {
hex_string_to_u32_arrayvec::<4>(token.as_str())?
} else {
random_token::<4>()?
};
let token_encrypt_key =
load_rsa_public_key(&opts.provisioning_data.token_encrypt_key_der_file)?;
let encrypted_rma_unlock_token = encrypt_token(&token_encrypt_key, &rma_unlock_token)?;
log::info!(
"Encrypted rma_unlock_token = {}",
hex::encode(&encrypted_rma_unlock_token)
);

// Parse and prepare individualization ujson data payload.
let _ft_individualize_data_in = ManufFtIndividualizeData {
Expand Down
1 change: 1 addition & 0 deletions sw/host/provisioning/orchestrator/configs/skus/sival.hjson
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,5 @@
key_type: "Raw",
key_id: "0xfe584ae7_53790cfd_8601a312_fb32d3c1_b822d112"
}
token_encrypt_key: "sw/device/silicon_creator/manuf/keys/fake/rma_unlock_enc_rsa3072.pub.der"
}
1 change: 1 addition & 0 deletions sw/host/provisioning/orchestrator/src/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ py_binary(
"//sw/device/silicon_creator/manuf/base:sram_ft_individualize_all",
"//sw/device/silicon_creator/manuf/keys/fake:dice_ca.pem",
"//sw/device/silicon_creator/manuf/keys/fake:ext_ca.pem",
"//sw/device/silicon_creator/manuf/keys/fake:rma_unlock_enc_rsa3072.pub.der",
"//sw/device/silicon_creator/manuf/keys/fake:sk.pkcs8.der",
"//sw/host/provisioning/cp",
"//sw/host/provisioning/ft:ft_all",
Expand Down
1 change: 0 additions & 1 deletion sw/host/provisioning/orchestrator/src/orchestrator.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,6 @@ def main(args_in):
)
parser.add_argument(
"--rma-unlock-token",
required=True,
type=parse_hexstring_to_int,
help="Raw RMA token to inject into OTP SECRET2 partition.",
)
Expand Down
3 changes: 3 additions & 0 deletions sw/host/provisioning/orchestrator/src/ot_dut.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,10 @@ def run_ft(self) -> None:
--rom-ext-security-version="0" \
--owner-security-version="0" \
--ca-config={ca_config_file.name} \
--token-encrypt-key-der-file={self.sku_config.token_encrypt_key} \
"""
if self.rma_unlock_token is not None:
cmd += f'--rma-unlock-token="{format_hex(self.rma_unlock_token, width=32)}" \\\n'

# Get user confirmation before running command.
logging.info(f"Running command: {cmd}")
Expand Down
1 change: 1 addition & 0 deletions sw/host/provisioning/orchestrator/src/sku_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class SkuConfig:
target_lc_state: str # valid: must be in ["dev", "prod", "prod_end"]
dice_ca: OrderedDict # valid: see CaConfig
ext_ca: OrderedDict # valid: see CaConfig
token_encrypt_key: str

def __post_init__(self):
# Load CA configs.
Expand Down
3 changes: 3 additions & 0 deletions sw/host/provisioning/util_lib/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ rust_library(
"@crate_index//:anyhow",
"@crate_index//:arrayvec",
"@crate_index//:hex",
"@crate_index//:rand",
"@crate_index//:rsa",
"@crate_index//:tiny-keccak",
"@crate_index//:zerocopy",
],
)
42 changes: 41 additions & 1 deletion sw/host/provisioning/util_lib/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,19 @@
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

use anyhow::Result;
use anyhow::{Context, Result};
use arrayvec::ArrayVec;
use hex::decode;
use rand::rngs::OsRng;
use rand::{CryptoRng, Rng};
use rsa::pkcs1::DecodeRsaPublicKey;
use rsa::pkcs1v15::Pkcs1v15Encrypt;
use rsa::pkcs8::DecodePublicKey;
use rsa::traits::PaddingScheme;
use rsa::RsaPublicKey;
use std::path::Path;
use tiny_keccak::{CShake, Hasher};
use zerocopy::AsBytes;

pub fn hex_string_to_u32_arrayvec<const N: usize>(hex_str: &str) -> Result<ArrayVec<u32, N>> {
let hex_str_no_sep = hex_str.replace('_', "");
Expand Down Expand Up @@ -53,3 +62,34 @@ pub fn hash_lc_token(input: &[u8]) -> Result<ArrayVec<u64, 2>> {
})
.collect::<ArrayVec<u64, 2>>())
}

fn _random_data<RNG>(rng: &mut RNG, data: &mut [u32]) -> Result<()>
where
RNG: Rng + CryptoRng,
{
rng.try_fill(data)?;
Ok(())
}

/// Generates a random token using a CSPRNG.
pub fn random_token<const N: usize>() -> Result<ArrayVec<u32, N>> {
let mut data = [0u32; N];
_random_data(&mut OsRng, &mut data)?;
Ok(ArrayVec::from(data))
}

/// Loads a DER-encoded RSA public key.
pub fn load_rsa_public_key(path: impl AsRef<Path>) -> Result<RsaPublicKey> {
let path = path.as_ref();
match DecodeRsaPublicKey::read_pkcs1_der_file(path)
.with_context(|| format!("read PKCS#1 der {path:?}"))
{
Ok(key) => Ok(key),
Err(e) => Ok(DecodePublicKey::read_public_key_der_file(path)
.with_context(|| format!("read PKCS#8 der {path:?} (previous error: {e})"))?),
}
}

pub fn encrypt_token(pub_key: &RsaPublicKey, token: &[u32]) -> Result<Vec<u8>> {
Ok(Pkcs1v15Encrypt.encrypt(&mut OsRng, pub_key, token.as_bytes())?)
}
Loading