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 //! Module that provides access to a device specific key for this application and serialize the
18 //! necessary information to derive the same key to a cose-encrypt0 type header
19 
20 use ciborium::Value;
21 use coset::{AsCborValue, CborSerializable, Header, ProtectedHeader};
22 use hwcryptohal_common::{err::HwCryptoError, hwcrypto_err};
23 use hwkey::{Hwkey, OsRollbackVersion, RollbackVersionSource};
24 use kmr_common::{
25     crypto::{self, Aes, Hkdf, Rng},
26     FallibleAllocExt,
27 };
28 
29 use crate::crypto_provider;
30 
31 /// Size of the base encryption key used by the service to derive other versioned context encryption
32 /// keys
33 const SERVICE_KEK_LENGTH: usize = 32;
34 
35 /// Size of the random context used to derive a versioned context specific encryption key
36 pub(crate) const KEY_DERIVATION_CTX_LENGTH: usize = 32;
37 
38 /// Nonce value of all zeroes used in AES-GCM key encryption.
39 const ZERO_NONCE: [u8; 12] = [0u8; 12];
40 
41 const KEY_DERIVATION_CTX_COSE_LABEL: i64 = -65539;
42 const KEY_DERIVATION_VERSION_COSE_LABEL: i64 = -65540;
43 const WRAPPED_CONTENT_TYPE_COSE_LABEL: i64 = -65541;
44 
45 // `EncryptionHeaderKey` defines if we derive a device key bound to HwCrypto service or if we use a
46 // provided key for that. This key will then be used to encrypt some content.
47 pub(crate) enum EncryptionHeaderKey<'a> {
48     // `KeyGenerationContext` will be used to derive a key from a key bounded to HwCrypto service
49     KeyGenerationContext(&'a [u8]),
50     // `ProvidedHkdfKey` will be directly fed to the hkdf algorithm.
51     ProvidedHkdfKey(Vec<u8>),
52 }
53 
54 #[derive(Copy, Clone, Debug, PartialEq)]
55 pub(crate) enum EncryptedContent {
56     DicePolicy = 1,
57     WrappedKeyMaterial = 2,
58     KeyMaterial = 3,
59 }
60 
61 impl TryFrom<u64> for EncryptedContent {
62     type Error = HwCryptoError;
63 
try_from(value: u64) -> Result<Self, Self::Error>64     fn try_from(value: u64) -> Result<Self, Self::Error> {
65         match value {
66             x if x == EncryptedContent::DicePolicy as u64 => Ok(EncryptedContent::DicePolicy),
67             x if x == EncryptedContent::WrappedKeyMaterial as u64 => {
68                 Ok(EncryptedContent::WrappedKeyMaterial)
69             }
70             x if x == EncryptedContent::KeyMaterial as u64 => Ok(EncryptedContent::KeyMaterial),
71             _ => Err(hwcrypto_err!(
72                 SERIALIZATION_ERROR,
73                 "invalid value for EncryptedContent: {}",
74                 value
75             )),
76         }
77     }
78 }
79 
80 impl TryFrom<ciborium::value::Integer> for EncryptedContent {
81     type Error = HwCryptoError;
82 
try_from(value: ciborium::value::Integer) -> Result<Self, Self::Error>83     fn try_from(value: ciborium::value::Integer) -> Result<Self, Self::Error> {
84         let value: u64 = value.try_into().map_err(|_| {
85             hwcrypto_err!(SERIALIZATION_ERROR, "couldn't convert CBOR integer into u64")
86         })?;
87         Ok(value.try_into().map_err(|_| {
88             hwcrypto_err!(
89                 SERIALIZATION_ERROR,
90                 "Error converting encrypted content type from ciborium value"
91             )
92         })?)
93     }
94 }
95 
96 impl From<EncryptedContent> for ciborium::value::Integer {
from(value: EncryptedContent) -> Self97     fn from(value: EncryptedContent) -> Self {
98         (value as u64).into()
99     }
100 }
101 
102 // Header used to derive a different key per each encrypted context. Encryption of the context is
103 // similar to what KeyMint does to wrap keys.
104 pub(crate) struct EncryptionHeader {
105     key_derivation_context: [u8; KEY_DERIVATION_CTX_LENGTH],
106     header_version: u32,
107     wrapped_content_type: EncryptedContent,
108 }
109 
110 impl EncryptionHeader {
new( key_derivation_context: [u8; KEY_DERIVATION_CTX_LENGTH], header_version: u32, wrapped_content_type: EncryptedContent, ) -> Self111     fn new(
112         key_derivation_context: [u8; KEY_DERIVATION_CTX_LENGTH],
113         header_version: u32,
114         wrapped_content_type: EncryptedContent,
115     ) -> Self {
116         Self { key_derivation_context, header_version, wrapped_content_type }
117     }
118 
generate(wrapped_content_type: EncryptedContent) -> Result<Self, HwCryptoError>119     pub(crate) fn generate(wrapped_content_type: EncryptedContent) -> Result<Self, HwCryptoError> {
120         let header_version = get_service_current_version()?;
121         Ok(Self::generate_with_version(header_version, wrapped_content_type))
122     }
123 
generate_with_version( header_version: u32, wrapped_content_type: EncryptedContent, ) -> Self124     pub(crate) fn generate_with_version(
125         header_version: u32,
126         wrapped_content_type: EncryptedContent,
127     ) -> Self {
128         let key_derivation_context = get_new_key_derivation_context();
129         Self::new(key_derivation_context, header_version, wrapped_content_type)
130     }
131 
132     // Function used to generate different device bound encryption keys tied to the HWCrypto service
133     // to be used for different purposes, which include VersionContext encryption and key wrapping.
derive_raw_service_encryption_key( &self, encryption_key: EncryptionHeaderKey, ) -> Result<Vec<u8>, HwCryptoError>134     pub(crate) fn derive_raw_service_encryption_key(
135         &self,
136         encryption_key: EncryptionHeaderKey,
137     ) -> Result<Vec<u8>, HwCryptoError> {
138         let encryption_key = match encryption_key {
139             EncryptionHeaderKey::KeyGenerationContext(key_context) => {
140                 get_encryption_key(self.header_version, key_context)?
141             }
142             EncryptionHeaderKey::ProvidedHkdfKey(key) => {
143                 if key.len() != SERVICE_KEK_LENGTH {
144                     return Err(hwcrypto_err!(
145                         INVALID_KEY,
146                         "We only support hkdf keys of length {}",
147                         SERVICE_KEK_LENGTH
148                     ));
149                 }
150                 key
151             }
152         };
153         derive_key_hkdf(&encryption_key, &self.key_derivation_context[..])
154     }
155 
derive_service_encryption_key( &self, encryption_key: EncryptionHeaderKey, ) -> Result<crypto::aes::Key, HwCryptoError>156     pub(crate) fn derive_service_encryption_key(
157         &self,
158         encryption_key: EncryptionHeaderKey,
159     ) -> Result<crypto::aes::Key, HwCryptoError> {
160         let raw_key = self.derive_raw_service_encryption_key(encryption_key)?;
161         let key_material = crypto::aes::Key::Aes256(
162             raw_key
163                 .try_into()
164                 .expect("should not fail, call with SERVICE_KEK_LENGTH returns 32 bytes"),
165         );
166         Ok(key_material)
167     }
168 
169     /// Encrypt CBOR serializable data using a device key derived using `key_context`
encrypt_content_service_encryption_key<T: AsCborValue>( &self, key_context: EncryptionHeaderKey, content: T, ) -> Result<Vec<u8>, HwCryptoError>170     pub(crate) fn encrypt_content_service_encryption_key<T: AsCborValue>(
171         &self,
172         key_context: EncryptionHeaderKey,
173         content: T,
174     ) -> Result<Vec<u8>, HwCryptoError> {
175         let kek = self.derive_service_encryption_key(key_context)?;
176         let aes = crypto_provider::AesImpl;
177         let cose_encrypt = coset::CoseEncrypt0Builder::new()
178             .protected(self.try_into()?)
179             .try_create_ciphertext::<_, HwCryptoError>(
180                 &content.to_cbor_value()?.to_vec()?,
181                 &[],
182                 move |pt, aad| {
183                     let mut op = aes.begin_aead(
184                         kek.into(),
185                         crypto::aes::GcmMode::GcmTag16 { nonce: ZERO_NONCE },
186                         crypto::SymmetricOperation::Encrypt,
187                     )?;
188                     op.update_aad(aad)?;
189                     let mut ct = op.update(pt)?;
190                     ct.try_extend_from_slice(&op.finish()?)?;
191                     Ok(ct)
192                 },
193             )?
194             .build();
195         Ok(cose_encrypt.to_vec()?)
196     }
197 
198     /// Decrypt CBOR serializable data using a device key derived using `key_context`. Data needs to
199     /// include an `EncryptionHeader` on the COSE protected header.
decrypt_content_service_encryption_key( encrypted_context: &[u8], key_context: EncryptionHeaderKey, wrapped_content_type: EncryptedContent, ) -> Result<(Self, Vec<u8>), HwCryptoError>200     pub(crate) fn decrypt_content_service_encryption_key(
201         encrypted_context: &[u8],
202         key_context: EncryptionHeaderKey,
203         wrapped_content_type: EncryptedContent,
204     ) -> Result<(Self, Vec<u8>), HwCryptoError> {
205         let context: coset::CoseEncrypt0 = coset::CborSerializable::from_slice(encrypted_context)?;
206         let encryption_header: EncryptionHeader = (&context.protected).try_into()?;
207         let kek = encryption_header.derive_service_encryption_key(key_context)?;
208 
209         if encryption_header.wrapped_content_type != wrapped_content_type {
210             return Err(hwcrypto_err!(
211                 BAD_PARAMETER,
212                 "provided content has wrong content. Expected {:?}",
213                 wrapped_content_type
214             ));
215         }
216 
217         let aes = crypto_provider::AesImpl;
218         let mut op = aes.begin_aead(
219             kek.into(),
220             crypto::aes::GcmMode::GcmTag16 { nonce: ZERO_NONCE },
221             crypto::SymmetricOperation::Decrypt,
222         )?;
223         let extended_aad = coset::enc_structure_data(
224             coset::EncryptionContext::CoseEncrypt0,
225             context.protected.clone(),
226             &[], // no external AAD
227         );
228         op.update_aad(&extended_aad)?;
229         let mut pt_data = op.update(&context.ciphertext.unwrap_or_default())?;
230         pt_data.try_extend_from_slice(
231             &op.finish()
232                 .map_err(|e| hwcrypto_err!(INVALID_KEY, "failed to decrypt context: {:?}", e))?,
233         )?;
234         Ok((encryption_header, pt_data))
235     }
236 }
237 
238 // Implementing conversion functions to easily convert from/to COSE headers and  `EncryptionHeader`s
239 impl TryFrom<&ProtectedHeader> for EncryptionHeader {
240     type Error = HwCryptoError;
241 
try_from(value: &ProtectedHeader) -> Result<EncryptionHeader, Self::Error>242     fn try_from(value: &ProtectedHeader) -> Result<EncryptionHeader, Self::Error> {
243         let cose_header_rest = &value.header.rest;
244         if cose_header_rest.len() != 3 {
245             return Err(hwcrypto_err!(
246                 BAD_PARAMETER,
247                 "header length was {} instead of 3",
248                 cose_header_rest.len()
249             ));
250         }
251         let mut key_derivation_context = None;
252         let mut header_version = None;
253         let mut wrapped_content_type: Option<EncryptedContent> = None;
254         for element in cose_header_rest {
255             let label: i64 = element
256                 .0
257                 .clone()
258                 .to_cbor_value()?
259                 .into_integer()
260                 .map_err(|_| {
261                     hwcrypto_err!(
262                         SERIALIZATION_ERROR,
263                         "unsupported string header label {:?}",
264                         element.0
265                     )
266                 })?
267                 .try_into()
268                 .map_err(|_| {
269                     hwcrypto_err!(
270                         SERIALIZATION_ERROR,
271                         "error converting cose label {:?}",
272                         element.0
273                     )
274                 })?;
275             match label {
276                 KEY_DERIVATION_CTX_COSE_LABEL => {
277                     key_derivation_context =
278                         Some(parse_cborium_bytes_to_fixed_array(&element.1, "KEK context")?);
279                 }
280                 KEY_DERIVATION_VERSION_COSE_LABEL => {
281                     header_version = Some(parse_cborium_u32(&element.1, "header version")?);
282                 }
283                 WRAPPED_CONTENT_TYPE_COSE_LABEL => {
284                     wrapped_content_type = Some(
285                         element
286                             .1
287                             .as_integer()
288                             .ok_or(hwcrypto_err!(
289                                 BAD_PARAMETER,
290                                 "wrapped_content_type was not an integer"
291                             ))?
292                             .try_into()?,
293                     );
294                 }
295                 _ => return Err(hwcrypto_err!(BAD_PARAMETER, "unknown label {}", label)),
296             }
297         }
298         let key_derivation_context = key_derivation_context
299             .ok_or(hwcrypto_err!(SERIALIZATION_ERROR, "couldn't parse key context"))?;
300         let header_version = header_version
301             .ok_or(hwcrypto_err!(SERIALIZATION_ERROR, "couldn't parse header version"))?;
302         let wrapped_content_type = wrapped_content_type
303             .ok_or(hwcrypto_err!(SERIALIZATION_ERROR, "couldn't parse content type"))?;
304         Ok(Self::new(key_derivation_context, header_version, wrapped_content_type))
305     }
306 }
307 
308 impl TryFrom<&EncryptionHeader> for Header {
309     type Error = HwCryptoError;
310 
try_from(value: &EncryptionHeader) -> Result<Header, Self::Error>311     fn try_from(value: &EncryptionHeader) -> Result<Header, Self::Error> {
312         let mut key_derivation_context = Vec::<u8>::new();
313         key_derivation_context.try_extend_from_slice(&value.key_derivation_context[..])?;
314         let cose_header = coset::HeaderBuilder::new()
315             .algorithm(coset::iana::Algorithm::A256GCM)
316             .value(KEY_DERIVATION_CTX_COSE_LABEL, Value::Bytes(key_derivation_context))
317             .value(KEY_DERIVATION_VERSION_COSE_LABEL, Value::Integer(value.header_version.into()))
318             .value(
319                 WRAPPED_CONTENT_TYPE_COSE_LABEL,
320                 Value::Integer(value.wrapped_content_type.into()),
321             )
322             .build();
323         Ok(cose_header)
324     }
325 }
326 
327 /// Get the base versioned encryption key used by the service to derive other versioned context
328 /// encryption keys
get_encryption_key(header_version: u32, key_context: &[u8]) -> Result<Vec<u8>, HwCryptoError>329 fn get_encryption_key(header_version: u32, key_context: &[u8]) -> Result<Vec<u8>, HwCryptoError> {
330     let mut key = Vec::<u8>::new();
331     key.try_reserve(SERVICE_KEK_LENGTH)?;
332     key.resize(SERVICE_KEK_LENGTH, 0);
333     let hwkey_session = Hwkey::open()
334         .map_err(|e| hwcrypto_err!(GENERIC_ERROR, "could not connect to hwkey service {:?}", e))?;
335     hwkey_session
336         .derive_key_req()
337         .unique_key()
338         .rollback_version_source(RollbackVersionSource::CommittedVersion)
339         .os_rollback_version(OsRollbackVersion::Version(header_version))
340         .derive(key_context, &mut key[..])
341         .map_err(|e| hwcrypto_err!(GENERIC_ERROR, "could derive key {:?}", e))?;
342     Ok(key)
343 }
344 
345 // Create an AES key compatible with the current crypto backend used
derive_key_hkdf( derivation_key: &[u8], derivation_context: &[u8], ) -> Result<Vec<u8>, HwCryptoError>346 fn derive_key_hkdf(
347     derivation_key: &[u8],
348     derivation_context: &[u8],
349 ) -> Result<Vec<u8>, HwCryptoError> {
350     let kdf = crypto_provider::HmacImpl;
351     Ok(kdf.hkdf(&[], &derivation_key, &derivation_context, SERVICE_KEK_LENGTH)?)
352 }
353 
get_new_key_derivation_context() -> [u8; KEY_DERIVATION_CTX_LENGTH]354 fn get_new_key_derivation_context() -> [u8; KEY_DERIVATION_CTX_LENGTH] {
355     let mut rng = crypto_provider::RngImpl::default();
356     let mut key_ctx = [0u8; KEY_DERIVATION_CTX_LENGTH];
357     rng.fill_bytes(&mut key_ctx[..]);
358     key_ctx
359 }
360 
parse_cborium_bytes_to_fixed_array( value: &ciborium::value::Value, name: &str, ) -> Result<[u8; KEY_DERIVATION_CTX_LENGTH], HwCryptoError>361 pub(crate) fn parse_cborium_bytes_to_fixed_array(
362     value: &ciborium::value::Value,
363     name: &str,
364 ) -> Result<[u8; KEY_DERIVATION_CTX_LENGTH], HwCryptoError> {
365     let value_bytes = value.as_bytes().ok_or(hwcrypto_err!(
366         SERIALIZATION_ERROR,
367         "wrong type when trying to parse bytes for {}",
368         name,
369     ))?;
370     if value_bytes.len() != KEY_DERIVATION_CTX_LENGTH {
371         return Err(hwcrypto_err!(
372             SERIALIZATION_ERROR,
373             "wrong number of bytes for {}, found {}, expected {}",
374             name,
375             value_bytes.len(),
376             KEY_DERIVATION_CTX_LENGTH
377         ));
378     }
379     Ok(value_bytes.as_slice().try_into().expect("Shouldn't fail, we checked size already"))
380 }
381 
parse_cborium_u32( value: &ciborium::value::Value, value_name: &str, ) -> Result<u32, HwCryptoError>382 fn parse_cborium_u32(
383     value: &ciborium::value::Value,
384     value_name: &str,
385 ) -> Result<u32, HwCryptoError> {
386     let integer_value = value.as_integer().ok_or(hwcrypto_err!(
387         SERIALIZATION_ERROR,
388         "wrong type when trying to parse a u32 from {}",
389         value_name
390     ))?;
391     integer_value.try_into().map_err(|e| {
392         hwcrypto_err!(SERIALIZATION_ERROR, "Error converting {} to u32: {}", value_name, e)
393     })
394 }
395 
get_service_current_version() -> Result<u32, HwCryptoError>396 pub(crate) fn get_service_current_version() -> Result<u32, HwCryptoError> {
397     let hwkey_session = Hwkey::open()?;
398 
399     match hwkey_session.query_current_os_version(RollbackVersionSource::CommittedVersion) {
400         Ok(OsRollbackVersion::Version(n)) => Ok(n),
401         _ => Err(hwcrypto_err!(GENERIC_ERROR, "error communicating with HwKey service")),
402     }
403 }
404 
405 #[cfg(test)]
406 mod tests {
407     use super::*;
408     use test::{expect, expect_eq};
409 
410     #[test]
header_encryption_decryption()411     fn header_encryption_decryption() {
412         let header = EncryptionHeader::generate(EncryptedContent::DicePolicy);
413         expect!(header.is_ok(), "couldn't generate header");
414         let header = header.unwrap();
415         let encrypted_content = header.encrypt_content_service_encryption_key(
416             EncryptionHeaderKey::KeyGenerationContext(b"fake_context"),
417             Value::Bytes(b"test_data".to_vec()),
418         );
419         expect!(encrypted_content.is_ok(), "couldn't generate header");
420         let encrypted_content = encrypted_content.unwrap();
421         let decrypted_data = EncryptionHeader::decrypt_content_service_encryption_key(
422             &encrypted_content[..],
423             EncryptionHeaderKey::KeyGenerationContext(b"fake_context"),
424             EncryptedContent::DicePolicy,
425         );
426         expect!(decrypted_data.is_ok(), "couldn't generate header");
427         let (decrypted_header, decrypted_content) = decrypted_data.unwrap();
428         let decrypted_content =
429             Value::from_slice(&decrypted_content[..]).unwrap().into_bytes().unwrap();
430         expect_eq!(
431             header.key_derivation_context,
432             decrypted_header.key_derivation_context,
433             "header key derivation context do not match"
434         );
435         expect_eq!(
436             header.header_version,
437             decrypted_header.header_version,
438             "header version do not match"
439         );
440         expect_eq!(decrypted_content, b"test_data", "decrypted data do not match");
441     }
442 }
443