blob: d62aac228492b72e750894b756c77f05c05415b4 [file] [log] [blame]
//! TA functionality related to key generation/import/upgrade.
use crate::{cert, device, AttestationChainInfo};
use alloc::collections::btree_map::Entry;
use alloc::vec::Vec;
use core::{borrow::Borrow, cmp::Ordering, convert::TryFrom};
use der::{Decode, Sequence};
use kmr_common::{
crypto::{self, aes, rsa, KeyMaterial, OpaqueOr},
der_err, get_bool_tag_value, get_opt_tag_value, get_tag_value, keyblob, km_err, tag,
try_to_vec, vec_try_with_capacity, Error, FallibleAllocExt,
};
use kmr_wire::{
keymint::{
AttestationKey, Digest, EcCurve, ErrorCode, HardwareAuthenticatorType, KeyCharacteristics,
KeyCreationResult, KeyFormat, KeyOrigin, KeyParam, KeyPurpose, SecurityLevel,
},
*,
};
use log::{error, warn};
use spki::SubjectPublicKeyInfo;
use x509_cert::ext::pkix::KeyUsages;
/// Maximum size of an attestation challenge value.
const MAX_ATTESTATION_CHALLENGE_LEN: usize = 128;
/// Contents of wrapping key data
///
/// ```asn1
/// SecureKeyWrapper ::= SEQUENCE {
/// version INTEGER, # Value 0
/// encryptedTransportKey OCTET_STRING,
/// initializationVector OCTET_STRING,
/// keyDescription KeyDescription, # See below
/// encryptedKey OCTET_STRING,
/// tag OCTET_STRING,
/// }
/// ```
#[derive(Debug, Clone, Sequence)]
pub struct SecureKeyWrapper<'a> {
pub version: i32,
#[asn1(type = "OCTET STRING")]
pub encrypted_transport_key: &'a [u8],
#[asn1(type = "OCTET STRING")]
pub initialization_vector: &'a [u8],
pub key_description: KeyDescription<'a>,
#[asn1(type = "OCTET STRING")]
pub encrypted_key: &'a [u8],
#[asn1(type = "OCTET STRING")]
pub tag: &'a [u8],
}
const SECURE_KEY_WRAPPER_VERSION: i32 = 0;
/// Contents of key description.
///
/// ``asn1
/// KeyDescription ::= SEQUENCE {
/// keyFormat INTEGER, # Values from KeyFormat enum
/// keyParams AuthorizationList, # See cert.rs
/// }
/// ```
#[derive(Debug, Clone, Sequence)]
pub struct KeyDescription<'a> {
pub key_format: i32,
pub key_params: cert::AuthorizationList<'a>,
}
/// Indication of whether key import has a secure wrapper.
#[derive(Debug, Clone, Copy)]
pub(crate) enum KeyImport {
Wrapped,
NonWrapped,
}
/// Combined information needed for signing a fresh public key.
#[derive(Clone)]
pub(crate) struct SigningInfo<'a> {
pub attestation_info: Option<(&'a [u8], &'a [u8])>, // (challenge, app_id)
pub signing_key: KeyMaterial,
/// ASN.1 DER encoding of subject field from first cert.
pub issuer_subject: Vec<u8>,
/// Cert chain starting with public key for `signing_key`.
pub chain: Vec<keymint::Certificate>,
}
impl<'a> crate::KeyMintTa<'a> {
/// Retrieve the signing information.
pub(crate) fn get_signing_info(
&self,
key_type: device::SigningKeyType,
) -> Result<SigningInfo<'a>, Error> {
// Retrieve the chain and issuer information, which is cached after first retrieval.
let mut attestation_chain_info = self.attestation_chain_info.borrow_mut();
let chain_info = match attestation_chain_info.entry(key_type) {
Entry::Occupied(e) => e.into_mut(),
Entry::Vacant(e) => {
// Retrieve and store the cert chain information (as this is public).
let chain = self.dev.sign_info.cert_chain(key_type)?;
let issuer = cert::extract_subject(
chain.get(0).ok_or_else(|| km_err!(UnknownError, "empty attestation chain"))?,
)?;
e.insert(AttestationChainInfo { chain, issuer })
}
};
// Retrieve the signing key information (which will be dropped when signing is done).
let signing_key = self.dev.sign_info.signing_key(key_type)?;
Ok(SigningInfo {
attestation_info: None,
signing_key,
issuer_subject: chain_info.issuer.clone(),
chain: chain_info.chain.clone(),
})
}
/// Generate an X.509 leaf certificate.
pub(crate) fn generate_cert(
&self,
info: Option<SigningInfo>,
spki: SubjectPublicKeyInfo,
params: &[KeyParam],
chars: &[KeyCharacteristics],
) -> Result<keymint::Certificate, Error> {
// Build and encode key usage extension value
let key_usage_ext_bits = cert::key_usage_extension_bits(params);
let key_usage_ext_val = cert::asn1_der_encode(&key_usage_ext_bits)
.map_err(|e| der_err!(e, "failed to encode KeyUsage {:?}", key_usage_ext_bits))?;
// Build and encode basic constraints extension value, based on the key usage extension
// value
let basic_constraints_ext_val =
if (key_usage_ext_bits.0 & KeyUsages::KeyCertSign).bits().count_ones() != 0 {
let basic_constraints = cert::basic_constraints_ext_value(true);
Some(cert::asn1_der_encode(&basic_constraints).map_err(|e| {
der_err!(e, "failed to encode basic constraints {:?}", basic_constraints)
})?)
} else {
None
};
// Build and encode attestation extension if present
let id_info = self.get_attestation_ids();
let attest_ext_val =
if let Some(SigningInfo { attestation_info: Some((challenge, app_id)), .. }) = &info {
let unique_id = self.calculate_unique_id(app_id, params)?;
let attest_ext = cert::attestation_extension(
challenge,
app_id,
self.hw_info.security_level,
id_info.as_ref().map(|v| v.borrow()),
params,
chars,
&unique_id,
self.boot_info.as_ref().ok_or_else(|| {
km_err!(HardwareNotYetAvailable, "root of trust info not found")
})?,
)?;
Some(
cert::asn1_der_encode(&attest_ext)
.map_err(|e| der_err!(e, "failed to encode attestation extension"))?,
)
} else {
None
};
let tbs_cert = cert::tbs_certificate(
&info,
spki,
&key_usage_ext_val,
basic_constraints_ext_val.as_deref(),
attest_ext_val.as_deref(),
tag::characteristics_at(chars, self.hw_info.security_level)?,
params,
)?;
let tbs_data = cert::asn1_der_encode(&tbs_cert)
.map_err(|e| der_err!(e, "failed to encode tbsCert"))?;
// If key does not have ATTEST_KEY or SIGN purpose, the certificate has empty signature
let sig_data = match info.as_ref() {
Some(info) => self.sign_cert_data(info.signing_key.clone(), tbs_data.as_slice())?,
None => Vec::new(),
};
let cert = cert::certificate(tbs_cert, &sig_data)?;
let cert_data = cert::asn1_der_encode(&cert)
.map_err(|e| der_err!(e, "failed to encode certificate"))?;
Ok(keymint::Certificate { encoded_certificate: cert_data })
}
/// Perform a complete signing operation using default modes.
fn sign_cert_data(&self, signing_key: KeyMaterial, tbs_data: &[u8]) -> Result<Vec<u8>, Error> {
match signing_key {
KeyMaterial::Rsa(key) => {
let mut op = self
.imp
.rsa
.begin_sign(key, rsa::SignMode::Pkcs1_1_5Padding(Digest::Sha256))?;
op.update(tbs_data)?;
op.finish()
}
KeyMaterial::Ec(curve, _, key) => {
let digest = if curve == EcCurve::Curve25519 {
// Ed25519 includes an internal digest and so does not use an external digest.
Digest::None
} else {
Digest::Sha256
};
let mut op = self.imp.ec.begin_sign(key, digest)?;
op.update(tbs_data)?;
op.finish()
}
_ => Err(km_err!(UnknownError, "unexpected cert signing key type")),
}
}
/// Calculate the `UNIQUE_ID` value for the parameters, if needed.
fn calculate_unique_id(&self, app_id: &[u8], params: &[KeyParam]) -> Result<Vec<u8>, Error> {
if !get_bool_tag_value!(params, IncludeUniqueId)? {
return Ok(Vec::new());
}
let creation_datetime =
get_tag_value!(params, CreationDatetime, ErrorCode::InvalidArgument)?;
let rounded_datetime = creation_datetime.ms_since_epoch / 2_592_000_000i64;
let datetime_data = rounded_datetime.to_ne_bytes();
let mut combined_input = vec_try_with_capacity!(datetime_data.len() + app_id.len() + 1)?;
combined_input.extend_from_slice(&datetime_data[..]);
combined_input.extend_from_slice(app_id);
combined_input.push(u8::from(get_bool_tag_value!(params, ResetSinceIdRotation)?));
let hbk = self.dev.keys.unique_id_hbk(self.imp.ckdf)?;
let mut hmac_op = self.imp.hmac.begin(hbk.into(), Digest::Sha256)?;
hmac_op.update(&combined_input)?;
let tag = hmac_op.finish()?;
try_to_vec(&tag[..16])
}
pub(crate) fn generate_key(
&mut self,
params: &[KeyParam],
attestation_key: Option<AttestationKey>,
) -> Result<KeyCreationResult, Error> {
let (key_material, chars) = self.generate_key_material(params)?;
self.finish_keyblob_creation(
params,
attestation_key,
chars,
key_material,
keyblob::SlotPurpose::KeyGeneration,
)
}
pub(crate) fn generate_key_material(
&mut self,
params: &[KeyParam],
) -> Result<(KeyMaterial, Vec<KeyCharacteristics>), Error> {
let (mut chars, keygen_info) = tag::extract_key_gen_characteristics(
self.secure_storage_available(),
params,
self.hw_info.security_level,
)?;
self.add_keymint_tags(&mut chars, KeyOrigin::Generated)?;
let key_material = match keygen_info {
crypto::KeyGenInfo::Aes(variant) => {
self.imp.aes.generate_key(&mut *self.imp.rng, variant, params)?
}
crypto::KeyGenInfo::TripleDes => {
self.imp.des.generate_key(&mut *self.imp.rng, params)?
}
crypto::KeyGenInfo::Hmac(key_size) => {
self.imp.hmac.generate_key(&mut *self.imp.rng, key_size, params)?
}
crypto::KeyGenInfo::Rsa(key_size, pub_exponent) => {
self.imp.rsa.generate_key(&mut *self.imp.rng, key_size, pub_exponent, params)?
}
crypto::KeyGenInfo::NistEc(curve) => {
self.imp.ec.generate_nist_key(&mut *self.imp.rng, curve, params)?
}
crypto::KeyGenInfo::Ed25519 => {
self.imp.ec.generate_ed25519_key(&mut *self.imp.rng, params)?
}
crypto::KeyGenInfo::X25519 => {
self.imp.ec.generate_x25519_key(&mut *self.imp.rng, params)?
}
};
Ok((key_material, chars))
}
pub(crate) fn import_key(
&mut self,
params: &[KeyParam],
key_format: KeyFormat,
key_data: &[u8],
attestation_key: Option<AttestationKey>,
import_type: KeyImport,
) -> Result<KeyCreationResult, Error> {
if !self.in_early_boot && get_bool_tag_value!(params, EarlyBootOnly)? {
return Err(km_err!(EarlyBootEnded, "attempt to use EARLY_BOOT key after early boot"));
}
let (mut chars, key_material) = tag::extract_key_import_characteristics(
&self.imp,
self.secure_storage_available(),
params,
self.hw_info.security_level,
key_format,
key_data,
)?;
match import_type {
KeyImport::NonWrapped => {
self.add_keymint_tags(&mut chars, KeyOrigin::Imported)?;
}
KeyImport::Wrapped => {
self.add_keymint_tags(&mut chars, KeyOrigin::SecurelyImported)?;
}
}
self.finish_keyblob_creation(
params,
attestation_key,
chars,
key_material,
keyblob::SlotPurpose::KeyImport,
)
}
/// Perform common processing for keyblob creation (for both generation and import).
pub fn finish_keyblob_creation(
&mut self,
params: &[KeyParam],
attestation_key: Option<AttestationKey>,
chars: Vec<KeyCharacteristics>,
key_material: KeyMaterial,
purpose: keyblob::SlotPurpose,
) -> Result<KeyCreationResult, Error> {
let keyblob = keyblob::PlaintextKeyBlob {
// Don't include any `SecurityLevel::Keystore` characteristics in the set that is bound
// to the key.
characteristics: chars
.iter()
.filter(|c| c.security_level != SecurityLevel::Keystore)
.cloned()
.collect(),
key_material: key_material.clone(),
};
let attest_keyblob;
let mut certificate_chain = Vec::new();
if let Some(spki) = keyblob.key_material.subject_public_key_info(
&mut Vec::<u8>::new(),
self.imp.ec,
self.imp.rsa,
)? {
// Asymmetric keys return the public key inside an X.509 certificate.
// Need to determine:
// - a key to sign the cert with (may be absent), together with any associated
// cert chain to append
// - whether to include an attestation extension
let attest_challenge = get_opt_tag_value!(params, AttestationChallenge)?;
let signing_info = if let Some(attest_challenge) = attest_challenge {
// Attestation requested.
if attest_challenge.len() > MAX_ATTESTATION_CHALLENGE_LEN {
return Err(km_err!(
InvalidInputLength,
"attestation challenge too large: {} bytes",
attest_challenge.len()
));
}
let attest_app_id = get_opt_tag_value!(params, AttestationApplicationId)?
.ok_or_else(|| {
km_err!(AttestationApplicationIdMissing, "attestation requested")
})?;
let attestation_info: Option<(&[u8], &[u8])> =
Some((attest_challenge, attest_app_id));
if let Some(attest_keyinfo) = attestation_key.as_ref() {
// User-specified attestation key provided.
(attest_keyblob, _) = self.keyblob_parse_decrypt(
&attest_keyinfo.key_blob,
&attest_keyinfo.attest_key_params,
)?;
attest_keyblob
.suitable_for(KeyPurpose::AttestKey, self.hw_info.security_level)?;
if attest_keyinfo.issuer_subject_name.is_empty() {
return Err(km_err!(InvalidArgument, "empty subject name"));
}
Some(SigningInfo {
attestation_info,
signing_key: attest_keyblob.key_material,
issuer_subject: attest_keyinfo.issuer_subject_name.clone(),
chain: Vec::new(),
})
} else {
// Need to use a device key for attestation. Look up the relevant device key and
// chain.
let which_key = match (
get_bool_tag_value!(params, DeviceUniqueAttestation)?,
self.is_strongbox(),
) {
(false, _) => device::SigningKey::Batch,
(true, true) => device::SigningKey::DeviceUnique,
(true, false) => {
return Err(km_err!(
InvalidArgument,
"device unique attestation supported only by Strongbox TA"
))
}
};
// Provide an indication of what's going to be signed, to allow the
// implementation to switch between EC and RSA signing keys if it so chooses.
let algo_hint = match &keyblob.key_material {
crypto::KeyMaterial::Rsa(_) => device::SigningAlgorithm::Rsa,
crypto::KeyMaterial::Ec(_, _, _) => device::SigningAlgorithm::Ec,
_ => return Err(km_err!(InvalidArgument, "unexpected key type!")),
};
let mut info = self
.get_signing_info(device::SigningKeyType { which: which_key, algo_hint })?;
info.attestation_info = attestation_info;
Some(info)
}
} else {
// No attestation challenge, so no attestation.
if attestation_key.is_some() {
return Err(km_err!(
AttestationChallengeMissing,
"got attestation key but no challenge"
));
}
// See if the generated key can self-sign.
let is_signing_key = params.iter().any(|param| {
matches!(
param,
KeyParam::Purpose(KeyPurpose::Sign)
| KeyParam::Purpose(KeyPurpose::AttestKey)
)
});
if is_signing_key {
Some(SigningInfo {
attestation_info: None,
signing_key: key_material,
issuer_subject: try_to_vec(tag::get_cert_subject(params)?)?,
chain: Vec::new(),
})
} else {
None
}
};
// Build the X.509 leaf certificate.
let leaf_cert = self.generate_cert(signing_info.clone(), spki, params, &chars)?;
certificate_chain.try_push(leaf_cert)?;
// Append the rest of the chain.
if let Some(info) = signing_info {
for cert in info.chain {
certificate_chain.try_push(cert)?;
}
}
}
// Now build the keyblob.
let kek_context = self.dev.keys.kek_context()?;
let root_kek = self.root_kek(&kek_context)?;
let hidden = tag::hidden(params, self.root_of_trust()?)?;
let encrypted_keyblob = keyblob::encrypt(
self.hw_info.security_level,
match &mut self.dev.sdd_mgr {
None => None,
Some(mr) => Some(*mr),
},
self.imp.aes,
self.imp.hkdf,
&mut *self.imp.rng,
&root_kek,
&kek_context,
keyblob,
hidden,
purpose,
)?;
let serialized_keyblob = encrypted_keyblob.into_vec()?;
Ok(KeyCreationResult {
key_blob: serialized_keyblob,
key_characteristics: chars,
certificate_chain,
})
}
pub(crate) fn import_wrapped_key(
&mut self,
wrapped_key_data: &[u8],
wrapping_key_blob: &[u8],
masking_key: &[u8],
unwrapping_params: &[KeyParam],
password_sid: i64,
biometric_sid: i64,
) -> Result<KeyCreationResult, Error> {
// Decrypt the wrapping key blob
let (wrapping_key, _) = self.keyblob_parse_decrypt(wrapping_key_blob, unwrapping_params)?;
let keyblob::PlaintextKeyBlob { characteristics, key_material } = wrapping_key;
// Decode the ASN.1 DER encoded `SecureKeyWrapper`.
let mut secure_key_wrapper = SecureKeyWrapper::from_der(wrapped_key_data)
.map_err(|e| der_err!(e, "failed to parse SecureKeyWrapper"))?;
if secure_key_wrapper.version != SECURE_KEY_WRAPPER_VERSION {
return Err(km_err!(InvalidArgument, "invalid version in Secure Key Wrapper."));
}
// Decrypt the masked transport key, using an RSA key. (Only RSA wrapping keys are supported
// by the spec, as RSA is the only algorithm supporting asymmetric decryption.)
let masked_transport_key = match key_material {
KeyMaterial::Rsa(key) => {
// Check the requirements on the wrapping key characterisitcs
let decrypt_mode = tag::check_rsa_wrapping_key_params(
tag::characteristics_at(&characteristics, self.hw_info.security_level)?,
unwrapping_params,
)?;
// Decrypt the masked and encrypted transport key
let mut crypto_op = self.imp.rsa.begin_decrypt(key, decrypt_mode)?;
crypto_op.as_mut().update(secure_key_wrapper.encrypted_transport_key)?;
crypto_op.finish()?
}
_ => {
return Err(km_err!(InvalidArgument, "invalid key algorithm for transport key"));
}
};
if masked_transport_key.len() != masking_key.len() {
return Err(km_err!(
InvalidArgument,
"masked transport key is {} bytes, but masking key is {} bytes",
masked_transport_key.len(),
masking_key.len()
));
}
let unmasked_transport_key: Vec<u8> =
masked_transport_key.iter().zip(masking_key).map(|(x, y)| x ^ y).collect();
let aes_transport_key =
aes::Key::Aes256(unmasked_transport_key.try_into().map_err(|_e| {
km_err!(
InvalidArgument,
"transport key len {} not correct for AES-256 key",
masked_transport_key.len()
)
})?);
// Validate the size of the IV and match the `aes::GcmMode` based on the tag size.
let iv_len = secure_key_wrapper.initialization_vector.len();
if iv_len != aes::GCM_NONCE_SIZE {
return Err(km_err!(
InvalidArgument,
"IV length is of {} bytes, which should be of {} bytes",
iv_len,
aes::GCM_NONCE_SIZE
));
}
let tag_len = secure_key_wrapper.tag.len();
let gcm_mode = match tag_len {
12 => crypto::aes::GcmMode::GcmTag12 {
nonce: secure_key_wrapper.initialization_vector.try_into()
.unwrap(/* safe: len checked */),
},
13 => crypto::aes::GcmMode::GcmTag13 {
nonce: secure_key_wrapper.initialization_vector.try_into()
.unwrap(/* safe: len checked */),
},
14 => crypto::aes::GcmMode::GcmTag14 {
nonce: secure_key_wrapper.initialization_vector.try_into()
.unwrap(/* safe: len checked */),
},
15 => crypto::aes::GcmMode::GcmTag15 {
nonce: secure_key_wrapper.initialization_vector.try_into()
.unwrap(/* safe: len checked */),
},
16 => crypto::aes::GcmMode::GcmTag16 {
nonce: secure_key_wrapper.initialization_vector.try_into()
.unwrap(/* safe: len checked */),
},
v => {
return Err(km_err!(
InvalidMacLength,
"want 12-16 byte tag for AES-GCM not {} bytes",
v
))
}
};
// Decrypt the encrypted key to be imported, using the ASN.1 DER (re-)encoding of the key
// description as the AAD.
let mut op = self.imp.aes.begin_aead(
OpaqueOr::Explicit(aes_transport_key),
gcm_mode,
crypto::SymmetricOperation::Decrypt,
)?;
op.update_aad(
&cert::asn1_der_encode(&secure_key_wrapper.key_description)
.map_err(|e| der_err!(e, "failed to re-encode SecureKeyWrapper"))?,
)?;
let mut imported_key_data = op.update(secure_key_wrapper.encrypted_key)?;
imported_key_data.try_extend_from_slice(&op.update(secure_key_wrapper.tag)?)?;
imported_key_data.try_extend_from_slice(&op.finish()?)?;
// The `Cow::to_mut()` call will not clone, because `from_der()` invokes
// `AuthorizationList::decode_value()` which creates the owned variant.
let imported_key_params: &mut Vec<KeyParam> =
secure_key_wrapper.key_description.key_params.auths.to_mut();
if let Some(secure_id) = get_opt_tag_value!(&*imported_key_params, UserSecureId)? {
let secure_id = *secure_id;
// If both the Password and Fingerprint bits are set in UserSecureId, the password SID
// should be used, because biometric auth tokens contain both password and fingerprint
// SIDs, but password auth tokens only contain the password SID.
if (secure_id & (HardwareAuthenticatorType::Password as u64)
== (HardwareAuthenticatorType::Password as u64))
&& (secure_id & (HardwareAuthenticatorType::Fingerprint as u64)
== (HardwareAuthenticatorType::Fingerprint as u64))
{
imported_key_params
.retain(|key_param| !matches!(key_param, KeyParam::UserSecureId(_)));
imported_key_params.try_push(KeyParam::UserSecureId(password_sid as u64))?;
} else if secure_id & (HardwareAuthenticatorType::Password as u64)
== (HardwareAuthenticatorType::Password as u64)
{
imported_key_params
.retain(|key_param| !matches!(key_param, KeyParam::UserSecureId(_)));
imported_key_params.try_push(KeyParam::UserSecureId(password_sid as u64))?;
} else if secure_id & (HardwareAuthenticatorType::Fingerprint as u64)
== (HardwareAuthenticatorType::Fingerprint as u64)
{
imported_key_params
.retain(|key_param| !matches!(key_param, KeyParam::UserSecureId(_)));
imported_key_params.try_push(KeyParam::UserSecureId(biometric_sid as u64))?;
}
};
self.import_key(
imported_key_params,
KeyFormat::try_from(secure_key_wrapper.key_description.key_format).map_err(|_e| {
km_err!(
UnsupportedKeyFormat,
"could not convert the provided keyformat {}",
secure_key_wrapper.key_description.key_format
)
})?,
&imported_key_data,
None,
KeyImport::Wrapped,
)
}
pub(crate) fn upgrade_key(
&mut self,
keyblob_to_upgrade: &[u8],
upgrade_params: Vec<KeyParam>,
) -> Result<Vec<u8>, Error> {
let (mut keyblob, mut modified) =
match self.keyblob_parse_decrypt_backlevel(keyblob_to_upgrade, &upgrade_params) {
Ok(v) => (v.0, false),
Err(Error::Hal(ErrorCode::KeyRequiresUpgrade, _)) => {
// Because `keyblob_parse_decrypt_backlevel` explicitly allows back-level
// versioned keys, a `KeyRequiresUpgrade` error indicates that the keyblob looks
// to be in legacy format. Try to convert it.
let legacy_handler = self
.dev
.legacy_key
.as_mut()
.ok_or_else(|| km_err!(UnknownError, "no legacy key handler"))?;
(
legacy_handler.convert_legacy_key(
keyblob_to_upgrade,
&upgrade_params,
self.boot_info
.as_ref()
.ok_or_else(|| km_err!(HardwareNotYetAvailable, "no boot info"))?,
self.hw_info.security_level,
)?,
// Force the emission of a new keyblob even if versions are the same.
true,
)
}
Err(e) => return Err(e),
};
fn upgrade(v: &mut u32, curr: u32, name: &str) -> Result<bool, Error> {
match (*v).cmp(&curr) {
Ordering::Less => {
*v = curr;
Ok(true)
}
Ordering::Equal => Ok(false),
Ordering::Greater => {
error!("refusing to downgrade {} from {} to {}", name, v, curr);
Err(km_err!(
InvalidArgument,
"keyblob with future {} {} (current {})",
name,
v,
curr
))
}
}
}
for chars in &mut keyblob.characteristics {
if chars.security_level != self.hw_info.security_level {
continue;
}
for param in &mut chars.authorizations {
match param {
KeyParam::OsVersion(v) => {
if let Some(hal_info) = &self.hal_info {
if hal_info.os_version == 0 {
// Special case: upgrades to OS version zero are always allowed.
warn!("forcing upgrade to OS version 0");
modified |= *v != 0;
*v = 0;
} else {
modified |= upgrade(v, hal_info.os_version, "OS version")?;
}
} else {
error!("OS version not available, can't upgrade from {}", v);
}
}
KeyParam::OsPatchlevel(v) => {
if let Some(hal_info) = &self.hal_info {
modified |= upgrade(v, hal_info.os_patchlevel, "OS patchlevel")?;
} else {
error!("OS patchlevel not available, can't upgrade from {}", v);
}
}
KeyParam::VendorPatchlevel(v) => {
if let Some(hal_info) = &self.hal_info {
modified |=
upgrade(v, hal_info.vendor_patchlevel, "vendor patchlevel")?;
} else {
error!("vendor patchlevel not available, can't upgrade from {}", v);
}
}
KeyParam::BootPatchlevel(v) => {
if let Some(boot_info) = &self.boot_info {
modified |= upgrade(v, boot_info.boot_patchlevel, "boot patchlevel")?;
} else {
error!("boot patchlevel not available, can't upgrade from {}", v);
}
}
_ => {}
}
}
}
if !modified {
// No upgrade needed, return empty data to indicate existing keyblob can still be used.
return Ok(Vec::new());
}
// Now re-build the keyblob. Use a potentially fresh key encryption key, and potentially a
// new secure deletion secret slot. (The old slot will be released when Keystore performs
// the corresponding `deleteKey` operation on the old keyblob.
let kek_context = self.dev.keys.kek_context()?;
let root_kek = self.root_kek(&kek_context)?;
let hidden = tag::hidden(&upgrade_params, self.root_of_trust()?)?;
let encrypted_keyblob = keyblob::encrypt(
self.hw_info.security_level,
match &mut self.dev.sdd_mgr {
None => None,
Some(mr) => Some(*mr),
},
self.imp.aes,
self.imp.hkdf,
&mut *self.imp.rng,
&root_kek,
&kek_context,
keyblob,
hidden,
keyblob::SlotPurpose::KeyUpgrade,
)?;
Ok(encrypted_keyblob.into_vec()?)
}
}