• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2022, The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 //! Key blob manipulation functionality.
16 
17 use crate::{
18     contains_tag_value, crypto, crypto::aes, km_err, tag, try_to_vec, vec_try, Error,
19     FallibleAllocExt,
20 };
21 use alloc::{
22     format,
23     string::{String, ToString},
24     vec::Vec,
25 };
26 use kmr_derive::AsCborValue;
27 use kmr_wire::keymint::{
28     BootInfo, KeyCharacteristics, KeyParam, KeyPurpose, SecurityLevel, VerifiedBootState,
29 };
30 use kmr_wire::{cbor, cbor_type_error, AsCborValue, CborError};
31 use log::{error, info};
32 use zeroize::ZeroizeOnDrop;
33 
34 pub mod legacy;
35 pub mod sdd_mem;
36 
37 #[cfg(test)]
38 mod tests;
39 
40 /// Nonce value of all zeroes used in AES-GCM key encryption.
41 const ZERO_NONCE: [u8; 12] = [0u8; 12];
42 
43 /// Identifier for secure deletion secret storage slot.
44 #[repr(transparent)]
45 #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, AsCborValue)]
46 pub struct SecureDeletionSlot(pub u32);
47 
48 /// Keyblob format version.
49 #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, AsCborValue)]
50 pub enum Version {
51     /// Version 1.
52     V1 = 0,
53 }
54 
55 /// Encrypted key material, as translated to/from CBOR.
56 #[derive(Clone, Debug)]
57 pub enum EncryptedKeyBlob {
58     /// Version 1 key blob.
59     V1(EncryptedKeyBlobV1),
60     // Future versions go here...
61 }
62 
63 impl EncryptedKeyBlob {
64     /// Construct from serialized data, mapping failure to `ErrorCode::InvalidKeyBlob`.
new(data: &[u8]) -> Result<Self, Error>65     pub fn new(data: &[u8]) -> Result<Self, Error> {
66         Self::from_slice(data)
67             .map_err(|e| km_err!(InvalidKeyBlob, "failed to parse keyblob: {:?}", e))
68     }
69     /// Return the secure deletion slot for the key, if present.
secure_deletion_slot(&self) -> Option<SecureDeletionSlot>70     pub fn secure_deletion_slot(&self) -> Option<SecureDeletionSlot> {
71         match self {
72             EncryptedKeyBlob::V1(blob) => blob.secure_deletion_slot,
73         }
74     }
75     /// Return the additional KEK context for the key.
kek_context(&self) -> &[u8]76     pub fn kek_context(&self) -> &[u8] {
77         match self {
78             EncryptedKeyBlob::V1(blob) => &blob.kek_context,
79         }
80     }
81 }
82 
83 impl AsCborValue for EncryptedKeyBlob {
from_cbor_value(value: cbor::value::Value) -> Result<Self, CborError>84     fn from_cbor_value(value: cbor::value::Value) -> Result<Self, CborError> {
85         let mut a = match value {
86             cbor::value::Value::Array(a) if a.len() == 2 => a,
87             _ => return cbor_type_error(&value, "arr len 2"),
88         };
89         let inner = a.remove(1);
90         let version = Version::from_cbor_value(a.remove(0))?;
91         match version {
92             Version::V1 => Ok(Self::V1(EncryptedKeyBlobV1::from_cbor_value(inner)?)),
93         }
94     }
to_cbor_value(self) -> Result<cbor::value::Value, CborError>95     fn to_cbor_value(self) -> Result<cbor::value::Value, CborError> {
96         Ok(match self {
97             EncryptedKeyBlob::V1(inner) => cbor::value::Value::Array(
98                 vec_try![Version::V1.to_cbor_value()?, inner.to_cbor_value()?]
99                     .map_err(|_e| CborError::AllocationFailed)?,
100             ),
101         })
102     }
cddl_typename() -> Option<String>103     fn cddl_typename() -> Option<String> {
104         Some("EncryptedKeyBlob".to_string())
105     }
cddl_schema() -> Option<String>106     fn cddl_schema() -> Option<String> {
107         Some(format!(
108             "&(
109     [{}, {}] ; Version::V1
110 )",
111             Version::V1 as i32,
112             EncryptedKeyBlobV1::cddl_ref()
113         ))
114     }
115 }
116 
117 /// Encrypted key material, as translated to/from CBOR.
118 #[derive(Clone, Debug, AsCborValue)]
119 pub struct EncryptedKeyBlobV1 {
120     /// Characteristics associated with the key.
121     pub characteristics: Vec<KeyCharacteristics>,
122     /// Nonce used for the key derivation.
123     pub key_derivation_input: [u8; 32],
124     /// Opaque context data needed for root KEK retrieval.
125     pub kek_context: Vec<u8>,
126     /// Key material encrypted with AES-GCM with:
127     ///  - key produced by [`derive_kek`]
128     ///  - plaintext is the CBOR-serialization of [`crypto::KeyMaterial`]
129     ///  - nonce is all zeroes
130     ///  - no additional data.
131     pub encrypted_key_material: coset::CoseEncrypt0,
132     /// Identifier for a slot in secure storage that holds additional secret values
133     /// that are required to derive the key encryption key.
134     pub secure_deletion_slot: Option<SecureDeletionSlot>,
135 }
136 
137 /// Trait to handle keyblobs in a format from a previous implementation.
138 pub trait LegacyKeyHandler {
139     /// Indicate whether a keyblob is a legacy key format.
is_legacy_key(&self, keyblob: &[u8], params: &[KeyParam], root_of_trust: &BootInfo) -> bool140     fn is_legacy_key(&self, keyblob: &[u8], params: &[KeyParam], root_of_trust: &BootInfo) -> bool {
141         // The `convert_legacy_key` method includes a security level parameter so that a new
142         // keyblob can be emitted with the key characterstics assigned appropriately.  However,
143         // for this method the new keyblob is thrown away, so just use `TrustedEnvironment`.
144         match self.convert_legacy_key(
145             keyblob,
146             params,
147             root_of_trust,
148             SecurityLevel::TrustedEnvironment,
149         ) {
150             Ok(_blob) => {
151                 // Successfully converted the keyblob into current format, so assume that means
152                 // that the keyblob was indeed in the legacy format.
153                 true
154             }
155             Err(e) => {
156                 info!("legacy keyblob conversion attempt failed: {:?}", e);
157                 false
158             }
159         }
160     }
161 
162     /// Convert a potentially-legacy key into current format.  Note that any secure deletion data
163     /// associated with the old keyblob should not be deleted until a subsequent call to
164     /// `delete_legacy_key` arrives.
convert_legacy_key( &self, keyblob: &[u8], params: &[KeyParam], root_of_trust: &BootInfo, sec_level: SecurityLevel, ) -> Result<PlaintextKeyBlob, Error>165     fn convert_legacy_key(
166         &self,
167         keyblob: &[u8],
168         params: &[KeyParam],
169         root_of_trust: &BootInfo,
170         sec_level: SecurityLevel,
171     ) -> Result<PlaintextKeyBlob, Error>;
172 
173     /// Delete a potentially-legacy keyblob.
delete_legacy_key(&mut self, keyblob: &[u8]) -> Result<(), Error>174     fn delete_legacy_key(&mut self, keyblob: &[u8]) -> Result<(), Error>;
175 }
176 
177 /// Secret data that can be mixed into the key derivation inputs for keys; if the secret data is
178 /// lost, the key is effectively deleted because the key encryption key for the keyblob cannot be
179 /// re-derived.
180 #[derive(Clone, PartialEq, Eq, AsCborValue, ZeroizeOnDrop)]
181 pub struct SecureDeletionData {
182     /// Secret value that is wiped on factory reset.  This should be populated for all keys, to
183     /// ensure that a factory reset invalidates all keys.
184     pub factory_reset_secret: [u8; 32],
185     /// Per-key secret value that is wiped on deletion of a specific key.  This is only populated
186     /// for keys with secure deletion support; for other keys this field will be all zeroes.
187     pub secure_deletion_secret: [u8; 16],
188 }
189 
190 /// Indication of what kind of key operation requires a secure deletion slot.
191 #[derive(Clone, Copy, PartialEq, Eq)]
192 pub enum SlotPurpose {
193     /// Secure deletion slot needed for key generation.
194     KeyGeneration,
195     /// Secure deletion slot needed for key import.
196     KeyImport,
197     /// Secure deletion slot needed for upgrade of an existing key.
198     KeyUpgrade,
199 }
200 
201 /// Manager for the mapping between secure deletion slots and the corresponding
202 /// [`SecureDeletionData`] instances.
203 pub trait SecureDeletionSecretManager {
204     /// Return a [`SecureDeletionData`] that has the `factory_reset_secret` populated but which has
205     /// all zeroes for the `secure_deletion_secret`. If a factory reset secret has not yet been
206     /// created, do so (possibly using `rng`)
get_or_create_factory_reset_secret( &mut self, rng: &mut dyn crypto::Rng, ) -> Result<SecureDeletionData, Error>207     fn get_or_create_factory_reset_secret(
208         &mut self,
209         rng: &mut dyn crypto::Rng,
210     ) -> Result<SecureDeletionData, Error>;
211 
212     /// Return a [`SecureDeletionData`] that has the `factory_reset_secret` populated
213     /// but which has all zeroes for the `secure_deletion_secret`.
get_factory_reset_secret(&self) -> Result<SecureDeletionData, Error>214     fn get_factory_reset_secret(&self) -> Result<SecureDeletionData, Error>;
215 
216     /// Find an empty slot, populate it with a fresh [`SecureDeletionData`] that includes a per-key
217     /// secret, and return the slot. If the purpose is `SlotPurpose::KeyUpgrade`, there will be a
218     /// subsequent call to `delete_secret()` for the slot associated with the original keyblob;
219     /// implementations should reserve additional expansion space to allow for this.
new_secret( &mut self, rng: &mut dyn crypto::Rng, purpose: SlotPurpose, ) -> Result<(SecureDeletionSlot, SecureDeletionData), Error>220     fn new_secret(
221         &mut self,
222         rng: &mut dyn crypto::Rng,
223         purpose: SlotPurpose,
224     ) -> Result<(SecureDeletionSlot, SecureDeletionData), Error>;
225 
226     /// Retrieve a [`SecureDeletionData`] identified by `slot`.
get_secret(&self, slot: SecureDeletionSlot) -> Result<SecureDeletionData, Error>227     fn get_secret(&self, slot: SecureDeletionSlot) -> Result<SecureDeletionData, Error>;
228 
229     /// Delete the [`SecureDeletionData`] identified by `slot`.
delete_secret(&mut self, slot: SecureDeletionSlot) -> Result<(), Error>230     fn delete_secret(&mut self, slot: SecureDeletionSlot) -> Result<(), Error>;
231 
232     /// Delete all secure deletion data, including the factory reset secret.
delete_all(&mut self)233     fn delete_all(&mut self);
234 }
235 
236 /// RAII class to hold a secure deletion slot.  The slot is deleted when the holder is dropped.
237 struct SlotHolder<'a> {
238     mgr: &'a mut dyn SecureDeletionSecretManager,
239     // Invariant: `slot` is non-`None` except on destruction.
240     slot: Option<SecureDeletionSlot>,
241 }
242 
243 impl Drop for SlotHolder<'_> {
drop(&mut self)244     fn drop(&mut self) {
245         if let Some(slot) = self.slot.take() {
246             if let Err(e) = self.mgr.delete_secret(slot) {
247                 error!("Failed to delete recently-acquired SDD slot {:?}: {:?}", slot, e);
248             }
249         }
250     }
251 }
252 
253 impl<'a> SlotHolder<'a> {
254     /// Reserve a new secure deletion slot.
new( mgr: &'a mut dyn SecureDeletionSecretManager, rng: &mut dyn crypto::Rng, purpose: SlotPurpose, ) -> Result<(Self, SecureDeletionData), Error>255     fn new(
256         mgr: &'a mut dyn SecureDeletionSecretManager,
257         rng: &mut dyn crypto::Rng,
258         purpose: SlotPurpose,
259     ) -> Result<(Self, SecureDeletionData), Error> {
260         let (slot, sdd) = mgr.new_secret(rng, purpose)?;
261         Ok((Self { mgr, slot: Some(slot) }, sdd))
262     }
263 
264     /// Acquire ownership of the secure deletion slot.
consume(mut self) -> SecureDeletionSlot265     fn consume(mut self) -> SecureDeletionSlot {
266         self.slot.take().unwrap() // Safe: `is_some()` invariant
267     }
268 }
269 
270 /// Root of trust information for binding into keyblobs.
271 #[derive(Debug, Clone, AsCborValue)]
272 pub struct RootOfTrustInfo {
273     /// Verified boot key.
274     pub verified_boot_key: Vec<u8>,
275     /// Whether the bootloader is locked.
276     pub device_boot_locked: bool,
277     /// State of verified boot for the device.
278     pub verified_boot_state: VerifiedBootState,
279 }
280 
281 /// Derive a key encryption key used for key blob encryption. The key is an AES-256 key derived
282 /// from `root_key` using HKDF (RFC 5869) with HMAC-SHA256:
283 /// - input keying material = a root key held in hardware. If it contains explicit key material,
284 ///                           perform full HKDF. If the root key is an opaque one, we assume that
285 ///                           the key is able to be directly used on the HKDF expand step.
286 /// - salt = absent
287 /// - info = the following three or four chunks of context data concatenated:
288 ///    - content of `key_derivation_input` (which is random data)
289 ///    - CBOR-serialization of `characteristics`
290 ///    - CBOR-serialized array of additional `KeyParam` items in `hidden`
291 ///    - (if `sdd` provided) CBOR serialization of the `SecureDeletionData`
derive_kek( kdf: &dyn crypto::Hkdf, root_key: &crypto::OpaqueOr<crypto::hmac::Key>, key_derivation_input: &[u8; 32], characteristics: Vec<KeyCharacteristics>, hidden: Vec<KeyParam>, sdd: Option<SecureDeletionData>, ) -> Result<crypto::OpaqueOr<crypto::aes::Key>, Error>292 pub fn derive_kek(
293     kdf: &dyn crypto::Hkdf,
294     root_key: &crypto::OpaqueOr<crypto::hmac::Key>,
295     key_derivation_input: &[u8; 32],
296     characteristics: Vec<KeyCharacteristics>,
297     hidden: Vec<KeyParam>,
298     sdd: Option<SecureDeletionData>,
299 ) -> Result<crypto::OpaqueOr<crypto::aes::Key>, Error> {
300     let mut info = try_to_vec(key_derivation_input)?;
301     info.try_extend_from_slice(&characteristics.into_vec()?)?;
302     info.try_extend_from_slice(&hidden.into_vec()?)?;
303     if let Some(sdd) = sdd {
304         info.try_extend_from_slice(&sdd.into_vec()?)?;
305     }
306 
307     match root_key {
308         crypto::OpaqueOr::Explicit(key_material) => {
309             kdf.hkdf_aes(&[], &key_material.0, &info, aes::Variant::Aes256)
310         }
311         key @ crypto::OpaqueOr::Opaque(_) => kdf.expand_aes(key, &info, aes::Variant::Aes256),
312     }
313 }
314 
315 /// Plaintext key blob.
316 #[derive(Clone, Debug, PartialEq, Eq)]
317 pub struct PlaintextKeyBlob {
318     /// Characteristics associated with the key.
319     pub characteristics: Vec<KeyCharacteristics>,
320     /// Key Material
321     pub key_material: crypto::KeyMaterial,
322 }
323 
324 impl PlaintextKeyBlob {
325     /// Return the set of key parameters at the provided security level.
characteristics_at(&self, sec_level: SecurityLevel) -> Result<&[KeyParam], Error>326     pub fn characteristics_at(&self, sec_level: SecurityLevel) -> Result<&[KeyParam], Error> {
327         tag::characteristics_at(&self.characteristics, sec_level)
328     }
329 
330     /// Check that the key is suitable for the given purpose.
suitable_for(&self, purpose: KeyPurpose, sec_level: SecurityLevel) -> Result<(), Error>331     pub fn suitable_for(&self, purpose: KeyPurpose, sec_level: SecurityLevel) -> Result<(), Error> {
332         if contains_tag_value!(self.characteristics_at(sec_level)?, Purpose, purpose) {
333             Ok(())
334         } else {
335             Err(km_err!(IncompatiblePurpose, "purpose {:?} not supported by keyblob", purpose))
336         }
337     }
338 }
339 
340 /// Consume a plaintext keyblob and emit an encrypted version.  If `sdd_mgr` is provided,
341 /// a secure deletion slot will be embedded into the keyblob.
342 #[allow(clippy::too_many_arguments)]
encrypt( sec_level: SecurityLevel, sdd_mgr: Option<&mut dyn SecureDeletionSecretManager>, aes: &dyn crypto::Aes, kdf: &dyn crypto::Hkdf, rng: &mut dyn crypto::Rng, root_key: &crypto::OpaqueOr<crypto::hmac::Key>, kek_context: &[u8], plaintext_keyblob: PlaintextKeyBlob, hidden: Vec<KeyParam>, purpose: SlotPurpose, ) -> Result<EncryptedKeyBlob, Error>343 pub fn encrypt(
344     sec_level: SecurityLevel,
345     sdd_mgr: Option<&mut dyn SecureDeletionSecretManager>,
346     aes: &dyn crypto::Aes,
347     kdf: &dyn crypto::Hkdf,
348     rng: &mut dyn crypto::Rng,
349     root_key: &crypto::OpaqueOr<crypto::hmac::Key>,
350     kek_context: &[u8],
351     plaintext_keyblob: PlaintextKeyBlob,
352     hidden: Vec<KeyParam>,
353     purpose: SlotPurpose,
354 ) -> Result<EncryptedKeyBlob, Error> {
355     // Determine if secure deletion is required by examining the key characteristics at our
356     // security level.
357     let requires_sdd = plaintext_keyblob
358         .characteristics_at(sec_level)?
359         .iter()
360         .any(|param| matches!(param, KeyParam::RollbackResistance | KeyParam::UsageCountLimit(1)));
361     let (slot_holder, sdd) = match (requires_sdd, sdd_mgr) {
362         (true, Some(sdd_mgr)) => {
363             // Reserve a slot and store it in a [`SlotHolder`] so that it will definitely be
364             // released if there are any errors encountered below.
365             let (holder, sdd) = SlotHolder::new(sdd_mgr, rng, purpose)?;
366             (Some(holder), Some(sdd))
367         }
368         (true, None) => {
369             return Err(km_err!(
370                 RollbackResistanceUnavailable,
371                 "no secure secret storage available"
372             ))
373         }
374         (false, Some(sdd_mgr)) => {
375             // Create a secure deletion secret that just has the factory reset secret in it.
376             (None, Some(sdd_mgr.get_or_create_factory_reset_secret(rng)?))
377         }
378         (false, None) => {
379             // No secure storage available, and none explicitly asked for.  However, this keyblob
380             // will survive factory reset.
381             (None, None)
382         }
383     };
384     let characteristics = plaintext_keyblob.characteristics;
385     let mut key_derivation_input = [0u8; 32];
386     rng.fill_bytes(&mut key_derivation_input[..]);
387     let kek =
388         derive_kek(kdf, root_key, &key_derivation_input, characteristics.clone(), hidden, sdd)?;
389 
390     // Encrypt the plaintext key material into a `Cose_Encrypt0` structure.
391     let cose_encrypt = coset::CoseEncrypt0Builder::new()
392         .protected(coset::HeaderBuilder::new().algorithm(coset::iana::Algorithm::A256GCM).build())
393         .try_create_ciphertext::<_, Error>(
394             &plaintext_keyblob.key_material.into_vec()?,
395             &[],
396             move |pt, aad| {
397                 let mut op = aes.begin_aead(
398                     kek,
399                     crypto::aes::GcmMode::GcmTag16 { nonce: ZERO_NONCE },
400                     crypto::SymmetricOperation::Encrypt,
401                 )?;
402                 op.update_aad(aad)?;
403                 let mut ct = op.update(pt)?;
404                 ct.try_extend_from_slice(&op.finish()?)?;
405                 Ok(ct)
406             },
407         )?
408         .build();
409 
410     Ok(EncryptedKeyBlob::V1(EncryptedKeyBlobV1 {
411         characteristics,
412         key_derivation_input,
413         kek_context: try_to_vec(kek_context)?,
414         encrypted_key_material: cose_encrypt,
415         secure_deletion_slot: slot_holder.map(|h| h.consume()),
416     }))
417 }
418 
419 /// Consume an encrypted keyblob and emit an decrypted version.
decrypt( sdd_mgr: Option<&dyn SecureDeletionSecretManager>, aes: &dyn crypto::Aes, kdf: &dyn crypto::Hkdf, root_key: &crypto::OpaqueOr<crypto::hmac::Key>, encrypted_keyblob: EncryptedKeyBlob, hidden: Vec<KeyParam>, ) -> Result<PlaintextKeyBlob, Error>420 pub fn decrypt(
421     sdd_mgr: Option<&dyn SecureDeletionSecretManager>,
422     aes: &dyn crypto::Aes,
423     kdf: &dyn crypto::Hkdf,
424     root_key: &crypto::OpaqueOr<crypto::hmac::Key>,
425     encrypted_keyblob: EncryptedKeyBlob,
426     hidden: Vec<KeyParam>,
427 ) -> Result<PlaintextKeyBlob, Error> {
428     let EncryptedKeyBlob::V1(encrypted_keyblob) = encrypted_keyblob;
429     let sdd = match (encrypted_keyblob.secure_deletion_slot, sdd_mgr) {
430         (Some(slot), Some(sdd_mgr)) => Some(sdd_mgr.get_secret(slot)?),
431         (Some(_slot), None) => {
432             return Err(km_err!(
433                 InvalidKeyBlob,
434                 "keyblob has sdd slot but no secure storage available"
435             ))
436         }
437         (None, Some(sdd_mgr)) => {
438             // Keyblob should be bound to (just) the factory reset secret.
439             Some(sdd_mgr.get_factory_reset_secret()?)
440         }
441         (None, None) => None,
442     };
443     let characteristics = encrypted_keyblob.characteristics;
444     let kek = derive_kek(
445         kdf,
446         root_key,
447         &encrypted_keyblob.key_derivation_input,
448         characteristics.clone(),
449         hidden,
450         sdd,
451     )?;
452     let cose_encrypt = encrypted_keyblob.encrypted_key_material;
453 
454     let extended_aad = coset::enc_structure_data(
455         coset::EncryptionContext::CoseEncrypt0,
456         cose_encrypt.protected.clone(),
457         &[], // no external AAD
458     );
459 
460     let mut op = aes.begin_aead(
461         kek,
462         crypto::aes::GcmMode::GcmTag16 { nonce: ZERO_NONCE },
463         crypto::SymmetricOperation::Decrypt,
464     )?;
465     op.update_aad(&extended_aad)?;
466     let mut pt_data = op.update(&cose_encrypt.ciphertext.unwrap_or_default())?;
467     pt_data.try_extend_from_slice(
468         &op.finish().map_err(|e| km_err!(InvalidKeyBlob, "failed to decrypt keyblob: {:?}", e))?,
469     )?;
470 
471     Ok(PlaintextKeyBlob {
472         characteristics,
473         key_material: <crypto::KeyMaterial>::from_slice(&pt_data)?,
474     })
475 }
476