1 //! Functionality for converting legacy keyblob formats.
2 
3 use alloc::vec::Vec;
4 use kmr_common::keyblob::{
5     legacy, SecureDeletionData, SecureDeletionSecretManager, SecureDeletionSlot,
6 };
7 use kmr_common::{
8     crypto,
9     crypto::{aes, OpaqueKeyMaterial, OpaqueOr},
10     explicit, get_bool_tag_value, get_opt_tag_value, get_tag_value, keyblob, km_err, tag,
11     try_to_vec, vec_try, Error, FallibleAllocExt,
12 };
13 use kmr_ta::device;
14 use kmr_wire::{
15     keymint,
16     keymint::{Algorithm, BootInfo, EcCurve, ErrorCode, KeyParam, KeyPurpose, SecurityLevel},
17     KeySizeInBits,
18 };
19 use log::error;
20 
21 /// Prefix for KEK derivation input when secure deletion not supported.
22 const AES_GCM_DESCRIPTOR_V1: &[u8] = b"AES-256-GCM-HKDF-SHA-256, version 1\0";
23 /// Prefix for KEK derivation input when secure deletion supported.
24 const AES_GCM_DESCRIPTOR_V2: &[u8] = b"AES-256-GCM-HKDF-SHA-256, version 2\0";
25 
26 /// Slot number used to indicate that a key has no per-key secure deletion data.
27 const NO_SDD_SLOT_IDX: u32 = 0;
28 
29 /// Legacy key handler that detects and converts `EncryptedKeyBlob` instances from
30 /// the previous Trusty implementation of KeyMint/Keymaster.
31 pub struct TrustyLegacyKeyBlobHandler {
32     pub aes: Box<dyn crypto::Aes>,
33     pub hkdf: Box<dyn crypto::Hkdf>,
34     pub sdd_mgr: Option<Box<dyn SecureDeletionSecretManager>>,
35     pub keys: Box<dyn device::RetrieveKeyMaterial>,
36 }
37 
38 impl TrustyLegacyKeyBlobHandler {
39     /// Build the derivation information needed for KEK derivation that is compatible with the
40     /// previous C++ implementation.
build_derivation_info( &self, encrypted_keyblob: &legacy::EncryptedKeyBlob, hidden: &[KeyParam], sdd_info: Option<(SecureDeletionData, u32)>, ) -> Result<Vec<u8>, Error>41     fn build_derivation_info(
42         &self,
43         encrypted_keyblob: &legacy::EncryptedKeyBlob,
44         hidden: &[KeyParam],
45         sdd_info: Option<(SecureDeletionData, u32)>,
46     ) -> Result<Vec<u8>, Error> {
47         let mut info = if sdd_info.is_some() {
48             try_to_vec(AES_GCM_DESCRIPTOR_V2)?
49         } else {
50             try_to_vec(AES_GCM_DESCRIPTOR_V1)?
51         };
52         info.try_extend_from_slice(&tag::legacy::serialize(hidden)?)?;
53         info.try_extend_from_slice(&tag::legacy::serialize(&encrypted_keyblob.hw_enforced)?)?;
54         info.try_extend_from_slice(&tag::legacy::serialize(&encrypted_keyblob.sw_enforced)?)?;
55         if let Some((sdd_data, slot)) = sdd_info {
56             info.try_extend_from_slice(
57                 &(sdd_data.factory_reset_secret.len() as u32).to_ne_bytes(),
58             )?;
59             info.try_extend_from_slice(&sdd_data.factory_reset_secret)?;
60 
61             // If the slot is zero, the per-key secret is empty.
62             let secret: &[u8] = if slot == 0 { &[] } else { &sdd_data.secure_deletion_secret };
63             info.try_extend_from_slice(&(secret.len() as u32).to_ne_bytes())?;
64             info.try_extend_from_slice(secret)?;
65 
66             info.try_extend_from_slice(&slot.to_ne_bytes())?;
67         }
68         Ok(info)
69     }
70 
71     /// Derive the key encryption key for a keyblob.
derive_kek( &self, root_kek: &OpaqueOr<kmr_common::crypto::hmac::Key>, encrypted_keyblob: &legacy::EncryptedKeyBlob, hidden: &[KeyParam], sdd_data: Option<(SecureDeletionData, u32)>, ) -> Result<crypto::aes::Key, Error>72     fn derive_kek(
73         &self,
74         root_kek: &OpaqueOr<kmr_common::crypto::hmac::Key>,
75         encrypted_keyblob: &legacy::EncryptedKeyBlob,
76         hidden: &[KeyParam],
77         sdd_data: Option<(SecureDeletionData, u32)>,
78     ) -> Result<crypto::aes::Key, Error> {
79         let info = self.build_derivation_info(encrypted_keyblob, hidden, sdd_data)?;
80         // Trusty uses explicit keys for legacy keyblobs.
81         let raw_key = self.hkdf.hkdf(&[], &explicit!(root_kek)?.0, &info, 256 / 8)?;
82         let aes_key = crypto::aes::Key::Aes256(
83             raw_key.try_into().map_err(|_e| km_err!(UnknownError, "unexpected HKDF output len"))?,
84         );
85         Ok(aes_key)
86     }
87 
88     /// Convert a keyblob from the legacy C++ format to the current format.
convert_key( &self, keyblob: &[u8], params: &[KeyParam], root_of_trust: &BootInfo, sec_level: SecurityLevel, ) -> Result<keyblob::PlaintextKeyBlob, Error>89     fn convert_key(
90         &self,
91         keyblob: &[u8],
92         params: &[KeyParam],
93         root_of_trust: &BootInfo,
94         sec_level: SecurityLevel,
95     ) -> Result<keyblob::PlaintextKeyBlob, Error> {
96         let encrypted_keyblob = legacy::EncryptedKeyBlob::deserialize(keyblob)?;
97 
98         // Find the secure deletion data (if any) for the key.
99         let sdd_info = match (
100             encrypted_keyblob.format.requires_secure_deletion(),
101             &self.sdd_mgr,
102             encrypted_keyblob.key_slot,
103         ) {
104             (true, Some(sdd_mgr), None) | (true, Some(sdd_mgr), Some(NO_SDD_SLOT_IDX)) => {
105                 // Zero slot index implies that just the factory reset secret is populated.
106                 let sdd_data = sdd_mgr.get_factory_reset_secret()?;
107                 Some((sdd_data, NO_SDD_SLOT_IDX))
108             }
109             (true, Some(sdd_mgr), Some(slot_idx)) => {
110                 let slot = SecureDeletionSlot(slot_idx);
111                 let sdd_data = sdd_mgr.get_secret(slot)?;
112 
113                 Some((sdd_data, slot_idx))
114             }
115             (true, None, _) => {
116                 return Err(km_err!(
117                     InvalidKeyBlob,
118                     "keyblob requires secure deletion but no implementation available",
119                 ))
120             }
121             (false, _, Some(slot)) => {
122                 return Err(km_err!(
123                     InvalidKeyBlob,
124                     "unexpected SDD slot {} for format {:?}",
125                     slot,
126                     encrypted_keyblob.format
127                 ))
128             }
129             (false, _, None) => None,
130         };
131 
132         // Convert the key characteristics to current form.
133         let mut characteristics = vec_try![keymint::KeyCharacteristics {
134             security_level: sec_level,
135             authorizations: try_to_vec(&encrypted_keyblob.hw_enforced)?,
136         }]?;
137         if !encrypted_keyblob.sw_enforced.is_empty() {
138             characteristics.try_push(keymint::KeyCharacteristics {
139                 security_level: keymint::SecurityLevel::Keystore,
140                 authorizations: try_to_vec(&encrypted_keyblob.sw_enforced)?,
141             })?;
142         }
143 
144         // Derive the KEK, using hidden inputs from params and root-of-trust.
145         let rots = &[
146             &root_of_trust.verified_boot_key[..],
147             &(root_of_trust.verified_boot_state as u32).to_ne_bytes(),
148             &[if root_of_trust.device_boot_locked { 0x01u8 } else { 0x00u8 }],
149         ];
150         let hidden_params = legacy::hidden(params, rots)?;
151 
152         let rollback_version = match encrypted_keyblob.addl_info {
153             Some(v) => Some(
154                 hwkey::OsRollbackVersion::try_from(v)
155                     .map_err(|e| km_err!(InvalidKeyBlob, "unexpected addl_info={} : {:?}", v, e))?,
156             ),
157             None => None,
158         };
159         let kek_context = super::TrustyKekContext::new(
160             encrypted_keyblob.format.is_versioned(),
161             encrypted_keyblob.kdf_version.map(hwkey::KdfVersion::from),
162             rollback_version,
163         )?
164         .to_raw()?;
165 
166         let root_kek = self.keys.root_kek(&kek_context)?;
167         let aes_key = self.derive_kek(&root_kek, &encrypted_keyblob, &hidden_params, sdd_info)?;
168 
169         // Key material is encrypted with AES-GCM; decrypt it.
170         let nonce: [u8; aes::GCM_NONCE_SIZE] = encrypted_keyblob
171             .nonce
172             .try_into()
173             .map_err(|_e| km_err!(InvalidKeyBlob, "unexpected nonce len",))?;
174         let mode = match encrypted_keyblob.tag.len() {
175             12 => crypto::aes::GcmMode::GcmTag12 { nonce },
176             13 => crypto::aes::GcmMode::GcmTag13 { nonce },
177             14 => crypto::aes::GcmMode::GcmTag14 { nonce },
178             15 => crypto::aes::GcmMode::GcmTag15 { nonce },
179             16 => crypto::aes::GcmMode::GcmTag16 { nonce },
180             l => return Err(km_err!(InvalidKeyBlob, "unexpected AES-GCM tag length {}", l)),
181         };
182         let mut op =
183             self.aes.begin_aead(aes_key.into(), mode, crypto::SymmetricOperation::Decrypt)?;
184         let mut raw_key_material = op.update(&encrypted_keyblob.ciphertext)?;
185         raw_key_material.try_extend_from_slice(&op.update(&encrypted_keyblob.tag)?)?;
186         raw_key_material.try_extend_from_slice(&op.finish()?)?;
187         if raw_key_material.len() != encrypted_keyblob.ciphertext.len() {
188             return Err(km_err!(
189                 UnknownError,
190                 "deciphered len {} != encrypted len {}",
191                 raw_key_material.len(),
192                 encrypted_keyblob.ciphertext.len()
193             ));
194         }
195 
196         // Convert the key material into current form.
197         let chars = &encrypted_keyblob.hw_enforced;
198         let key_material = match get_tag_value!(chars, Algorithm, ErrorCode::InvalidKeyBlob)? {
199             // Symmetric keys have the key material stored as raw bytes.
200             Algorithm::Aes => {
201                 // Special case: an AES key might be a storage key.
202                 if get_bool_tag_value!(chars, StorageKey)? {
203                     // Storage key is opaque data.
204                     crypto::KeyMaterial::Aes(OpaqueOr::Opaque(OpaqueKeyMaterial(raw_key_material)))
205                 } else {
206                     // Normal case: expect explicit AES key material.
207                     crypto::KeyMaterial::Aes(crypto::aes::Key::new(raw_key_material)?.into())
208                 }
209             }
210             Algorithm::TripleDes => {
211                 crypto::KeyMaterial::TripleDes(crypto::des::Key::new(raw_key_material)?.into())
212             }
213             Algorithm::Hmac => {
214                 crypto::KeyMaterial::Hmac(crypto::hmac::Key::new(raw_key_material).into())
215             }
216 
217             // RSA keys have key material stored as a PKCS#1 `RSAPrivateKey` structure, DER-encoded,
218             // as decoded by the BoringSSL `RSA_parse_private_key()` function. This matches the
219             // internal form of a [`crypto::rsa::Key`].
220             Algorithm::Rsa => crypto::KeyMaterial::Rsa(crypto::rsa::Key(raw_key_material).into()),
221 
222             Algorithm::Ec => {
223                 // Determine the EC curve, allowing for old keys that don't include `EC_CURVE` tag.
224                 let ec_curve = match get_opt_tag_value!(chars, EcCurve)? {
225                     Some(c) => *c,
226                     None => match get_tag_value!(chars, KeySize, ErrorCode::InvalidKeyBlob)? {
227                         KeySizeInBits(224) => EcCurve::P224,
228                         KeySizeInBits(384) => EcCurve::P384,
229                         KeySizeInBits(256) => {
230                             return Err(km_err!(InvalidKeyBlob, "key size 256 ambiguous for EC"))
231                         }
232                         KeySizeInBits(521) => EcCurve::P521,
233                         sz => return Err(km_err!(InvalidKeyBlob, "key size {:?} invalid", sz)),
234                     },
235                 };
236                 match ec_curve {
237                     // NIST curve EC keys are stored as an `ECPrivateKey` structure, DER-encoded, as
238                     // decoded by the BoringSSL `EC_KEY_parse_private_key()` function. This matches
239                     // the internal form of a [`crypto::ec::NistKey`].
240                     EcCurve::P224 => crypto::KeyMaterial::Ec(
241                         ec_curve,
242                         crypto::CurveType::Nist,
243                         crypto::ec::Key::P224(crypto::ec::NistKey(raw_key_material)).into(),
244                     ),
245                     EcCurve::P256 => crypto::KeyMaterial::Ec(
246                         ec_curve,
247                         crypto::CurveType::Nist,
248                         crypto::ec::Key::P256(crypto::ec::NistKey(raw_key_material)).into(),
249                     ),
250                     EcCurve::P384 => crypto::KeyMaterial::Ec(
251                         ec_curve,
252                         crypto::CurveType::Nist,
253                         crypto::ec::Key::P384(crypto::ec::NistKey(raw_key_material)).into(),
254                     ),
255                     EcCurve::P521 => crypto::KeyMaterial::Ec(
256                         ec_curve,
257                         crypto::CurveType::Nist,
258                         crypto::ec::Key::P521(crypto::ec::NistKey(raw_key_material)).into(),
259                     ),
260                     EcCurve::Curve25519 => {
261                         let key = crypto::ec::import_pkcs8_key(&raw_key_material)?;
262                         if let crypto::KeyMaterial::Ec(EcCurve::Curve25519, curve_type, _ec_key) =
263                             &key
264                         {
265                             match curve_type {
266                                 crypto::CurveType::Nist => {
267                                     return Err(km_err!(
268                                         InvalidKeyBlob,
269                                         "unexpected NIST key with curve25519"
270                                     ))
271                                 }
272                                 crypto::CurveType::Xdh => {
273                                     if tag::primary_purpose(chars)? != KeyPurpose::AgreeKey {
274                                         return Err(km_err!(
275                                             InvalidKeyBlob,
276                                             "purpose not AGREE_KEY for X25519 key"
277                                         ));
278                                     }
279                                 }
280                                 crypto::CurveType::EdDsa => {
281                                     if tag::primary_purpose(chars)? == KeyPurpose::AgreeKey {
282                                         return Err(km_err!(
283                                             InvalidKeyBlob,
284                                             "AGREE_KEY purpose for non-XDH 25519 key"
285                                         ));
286                                     }
287                                 }
288                             }
289                         } else {
290                             return Err(km_err!(
291                                 InvalidKeyBlob,
292                                 "curve25519 key with wrong contents"
293                             ));
294                         }
295                         key
296                     }
297                 }
298             }
299         };
300 
301         Ok(keyblob::PlaintextKeyBlob { characteristics, key_material })
302     }
303 }
304 
305 impl keyblob::LegacyKeyHandler for TrustyLegacyKeyBlobHandler {
convert_legacy_key( &self, keyblob: &[u8], params: &[KeyParam], root_of_trust: &BootInfo, sec_level: SecurityLevel, ) -> Result<keyblob::PlaintextKeyBlob, Error>306     fn convert_legacy_key(
307         &self,
308         keyblob: &[u8],
309         params: &[KeyParam],
310         root_of_trust: &BootInfo,
311         sec_level: SecurityLevel,
312     ) -> Result<keyblob::PlaintextKeyBlob, Error> {
313         self.convert_key(keyblob, params, root_of_trust, sec_level)
314     }
315 
delete_legacy_key(&mut self, keyblob: &[u8]) -> Result<(), Error>316     fn delete_legacy_key(&mut self, keyblob: &[u8]) -> Result<(), Error> {
317         let encrypted_keyblob = legacy::EncryptedKeyBlob::deserialize(keyblob)?;
318         if let Some(slot) = encrypted_keyblob.key_slot {
319             if slot != NO_SDD_SLOT_IDX {
320                 if !encrypted_keyblob.format.requires_secure_deletion() {
321                     return Err(km_err!(
322                         UnknownError,
323                         "legacy keyblob of non-SDD format {:?} has non-empty SDD slot {:?}!",
324                         encrypted_keyblob.format,
325                         slot
326                     ));
327                 }
328                 if let Some(sdd_mgr) = self.sdd_mgr.as_mut() {
329                     if let Err(e) = sdd_mgr.delete_secret(SecureDeletionSlot(slot)) {
330                         error!("failed to delete SDD slot {:?} for legacy key: {:?}", slot, e);
331                     }
332                 } else {
333                     error!("legacy key has SDD slot {:?} but no SDD mgr available!", slot);
334                 }
335             }
336         }
337         Ok(())
338     }
339 }
340