blob: 8bb85647476accff79a7681432d620ab8e89ebfa [file] [log] [blame]
/*
* 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<BootUniqueValue, HwCryptoError> {
get_boot_unique_value()
}
}
// Boot unique value is lazily initialized on the first call to retrieve it
static BOOT_UNIQUE_VALUE: OnceLock<BootUniqueValue> = 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<BootUniqueValue, HwCryptoError> {
// 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<Value>,
}
impl DerivationContext {
pub(crate) fn new(op_type: HkdfOperationType) -> Result<Self, HwCryptoError> {
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<u8>,
) -> 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<Vec<u8>, HwCryptoError> {
let context = Value::Array(self.context_components);
Ok(context.to_vec()?)
}
}
#[derive(Debug)]
struct KeyHeaderPolicy {
key_lifetime: KeyLifetimeSerializable,
key_permissions: Vec<KeyPermissions>,
key_usage: KeyUseSerializable,
key_type: KeyTypeSerializable,
management_key: bool,
}
impl KeyHeaderPolicy {
fn new(policy: &KeyPolicy) -> Result<Self, HwCryptoError> {
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<KeyPolicy, HwCryptoError> {
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<Self, HwCryptoError> {
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<bool, HwCryptoError> {
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<u64>,
protection_id_settings: BTreeMap<ProtectionIdSerializable, ProtectionSetting>,
}
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<Self, HwCryptoError> {
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<Value, HwCryptoError> {
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<ProtectionIdSerializable, ProtectionSetting>,
> = None;
let mut expiration_time: Option<Option<u64>> = 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<KeyHeaderMetadata>,
}
impl KeyHeader {
fn new(policy: &KeyPolicy) -> Result<Self, HwCryptoError> {
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<Self, HwCryptoError> {
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<KeyPolicy, HwCryptoError> {
self.key_policy.get_policy()
}
fn try_clone(&self) -> Result<Self, HwCryptoError> {
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<Value, HwCryptoError> {
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<Value, CoseError> {
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<Self, CoseError> {
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<bool> = None;
let mut read_protection: Option<bool> = 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<Vec<u8>, HwCryptoError> {
let mut context = Vec::<u8>::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<OpaqueKey> for binder::Strong<dyn IOpaqueKey> {
fn from(value: OpaqueKey) -> binder::Strong<dyn IOpaqueKey> {
BnOpaqueKey::new_binder(value, binder::BinderFeatures::default())
}
}
impl TryFrom<&binder::Strong<dyn IOpaqueKey>> for OpaqueKey {
type Error = HwCryptoError;
fn try_from(value: &binder::Strong<dyn IOpaqueKey>) -> Result<OpaqueKey, Self::Error> {
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<BnOpaqueKey> =
binder.try_into().expect("because binder is local this should not fail");
let opaque_key_material = (*binder_key).downcast_binder::<OpaqueKey>();
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<Value, CoseError> {
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<Self, CoseError> {
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<KeyMaterial> = None;
let mut key_policy: Option<KeyPolicy> = None;
let mut boot_value: Option<BootUniqueValue> = None;
let mut key_metadata: Option<Value> = 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<binder::Strong<dyn IOpaqueKey>> {
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<binder::Strong<dyn IOpaqueKey>> {
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<Vec<u8>, 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<Self, HwCryptoError> {
// 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<binder::Strong<dyn IOpaqueKey>> {
let key_material = generate_key_material(&policy.keyType, None)?;
OpaqueKey::new_binder(policy, key_material, connection_info)
}
fn try_clone(&self) -> Result<Self, HwCryptoError> {
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<u8>,
connection_info: Uuid,
) -> binder::Result<binder::Strong<dyn IOpaqueKey>> {
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<Vec<u8>, 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<Vec<u8>, 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<Vec<u8>, 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<Vec<u8>, 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<binder::Strong<dyn IOpaqueKey>> {
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<bool, HwCryptoError> {
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<dyn IOpaqueKey>,
) -> binder::Result<Vec<u8>> {
Err(binder::Status::new_exception_str(
binder::ExceptionCode::UNSUPPORTED_OPERATION,
Some("export_wrapped_key has not been implemented yet"),
))
}
fn getKeyPolicy(&self) -> binder::Result<KeyPolicy> {
Ok(self.key_header.get_policy()?)
}
fn getPublicKey(&self) -> binder::Result<Vec<u8>> {
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<OpaqueKeyToken> {
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<crypto::aes::Variant, 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 => 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<usize, 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 => 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<T, U: std::convert::TryInto<T>>(input: U) -> Result<T, HwCryptoError> {
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<Vec<u8>>,
) -> Result<KeyMaterial, HwCryptoError> {
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");
}
}