/* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ //! Implementation of the `IOpaqueKey` AIDL interface. It is used as a handle to key material use alloc::collections::btree_map::BTreeMap; use android_hardware_security_see_hwcrypto::aidl::android::hardware::security::see::hwcrypto::types::{ AesCipherMode::AesCipherMode, KeyLifetime::KeyLifetime, KeyPermissions::KeyPermissions, KeyType::KeyType, KeyUse::KeyUse, OpaqueKeyToken::OpaqueKeyToken, OperationType::OperationType, ProtectionId::ProtectionId, SymmetricCryptoParameters::SymmetricCryptoParameters, SymmetricOperation::SymmetricOperation, }; use android_hardware_security_see_hwcrypto::aidl::android::hardware::security::see::hwcrypto::{ IHwCryptoKey::{ DeviceKeyId::DeviceKeyId, DiceBoundDerivationKey::DiceBoundDerivationKey, DiceBoundKeyResult::DiceBoundKeyResult, }, IOpaqueKey::{BnOpaqueKey, IOpaqueKey}, KeyPolicy::KeyPolicy, }; use android_hardware_security_see_hwcrypto::binder; use binder::binder_impl::Binder; use ciborium::Value; use core::fmt; use coset::{AsCborValue, CborSerializable, CoseError}; use hwcryptohal_common::{ aidl_enum_wrapper, cose_enum_gen, err::HwCryptoError, hwcrypto_err, policy::{ self, cbor_policy_to_aidl, cbor_serialize_key_policy, KeyLifetimeSerializable, KeyTypeSerializable, KeyUseSerializable, }, }; use kmr_common::{ crypto::{self, Aes, CurveType, Hkdf, Hmac, KeyMaterial, OpaqueOr, Rng}, explicit, FallibleAllocExt, }; use kmr_wire::{keymint::EcCurve, AsCborValue as _}; use std::sync::{Mutex, OnceLock}; use tipc::Uuid; use crate::crypto_provider; use crate::helpers; use crate::hwcrypto_device_key::HwCryptoKey; use crate::service_encryption_key::{EncryptedContent, EncryptionHeader, EncryptionHeaderKey}; /// Number of bytes of unique value used to check if a key was created on current HWCrypto boot. const UNIQUE_VALUE_SIZEOF: usize = 32; const SEALING_KEY_DERIVATION_HMAC_256_CTX: &[u8] = b"SEALING_KEY_DERIVATION_HMAC_256_CTX"; const HW_CRYPTO_WRAP_KEY_HMAC_256_CTX: &[u8] = b"HW_CRYPTO_WRAP_KEY_HMAC_256_CTX"; /// Struct to wrap boot unique counter. It is used to tag objects to the current boot. #[derive(Clone)] struct BootUniqueValue([u8; UNIQUE_VALUE_SIZEOF]); impl fmt::Debug for BootUniqueValue { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "[BootUniqueValue size: {}; data redacted]", UNIQUE_VALUE_SIZEOF) } } impl PartialEq for BootUniqueValue { fn eq(&self, other: &Self) -> bool { openssl::memcmp::eq(&self.0, &other.0) } } impl Eq for BootUniqueValue {} impl BootUniqueValue { fn new() -> Result { get_boot_unique_value() } } // Boot unique value is lazily initialized on the first call to retrieve it static BOOT_UNIQUE_VALUE: OnceLock = OnceLock::new(); /// Retrieves boot unique value used to check if the key material was created on this boot. It /// lazily initializes it. fn get_boot_unique_value() -> Result { // The function returns a `Result` even if it is currently infallible to allow replacing its // current implementation with one that can fail when trying to retrieve a random number. // If the RNG changes to a fallible one we could use `get_or_try_init`. let boot_unique_value = BOOT_UNIQUE_VALUE.get_or_init(|| { let mut rng = crypto_provider::RngImpl::default(); let mut new_boot_unique_value = BootUniqueValue([0u8; UNIQUE_VALUE_SIZEOF]); rng.fill_bytes(&mut new_boot_unique_value.0[..]); new_boot_unique_value }); Ok(boot_unique_value.clone()) } #[derive(Copy, Clone)] pub(crate) enum HkdfOperationType { DiceBoundDerivation = 1, ClearKeyDerivation = 3, OpaqueKeyDerivation = 4, InternalSealingKeyDerivation = 5, } pub(crate) struct DerivationContext { context_components: Vec, } impl DerivationContext { pub(crate) fn new(op_type: HkdfOperationType) -> Result { let mut context_components = Vec::new(); context_components.try_reserve(1)?; context_components.push(Value::Integer((op_type as u8).into())); Ok(Self { context_components }) } pub(crate) fn add_binary_string(&mut self, binary_string: &[u8]) -> Result<(), HwCryptoError> { self.context_components.try_reserve(1)?; let mut context = Vec::new(); context.try_reserve(binary_string.len())?; context.extend_from_slice(binary_string); self.context_components.push(Value::Bytes(context)); Ok(()) } pub(crate) fn add_owned_binary_string( &mut self, binary_string: Vec, ) -> Result<(), HwCryptoError> { self.context_components.try_reserve(1)?; self.context_components.push(Value::Bytes(binary_string)); Ok(()) } pub(crate) fn add_unsigned_integer(&mut self, value: u64) -> Result<(), HwCryptoError> { self.context_components.try_reserve(1)?; self.context_components.push(Value::Integer(value.into())); Ok(()) } pub(crate) fn create_key_derivation_context(self) -> Result, HwCryptoError> { let context = Value::Array(self.context_components); Ok(context.to_vec()?) } } #[derive(Debug)] struct KeyHeaderPolicy { key_lifetime: KeyLifetimeSerializable, key_permissions: Vec, key_usage: KeyUseSerializable, key_type: KeyTypeSerializable, management_key: bool, } impl KeyHeaderPolicy { fn new(policy: &KeyPolicy) -> Result { let mut key_permissions = Vec::new(); key_permissions.try_extend_from_slice(&policy.keyPermissions[..])?; Ok(Self { key_lifetime: KeyLifetimeSerializable(policy.keyLifetime), key_permissions, key_usage: KeyUseSerializable(policy.usage), key_type: KeyTypeSerializable(policy.keyType), management_key: policy.keyManagementKey, }) } fn get_policy(&self) -> Result { let mut key_permissions = Vec::new(); key_permissions.try_extend_from_slice(&self.key_permissions[..])?; Ok(KeyPolicy { usage: self.key_usage.0, keyLifetime: self.key_lifetime.0, keyPermissions: key_permissions, keyType: self.key_type.0, keyManagementKey: self.management_key, }) } fn try_clone(&self) -> Result { let mut key_permissions = Vec::new(); key_permissions.try_extend_from_slice(&self.key_permissions[..])?; Ok(Self { key_lifetime: self.key_lifetime, key_permissions, key_usage: self.key_usage, key_type: self.key_type, management_key: self.management_key, }) } } fn check_protection_id_settings( protection_id: &ProtectionIdSerializable, settings: &ProtectionSetting, ) -> Result { match protection_id.0 { ProtectionId::WIDEVINE_OUTPUT_BUFFER => { // For Widevine buffers we cannot create a key that can read into this area Ok(!settings.read_protection) } _ => Err(hwcrypto_err!(BAD_PARAMETER, "unsupported protection_id {:?}", protection_id,)), } } #[derive(Debug)] struct KeyHeaderMetadata { expiration_time: Option, protection_id_settings: BTreeMap, } impl KeyHeaderMetadata { fn new() -> Self { Self { expiration_time: None, protection_id_settings: BTreeMap::new() } } // While the current metadata definition wouldn't fail on this operation, we are doing this // division to add an element to metadata that could fail while ying to clone fn try_clone(&self) -> Result { let mut protection_id_settings = BTreeMap::new(); protection_id_settings.extend(self.protection_id_settings.iter()); Ok(Self { expiration_time: None, protection_id_settings }) } fn add_protection_id( &mut self, protection_id: ProtectionId, allowed_operations: &[OperationType], ) -> Result<(), HwCryptoError> { if allowed_operations.is_empty() { return Err(hwcrypto_err!( BAD_PARAMETER, "didn't receive any allowed operations for add_protection_id", )); } let protection_id = ProtectionIdSerializable::try_from(protection_id)?; if !self.protection_id_settings.contains_key(&protection_id) { return Err(hwcrypto_err!( BAD_PARAMETER, "settings for protection id {:?} have already been set", protection_id )); } let mut protection_setting = ProtectionSetting { write_protection: false, read_protection: false }; for operation in allowed_operations { match *operation { OperationType::READ => protection_setting.read_protection = true, OperationType::WRITE => protection_setting.write_protection = true, _ => { return Err(hwcrypto_err!( BAD_PARAMETER, "received unsupported OperationType {:?}", operation )) } } } if !check_protection_id_settings(&protection_id, &protection_setting)? { return Err(hwcrypto_err!( BAD_PARAMETER, "unsupported setting for permissions {:?}: {:?}", protection_id, protection_setting )); } self.protection_id_settings.insert(protection_id, protection_setting); Ok(()) } fn get_metadata_as_cbor(&self) -> Result { let mut cbor_map = Vec::<(Value, Value)>::new(); cbor_map.try_reserve(2)?; // Adding expiration time let expiration_time_value = if let Some(expiration_time) = self.expiration_time { Value::Integer(expiration_time.into()) } else { Value::Null }; let key = Value::Integer((KeyMetadataCoseLabels::KeyExpirationPeriod as i64).into()); cbor_map.push((key, expiration_time_value)); // Adding protection IDs let mut protection_id_cbor_map = Vec::<(Value, Value)>::new(); protection_id_cbor_map.try_reserve(self.protection_id_settings.len())?; for (protection_id, protection_id_setting) in &self.protection_id_settings { let protection_id_key = ciborium::Value::Integer((*protection_id).into()); protection_id_cbor_map.push(( protection_id_key, protection_id_setting.to_cbor_value().map_err(|_| { hwcrypto_err!( BAD_PARAMETER, "couldn't get cbor representation of protection id setting" ) })?, )) } let key = Value::Integer((KeyMetadataCoseLabels::ProtectionIdSettings as i64).into()); cbor_map.push((key, ciborium::Value::Map(protection_id_cbor_map))); Ok(Value::Map(cbor_map)) } fn set_metadata_from_cbor(&mut self, metadata_as_cbor: Value) -> Result<(), HwCryptoError> { let metadata = metadata_as_cbor .into_map() .map_err(|_| hwcrypto_err!(BAD_PARAMETER, "received cbor wasn't a map"))?; let mut protection_id_settings: Option< BTreeMap, > = None; let mut expiration_time: Option> = None; for (map_key, map_val) in metadata { let key = map_key .into_integer() .map_err(|_| hwcrypto_err!(BAD_PARAMETER, "received map key wasn't an integer"))?; match key.try_into()? { KeyMetadataCoseLabels::KeyExpirationPeriod => { expiration_time = if map_val.is_null() { Some(None) } else { let value = map_val .into_integer() .map_err(|_| { hwcrypto_err!(BAD_PARAMETER, "protection id key wasn't an integer") })? .try_into() .map_err(|_| { hwcrypto_err!(BAD_PARAMETER, "couldn't decode expiration time") })?; Some(Some(value)) } } KeyMetadataCoseLabels::ProtectionIdSettings => { let mut settings = BTreeMap::new(); for (protection_id, protection_setting) in map_val .into_map() .map_err(|_| hwcrypto_err!(BAD_PARAMETER, "received cbor wasn't a map"))? { //settings.try_reserve(1).map_err(|_| CoseError::EncodeFailed)?; let protection_id: ProtectionIdSerializable = protection_id .into_integer() .map_err(|_| { hwcrypto_err!(BAD_PARAMETER, "protection id key wasn't an integer") })? .try_into() .map_err(|_| { hwcrypto_err!(BAD_PARAMETER, "couldn't decode protection ID") })?; let protection_setting = ProtectionSetting::from_cbor_value(protection_setting)?; if settings.contains_key(&protection_id) { return Err(hwcrypto_err!( BAD_PARAMETER, "received duplicated protection ID entry" )); } settings.insert(protection_id, protection_setting); } protection_id_settings = Some(settings); } } } self.expiration_time = expiration_time .ok_or(hwcrypto_err!(BAD_PARAMETER, "didn't find expiration time on metadata"))?; self.protection_id_settings = protection_id_settings.ok_or(hwcrypto_err!( BAD_PARAMETER, "didn't find protection_ id settings on metadata" ))?; Ok(()) } } /// Header for a `ClearKey` which contains the key policy along with some data needed to manipulate /// the key. #[derive(Debug)] pub(crate) struct KeyHeader { boot_unique_value: BootUniqueValue, key_policy: KeyHeaderPolicy, key_metadata: Mutex, } impl KeyHeader { fn new(policy: &KeyPolicy) -> Result { let boot_unique_value = BootUniqueValue::new()?; Self::new_with_boot_value(policy, boot_unique_value) } fn new_with_boot_value( policy: &KeyPolicy, boot_unique_value: BootUniqueValue, ) -> Result { let key_policy = KeyHeaderPolicy::new(policy)?; let key_metadata = Mutex::new(KeyHeaderMetadata::new()); Ok(Self { boot_unique_value, key_policy, key_metadata }) } fn get_policy(&self) -> Result { self.key_policy.get_policy() } fn try_clone(&self) -> Result { let key_policy = self.key_policy.try_clone()?; let key_metadata = self.key_metadata.lock()?.try_clone()?; Ok(Self { boot_unique_value: self.boot_unique_value.clone(), key_policy, key_metadata: Mutex::new(key_metadata), }) } fn get_metadata_as_cbor(&self) -> Result { self.key_metadata.lock()?.get_metadata_as_cbor() } fn set_metadata_from_cbor(&mut self, metadata_as_cbor: Value) -> Result<(), HwCryptoError> { self.key_metadata.lock()?.set_metadata_from_cbor(metadata_as_cbor) } } cose_enum_gen! { enum OpaqueKeyCoseLabels { KeyMaterial = -66000, KeyPolicy = -66001, BootValue = -66002, KeyMetadata = -66003, } } cose_enum_gen! { enum ProtectionSettingsCoseLabels { WriteProtection = -67000, ReadProtection = -67001, } } cose_enum_gen! { enum KeyMetadataCoseLabels { KeyExpirationPeriod = -68000, ProtectionIdSettings = -68001, } } aidl_enum_wrapper! { aidl_name: ProtectionId, wrapper_name: ProtectionIdSerializable, fields: [WIDEVINE_OUTPUT_BUFFER] } #[derive(Debug, Copy, Clone)] struct ProtectionSetting { write_protection: bool, read_protection: bool, } impl AsCborValue for ProtectionSetting { fn to_cbor_value(self) -> Result { let mut cbor_map = Vec::<(Value, Value)>::new(); cbor_map.try_reserve(2).map_err(|_| CoseError::EncodeFailed)?; let key = Value::Integer((ProtectionSettingsCoseLabels::WriteProtection as i64).into()); let value = Value::Bool(self.write_protection.into()); cbor_map.push((key, value)); let key = Value::Integer((ProtectionSettingsCoseLabels::ReadProtection as i64).into()); let value = Value::Bool(self.read_protection.into()); cbor_map.push((key, value)); Ok(Value::Map(cbor_map)) } fn from_cbor_value(value: Value) -> Result { let opaque_key_map = value.into_map().map_err(|_| CoseError::ExtraneousData)?; if opaque_key_map.len() != 2 { return Err(CoseError::ExtraneousData); } let mut write_protection: Option = None; let mut read_protection: Option = None; for (map_key, map_val) in opaque_key_map { match map_key { Value::Integer(key) => match key.try_into()? { ProtectionSettingsCoseLabels::WriteProtection => { write_protection = Some(map_val.as_bool().ok_or(CoseError::EncodeFailed)?); } ProtectionSettingsCoseLabels::ReadProtection => { read_protection = Some(map_val.as_bool().ok_or(CoseError::EncodeFailed)?); } }, _ => return Err(CoseError::ExtraneousData), } } let write_protection = write_protection.ok_or(CoseError::EncodeFailed)?; let read_protection = read_protection.ok_or(CoseError::EncodeFailed)?; Ok(Self { write_protection, read_protection }) } } fn get_dice_sealing_key_derivation_context() -> Result, HwCryptoError> { let mut context = Vec::::new(); context.try_reserve(SEALING_KEY_DERIVATION_HMAC_256_CTX.len() + UNIQUE_VALUE_SIZEOF)?; context.extend_from_slice(SEALING_KEY_DERIVATION_HMAC_256_CTX); context.extend_from_slice(&get_boot_unique_value()?.0); Ok(context) } /// `IOpaqueKey` implementation. pub struct OpaqueKey { pub(crate) key_header: KeyHeader, pub(crate) key_material: KeyMaterial, pub(crate) key_in_owner_control: bool, } impl From for binder::Strong { fn from(value: OpaqueKey) -> binder::Strong { BnOpaqueKey::new_binder(value, binder::BinderFeatures::default()) } } impl TryFrom<&binder::Strong> for OpaqueKey { type Error = HwCryptoError; fn try_from(value: &binder::Strong) -> Result { let binder = value.as_binder(); let remote = binder.is_remote(); if remote { return Err(hwcrypto_err!(UNSUPPORTED, "binder is not local")); } let binder_key: Binder = binder.try_into().expect("because binder is local this should not fail"); let opaque_key_material = (*binder_key).downcast_binder::(); match opaque_key_material { Some(key) => key.try_clone(), None => Err(hwcrypto_err!(UNSUPPORTED, "couldn't cast back key")), } } } impl AsCborValue for OpaqueKey { fn to_cbor_value(self) -> Result { let mut cbor_map = Vec::<(Value, Value)>::new(); cbor_map.try_reserve(4).map_err(|_| CoseError::EncodeFailed)?; let key = Value::Integer((OpaqueKeyCoseLabels::KeyPolicy as i64).into()); let key_policy = self.getKeyPolicy().map_err(|_| CoseError::EncodeFailed)?; let cbor_key_policy = cbor_serialize_key_policy(&key_policy).map_err(|_| CoseError::EncodeFailed)?; cbor_map.push((key, Value::Bytes(cbor_key_policy))); let key = Value::Integer((OpaqueKeyCoseLabels::KeyMaterial as i64).into()); cbor_map .push((key, self.key_material.to_cbor_value().map_err(|_| CoseError::EncodeFailed)?)); let key = Value::Integer((OpaqueKeyCoseLabels::BootValue as i64).into()); let mut boot_value = Vec::new(); boot_value .try_reserve(self.key_header.boot_unique_value.0.len()) .map_err(|_| CoseError::EncodeFailed)?; boot_value.extend_from_slice(&self.key_header.boot_unique_value.0); cbor_map.push((key, Value::Bytes(boot_value))); let key = Value::Integer((OpaqueKeyCoseLabels::KeyMetadata as i64).into()); cbor_map.push(( key, self.key_header.get_metadata_as_cbor().map_err(|_| CoseError::EncodeFailed)?, )); Ok(Value::Map(cbor_map)) } fn from_cbor_value(value: Value) -> Result { let opaque_key_map = value.into_map().map_err(|_| CoseError::ExtraneousData)?; if opaque_key_map.len() != 4 { return Err(CoseError::ExtraneousData); } let mut key_material: Option = None; let mut key_policy: Option = None; let mut boot_value: Option = None; let mut key_metadata: Option = None; for (map_key, map_val) in opaque_key_map { match map_key { Value::Integer(key) => match key.try_into()? { OpaqueKeyCoseLabels::KeyMaterial => { key_material = Some( KeyMaterial::from_cbor_value(map_val) .map_err(|_| CoseError::EncodeFailed)?, ) } OpaqueKeyCoseLabels::KeyPolicy => { let policy_bytes = map_val.as_bytes().ok_or(CoseError::EncodeFailed)?; key_policy = Some( cbor_policy_to_aidl(policy_bytes.as_slice()) .map_err(|_| CoseError::EncodeFailed)?, ) } OpaqueKeyCoseLabels::BootValue => { let boot_value_bytes = map_val.as_bytes().ok_or(CoseError::EncodeFailed)?; boot_value = Some(BootUniqueValue( (boot_value_bytes.clone()) .try_into() .map_err(|_| CoseError::EncodeFailed)?, )) } OpaqueKeyCoseLabels::KeyMetadata => key_metadata = Some(map_val), }, _ => return Err(CoseError::ExtraneousData), } } let key_material = key_material.ok_or(CoseError::EncodeFailed)?; let key_policy = key_policy.ok_or(CoseError::EncodeFailed)?; let boot_value = boot_value.ok_or(CoseError::EncodeFailed)?; let mut key_header = KeyHeader::new_with_boot_value(&key_policy, boot_value) .map_err(|_| CoseError::EncodeFailed)?; let key_metadata = key_metadata.ok_or(CoseError::EncodeFailed)?; key_header.set_metadata_from_cbor(key_metadata).map_err(|_| CoseError::EncodeFailed)?; Ok(OpaqueKey { key_material, key_header, key_in_owner_control: false }) } } impl OpaqueKey { pub(crate) fn new_binder( policy: &KeyPolicy, key_material: KeyMaterial, _connection_info: Uuid, ) -> binder::Result> { let key_header = KeyHeader::new(policy)?; check_key_material_with_policy(&key_material, policy)?; let opaque_key = OpaqueKey { key_header, key_material, key_in_owner_control: true }; let opaque_keybinder = BnOpaqueKey::new_binder(opaque_key, binder::BinderFeatures::default()); Ok(opaque_keybinder) } fn check_clear_import_policy(policy: &KeyPolicy) -> Result<(), HwCryptoError> { if policy.keyLifetime != KeyLifetime::PORTABLE { return Err(hwcrypto_err!( BAD_PARAMETER, "imported clear keys should have a PORTABLE lifetime" )); } Ok(()) } pub(crate) fn import_key_material( policy: &KeyPolicy, key_material: KeyMaterial, connection_info: Uuid, ) -> binder::Result> { check_key_material_with_policy(&key_material, policy)?; Self::check_clear_import_policy(policy)?; Self::new_binder(policy, key_material, connection_info) } fn check_ownership(&self) -> bool { self.key_in_owner_control } // Create a key token sealed using the receiver DICE policy. This means that only the // intended token receiver can import this token. The token has 2 levels of encryption, // the outer layer is provided by a device key bounded to the HwCrypto service and the // outer layer is generated using the receiver DICE policy. fn create_token(&self, sealing_dice_policy: &[u8]) -> Result, HwCryptoError> { if !self.check_ownership() { // We haven't created this key, so we cannot export it // TODO: Change the error type to UNAUTORIZED return Err(hwcrypto_err!(GENERIC_ERROR, "only the owner of a key can export it")); } let key: OpaqueKey = self.try_clone()?; let token_creator = EncryptionHeader::generate(EncryptedContent::KeyMaterial)?; // This is a temporary workaround to create a DICE bound key because we will move to // using DICE policies and the AuthMgr instead of UUIDs. let hw_device_key = HwCryptoKey { uuid: Uuid::new_from_string("ffffffff-ffff-ffff-ffff-ffffffffffff") .expect("shouldn't happen, string can be parsed to uuid"), }; // Create a DICE key bound to the receiver policy. let DiceBoundKeyResult { diceBoundKey: sealing_dice_key, dicePolicyWasCurrent: _ } = hw_device_key.derive_dice_policy_bound_key( &DiceBoundDerivationKey::KeyId(DeviceKeyId::DEVICE_BOUND_KEY), sealing_dice_policy, false, )?; let sealing_dice_key: OpaqueKey = sealing_dice_key .as_ref() .ok_or(hwcrypto_err!(GENERIC_ERROR, "shouldn't happen, sealing key is local"))? .try_into()?; let context = get_dice_sealing_key_derivation_context()?; let sealing_key = sealing_dice_key .derive_internal_sealing_key(&context, get_key_size_in_bytes(&KeyType::HMAC_SHA256)?)?; // Inner encryption using the DICE policy bound key let inner_content = token_creator.encrypt_content_service_encryption_key( EncryptionHeaderKey::ProvidedHkdfKey(sealing_key), key, )?; // External encryption using the HwCrypto service key let token_creator = EncryptionHeader::generate(EncryptedContent::WrappedKeyMaterial)?; let content = token_creator.encrypt_content_service_encryption_key( EncryptionHeaderKey::KeyGenerationContext(HW_CRYPTO_WRAP_KEY_HMAC_256_CTX), Value::Bytes(inner_content), )?; Ok(content) } pub(crate) fn import_token( key_token: &[u8], sealing_dice_key: OpaqueKey, _connection_information: Uuid, ) -> Result { // External encryption layer used a HwCrypto service device key let (_, content) = EncryptionHeader::decrypt_content_service_encryption_key( key_token, EncryptionHeaderKey::KeyGenerationContext(HW_CRYPTO_WRAP_KEY_HMAC_256_CTX), EncryptedContent::WrappedKeyMaterial, )?; let context = get_dice_sealing_key_derivation_context()?; // Preparing internal encryption DICE policy bound key let sealing_key = sealing_dice_key .derive_internal_sealing_key(&context, get_key_size_in_bytes(&KeyType::HMAC_SHA256)?)?; let cbor_bytes = Value::from_slice(content.as_slice())?; let inner_content = cbor_bytes.as_bytes().ok_or(hwcrypto_err!( GENERIC_ERROR, "shouldn't happen, inner content was encrypted by us" ))?; let (_, inner_key) = EncryptionHeader::decrypt_content_service_encryption_key( &inner_content, EncryptionHeaderKey::ProvidedHkdfKey(sealing_key), EncryptedContent::KeyMaterial, )?; let opaque_key = Self::from_cbor_value(Value::from_slice(inner_key.as_slice())?)?; Ok(opaque_key) } #[allow(unused)] pub(crate) fn generate_opaque_key( policy: &KeyPolicy, connection_info: Uuid, ) -> binder::Result> { let key_material = generate_key_material(&policy.keyType, None)?; OpaqueKey::new_binder(policy, key_material, connection_info) } fn try_clone(&self) -> Result { let key_header = self.key_header.try_clone()?; let key_material = self.key_material.clone(); Ok(OpaqueKey { key_header, key_material, key_in_owner_control: self.key_in_owner_control }) } pub(crate) fn new_opaque_key_from_raw_bytes( policy: &KeyPolicy, key_material: Vec, connection_info: Uuid, ) -> binder::Result> { let key_material = generate_key_material(&policy.keyType, Some(key_material))?; OpaqueKey::new_binder(policy, key_material, connection_info) } pub(crate) fn check_key_derivation_parameters( &self, policy: &KeyPolicy, ) -> Result<(), HwCryptoError> { // Check that we are trying to derive a supported key type check_type_derived_key(policy.keyType)?; // Check that the requested lifetime is compatible with the provided key lifetime if !self.derivation_allowed_lifetime(policy.keyLifetime)? { return Err(hwcrypto_err!( BAD_PARAMETER, "requested lifetime cannot be used {:?}", &policy.keyLifetime )); } // Check that the derivation key can be used to derive keys (KeyPermissions/KeyPolicies) self.key_can_be_used_for_derivation() } // All key derivation functions that uses an `OpaqueKey` as key material should use this // function. If the key derivation do not fit one of the current use cases defined in // `HkdfOperationType`, a new enum value should be added to `HkdfOperationType` for the use // case. fn derive_raw_key_material( &self, context: DerivationContext, derived_key_size: usize, ) -> Result, HwCryptoError> { let context_with_op_type = context.create_key_derivation_context()?; match &self.key_material { KeyMaterial::Hmac(key) => { let hkdf = crypto_provider::HmacImpl; let explicit_key = explicit!(key).map_err(|_| { hwcrypto_err!(BAD_PARAMETER, "only explicit HMAC keys supported") })?; let raw_key = hkdf .hkdf(&[], &explicit_key.0, context_with_op_type.as_slice(), derived_key_size) .map_err(|e| hwcrypto_err!(GENERIC_ERROR, "couldn't derive key {:?}", e))?; Ok(raw_key) } _ => Err(hwcrypto_err!(BAD_PARAMETER, "only HMAC keys supported")), } } fn derive_clear_key_from_derivation_context( &self, mut op_context: DerivationContext, context: &[u8], derived_key_size: usize, ) -> Result, HwCryptoError> { op_context.add_unsigned_integer(derived_key_size as u64)?; op_context.add_binary_string(context)?; self.derive_raw_key_material(op_context, derived_key_size) } pub(crate) fn derive_internal_sealing_key( &self, context: &[u8], derived_key_size: usize, ) -> Result, HwCryptoError> { let op_context = DerivationContext::new(HkdfOperationType::InternalSealingKeyDerivation)?; self.derive_clear_key_from_derivation_context(op_context, context, derived_key_size) } pub(crate) fn derive_clear_key_material( &self, context: &[u8], derived_key_size: usize, ) -> Result, HwCryptoError> { let op_context = DerivationContext::new(HkdfOperationType::ClearKeyDerivation)?; self.derive_clear_key_from_derivation_context(op_context, context, derived_key_size) } pub(crate) fn derive_opaque_key( &self, policy: &[u8], context: &[u8], connection_info: Uuid, ) -> binder::Result> { let aidl_policy = policy::cbor_policy_to_aidl(policy)?; self.check_key_derivation_parameters(&aidl_policy)?; let derived_key_size = get_key_size_in_bytes(&aidl_policy.keyType)?; let mut op_context = DerivationContext::new(HkdfOperationType::OpaqueKeyDerivation)?; op_context.add_binary_string(policy)?; op_context.add_binary_string(context)?; let raw_key_material = self.derive_raw_key_material(op_context, derived_key_size)?; Self::new_opaque_key_from_raw_bytes(&aidl_policy, raw_key_material, connection_info) } fn derivation_allowed_lifetime( &self, derived_key_lifetime: KeyLifetime, ) -> Result { validate_lifetime(self.key_header.key_policy.key_lifetime.0)?; validate_lifetime(derived_key_lifetime)?; match self.key_header.key_policy.key_lifetime.0 { //ephemeral keys can be used to derive/wrap any other key KeyLifetime::EPHEMERAL => Ok(true), KeyLifetime::HARDWARE => { // Hardware keys cannot be used to derive/wrap ephemeral keys if derived_key_lifetime.0 == KeyLifetime::EPHEMERAL.0 { Ok(false) } else { Ok(true) } } KeyLifetime::PORTABLE => { // portable keys can only derive/wrap other portable keys if derived_key_lifetime.0 == KeyLifetime::PORTABLE.0 { Ok(true) } else { Ok(false) } } _ => Err(hwcrypto_err!( UNSUPPORTED, "unsupported Key lifetime {:?}", self.key_header.key_policy.key_lifetime )), } } fn key_can_be_used_for_derivation(&self) -> Result<(), HwCryptoError> { match self.key_material { KeyMaterial::Hmac(_) => Ok(()), _ => Err(hwcrypto_err!(UNSUPPORTED, "Only HMAC keys can be used for key derivation")), }?; if self.key_header.key_policy.key_usage.0 != KeyUse::DERIVE { return Err(hwcrypto_err!(BAD_PARAMETER, "key was not exclusively a derive key")); } Ok(()) } pub(crate) fn key_usage_supported(&self, usage: KeyUse) -> bool { (usage.0 & self.key_header.key_policy.key_usage.0 .0) == usage.0 } pub fn get_key_type(&self) -> KeyType { self.key_header.key_policy.key_type.0 } pub fn supports_pattern_encryption(&self) -> Result<(), HwCryptoError> { match self.key_header.key_policy.key_type.0 { KeyType::AES_128_CBC_NO_PADDING => Ok(()), _ => Err(hwcrypto_err!(BAD_PARAMETER, "only AES CBC supports pattern encryption")), } } /// Checks if the requested operation (encrypt/decrypt) can be done with this key pub(crate) fn symmetric_operation_is_compatible( &self, direction: SymmetricOperation, ) -> Result<(), HwCryptoError> { let dir = helpers::direction_to_key_usage(&direction)?; if !self.key_usage_supported(dir) { Err(hwcrypto_err!(BAD_PARAMETER, "provided key do not support {:?}", dir)) } else { Ok(()) } } /// Checks if the requested algorithm parameters are compatible with this key pub(crate) fn parameters_are_compatible_symmetric_cipher( &self, parameters: &SymmetricCryptoParameters, ) -> Result<(), HwCryptoError> { match parameters { SymmetricCryptoParameters::Aes(aes_parameters) => match aes_parameters { AesCipherMode::Cbc(_) => match self.get_key_type() { KeyType::AES_128_CBC_NO_PADDING | KeyType::AES_128_CBC_PKCS7_PADDING | KeyType::AES_256_CBC_NO_PADDING | KeyType::AES_256_CBC_PKCS7_PADDING => Ok(()), _ => Err(hwcrypto_err!(BAD_PARAMETER, "provided incompatible AES key for CBC")), }, AesCipherMode::Ctr(_) => match self.get_key_type() { KeyType::AES_128_CTR | KeyType::AES_256_CTR => Ok(()), _ => Err(hwcrypto_err!(BAD_PARAMETER, "provided incompatible AES key for CTR")), }, }, } } fn add_protection_id( &self, protection_id: ProtectionId, allowed_operations: &[OperationType], ) -> Result<(), HwCryptoError> { if !self.check_ownership() { // We haven't created this key, so we cannot export it // TODO: Change the error type to UNAUTORIZED return Err(hwcrypto_err!( GENERIC_ERROR, "only the owner of a key can modify protection IDs" )); } self.key_header.key_metadata.lock()?.add_protection_id(protection_id, allowed_operations) } } impl binder::Interface for OpaqueKey {} impl IOpaqueKey for OpaqueKey { fn exportWrappedKey( &self, _wrapping_key: &binder::Strong, ) -> binder::Result> { Err(binder::Status::new_exception_str( binder::ExceptionCode::UNSUPPORTED_OPERATION, Some("export_wrapped_key has not been implemented yet"), )) } fn getKeyPolicy(&self) -> binder::Result { Ok(self.key_header.get_policy()?) } fn getPublicKey(&self) -> binder::Result> { Err(binder::Status::new_exception_str( binder::ExceptionCode::UNSUPPORTED_OPERATION, Some("get_public_key has not been implemented yet"), )) } fn getShareableToken(&self, sealing_dice_policy: &[u8]) -> binder::Result { Ok(OpaqueKeyToken { keyToken: self.create_token(sealing_dice_policy)? }) } fn setProtectionId( &self, protection_id: ProtectionId, allowed_operations: &[OperationType], ) -> Result<(), binder::Status> { Ok(self.add_protection_id(protection_id, allowed_operations)?) } } pub(crate) fn check_key_material_with_policy( key_material: &KeyMaterial, policy: &KeyPolicy, ) -> Result<(), HwCryptoError> { let key_type = &policy.keyType; // `KeyMaterial` doesn't fully describe the cryptographic algorithm as `KeyType` does, so we can // only check if both of them are compatible, not provide a 1:1 mapping match key_material { KeyMaterial::Aes(aes_key) => match aes_key { OpaqueOr::Opaque(_) => Err(hwcrypto_err!(BAD_PARAMETER, "opaque aes key provided")), OpaqueOr::Explicit(aes_key) => match aes_key { crypto::aes::Key::Aes128(km) => match *key_type { KeyType::AES_128_CBC_NO_PADDING | KeyType::AES_128_CBC_PKCS7_PADDING | KeyType::AES_128_CTR | KeyType::AES_128_GCM | KeyType::AES_128_CMAC => Ok(()), _ => Err(hwcrypto_err!( BAD_PARAMETER, "type mismatch: key material {:?} key type: {:?}", km, key_type )), }, crypto::aes::Key::Aes192(_) => { Err(hwcrypto_err!(BAD_PARAMETER, "AES keys of length 192 are not supported")) } crypto::aes::Key::Aes256(km) => match *key_type { KeyType::AES_256_CBC_NO_PADDING | KeyType::AES_256_CBC_PKCS7_PADDING | KeyType::AES_256_CTR | KeyType::AES_256_GCM | KeyType::AES_256_CMAC => Ok(()), _ => Err(hwcrypto_err!( BAD_PARAMETER, "type mismatch: key material {:?} key type: {:?}", km, key_type )), }, }, }, KeyMaterial::TripleDes(_) => { Err(hwcrypto_err!(BAD_PARAMETER, "TDES is not currently supported")) } KeyMaterial::Hmac(hmac_key) => match hmac_key { OpaqueOr::Opaque(_) => Err(hwcrypto_err!(BAD_PARAMETER, "opaque HMAC key provided")), OpaqueOr::Explicit(km) => match *key_type { KeyType::HMAC_SHA256 | KeyType::HMAC_SHA512 => { let expected_size = get_key_size_in_bytes(key_type)?; let km_size = km.0.len(); match policy.usage { KeyUse::SIGN | KeyUse::DERIVE => Ok(()), _ => Err(hwcrypto_err!( BAD_PARAMETER, "wrong key use for hmac key, received {:?}", policy.usage )), }?; if km_size == expected_size { Ok(()) } else { Err(hwcrypto_err!( BAD_PARAMETER, "bad len for hmac key received {} bytes, expected {} bytes", km_size, expected_size )) } } _ => Err(hwcrypto_err!( BAD_PARAMETER, "type mismatch for HMAC key key type: {:?}", key_type )), }, }, KeyMaterial::Rsa(rsa_key) => match rsa_key { OpaqueOr::Opaque(_) => Err(hwcrypto_err!(BAD_PARAMETER, "opaque RSA key provided")), OpaqueOr::Explicit(rsa_key) => { let key_size = rsa_key.size(); match (key_size, *key_type) { (2048, KeyType::RSA2048_PSS_SHA256) => Ok(()), (2048, KeyType::RSA2048_PKCS1_5_SHA256) => Ok(()), _ => Err(hwcrypto_err!( BAD_PARAMETER, "type mismatch for RSA key length {} and type: {:?}", key_size, key_type )), } } }, KeyMaterial::Ec(curve, curve_type, _) => match (curve, *key_type) { (EcCurve::P256, KeyType::ECC_NIST_P256_SIGN_NO_PADDING) => Ok(()), (EcCurve::P256, KeyType::ECC_NIST_P256_SIGN_SHA256) => Ok(()), (EcCurve::P521, KeyType::ECC_NIST_P521_SIGN_NO_PADDING) => Ok(()), (EcCurve::P521, KeyType::ECC_NIST_P521_SIGN_SHA512) => Ok(()), (EcCurve::Curve25519, _) => match (curve_type, *key_type) { (CurveType::EdDsa, KeyType::ECC_ED25519_SIGN) => Ok(()), _ => Err(hwcrypto_err!( BAD_PARAMETER, "type mismatch for Ec Key curve {:?} and type: {:?}", curve, key_type )), }, _ => Err(hwcrypto_err!( BAD_PARAMETER, "type mismatch for Ec Key curve {:?} and type: {:?}", curve, key_type )), }, } } // Get key size given the backend AES key type. Used to check if we received enough bytes from the // caller for an AES key. fn get_aes_variant_key_size(variant: &crypto::aes::Variant) -> usize { match variant { crypto::aes::Variant::Aes128 => 16, crypto::aes::Variant::Aes192 => 24, crypto::aes::Variant::Aes256 => 32, } } // Translating a policy AES `KeyType` into the type understood by the cryptographic backend we are // currently used // TODO: change this into a `TryFrom` once we refactor `KeyType` to be a newtype. fn get_aes_variant(key_type: &KeyType) -> Result { match *key_type { KeyType::AES_128_CBC_NO_PADDING | KeyType::AES_128_CBC_PKCS7_PADDING | KeyType::AES_128_CTR | KeyType::AES_128_GCM | KeyType::AES_128_CMAC => Ok(crypto::aes::Variant::Aes128), KeyType::AES_256_CBC_NO_PADDING | KeyType::AES_256_CBC_PKCS7_PADDING | KeyType::AES_256_CTR | KeyType::AES_256_GCM | KeyType::AES_256_CMAC => Ok(crypto::aes::Variant::Aes256), _ => Err(hwcrypto_err!(BAD_PARAMETER, "not an AES key type: {:?}", key_type)), } } // Return a keysize given a `KeyType`. Because HMAC key sizes can be defined by the // caller, `key_size_bits` is needed to cover all cases. pub(crate) fn get_key_size_in_bytes(key_type: &KeyType) -> Result { match *key_type { KeyType::AES_128_CBC_NO_PADDING | KeyType::AES_128_CBC_PKCS7_PADDING | KeyType::AES_128_CTR | KeyType::AES_128_GCM | KeyType::AES_128_CMAC => Ok(16), KeyType::AES_256_CBC_NO_PADDING | KeyType::AES_256_CBC_PKCS7_PADDING | KeyType::AES_256_CTR | KeyType::AES_256_GCM | KeyType::AES_256_CMAC => Ok(32), KeyType::HMAC_SHA256 => Ok(32), KeyType::HMAC_SHA512 => Ok(64), _ => unimplemented!("Only AES and HMAC has been implemented"), } } fn validate_lifetime(lifetime: KeyLifetime) -> Result<(), HwCryptoError> { match lifetime { KeyLifetime::EPHEMERAL | KeyLifetime::HARDWARE | KeyLifetime::PORTABLE => Ok(()), // AIDL structure have more values added than the ones defined on the AIDL file _ => Err(hwcrypto_err!(UNSUPPORTED, "unsupported Key lifetime {:?}", lifetime)), } } fn check_type_derived_key(key_type: KeyType) -> Result<(), HwCryptoError> { match key_type { KeyType::AES_128_CBC_NO_PADDING | KeyType::AES_128_CBC_PKCS7_PADDING | KeyType::AES_128_CTR | KeyType::AES_128_GCM | KeyType::AES_128_CMAC | KeyType::AES_256_CBC_NO_PADDING | KeyType::AES_256_CBC_PKCS7_PADDING | KeyType::AES_256_CTR | KeyType::AES_256_GCM | KeyType::AES_256_CMAC | KeyType::HMAC_SHA256 | KeyType::HMAC_SHA512 => Ok(()), _ => Err(hwcrypto_err!(BAD_PARAMETER, "Only HMAC and AES keys are supported")), } } fn key_vec_to_array>(input: U) -> Result { input .try_into() .map_err(|_| hwcrypto_err!(BAD_PARAMETER, "couldn't transform vector into array")) } // Create a backend-compatible cryptographic key from either a provided vector of uniform random // bytes or, if this not provided, use the cryptographic backend to create it. The type is based on // the policy `KeyType`. Because HMAC keys can have arbitrary sizes, include an optional // `key_size_bits` for that case. pub(crate) fn generate_key_material( key_type: &KeyType, key_random_bytes: Option>, ) -> Result { let aes = crypto_provider::AesImpl; let hmac = crypto_provider::HmacImpl; let mut rng = crypto_provider::RngImpl::default(); match *key_type { KeyType::AES_128_CBC_NO_PADDING | KeyType::AES_128_CBC_PKCS7_PADDING | KeyType::AES_128_CTR | KeyType::AES_128_GCM | KeyType::AES_128_CMAC | KeyType::AES_256_CBC_NO_PADDING | KeyType::AES_256_CBC_PKCS7_PADDING | KeyType::AES_256_CTR | KeyType::AES_256_GCM | KeyType::AES_256_CMAC => { let variant = get_aes_variant(key_type)?; if let Some(key_bytes) = key_random_bytes { if key_bytes.len() != get_aes_variant_key_size(&variant) { return Err(hwcrypto_err!( BAD_PARAMETER, "for aes key needed {} bytes, received {}", key_bytes.len(), get_aes_variant_key_size(&variant) )); } match variant { crypto::aes::Variant::Aes128 => Ok(KeyMaterial::Aes( crypto::aes::Key::Aes128(key_vec_to_array(key_bytes)?).into(), )), crypto::aes::Variant::Aes192 => Err(hwcrypto_err!( BAD_PARAMETER, "AES keys of length 192 are not supported", )), crypto::aes::Variant::Aes256 => Ok(KeyMaterial::Aes( crypto::aes::Key::Aes256(key_vec_to_array(key_bytes)?).into(), )), } } else { Ok(aes.generate_key(&mut rng, variant, &[])?) } } KeyType::HMAC_SHA256 | KeyType::HMAC_SHA512 => { let key_size_bytes = get_key_size_in_bytes(key_type)?; if let Some(key_bytes) = key_random_bytes { if key_bytes.len() != key_size_bytes { Err(hwcrypto_err!( BAD_PARAMETER, "for hmac key needed {} bytes, received {}", key_bytes.len(), key_size_bytes )) } else { Ok(KeyMaterial::Hmac(crypto::hmac::Key::new(key_bytes).into())) } } else { Ok(hmac.generate_key( &mut rng, kmr_wire::KeySizeInBits((key_size_bytes * 8).try_into().map_err(|_| { hwcrypto_err!( GENERIC_ERROR, "shouldn't happen, key_size_bytes * 8 should fit on an u32" ) })?), &[], )?) } } _ => unimplemented!("key material other than AES and HMAC not implemented yet"), } } #[cfg(test)] mod tests { use super::*; use test::{expect, expect_eq}; #[test] fn boot_unique_values_match() { let boot_value = BootUniqueValue::new().expect("couldn't get boot unique value"); let boot_value2 = BootUniqueValue::new().expect("couldn't get boot unique value"); expect_eq!(boot_value, boot_value2, "boot unique values should match"); } #[test] fn generate_key_material_test() { let usage = KeyUse::ENCRYPT; let key_type = KeyType::AES_256_GCM; let policy = KeyPolicy { usage, keyLifetime: KeyLifetime::EPHEMERAL, keyPermissions: Vec::new(), keyType: key_type, keyManagementKey: false, }; let key_material = generate_key_material(&policy.keyType, None); expect!(key_material.is_ok(), "couldn't retrieve key material"); let key_material = key_material.unwrap(); let check_result = check_key_material_with_policy(&key_material, &policy); expect!(check_result.is_ok(), "wrong key type"); } }