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) = &parameters.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(&parameters.context);
327         concat_context.try_reserve(OPAQUE_CLEAR_CTX_SIZE).map_err(|_| StatusCode::NO_MEMORY)?;
328 
329         match &parameters.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(&params);
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(&params));
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(&params));
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(&params));
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