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