Initial implementation of hwkeyDeriveVersioned

Implement HWCryptoDeviceKeyAccess::hwkeyDeriveVersioned.
Currently it doesn't uses a DICE policy for versioning,
but a custom encrypted blob that can be consumed by Trusty

Bug: 284156656
Test: build & unit test to connect to server
Change-Id: I582a21115c7a150721a81e1cbe3cce1057e8491a
diff --git a/hwcrypto/hwcrypto_consts.json b/hwcrypto/hwcrypto_consts.json
index 9e81027..91e586d 100644
--- a/hwcrypto/hwcrypto_consts.json
+++ b/hwcrypto/hwcrypto_consts.json
@@ -75,6 +75,16 @@
             "name": "HWBCC_UNITTEST_RUST_APP_UUID",
             "value": "67925337-2c03-49ed-9240-d51b6fea3e30",
             "type": "uuid"
+        },
+        {
+            "name": "HWCRYPTOHAL_UNITTEST_RUST_APP_UUID",
+            "value": "f41a7796-975a-4279-8cc4-b73f8820430d",
+            "type": "uuid"
+        },
+        {
+            "name": "HWCRYPTOHAL_RUST_APP_UUID",
+            "value": "f49e28c4-d8b0-41c2-8197-11f27402c0f8",
+            "type": "uuid"
         }
     ]
 }
diff --git a/hwcrypto/hwkey_srv_fake_provider.c b/hwcrypto/hwkey_srv_fake_provider.c
index d8b8435..af34468 100644
--- a/hwcrypto/hwkey_srv_fake_provider.c
+++ b/hwcrypto/hwkey_srv_fake_provider.c
@@ -604,8 +604,15 @@
 #if TEST_BUILD
 /* KM rust unit test uuid */
 static const uuid_t km_rust_unittest_uuid = KM_RUST_UNITTEST_UUID;
+
+/* HWCrypto HAL rust unit test uuid */
+static const uuid_t hwcryptohal_rust_unittest_uuid =
+        HWCRYPTOHAL_UNITTEST_RUST_APP_UUID;
 #endif
 
+/* HWCrypto HAL rust unit test uuid */
+static const uuid_t hwcryptohal_rust_uuid = HWCRYPTOHAL_RUST_APP_UUID;
+
 static uint8_t kak_salt[KM_KAK_SIZE] = {
         0x70, 0xc4, 0x7c, 0xfa, 0x2c, 0xb1, 0xee, 0xdc, 0xa5, 0xdf, 0xbc,
         0x8d, 0xd4, 0xf7, 0x0d, 0x42, 0x93, 0x3b, 0x7f, 0x7b, 0xc2, 0x9e,
@@ -817,7 +824,10 @@
         &km_rust_uuid,
 #if TEST_BUILD
         &km_rust_unittest_uuid,
+        /*HWCrypto HAL needs to derive key and access keylots*/
+        &hwcryptohal_rust_unittest_uuid,
 #endif
+        &hwcryptohal_rust_uuid,
         /* Needs access to opaque keys */
         &hwaes_uuid,
         /* Needs to derive keys */
diff --git a/hwcryptohal/common/err.rs b/hwcryptohal/common/err.rs
index 50192b9..eb49044 100644
--- a/hwcryptohal/common/err.rs
+++ b/hwcryptohal/common/err.rs
@@ -21,6 +21,7 @@
 use android_hardware_security_see::binder;
 use core::array::TryFromSliceError;
 use coset::CoseError;
+use tipc::TipcError;
 
 /// Macro used to create a `HwCryptoError::HalError` by providing the AIDL `HalErrorCode` and a
 /// message: `hwcrypto_err!(UNSUPPORTED, "unsupported operation")`
@@ -53,6 +54,12 @@
     }
 }
 
+impl From<TipcError> for HwCryptoError {
+    fn from(e: TipcError) -> Self {
+        hwcrypto_err!(GENERIC_ERROR, "tipc communication error: {:?}", e)
+    }
+}
+
 impl From<kmr_common::Error> for HwCryptoError {
     fn from(e: kmr_common::Error) -> Self {
         HwCryptoError::KmError(e)
diff --git a/hwcryptohal/server/helpers.rs b/hwcryptohal/server/helpers.rs
new file mode 100644
index 0000000..500d4cf
--- /dev/null
+++ b/hwcryptohal/server/helpers.rs
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+//! Helper functions that includes data transformation for AIDL types.
+
+/// Macro to create enums that can easily be used as cose labels for serialization
+/// It expects the macro definition to have the following form:
+///
+/// cose_enum_gen! {
+///     enum CoseEnumName {
+///         CoseEnumField1 = value1,
+///         CoseEnumField2 = value2,
+///     }
+/// }
+#[macro_export]
+macro_rules! cose_enum_gen {
+    (enum $name:ident {$($field:ident = $field_val:literal),+ $(,)*}) => {
+        enum $name {
+            $($field = $field_val),+
+        }
+
+        impl TryFrom<i64> for $name {
+            type Error = hwcryptohal_common::err::HwCryptoError;
+
+            fn try_from(value: i64) -> Result<Self, Self::Error> {
+                match value {
+                    $(x if x == $name::$field as i64 => Ok($name::$field)),+,
+                    _ => Err(hwcrypto_err!(SERIALIZATION_ERROR, "unsupported COSE enum label val {}", value)),
+                }
+            }
+        }
+
+        impl TryFrom<ciborium::value::Integer> for $name {
+            type Error = coset::CoseError;
+
+            fn try_from(value: ciborium::value::Integer) -> Result<Self, Self::Error> {
+                let value: i64 = value.try_into()?;
+                Ok(value.try_into().map_err(|_| coset::CoseError::EncodeFailed)?)
+            }
+        }
+    }
+}
diff --git a/hwcryptohal/server/hwcrypto_device_key.rs b/hwcryptohal/server/hwcrypto_device_key.rs
index 0a65036..6fefdcd 100644
--- a/hwcryptohal/server/hwcrypto_device_key.rs
+++ b/hwcryptohal/server/hwcrypto_device_key.rs
@@ -18,16 +18,165 @@
 //! retrieve device specific keys.
 
 use android_hardware_security_see::aidl::android::hardware::security::see::hwcrypto::{
-    IHwCryptoKey::BnHwCryptoKey,
+    types::{KeyLifetime::KeyLifetime, KeyType::KeyType, KeyUse::KeyUse},
     IHwCryptoKey::{
-        DerivedKey::DerivedKey, DerivedKeyParameters::DerivedKeyParameters,
-        DiceBoundDerivationKey::DiceBoundDerivationKey, DiceBoundKeyResult::DiceBoundKeyResult,
+        BnHwCryptoKey, DerivedKey::DerivedKey, DerivedKeyParameters::DerivedKeyParameters,
+        DeviceKeyId::DeviceKeyId, DiceBoundDerivationKey::DiceBoundDerivationKey,
+        DiceBoundKeyResult::DiceBoundKeyResult,
         DiceCurrentBoundKeyResult::DiceCurrentBoundKeyResult, IHwCryptoKey,
     },
+    KeyPolicy::KeyPolicy,
 };
 use android_hardware_security_see::binder;
+use ciborium::{cbor, Value};
+use coset::{AsCborValue, CborSerializable, CoseError};
+use hwcryptohal_common::{err::HwCryptoError, hwcrypto_err};
+use hwkey::{Hwkey, KdfVersion};
 use tipc::Uuid;
 
+use crate::cose_enum_gen;
+use crate::opaque_key;
+use crate::service_encryption_key::{self, EncryptionHeader};
+
+const DEVICE_KEY_CTX: &[u8] = b"device_key_derivation_contextKEK";
+const DICE_BOUND_POLICY_CTX: &[u8] = b"dice_bound";
+
+// enum used for serializing the `VersionContext`
+cose_enum_gen! {
+    enum VersionContextCoseLabels {
+        Uuid = -65537,
+        Version = -65538,
+    }
+}
+
+// TODO: `ConnectionInformation` will be opaque to the HwCrypto service once we have a connection
+//        manager.
+struct ConnectionInformation {
+    uuid: Uuid,
+}
+
+// Mock version object to be used until we have more DICE support. It is based on the trusty version
+// retrievable from HwKey and the uuid of the caller. `VersionContext`` encryption is similar to
+// what KeyMint uses to wrap keys.
+struct VersionContext {
+    uuid: Uuid,
+    version: u32,
+    header: Option<EncryptionHeader>,
+}
+
+impl VersionContext {
+    fn get_current_version() -> Result<u32, HwCryptoError> {
+        service_encryption_key::get_service_current_version()
+    }
+
+    fn new_current(uuid: Uuid) -> Result<Self, HwCryptoError> {
+        let header = Some(EncryptionHeader::generate()?);
+        let version = Self::get_current_version()?;
+        Ok(VersionContext { uuid, version, header })
+    }
+
+    fn new_current_encrypted(uuid: Uuid) -> Result<Vec<u8>, HwCryptoError> {
+        let ctx = Self::new_current(uuid)?;
+        Ok(ctx.encrypt_context()?)
+    }
+
+    fn check_version(&self) -> Result<(), HwCryptoError> {
+        let current_version = Self::get_current_version()?;
+        if self.version > current_version {
+            return Err(hwcrypto_err!(BAD_PARAMETER, "version is not valid"));
+        }
+        Ok(())
+    }
+
+    fn check_context(&self, connection: ConnectionInformation) -> Result<(), HwCryptoError> {
+        if connection.uuid != self.uuid {
+            return Err(hwcrypto_err!(BAD_PARAMETER, "uuid mismatch"));
+        }
+        self.check_version()
+    }
+
+    fn check_encrypted_context(
+        encrypted_ctx: &[u8],
+        connection: ConnectionInformation,
+    ) -> Result<(), HwCryptoError> {
+        let context = Self::decrypt_context(encrypted_ctx)?;
+        context.check_context(connection)
+    }
+
+    fn is_context_current(encrypted_ctx: &[u8]) -> Result<bool, HwCryptoError> {
+        let context = Self::decrypt_context(encrypted_ctx)?;
+        let current_version = Self::get_current_version()?;
+        Ok(context.version >= current_version)
+    }
+
+    fn decrypt_context(encrypted_context: &[u8]) -> Result<Self, HwCryptoError> {
+        let (version_ctx_header, decrypted_data) =
+            EncryptionHeader::decrypt_content_service_encryption_key(
+                encrypted_context,
+                DEVICE_KEY_CTX,
+            )?;
+
+        let mut version_context =
+            VersionContext::from_cbor_value(Value::from_slice(&decrypted_data[..])?)?;
+        version_context.header = Some(version_ctx_header);
+        Ok(version_context)
+    }
+
+    fn encrypt_context(mut self) -> Result<Vec<u8>, HwCryptoError> {
+        let header = self.header.take().ok_or(hwcrypto_err!(BAD_PARAMETER, "no header found"))?;
+        header.encrypt_content_service_encryption_key(DEVICE_KEY_CTX, self)
+    }
+
+    fn get_stable_context(encrypted_context: &[u8]) -> Result<Vec<u8>, HwCryptoError> {
+        let decrypted_context = Self::decrypt_context(encrypted_context)?;
+        Ok(decrypted_context.to_cbor_value()?.to_vec()?)
+    }
+}
+
+impl AsCborValue for VersionContext {
+    fn to_cbor_value(self) -> Result<Value, CoseError> {
+        cbor!({
+            (VersionContextCoseLabels::Uuid as i64) => self.uuid.to_string(),
+            (VersionContextCoseLabels::Version as i64) => self.version,
+        })
+        .map_err(|_| CoseError::ExtraneousData)
+    }
+
+    fn from_cbor_value(value: Value) -> Result<Self, CoseError> {
+        let version_context = value.into_map().map_err(|_| CoseError::ExtraneousData)?;
+        let mut uuid: Option<Uuid> = None;
+        let mut version: Option<u32> = None;
+        for (map_key, map_val) in version_context {
+            match map_key {
+                Value::Integer(key) => {
+                    match key.try_into().map_err(|_| CoseError::EncodeFailed)? {
+                        VersionContextCoseLabels::Uuid => {
+                            let uuid_str =
+                                map_val.into_text().map_err(|_| CoseError::EncodeFailed)?;
+                            let parsed_uuid = Uuid::new_from_string(&uuid_str)
+                                .map_err(|_| CoseError::EncodeFailed)?;
+                            uuid = Some(parsed_uuid);
+                        }
+                        VersionContextCoseLabels::Version => {
+                            let parsed_version = map_val
+                                .into_integer()
+                                .map_err(|_| CoseError::EncodeFailed)?
+                                .try_into()
+                                .map_err(|_| CoseError::ExtraneousData)?;
+                            version = Some(parsed_version);
+                        }
+                    }
+                }
+                _ => return Err(CoseError::ExtraneousData),
+            }
+        }
+        let uuid = uuid.ok_or(CoseError::EncodeFailed)?;
+        let version = version.ok_or(CoseError::EncodeFailed)?;
+        // Header travels in the clear, the decoded section only contains the encrypted fields
+        Ok(VersionContext { uuid, version, header: None })
+    }
+}
+
 /// The `IHwCryptoKey` implementation.
 #[derive(Debug)]
 pub struct HwCryptoKey {
@@ -42,28 +191,100 @@
         let hwcrypto_device_key = HwCryptoKey { uuid };
         BnHwCryptoKey::new_binder(hwcrypto_device_key, binder::BinderFeatures::default())
     }
+
+    fn derive_dice_policy_bound_key(
+        &self,
+        derivation_key: &DiceBoundDerivationKey,
+        dice_policy_for_key_version: &[u8],
+    ) -> Result<DiceBoundKeyResult, HwCryptoError> {
+        // Verifying provided DICE policy
+        let connection_info = ConnectionInformation { uuid: self.uuid.clone() };
+        VersionContext::check_encrypted_context(dice_policy_for_key_version, connection_info)?;
+        // Getting back a stable DICE policy for context, so keys derived with the same version will
+        // match
+        let dice_context = VersionContext::get_stable_context(dice_policy_for_key_version)?;
+        let mut concat_context = Vec::<u8>::new();
+        concat_context.try_reserve(DICE_BOUND_POLICY_CTX.len())?;
+        concat_context.extend_from_slice(DICE_BOUND_POLICY_CTX);
+        concat_context.try_reserve(dice_context.len())?;
+        concat_context.extend_from_slice(dice_context.as_slice());
+
+        // The returned key will only be used for derivation, so fixing tis type to HMAC_SHA256
+        let key_type = KeyType::HMAC_SHA256;
+        let key_size = opaque_key::get_key_size_in_bytes(&key_type)?;
+        // Create an array big enough to hold the bytes of the derived key material
+        let mut derived_key = Vec::<u8>::new();
+        derived_key.try_reserve(key_size)?;
+        derived_key.resize(key_size, 0);
+
+        match derivation_key {
+            DiceBoundDerivationKey::KeyId(key_id) => {
+                let hwkey_session = Hwkey::open().map_err(|e| {
+                    hwcrypto_err!(GENERIC_ERROR, "could not connect to hwkey service {:?}", e)
+                })?;
+                let session_req = match *key_id {
+                    DeviceKeyId::DEVICE_BOUND_KEY => {
+                        Ok(hwkey_session.derive_key_req().unique_key())
+                    }
+                    DeviceKeyId::BATCH_KEY => Ok(hwkey_session.derive_key_req().shared_key()),
+                    _ => Err(hwcrypto_err!(UNSUPPORTED, "unknown key id {:?}", key_id)),
+                }?;
+
+                session_req
+                    .kdf(KdfVersion::Best)
+                    .derive(&concat_context, &mut derived_key[..])
+                    .map_err(|e| hwcrypto_err!(GENERIC_ERROR, "failed to derive key {:?}", e))?;
+
+                let policy = KeyPolicy {
+                    usage: KeyUse::DERIVE,
+                    keyLifetime: KeyLifetime::EPHEMERAL,
+                    keyPermissions: Vec::new(),
+                    keyType: key_type,
+                    keyManagementKey: false,
+                };
+                // Create a new opaque key from the generated key material
+                let km = opaque_key::generate_key_material(&policy.keyType, Some(derived_key))?;
+                let key = opaque_key::OpaqueKey::new_binder(&policy, km)
+                    .map_err(|e| hwcrypto_err!(GENERIC_ERROR, "failed to create key {:?}", e))?;
+                let dice_policy_current =
+                    VersionContext::is_context_current(dice_policy_for_key_version)?;
+                Ok(DiceBoundKeyResult {
+                    diceBoundKey: Some(key),
+                    dicePolicyWasCurrent: dice_policy_current,
+                })
+            }
+            DiceBoundDerivationKey::OpaqueKey(_opaque_key) => Err(hwcrypto_err!(
+                UNSUPPORTED,
+                "derivation of DICE bound keys using opaque keys not supported yet"
+            )),
+        }
+    }
 }
 
 impl IHwCryptoKey for HwCryptoKey {
     fn deriveCurrentDicePolicyBoundKey(
         &self,
-        _derivation_key: &DiceBoundDerivationKey,
+        derivation_key: &DiceBoundDerivationKey,
     ) -> binder::Result<DiceCurrentBoundKeyResult> {
-        Err(binder::Status::new_exception_str(
-            binder::ExceptionCode::UNSUPPORTED_OPERATION,
-            Some("operation has not been implemented yet"),
-        ))
+        let dice_policy = VersionContext::new_current_encrypted(self.uuid.clone())?;
+        let derived_key_result = self.derive_dice_policy_bound_key(derivation_key, &dice_policy)?;
+        let DiceBoundKeyResult { diceBoundKey: key, dicePolicyWasCurrent: policy_current } =
+            derived_key_result;
+        if !policy_current {
+            return Err(binder::Status::new_exception_str(
+                binder::ExceptionCode::UNSUPPORTED_OPERATION,
+                Some("generated a policy that was not the latest"),
+            ));
+        }
+        Ok(DiceCurrentBoundKeyResult { diceBoundKey: key, dicePolicyForKeyVersion: dice_policy })
     }
 
     fn deriveDicePolicyBoundKey(
         &self,
-        _derivation_key: &DiceBoundDerivationKey,
-        _dice_policy_for_key_version: &[u8],
+        derivation_key: &DiceBoundDerivationKey,
+        dice_policy_for_key_version: &[u8],
     ) -> binder::Result<DiceBoundKeyResult> {
-        Err(binder::Status::new_exception_str(
-            binder::ExceptionCode::UNSUPPORTED_OPERATION,
-            Some("operation has not been implemented yet"),
-        ))
+        Ok(self.derive_dice_policy_bound_key(derivation_key, dice_policy_for_key_version)?)
     }
 
     fn deriveKey(&self, _parameters: &DerivedKeyParameters) -> binder::Result<DerivedKey> {
@@ -73,3 +294,44 @@
         ))
     }
 }
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use test::{assert_ok, expect};
+
+    #[test]
+    fn derived_dice_bound_keys() {
+        let hw_device_key = HwCryptoKey::new_binder(
+            Uuid::new_from_string("f41a7796-975a-4279-8cc4-b73f8820430d").unwrap(),
+        );
+
+        let derivation_key = DiceBoundDerivationKey::KeyId(DeviceKeyId::DEVICE_BOUND_KEY);
+        let key_and_policy =
+            assert_ok!(hw_device_key.deriveCurrentDicePolicyBoundKey(&derivation_key));
+        let DiceCurrentBoundKeyResult { diceBoundKey: key, dicePolicyForKeyVersion: policy } =
+            key_and_policy;
+        expect!(key.is_some(), "should have received a key");
+        expect!(policy.len() > 0, "should have received a DICE policy");
+        let key_and_policy =
+            assert_ok!(hw_device_key.deriveDicePolicyBoundKey(&derivation_key, &policy));
+        let DiceBoundKeyResult { diceBoundKey: key, dicePolicyWasCurrent: current_policy } =
+            key_and_policy;
+        expect!(key.is_some(), "should have received a key");
+        expect!(current_policy, "policy should have been current");
+
+        let derivation_key = DiceBoundDerivationKey::KeyId(DeviceKeyId::BATCH_KEY);
+        let key_and_policy =
+            assert_ok!(hw_device_key.deriveCurrentDicePolicyBoundKey(&derivation_key));
+        let DiceCurrentBoundKeyResult { diceBoundKey: key, dicePolicyForKeyVersion: policy } =
+            key_and_policy;
+        expect!(key.is_some(), "should have received a key");
+        expect!(policy.len() > 0, "should have received a DICE policy");
+        let key_and_policy =
+            assert_ok!(hw_device_key.deriveDicePolicyBoundKey(&derivation_key, &policy));
+        let DiceBoundKeyResult { diceBoundKey: key, dicePolicyWasCurrent: current_policy } =
+            key_and_policy;
+        expect!(key.is_some(), "should have received a key");
+        expect!(current_policy, "policy should have been current");
+    }
+}
diff --git a/hwcryptohal/server/lib.rs b/hwcryptohal/server/lib.rs
index 8b47b3d..d19bfe7 100644
--- a/hwcryptohal/server/lib.rs
+++ b/hwcryptohal/server/lib.rs
@@ -22,9 +22,11 @@
 
 mod crypto_provider;
 mod ffi_bindings;
+mod helpers;
 mod hwcrypto_device_key;
 mod opaque_key;
 mod platform_functions;
+mod service_encryption_key;
 
 #[cfg(test)]
 mod tests {
diff --git a/hwcryptohal/server/opaque_key.rs b/hwcryptohal/server/opaque_key.rs
index c5b2cba..36e9d15 100644
--- a/hwcryptohal/server/opaque_key.rs
+++ b/hwcryptohal/server/opaque_key.rs
@@ -146,7 +146,7 @@
     pub(crate) fn generate_opaque_key(
         policy: &KeyPolicy,
     ) -> binder::Result<binder::Strong<dyn IOpaqueKey>> {
-        let key_material = generate_key_material(&policy.keyType)?;
+        let key_material = generate_key_material(&policy.keyType, None)?;
         OpaqueKey::new_binder(policy, key_material)
     }
 }
@@ -273,6 +273,16 @@
     }
 }
 
+// Get key size in bytesgiven 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.
@@ -292,9 +302,40 @@
     }
 }
 
-// Use the cryptographic backend to create key material based on the policy `KeyType`. Because HMAC
-// keys can have arbitrary sizes, include an optional `key_size_bits` for that case.
-fn generate_key_material(key_type: &KeyType) -> Result<KeyMaterial, HwCryptoError> {
+// 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 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();
@@ -310,13 +351,56 @@
         | KeyType::AES_256_GCM
         | KeyType::AES_256_CMAC => {
             let variant = get_aes_variant(key_type)?;
-            Ok(aes.generate_key(&mut rng, variant, &[])?)
+            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 => {
-            Ok(hmac.generate_key(&mut rng, kmr_wire::KeySizeInBits(256), &[])?)
-        }
-        KeyType::HMAC_SHA512 => {
-            Ok(hmac.generate_key(&mut rng, kmr_wire::KeySizeInBits(512), &[])?)
+        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"),
     }
@@ -345,7 +429,7 @@
             keyType: key_type,
             keyManagementKey: false,
         };
-        let key_material = generate_key_material(&policy.keyType);
+        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);
diff --git a/hwcryptohal/server/rules.mk b/hwcryptohal/server/rules.mk
index a2b8cb9..608d3c6 100644
--- a/hwcryptohal/server/rules.mk
+++ b/hwcryptohal/server/rules.mk
@@ -30,6 +30,7 @@
 	frameworks/native/libs/binder/trusty/rust/rpcbinder \
 	trusty/user/app/sample/hwcryptohal/aidl/rust  \
 	trusty/user/app/sample/hwcryptohal/common  \
+	trusty/user/base/lib/hwkey/rust \
 	trusty/user/base/lib/keymint-rust/boringssl \
 	trusty/user/base/lib/keymint-rust/common \
 	trusty/user/base/lib/openssl-rust \
diff --git a/hwcryptohal/server/service_encryption_key.rs b/hwcryptohal/server/service_encryption_key.rs
new file mode 100644
index 0000000..61b3d3d
--- /dev/null
+++ b/hwcryptohal/server/service_encryption_key.rs
@@ -0,0 +1,324 @@
+/*
+ * 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.
+ */
+
+//! Module that provides access to a device specific key for this application and serialize the
+//! necessary information to derive the same key to a cose-encrypt0 type header
+
+use ciborium::Value;
+use coset::{AsCborValue, CborSerializable, Header, ProtectedHeader};
+use hwcryptohal_common::{err::HwCryptoError, hwcrypto_err};
+use hwkey::{Hwkey, OsRollbackVersion, RollbackVersionSource};
+use kmr_common::{
+    crypto::{self, Aes, Hkdf, Rng},
+    FallibleAllocExt,
+};
+
+use crate::crypto_provider;
+
+/// Size of the base encryption key used by the service to derive other versioned context encryption
+/// keys
+const SERVICE_KEK_LENGTH: usize = 32;
+
+/// Size of the random context used to derive a versioned context specific encryption key
+pub(crate) const KEY_DERIVATION_CTX_LENGTH: usize = 32;
+
+/// Nonce value of all zeroes used in AES-GCM key encryption.
+const ZERO_NONCE: [u8; 12] = [0u8; 12];
+
+const KEY_DERIVATION_CTX_COSE_LABEL: i64 = -65539;
+const KEY_DERIVATION_VERSION_COSE_LABEL: i64 = -65540;
+
+// Header used to derive a different key per each encrypted context. Encryption of the context is
+// similar to what KeyMint does to wrap keys.
+pub(crate) struct EncryptionHeader {
+    key_derivation_context: [u8; KEY_DERIVATION_CTX_LENGTH],
+    header_version: u32,
+}
+
+impl EncryptionHeader {
+    fn new(key_derivation_context: [u8; KEY_DERIVATION_CTX_LENGTH], header_version: u32) -> Self {
+        Self { key_derivation_context, header_version }
+    }
+
+    pub(crate) fn generate() -> Result<Self, HwCryptoError> {
+        let header_version = get_service_current_version()?;
+        Ok(Self::generate_with_version(header_version))
+    }
+
+    pub(crate) fn generate_with_version(header_version: u32) -> Self {
+        let key_derivation_context = get_new_key_derivation_context();
+        Self::new(key_derivation_context, header_version)
+    }
+
+    // Function used to generate different device bound encryption keys tied to the HWCrypto service
+    // to be used for different purposes, which include VersionContext encryption and key wrapping.
+    fn derive_service_encryption_key(
+        &self,
+        key_context: &[u8],
+    ) -> Result<crypto::aes::Key, HwCryptoError> {
+        let encryption_key = get_encryption_key(self.header_version, key_context)?;
+        derive_key_hkdf(&encryption_key, &self.key_derivation_context[..])
+    }
+
+    /// Encrypt CBOR serializable data using a device key derived using `key_context`
+    pub(crate) fn encrypt_content_service_encryption_key<T: AsCborValue>(
+        &self,
+        key_context: &[u8],
+        content: T,
+    ) -> Result<Vec<u8>, HwCryptoError> {
+        let kek = self.derive_service_encryption_key(key_context)?;
+        let aes = crypto_provider::AesImpl;
+        let cose_encrypt = coset::CoseEncrypt0Builder::new()
+            .protected(self.try_into()?)
+            .try_create_ciphertext::<_, HwCryptoError>(
+                &content.to_cbor_value()?.to_vec()?,
+                &[],
+                move |pt, aad| {
+                    let mut op = aes.begin_aead(
+                        kek.into(),
+                        crypto::aes::GcmMode::GcmTag16 { nonce: ZERO_NONCE },
+                        crypto::SymmetricOperation::Encrypt,
+                    )?;
+                    op.update_aad(aad)?;
+                    let mut ct = op.update(pt)?;
+                    ct.try_extend_from_slice(&op.finish()?)?;
+                    Ok(ct)
+                },
+            )?
+            .build();
+        Ok(cose_encrypt.to_vec()?)
+    }
+
+    /// Decrypt CBOR serializable data using a device key derived using `key_context`. Data needs to
+    /// include an `EncryptionHeader` on the COSE protected header.
+    pub(crate) fn decrypt_content_service_encryption_key(
+        encrypted_context: &[u8],
+        key_context: &[u8],
+    ) -> Result<(Self, Vec<u8>), HwCryptoError> {
+        let context: coset::CoseEncrypt0 = coset::CborSerializable::from_slice(encrypted_context)?;
+        let encryption_header: EncryptionHeader = (&context.protected).try_into()?;
+        let kek = encryption_header.derive_service_encryption_key(key_context)?;
+
+        let aes = crypto_provider::AesImpl;
+        let mut op = aes.begin_aead(
+            kek.into(),
+            crypto::aes::GcmMode::GcmTag16 { nonce: ZERO_NONCE },
+            crypto::SymmetricOperation::Decrypt,
+        )?;
+        let extended_aad = coset::enc_structure_data(
+            coset::EncryptionContext::CoseEncrypt0,
+            context.protected.clone(),
+            &[], // no external AAD
+        );
+        op.update_aad(&extended_aad)?;
+        let mut pt_data = op.update(&context.ciphertext.unwrap_or_default())?;
+        pt_data.try_extend_from_slice(
+            &op.finish()
+                .map_err(|e| hwcrypto_err!(INVALID_KEY, "failed to decrypt context: {:?}", e))?,
+        )?;
+        Ok((encryption_header, pt_data))
+    }
+}
+
+// Implementing conversion functions to easily convert from/to COSE headers and  `EncryptionHeader`s
+impl TryFrom<&ProtectedHeader> for EncryptionHeader {
+    type Error = HwCryptoError;
+
+    fn try_from(value: &ProtectedHeader) -> Result<EncryptionHeader, Self::Error> {
+        let cose_header_rest = &value.header.rest;
+        if cose_header_rest.len() != 2 {
+            return Err(hwcrypto_err!(
+                BAD_PARAMETER,
+                "header length was {} instead of 2",
+                cose_header_rest.len()
+            ));
+        }
+        let mut key_derivation_context = None;
+        let mut header_version = None;
+        for element in cose_header_rest {
+            let label: i64 = element
+                .0
+                .clone()
+                .to_cbor_value()?
+                .into_integer()
+                .map_err(|_| {
+                    hwcrypto_err!(
+                        SERIALIZATION_ERROR,
+                        "unsupported string header label {:?}",
+                        element.0
+                    )
+                })?
+                .try_into()
+                .map_err(|_| {
+                    hwcrypto_err!(
+                        SERIALIZATION_ERROR,
+                        "error converting cose label {:?}",
+                        element.0
+                    )
+                })?;
+            match label {
+                KEY_DERIVATION_CTX_COSE_LABEL => {
+                    key_derivation_context =
+                        Some(parse_cborium_bytes_to_fixed_array(&element.1, "KEK context")?);
+                }
+                KEY_DERIVATION_VERSION_COSE_LABEL => {
+                    header_version = Some(parse_cborium_u32(&element.1, "header version")?);
+                }
+                _ => return Err(hwcrypto_err!(BAD_PARAMETER, "unknown label {}", label)),
+            }
+        }
+        let key_derivation_context = key_derivation_context
+            .ok_or(hwcrypto_err!(SERIALIZATION_ERROR, "couldn't parse key context"))?;
+        let header_version = header_version
+            .ok_or(hwcrypto_err!(SERIALIZATION_ERROR, "couldn't parse header version"))?;
+        Ok(Self::new(key_derivation_context, header_version))
+    }
+}
+
+impl TryFrom<&EncryptionHeader> for Header {
+    type Error = HwCryptoError;
+
+    fn try_from(value: &EncryptionHeader) -> Result<Header, Self::Error> {
+        let mut key_derivation_context = Vec::<u8>::new();
+        key_derivation_context.try_extend_from_slice(&value.key_derivation_context[..])?;
+        let cose_header = coset::HeaderBuilder::new()
+            .algorithm(coset::iana::Algorithm::A256GCM)
+            .value(KEY_DERIVATION_CTX_COSE_LABEL, Value::Bytes(key_derivation_context))
+            .value(KEY_DERIVATION_VERSION_COSE_LABEL, Value::Integer(value.header_version.into()))
+            .build();
+        Ok(cose_header)
+    }
+}
+
+/// Get the base versioned encryption key used by the service to derive other versioned context
+/// encryption keys
+fn get_encryption_key(header_version: u32, key_context: &[u8]) -> Result<Vec<u8>, HwCryptoError> {
+    let mut key = Vec::<u8>::new();
+    key.try_reserve(SERVICE_KEK_LENGTH)?;
+    key.resize(SERVICE_KEK_LENGTH, 0);
+    let hwkey_session = Hwkey::open()
+        .map_err(|e| hwcrypto_err!(GENERIC_ERROR, "could not connect to hwkey service {:?}", e))?;
+    hwkey_session
+        .derive_key_req()
+        .unique_key()
+        .rollback_version_source(RollbackVersionSource::CommittedVersion)
+        .os_rollback_version(OsRollbackVersion::Version(header_version))
+        .derive(key_context, &mut key[..])
+        .map_err(|e| hwcrypto_err!(GENERIC_ERROR, "could derive key {:?}", e))?;
+    Ok(key)
+}
+
+// Create an AES key compatible with the current crypto backend used
+fn derive_key_hkdf(
+    derivation_key: &[u8],
+    derivation_context: &[u8],
+) -> Result<crypto::aes::Key, HwCryptoError> {
+    let kdf = crypto_provider::HmacImpl;
+    let raw_key = kdf.hkdf(&[], &derivation_key, &derivation_context, SERVICE_KEK_LENGTH)?;
+    let key_material = crypto::aes::Key::Aes256(
+        raw_key.try_into().expect("should not fail, call with SERVICE_KEK_LENGTH returns 32 bytes"),
+    );
+    Ok(key_material)
+}
+
+fn get_new_key_derivation_context() -> [u8; KEY_DERIVATION_CTX_LENGTH] {
+    let mut rng = crypto_provider::RngImpl::default();
+    let mut key_ctx = [0u8; KEY_DERIVATION_CTX_LENGTH];
+    rng.fill_bytes(&mut key_ctx[..]);
+    key_ctx
+}
+
+fn parse_cborium_bytes_to_fixed_array(
+    value: &ciborium::value::Value,
+    name: &str,
+) -> Result<[u8; KEY_DERIVATION_CTX_LENGTH], HwCryptoError> {
+    let value_bytes = value.as_bytes().ok_or(hwcrypto_err!(
+        SERIALIZATION_ERROR,
+        "wrong type when trying to parse bytes for {}",
+        name,
+    ))?;
+    if value_bytes.len() != KEY_DERIVATION_CTX_LENGTH {
+        return Err(hwcrypto_err!(
+            SERIALIZATION_ERROR,
+            "wrong number of bytes for {}, found {}, expected {}",
+            name,
+            value_bytes.len(),
+            KEY_DERIVATION_CTX_LENGTH
+        ));
+    }
+    Ok(value_bytes.as_slice().try_into().expect("Shouldn't fail, we checked size already"))
+}
+
+fn parse_cborium_u32(
+    value: &ciborium::value::Value,
+    value_name: &str,
+) -> Result<u32, HwCryptoError> {
+    let integer_value = value.as_integer().ok_or(hwcrypto_err!(
+        SERIALIZATION_ERROR,
+        "wrong type when trying to parse a u32 from {}",
+        value_name
+    ))?;
+    integer_value.try_into().map_err(|e| {
+        hwcrypto_err!(SERIALIZATION_ERROR, "Error converting {} to u32: {}", value_name, e)
+    })
+}
+
+pub(crate) fn get_service_current_version() -> Result<u32, HwCryptoError> {
+    let hwkey_session = Hwkey::open()?;
+
+    match hwkey_session.query_current_os_version(RollbackVersionSource::CommittedVersion) {
+        Ok(OsRollbackVersion::Version(n)) => Ok(n),
+        _ => Err(hwcrypto_err!(GENERIC_ERROR, "error communicating with HwKey service")),
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use test::{expect, expect_eq};
+
+    #[test]
+    fn header_encryption_decryption() {
+        let header = EncryptionHeader::generate();
+        expect!(header.is_ok(), "couldn't generate header");
+        let header = header.unwrap();
+        let encrypted_content = header.encrypt_content_service_encryption_key(
+            b"fake_context",
+            Value::Bytes(b"test_data".to_vec()),
+        );
+        expect!(encrypted_content.is_ok(), "couldn't generate header");
+        let encrypted_content = encrypted_content.unwrap();
+        let decrypted_data = EncryptionHeader::decrypt_content_service_encryption_key(
+            &encrypted_content[..],
+            b"fake_context",
+        );
+        expect!(decrypted_data.is_ok(), "couldn't generate header");
+        let (decrypted_header, decrypted_content) = decrypted_data.unwrap();
+        let decrypted_content =
+            Value::from_slice(&decrypted_content[..]).unwrap().into_bytes().unwrap();
+        expect_eq!(
+            header.key_derivation_context,
+            decrypted_header.key_derivation_context,
+            "header key derivation context do not match"
+        );
+        expect_eq!(
+            header.header_version,
+            decrypted_header.header_version,
+            "header version do not match"
+        );
+        expect_eq!(decrypted_content, b"test_data", "decrypted data do not match");
+    }
+}