1 /* 2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 //! Implementation of the `IHwCryptoKey` AIDL interface. It can be use to generate and 18 //! retrieve device specific keys. 19 20 use android_hardware_security_see::aidl::android::hardware::security::see::hwcrypto::{ 21 types::{KeyLifetime::KeyLifetime, KeyType::KeyType, KeyUse::KeyUse}, 22 IHwCryptoKey::{ 23 BnHwCryptoKey, DerivedKey::DerivedKey, DerivedKeyParameters::DerivedKeyParameters, 24 DerivedKeyPolicy::DerivedKeyPolicy, DeviceKeyId::DeviceKeyId, 25 DiceBoundDerivationKey::DiceBoundDerivationKey, DiceBoundKeyResult::DiceBoundKeyResult, 26 DiceCurrentBoundKeyResult::DiceCurrentBoundKeyResult, IHwCryptoKey, 27 }, 28 KeyPolicy::KeyPolicy, 29 }; 30 use android_hardware_security_see::binder; 31 use binder::StatusCode; 32 use ciborium::{cbor, Value}; 33 use coset::{AsCborValue, CborSerializable, CoseError}; 34 use hwcryptohal_common::{err::HwCryptoError, hwcrypto_err}; 35 use hwkey::{Hwkey, KdfVersion}; 36 use tipc::Uuid; 37 38 use crate::cose_enum_gen; 39 use crate::opaque_key::{self, OpaqueKey}; 40 use crate::service_encryption_key::{self, EncryptionHeader}; 41 42 const DEVICE_KEY_CTX: &[u8] = b"device_key_derivation_contextKEK"; 43 const DICE_BOUND_POLICY_CTX: &[u8] = b"dice_bound"; 44 45 const OPAQUE_KEY_CTX: &[u8] = b"opaque"; 46 const CLEAR_KEY_CTX: &[u8] = b"cleark"; 47 const OPAQUE_CLEAR_CTX_SIZE: usize = 6; 48 49 // Checking that both context have the same size and it is equal to `OPAQUE_CLEAR_CTX_SIZE` 50 const _: () = assert!( 51 (OPAQUE_KEY_CTX.len() == OPAQUE_CLEAR_CTX_SIZE), 52 "opaque context size must match OPAQUE_CLEAR_CTX_SIZE" 53 ); 54 const _: () = assert!( 55 (CLEAR_KEY_CTX.len() == OPAQUE_CLEAR_CTX_SIZE), 56 "clear context size must match OPAQUE_CLEAR_CTX_SIZE" 57 ); 58 59 // enum used for serializing the `VersionContext` 60 cose_enum_gen! { 61 enum VersionContextCoseLabels { 62 Uuid = -65537, 63 Version = -65538, 64 } 65 } 66 67 // TODO: `ConnectionInformation` will be opaque to the HwCrypto service once we have a connection 68 // manager. 69 struct ConnectionInformation { 70 uuid: Uuid, 71 } 72 73 // Mock version object to be used until we have more DICE support. It is based on the trusty version 74 // retrievable from HwKey and the uuid of the caller. `VersionContext`` encryption is similar to 75 // what KeyMint uses to wrap keys. 76 struct VersionContext { 77 uuid: Uuid, 78 version: u32, 79 header: Option<EncryptionHeader>, 80 } 81 82 impl VersionContext { get_current_version() -> Result<u32, HwCryptoError>83 fn get_current_version() -> Result<u32, HwCryptoError> { 84 service_encryption_key::get_service_current_version() 85 } 86 new_current(uuid: Uuid) -> Result<Self, HwCryptoError>87 fn new_current(uuid: Uuid) -> Result<Self, HwCryptoError> { 88 let header = Some(EncryptionHeader::generate()?); 89 let version = Self::get_current_version()?; 90 Ok(VersionContext { uuid, version, header }) 91 } 92 new_current_encrypted(uuid: Uuid) -> Result<Vec<u8>, HwCryptoError>93 fn new_current_encrypted(uuid: Uuid) -> Result<Vec<u8>, HwCryptoError> { 94 let ctx = Self::new_current(uuid)?; 95 Ok(ctx.encrypt_context()?) 96 } 97 check_version(&self) -> Result<(), HwCryptoError>98 fn check_version(&self) -> Result<(), HwCryptoError> { 99 let current_version = Self::get_current_version()?; 100 if self.version > current_version { 101 return Err(hwcrypto_err!(BAD_PARAMETER, "version is not valid")); 102 } 103 Ok(()) 104 } 105 check_context(&self, connection: ConnectionInformation) -> Result<(), HwCryptoError>106 fn check_context(&self, connection: ConnectionInformation) -> Result<(), HwCryptoError> { 107 if connection.uuid != self.uuid { 108 return Err(hwcrypto_err!(BAD_PARAMETER, "uuid mismatch")); 109 } 110 self.check_version() 111 } 112 check_encrypted_context( encrypted_ctx: &[u8], connection: ConnectionInformation, ) -> Result<(), HwCryptoError>113 fn check_encrypted_context( 114 encrypted_ctx: &[u8], 115 connection: ConnectionInformation, 116 ) -> Result<(), HwCryptoError> { 117 let context = Self::decrypt_context(encrypted_ctx)?; 118 context.check_context(connection) 119 } 120 is_context_current(encrypted_ctx: &[u8]) -> Result<bool, HwCryptoError>121 fn is_context_current(encrypted_ctx: &[u8]) -> Result<bool, HwCryptoError> { 122 let context = Self::decrypt_context(encrypted_ctx)?; 123 let current_version = Self::get_current_version()?; 124 Ok(context.version >= current_version) 125 } 126 decrypt_context(encrypted_context: &[u8]) -> Result<Self, HwCryptoError>127 fn decrypt_context(encrypted_context: &[u8]) -> Result<Self, HwCryptoError> { 128 let (version_ctx_header, decrypted_data) = 129 EncryptionHeader::decrypt_content_service_encryption_key( 130 encrypted_context, 131 DEVICE_KEY_CTX, 132 )?; 133 134 let mut version_context = 135 VersionContext::from_cbor_value(Value::from_slice(&decrypted_data[..])?)?; 136 version_context.header = Some(version_ctx_header); 137 Ok(version_context) 138 } 139 encrypt_context(mut self) -> Result<Vec<u8>, HwCryptoError>140 fn encrypt_context(mut self) -> Result<Vec<u8>, HwCryptoError> { 141 let header = self.header.take().ok_or(hwcrypto_err!(BAD_PARAMETER, "no header found"))?; 142 header.encrypt_content_service_encryption_key(DEVICE_KEY_CTX, self) 143 } 144 get_stable_context(encrypted_context: &[u8]) -> Result<Vec<u8>, HwCryptoError>145 fn get_stable_context(encrypted_context: &[u8]) -> Result<Vec<u8>, HwCryptoError> { 146 let decrypted_context = Self::decrypt_context(encrypted_context)?; 147 Ok(decrypted_context.to_cbor_value()?.to_vec()?) 148 } 149 } 150 151 impl AsCborValue for VersionContext { to_cbor_value(self) -> Result<Value, CoseError>152 fn to_cbor_value(self) -> Result<Value, CoseError> { 153 cbor!({ 154 (VersionContextCoseLabels::Uuid as i64) => self.uuid.to_string(), 155 (VersionContextCoseLabels::Version as i64) => self.version, 156 }) 157 .map_err(|_| CoseError::ExtraneousData) 158 } 159 from_cbor_value(value: Value) -> Result<Self, CoseError>160 fn from_cbor_value(value: Value) -> Result<Self, CoseError> { 161 let version_context = value.into_map().map_err(|_| CoseError::ExtraneousData)?; 162 let mut uuid: Option<Uuid> = None; 163 let mut version: Option<u32> = None; 164 for (map_key, map_val) in version_context { 165 match map_key { 166 Value::Integer(key) => { 167 match key.try_into().map_err(|_| CoseError::EncodeFailed)? { 168 VersionContextCoseLabels::Uuid => { 169 let uuid_str = 170 map_val.into_text().map_err(|_| CoseError::EncodeFailed)?; 171 let parsed_uuid = Uuid::new_from_string(&uuid_str) 172 .map_err(|_| CoseError::EncodeFailed)?; 173 uuid = Some(parsed_uuid); 174 } 175 VersionContextCoseLabels::Version => { 176 let parsed_version = map_val 177 .into_integer() 178 .map_err(|_| CoseError::EncodeFailed)? 179 .try_into() 180 .map_err(|_| CoseError::ExtraneousData)?; 181 version = Some(parsed_version); 182 } 183 } 184 } 185 _ => return Err(CoseError::ExtraneousData), 186 } 187 } 188 let uuid = uuid.ok_or(CoseError::EncodeFailed)?; 189 let version = version.ok_or(CoseError::EncodeFailed)?; 190 // Header travels in the clear, the decoded section only contains the encrypted fields 191 Ok(VersionContext { uuid, version, header: None }) 192 } 193 } 194 195 /// The `IHwCryptoKey` implementation. 196 #[derive(Debug)] 197 pub struct HwCryptoKey { 198 #[allow(dead_code)] 199 uuid: Uuid, 200 } 201 202 impl binder::Interface for HwCryptoKey {} 203 204 impl HwCryptoKey { new_binder(uuid: Uuid) -> binder::Strong<dyn IHwCryptoKey>205 pub(crate) fn new_binder(uuid: Uuid) -> binder::Strong<dyn IHwCryptoKey> { 206 let hwcrypto_device_key = HwCryptoKey { uuid }; 207 BnHwCryptoKey::new_binder(hwcrypto_device_key, binder::BinderFeatures::default()) 208 } 209 derive_dice_policy_bound_key( &self, derivation_key: &DiceBoundDerivationKey, dice_policy_for_key_version: &[u8], ) -> Result<DiceBoundKeyResult, HwCryptoError>210 fn derive_dice_policy_bound_key( 211 &self, 212 derivation_key: &DiceBoundDerivationKey, 213 dice_policy_for_key_version: &[u8], 214 ) -> Result<DiceBoundKeyResult, HwCryptoError> { 215 // Verifying provided DICE policy 216 let connection_info = ConnectionInformation { uuid: self.uuid.clone() }; 217 VersionContext::check_encrypted_context(dice_policy_for_key_version, connection_info)?; 218 // Getting back a stable DICE policy for context, so keys derived with the same version will 219 // match 220 let dice_context = VersionContext::get_stable_context(dice_policy_for_key_version)?; 221 let mut concat_context = Vec::<u8>::new(); 222 concat_context.try_reserve(DICE_BOUND_POLICY_CTX.len())?; 223 concat_context.extend_from_slice(DICE_BOUND_POLICY_CTX); 224 concat_context.try_reserve(dice_context.len())?; 225 concat_context.extend_from_slice(dice_context.as_slice()); 226 227 // The returned key will only be used for derivation, so fixing tis type to HMAC_SHA256 228 let key_type = KeyType::HMAC_SHA256; 229 let key_size = opaque_key::get_key_size_in_bytes(&key_type)?; 230 // Create an array big enough to hold the bytes of the derived key material 231 let mut derived_key = Vec::<u8>::new(); 232 derived_key.try_reserve(key_size)?; 233 derived_key.resize(key_size, 0); 234 235 match derivation_key { 236 DiceBoundDerivationKey::KeyId(key_id) => { 237 let hwkey_session = Hwkey::open().map_err(|e| { 238 hwcrypto_err!(GENERIC_ERROR, "could not connect to hwkey service {:?}", e) 239 })?; 240 let session_req = match *key_id { 241 DeviceKeyId::DEVICE_BOUND_KEY => { 242 Ok(hwkey_session.derive_key_req().unique_key()) 243 } 244 DeviceKeyId::BATCH_KEY => Ok(hwkey_session.derive_key_req().shared_key()), 245 _ => Err(hwcrypto_err!(UNSUPPORTED, "unknown key id {:?}", key_id)), 246 }?; 247 248 session_req 249 .kdf(KdfVersion::Best) 250 .derive(&concat_context, &mut derived_key[..]) 251 .map_err(|e| hwcrypto_err!(GENERIC_ERROR, "failed to derive key {:?}", e))?; 252 253 let policy = KeyPolicy { 254 usage: KeyUse::DERIVE, 255 keyLifetime: KeyLifetime::EPHEMERAL, 256 keyPermissions: Vec::new(), 257 keyType: key_type, 258 keyManagementKey: false, 259 }; 260 // Create a new opaque key from the generated key material 261 let km = opaque_key::generate_key_material(&policy.keyType, Some(derived_key))?; 262 let key = opaque_key::OpaqueKey::new_binder(&policy, km) 263 .map_err(|e| hwcrypto_err!(GENERIC_ERROR, "failed to create key {:?}", e))?; 264 let dice_policy_current = 265 VersionContext::is_context_current(dice_policy_for_key_version)?; 266 Ok(DiceBoundKeyResult { 267 diceBoundKey: Some(key), 268 dicePolicyWasCurrent: dice_policy_current, 269 }) 270 } 271 DiceBoundDerivationKey::OpaqueKey(_opaque_key) => Err(hwcrypto_err!( 272 UNSUPPORTED, 273 "derivation of DICE bound keys using opaque keys not supported yet" 274 )), 275 } 276 } 277 } 278 279 impl IHwCryptoKey for HwCryptoKey { deriveCurrentDicePolicyBoundKey( &self, derivation_key: &DiceBoundDerivationKey, ) -> binder::Result<DiceCurrentBoundKeyResult>280 fn deriveCurrentDicePolicyBoundKey( 281 &self, 282 derivation_key: &DiceBoundDerivationKey, 283 ) -> binder::Result<DiceCurrentBoundKeyResult> { 284 let dice_policy = VersionContext::new_current_encrypted(self.uuid.clone())?; 285 let derived_key_result = self.derive_dice_policy_bound_key(derivation_key, &dice_policy)?; 286 let DiceBoundKeyResult { diceBoundKey: key, dicePolicyWasCurrent: policy_current } = 287 derived_key_result; 288 if !policy_current { 289 return Err(binder::Status::new_exception_str( 290 binder::ExceptionCode::UNSUPPORTED_OPERATION, 291 Some("generated a policy that was not the latest"), 292 )); 293 } 294 Ok(DiceCurrentBoundKeyResult { diceBoundKey: key, dicePolicyForKeyVersion: dice_policy }) 295 } 296 deriveDicePolicyBoundKey( &self, derivation_key: &DiceBoundDerivationKey, dice_policy_for_key_version: &[u8], ) -> binder::Result<DiceBoundKeyResult>297 fn deriveDicePolicyBoundKey( 298 &self, 299 derivation_key: &DiceBoundDerivationKey, 300 dice_policy_for_key_version: &[u8], 301 ) -> binder::Result<DiceBoundKeyResult> { 302 Ok(self.derive_dice_policy_bound_key(derivation_key, dice_policy_for_key_version)?) 303 } 304 deriveKey(&self, parameters: &DerivedKeyParameters) -> binder::Result<DerivedKey>305 fn deriveKey(&self, parameters: &DerivedKeyParameters) -> binder::Result<DerivedKey> { 306 if let DerivedKeyPolicy::ClearKey(policy) = ¶meters.keyPolicy { 307 if policy.keySizeBytes <= 0 { 308 return Err(binder::Status::new_exception_str( 309 binder::ExceptionCode::UNSUPPORTED_OPERATION, 310 Some("we do not support keys of length less or equal to 0"), 311 )); 312 } 313 } 314 315 let derivation_key: OpaqueKey = parameters 316 .derivationKey 317 .as_ref() 318 .ok_or(binder::Status::new_exception_str( 319 binder::ExceptionCode::UNSUPPORTED_OPERATION, 320 Some("didn't receive a derivation key"), 321 ))? 322 .try_into()?; 323 324 let mut concat_context = Vec::<u8>::new(); 325 concat_context.try_reserve(parameters.context.len()).map_err(|_| StatusCode::NO_MEMORY)?; 326 concat_context.extend_from_slice(¶meters.context); 327 concat_context.try_reserve(OPAQUE_CLEAR_CTX_SIZE).map_err(|_| StatusCode::NO_MEMORY)?; 328 329 match ¶meters.keyPolicy { 330 DerivedKeyPolicy::ClearKey(clear_policy) => { 331 concat_context.extend_from_slice(CLEAR_KEY_CTX); 332 // Adding key size to the context as well for a similar reason as to add the key 333 // policy to the context. 334 let key_size = clear_policy.keySizeBytes.try_into().map_err(|_| { 335 binder::Status::new_exception_str( 336 binder::ExceptionCode::UNSUPPORTED_OPERATION, 337 Some("shouldn't happen, we checked that keySize was positive"), 338 ) 339 })?; 340 // A u32 fits on a usize on the architectures we use, so conversion is correct 341 if key_size > (u32::MAX as usize) { 342 return Err(binder::Status::new_exception_str( 343 binder::ExceptionCode::UNSUPPORTED_OPERATION, 344 Some("requested key size was too big"), 345 )); 346 } 347 let key_size_as_bytes = (key_size as u32).to_le_bytes(); 348 concat_context 349 .try_reserve(key_size_as_bytes.len()) 350 .map_err(|_| StatusCode::NO_MEMORY)?; 351 concat_context.extend_from_slice(&key_size_as_bytes[..]); 352 353 let derived_key = 354 derivation_key.derive_raw_key_material(concat_context.as_slice(), key_size)?; 355 Ok(DerivedKey::ExplicitKey(derived_key)) 356 } 357 DerivedKeyPolicy::OpaqueKey(key_policy) => { 358 concat_context.extend_from_slice(OPAQUE_KEY_CTX); 359 // TODO: Add keyPolicy to the context to mitigate attacks trying to use the same 360 // generated key material under different algorithms. 361 let _derived_key = 362 derivation_key.derive_key(key_policy, concat_context.as_slice())?; 363 Err(binder::Status::new_exception_str( 364 binder::ExceptionCode::UNSUPPORTED_OPERATION, 365 Some("cannot return opaque keys until we add its policy to context"), 366 )) 367 } 368 } 369 } 370 } 371 372 #[cfg(test)] 373 mod tests { 374 use super::*; 375 use android_hardware_security_see::aidl::android::hardware::security::see::hwcrypto::IHwCryptoKey::ClearKeyPolicy::ClearKeyPolicy; 376 use test::{assert_ok, expect}; 377 378 #[test] derived_dice_bound_keys()379 fn derived_dice_bound_keys() { 380 let hw_device_key = HwCryptoKey::new_binder( 381 Uuid::new_from_string("f41a7796-975a-4279-8cc4-b73f8820430d").unwrap(), 382 ); 383 384 let derivation_key = DiceBoundDerivationKey::KeyId(DeviceKeyId::DEVICE_BOUND_KEY); 385 let key_and_policy = 386 assert_ok!(hw_device_key.deriveCurrentDicePolicyBoundKey(&derivation_key)); 387 let DiceCurrentBoundKeyResult { diceBoundKey: key, dicePolicyForKeyVersion: policy } = 388 key_and_policy; 389 expect!(key.is_some(), "should have received a key"); 390 expect!(policy.len() > 0, "should have received a DICE policy"); 391 let key_and_policy = 392 assert_ok!(hw_device_key.deriveDicePolicyBoundKey(&derivation_key, &policy)); 393 let DiceBoundKeyResult { diceBoundKey: key, dicePolicyWasCurrent: current_policy } = 394 key_and_policy; 395 expect!(key.is_some(), "should have received a key"); 396 expect!(current_policy, "policy should have been current"); 397 398 let derivation_key = DiceBoundDerivationKey::KeyId(DeviceKeyId::BATCH_KEY); 399 let key_and_policy = 400 assert_ok!(hw_device_key.deriveCurrentDicePolicyBoundKey(&derivation_key)); 401 let DiceCurrentBoundKeyResult { diceBoundKey: key, dicePolicyForKeyVersion: policy } = 402 key_and_policy; 403 expect!(key.is_some(), "should have received a key"); 404 expect!(policy.len() > 0, "should have received a DICE policy"); 405 let key_and_policy = 406 assert_ok!(hw_device_key.deriveDicePolicyBoundKey(&derivation_key, &policy)); 407 let DiceBoundKeyResult { diceBoundKey: key, dicePolicyWasCurrent: current_policy } = 408 key_and_policy; 409 expect!(key.is_some(), "should have received a key"); 410 expect!(current_policy, "policy should have been current"); 411 } 412 413 #[test] derived_clear_key()414 fn derived_clear_key() { 415 let hw_device_key = HwCryptoKey::new_binder( 416 Uuid::new_from_string("f41a7796-975a-4279-8cc4-b73f8820430d").unwrap(), 417 ); 418 419 let derivation_key = DiceBoundDerivationKey::KeyId(DeviceKeyId::DEVICE_BOUND_KEY); 420 let key_and_policy = 421 assert_ok!(hw_device_key.deriveCurrentDicePolicyBoundKey(&derivation_key)); 422 let DiceCurrentBoundKeyResult { diceBoundKey: key, dicePolicyForKeyVersion: policy } = 423 key_and_policy; 424 expect!(key.is_some(), "should have received a key"); 425 expect!(policy.len() > 0, "should have received a DICE policy"); 426 427 let clear_key_policy = DerivedKeyPolicy::ClearKey(ClearKeyPolicy { keySizeBytes: 0 }); 428 let mut params = DerivedKeyParameters { 429 derivationKey: key, 430 keyPolicy: clear_key_policy, 431 context: "context".as_bytes().to_vec(), 432 }; 433 let key = hw_device_key.deriveKey(¶ms); 434 expect!(key.is_err(), "shouldn't be able to create a key of length 0"); 435 436 let clear_key_policy = DerivedKeyPolicy::ClearKey(ClearKeyPolicy { keySizeBytes: 32 }); 437 params.keyPolicy = clear_key_policy; 438 let derived_key = assert_ok!(hw_device_key.deriveKey(¶ms)); 439 let key1 = match derived_key { 440 DerivedKey::ExplicitKey(key) => key, 441 DerivedKey::Opaque(_) => panic!("wrong type of key received"), 442 }; 443 444 let key_and_policy = 445 assert_ok!(hw_device_key.deriveDicePolicyBoundKey(&derivation_key, &policy)); 446 let DiceBoundKeyResult { diceBoundKey: key, dicePolicyWasCurrent: current_policy } = 447 key_and_policy; 448 expect!(key.is_some(), "should have received a key"); 449 expect!(current_policy, "policy should have been current"); 450 params.derivationKey = key; 451 let derived_key = assert_ok!(hw_device_key.deriveKey(¶ms)); 452 let key2 = match derived_key { 453 DerivedKey::ExplicitKey(key) => key, 454 DerivedKey::Opaque(_) => panic!("wrong type of key received"), 455 }; 456 expect!(openssl::memcmp::eq(&key1, &key2), "keys should have matched"); 457 458 params.context = "cont3xt".as_bytes().to_vec(); 459 let derived_key = assert_ok!(hw_device_key.deriveKey(¶ms)); 460 let key3 = match derived_key { 461 DerivedKey::ExplicitKey(key) => key, 462 DerivedKey::Opaque(_) => panic!("wrong type of key received"), 463 }; 464 expect!(!openssl::memcmp::eq(&key1, &key3), "keys shouldn't have matched"); 465 } 466 } 467