/* * 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. */ //! KeyPolicy serialization facilities use alloc::collections::btree_set::BTreeSet; use android_hardware_security_see_hwcrypto::aidl::android::hardware::security::see::hwcrypto::types::{ KeyLifetime::KeyLifetime, KeyPermissions::KeyPermissions, KeyType::KeyType, KeyUse::KeyUse, }; use android_hardware_security_see_hwcrypto::aidl::android::hardware::security::see::hwcrypto::KeyPolicy::KeyPolicy; use ciborium::Value; use coset::{AsCborValue, CborSerializable, CoseError}; use crate::{aidl_enum_wrapper, cose_enum_gen}; use crate::{err::HwCryptoError, hwcrypto_err}; aidl_enum_wrapper! { aidl_name: KeyUse, wrapper_name: KeyUseSerializable, fields: [ENCRYPT, DECRYPT, ENCRYPT_DECRYPT, SIGN, DERIVE, WRAP] } aidl_enum_wrapper! { aidl_name: KeyLifetime, wrapper_name: KeyLifetimeSerializable, fields: [EPHEMERAL, HARDWARE, PORTABLE] } aidl_enum_wrapper! { aidl_name: KeyType, wrapper_name: KeyTypeSerializable, fields: [AES_128_CBC_NO_PADDING, AES_128_CBC_PKCS7_PADDING, AES_128_CTR, AES_128_GCM, AES_128_CMAC, AES_256_CBC_NO_PADDING, AES_256_CBC_PKCS7_PADDING, AES_256_CTR, AES_256_GCM, AES_256_CMAC, HMAC_SHA256, HMAC_SHA512, RSA2048_PKCS1_5_SHA256, RSA2048_PSS_SHA256, ECC_NIST_P256_SIGN_NO_PADDING, ECC_NIST_P256_SIGN_SHA256, ECC_NIST_P521_SIGN_NO_PADDING, ECC_NIST_P521_SIGN_SHA512, ECC_ED25519_SIGN] } aidl_enum_wrapper! { aidl_name: KeyPermissions, wrapper_name: KeyPermissionsSerializable, fields: [ALLOW_EPHEMERAL_KEY_WRAPPING, ALLOW_HARDWARE_KEY_WRAPPING, ALLOW_PORTABLE_KEY_WRAPPING] } #[derive(Debug, PartialEq)] struct SerializableKeyPolicy { key_lifetime: KeyLifetimeSerializable, key_permissions: BTreeSet, key_usage: KeyUseSerializable, key_type: KeyTypeSerializable, management_key: bool, } impl SerializableKeyPolicy { fn new(key_policy: &KeyPolicy) -> Result { let mut key_permissions = BTreeSet::new(); for permission in &key_policy.keyPermissions { key_permissions.insert(KeyPermissionsSerializable(*permission)); } Ok(Self { key_lifetime: KeyLifetimeSerializable(key_policy.keyLifetime), key_permissions, key_usage: KeyUseSerializable(key_policy.usage), key_type: KeyTypeSerializable(key_policy.keyType), management_key: key_policy.keyManagementKey, }) } } impl TryFrom<&KeyPolicy> for SerializableKeyPolicy { type Error = crate::err::HwCryptoError; fn try_from(value: &KeyPolicy) -> Result { Self::new(value) } } impl TryFrom for SerializableKeyPolicy { type Error = crate::err::HwCryptoError; fn try_from(value: KeyPolicy) -> Result { (&value).try_into() } } impl TryFrom<&SerializableKeyPolicy> for KeyPolicy { type Error = crate::err::HwCryptoError; fn try_from(value: &SerializableKeyPolicy) -> Result { let mut key_permissions = Vec::new(); key_permissions.try_reserve(value.key_permissions.len())?; // permissions on the returned key policy will be sorted because they are retrieved that // way from the SerializableKeyPolicy for permission in &value.key_permissions { key_permissions.push((*permission).into()); } Ok(Self { keyLifetime: value.key_lifetime.into(), keyPermissions: key_permissions, usage: value.key_usage.into(), keyType: value.key_type.into(), keyManagementKey: value.management_key, }) } } impl TryFrom for KeyPolicy { type Error = crate::err::HwCryptoError; fn try_from(value: SerializableKeyPolicy) -> Result { (&value).try_into() } } cose_enum_gen! { enum HeaderCoseLabels { KeyUsage = -65701, KeyLifetime = -65702, KeyPermissions = -65703, KeyType = -65704, ManagementKey = -65705, } } impl AsCborValue for SerializableKeyPolicy { fn to_cbor_value(self) -> Result { let mut cbor_map = Vec::<(Value, Value)>::new(); let key = Value::Integer((HeaderCoseLabels::KeyLifetime as i64).into()); let value = Value::Integer(self.key_lifetime.into()); cbor_map.try_reserve_exact(5).map_err(|_| CoseError::EncodeFailed)?; cbor_map.push((key, value)); // Creating key permissions array // We need this array to always be sorted so the created CBOR structure will always match // if the input vector has the same permissions, this is currently provided by // `BTreeSet::into_iter` always returning the elements ordered in ascending order. let mut permissions = Vec::new(); permissions.try_reserve(self.key_permissions.len()).map_err(|_| CoseError::EncodeFailed)?; for permission in self.key_permissions.into_iter() { permissions.push(Value::Integer(permission.into())); } let key = Value::Integer((HeaderCoseLabels::KeyPermissions as i64).into()); let value = Value::Array(permissions); cbor_map.push((key, value)); let key = Value::Integer((HeaderCoseLabels::KeyUsage as i64).into()); let value = Value::Integer(self.key_usage.into()); cbor_map.push((key, value)); let key = Value::Integer((HeaderCoseLabels::KeyType as i64).into()); let value = Value::Integer(self.key_type.into()); cbor_map.push((key, value)); let key = Value::Integer((HeaderCoseLabels::ManagementKey as i64).into()); let value = Value::Bool(self.management_key.into()); cbor_map.push((key, value)); Ok(Value::Map(cbor_map)) } fn from_cbor_value(value: Value) -> Result { let key_policy = value.into_map().map_err(|_| CoseError::ExtraneousData)?; let mut key_lifetime: Option = None; let mut key_permissions: Option> = None; let mut key_usage: Option = None; let mut key_type: Option = None; let mut management_key: Option = None; for (map_key, map_val) in key_policy { let key = map_key.into_integer().map_err(|_| CoseError::ExtraneousData)?; match key.try_into()? { HeaderCoseLabels::KeyLifetime => { key_lifetime = Some( map_val .as_integer() .ok_or(CoseError::EncodeFailed)? .try_into() .map_err(|_| CoseError::EncodeFailed)?, ); } HeaderCoseLabels::KeyPermissions => { let mut permissions = BTreeSet::new(); for permission in map_val.as_array().ok_or(CoseError::EncodeFailed)? { permissions.insert( permission .as_integer() .ok_or(CoseError::EncodeFailed)? .try_into() .map_err(|_| CoseError::EncodeFailed)?, ); } key_permissions = Some(permissions); } HeaderCoseLabels::KeyUsage => { key_usage = Some( map_val .as_integer() .ok_or(CoseError::EncodeFailed)? .try_into() .map_err(|_| CoseError::EncodeFailed)?, ); } HeaderCoseLabels::KeyType => { key_type = Some( map_val .as_integer() .ok_or(CoseError::EncodeFailed)? .try_into() .map_err(|_| CoseError::EncodeFailed)?, ); } HeaderCoseLabels::ManagementKey => { management_key = Some(map_val.as_bool().ok_or(CoseError::EncodeFailed)?); } } } let key_lifetime = key_lifetime.ok_or(CoseError::EncodeFailed)?; let key_permissions = key_permissions.ok_or(CoseError::EncodeFailed)?; let key_usage = key_usage.ok_or(CoseError::EncodeFailed)?; let key_type = key_type.ok_or(CoseError::EncodeFailed)?; let management_key = management_key.ok_or(CoseError::EncodeFailed)?; Ok(SerializableKeyPolicy { key_lifetime, key_permissions, key_usage, key_type, management_key, }) } } pub static AES_SYMMETRIC_KEY_USES_MASK: i32 = KeyUse::ENCRYPT_DECRYPT.0 | KeyUse::WRAP.0; pub static HMAC_KEY_USES_MASK: i32 = KeyUse::DERIVE.0; pub fn check_key_policy(key_policy: &KeyPolicy) -> Result<(), HwCryptoError> { match key_policy.keyType { KeyType::AES_128_CBC_NO_PADDING | KeyType::AES_128_CBC_PKCS7_PADDING | KeyType::AES_128_CTR | KeyType::AES_128_GCM | KeyType::AES_256_CBC_NO_PADDING | KeyType::AES_256_CBC_PKCS7_PADDING | KeyType::AES_256_CTR | KeyType::AES_256_GCM => { if (key_policy.usage.0 & !AES_SYMMETRIC_KEY_USES_MASK) != 0 { Err(hwcrypto_err!( BAD_PARAMETER, "usage not supported for AES symmetric key: {}", key_policy.usage.0 )) } else { Ok(()) } } KeyType::HMAC_SHA256 | KeyType::HMAC_SHA512 => { if (key_policy.usage.0 & !HMAC_KEY_USES_MASK) != 0 { Err(hwcrypto_err!( BAD_PARAMETER, "usage not supported for HMAC key: {}", key_policy.usage.0 )) } else { Ok(()) } } KeyType::AES_128_CMAC | KeyType::AES_256_CMAC | KeyType::RSA2048_PSS_SHA256 | KeyType::RSA2048_PKCS1_5_SHA256 | KeyType::ECC_NIST_P256_SIGN_NO_PADDING | KeyType::ECC_NIST_P256_SIGN_SHA256 | KeyType::ECC_NIST_P521_SIGN_NO_PADDING | KeyType::ECC_NIST_P521_SIGN_SHA512 | KeyType::ECC_ED25519_SIGN => { Err(hwcrypto_err!(UNSUPPORTED, "key type not supported yet")) } _ => Err(hwcrypto_err!(BAD_PARAMETER, "unknown keytype provided {:?}", key_policy.keyType)), } } pub fn cbor_serialize_key_policy(key_policy: &KeyPolicy) -> Result, HwCryptoError> { let serializable_key_policy: SerializableKeyPolicy = key_policy.try_into()?; serializable_key_policy .to_cbor_value()? .to_vec() .map_err(|_| hwcrypto_err!(SERIALIZATION_ERROR, "couldn't serialize policy")) } pub fn cbor_policy_to_aidl(cbor_key_policy: &[u8]) -> Result { let policy = SerializableKeyPolicy::from_cbor_value(Value::from_slice(cbor_key_policy)?)?.try_into()?; check_key_policy(&policy)?; Ok(policy) } #[cfg(test)] mod tests { use super::*; use test::{expect, expect_eq}; #[test] fn serialize_policy() { let policy = KeyPolicy { usage: KeyUse::ENCRYPT, keyLifetime: KeyLifetime::EPHEMERAL, keyPermissions: Vec::new(), keyType: KeyType::AES_256_GCM, keyManagementKey: false, }; let serialize_result = cbor_serialize_key_policy(&policy); expect!(serialize_result.is_ok(), "couldn't serialize policy"); let serialized_policy = serialize_result.unwrap(); let deserialization = cbor_policy_to_aidl(serialized_policy.as_slice()); expect!(deserialization.is_ok(), "couldn't deserialize policy"); let deserialized_policy = deserialization.unwrap(); let policy: SerializableKeyPolicy = policy.try_into().unwrap(); let deserialized_policy: SerializableKeyPolicy = (&deserialized_policy).try_into().unwrap(); expect_eq!(policy, deserialized_policy, "policies should match"); } #[test] fn bad_policies() { let mut policy = KeyPolicy { usage: KeyUse::SIGN, keyLifetime: KeyLifetime::EPHEMERAL, keyPermissions: Vec::new(), keyType: KeyType::AES_256_GCM, keyManagementKey: false, }; let serialize_result = cbor_serialize_key_policy(&policy); expect!(serialize_result.is_ok(), "couldn't serialize policy"); let serialized_policy = serialize_result.unwrap(); let deserialization = cbor_policy_to_aidl(serialized_policy.as_slice()); expect!(deserialization.is_err(), "shouldn't be able to deserailize incorrect policy"); policy.usage = KeyUse::DERIVE; let serialize_result = cbor_serialize_key_policy(&policy); expect!(serialize_result.is_ok(), "couldn't serialize policy"); let serialized_policy = serialize_result.unwrap(); let deserialization = cbor_policy_to_aidl(serialized_policy.as_slice()); expect!(deserialization.is_err(), "shouldn't be able to deserailize incorrect policy"); policy.keyType = KeyType::HMAC_SHA256; policy.usage = KeyUse::ENCRYPT; let serialize_result = cbor_serialize_key_policy(&policy); expect!(serialize_result.is_ok(), "couldn't serialize policy"); let serialized_policy = serialize_result.unwrap(); let deserialization = cbor_policy_to_aidl(serialized_policy.as_slice()); expect!(deserialization.is_err(), "shouldn't be able to deserailize incorrect policy"); policy.usage = KeyUse::DECRYPT; let serialize_result = cbor_serialize_key_policy(&policy); expect!(serialize_result.is_ok(), "couldn't serialize policy"); let serialized_policy = serialize_result.unwrap(); let deserialization = cbor_policy_to_aidl(serialized_policy.as_slice()); expect!(deserialization.is_err(), "shouldn't be able to deserailize incorrect policy"); policy.keyType = KeyType::HMAC_SHA512; policy.usage = KeyUse::ENCRYPT_DECRYPT; let serialize_result = cbor_serialize_key_policy(&policy); expect!(serialize_result.is_ok(), "couldn't serialize policy"); let serialized_policy = serialize_result.unwrap(); let deserialization = cbor_policy_to_aidl(serialized_policy.as_slice()); expect!(deserialization.is_err(), "shouldn't be able to deserailize incorrect policy"); } }