blob: 237d7d236d514289a6ba183c450aae51e4756359 [file] [log] [blame]
Paul Crowley44c02da2021-04-08 17:04:43 +00001// Copyright 2021, 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//! Offer keys based on the "boot level" for superencryption.
16
Janis Danisevskis0cabd712021-05-25 11:07:10 -070017use crate::{
18 database::{KeyType, KeystoreDB},
19 key_parameter::KeyParameterValue,
20 raw_device::KeyMintDevice,
21};
Paul Crowley44c02da2021-04-08 17:04:43 +000022use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
Janis Danisevskisacebfa22021-05-25 10:56:10 -070023 Algorithm::Algorithm, Digest::Digest, KeyParameter::KeyParameter as KmKeyParameter,
Paul Crowleye1e17232022-08-08 16:17:18 -070024 KeyPurpose::KeyPurpose, SecurityLevel::SecurityLevel,
Paul Crowley44c02da2021-04-08 17:04:43 +000025};
Paul Crowley44c02da2021-04-08 17:04:43 +000026use anyhow::{Context, Result};
Paul Crowley44c02da2021-04-08 17:04:43 +000027use keystore2_crypto::{hkdf_expand, ZVec, AES_256_KEY_LENGTH};
28use std::{collections::VecDeque, convert::TryFrom};
29
Paul Crowleye1e17232022-08-08 16:17:18 -070030/// Strategies used to prevent later boot stages from using the KM key that protects the level 0
31/// key
32#[derive(Debug, PartialEq, Eq, Clone, Copy)]
33enum DenyLaterStrategy {
34 /// set MaxUsesPerBoot to 1. This is much less secure, since the attacker can replace the key
35 /// itself, and therefore create artifacts which appear to come from early boot.
36 MaxUsesPerBoot,
37 /// set the EarlyBootOnly property. This property is only supported in KM from 4.1 on, but
38 /// it ensures that the level 0 key was genuinely created in early boot
39 EarlyBootOnly,
40}
41
Paul Crowleyf71fe8a2022-08-22 13:02:38 -070042/// Generally the L0 KM and strategy are chosen by probing KM versions in TEE and Strongbox.
43/// However, once a device is launched the KM and strategy must never change, even if the
44/// KM version in TEE or Strongbox is updated. Setting this property at build time using
45/// `PRODUCT_VENDOR_PROPERTIES` means that the strategy can be fixed no matter what versions
46/// of KM are present.
47const PROPERTY_NAME: &str = "ro.keystore.boot_level_key.strategy";
48
49fn lookup_level_zero_km_and_strategy() -> Result<Option<(SecurityLevel, DenyLaterStrategy)>> {
50 let property_val = rustutils::system_properties::read(PROPERTY_NAME).with_context(|| {
51 format!("In lookup_level_zero_km_and_strategy: property read failed: {}", PROPERTY_NAME)
52 })?;
53 // TODO: use feature(let_else) when that's stabilized.
54 let property_val = if let Some(p) = property_val {
55 p
56 } else {
57 log::info!("{} not set, inferring from installed KM instances", PROPERTY_NAME);
58 return Ok(None);
59 };
60 let (level, strategy) = if let Some(c) = property_val.split_once(':') {
61 c
62 } else {
63 log::error!("Missing colon in {}: {:?}", PROPERTY_NAME, property_val);
64 return Ok(None);
65 };
66 let level = match level {
67 "TRUSTED_ENVIRONMENT" => SecurityLevel::TRUSTED_ENVIRONMENT,
68 "STRONGBOX" => SecurityLevel::STRONGBOX,
69 _ => {
70 log::error!("Unknown security level in {}: {:?}", PROPERTY_NAME, level);
71 return Ok(None);
72 }
73 };
74 let strategy = match strategy {
75 "EARLY_BOOT_ONLY" => DenyLaterStrategy::EarlyBootOnly,
76 "MAX_USES_PER_BOOT" => DenyLaterStrategy::MaxUsesPerBoot,
77 _ => {
78 log::error!("Unknown DenyLaterStrategy in {}: {:?}", PROPERTY_NAME, strategy);
79 return Ok(None);
80 }
81 };
82 log::info!("Set from {}: {}", PROPERTY_NAME, property_val);
83 Ok(Some((level, strategy)))
84}
85
Paul Crowleye1e17232022-08-08 16:17:18 -070086fn get_level_zero_key_km_and_strategy() -> Result<(KeyMintDevice, DenyLaterStrategy)> {
Paul Crowleyf71fe8a2022-08-22 13:02:38 -070087 if let Some((level, strategy)) = lookup_level_zero_km_and_strategy()? {
88 return Ok((
89 KeyMintDevice::get(level)
90 .context("In get_level_zero_key_km_and_strategy: Get KM instance failed.")?,
91 strategy,
92 ));
93 }
Janis Danisevskis5c748212021-05-17 17:13:56 -070094 let tee = KeyMintDevice::get(SecurityLevel::TRUSTED_ENVIRONMENT)
Paul Crowleye1e17232022-08-08 16:17:18 -070095 .context("In get_level_zero_key_km_and_strategy: Get TEE instance failed.")?;
Janis Danisevskis5c748212021-05-17 17:13:56 -070096 if tee.version() >= KeyMintDevice::KEY_MASTER_V4_1 {
Paul Crowleye1e17232022-08-08 16:17:18 -070097 Ok((tee, DenyLaterStrategy::EarlyBootOnly))
Janis Danisevskis5c748212021-05-17 17:13:56 -070098 } else {
Paul Crowleye1e17232022-08-08 16:17:18 -070099 match KeyMintDevice::get_or_none(SecurityLevel::STRONGBOX)
100 .context("In get_level_zero_key_km_and_strategy: Get Strongbox instance failed.")?
101 {
Janis Danisevskis5c748212021-05-17 17:13:56 -0700102 Some(strongbox) if strongbox.version() >= KeyMintDevice::KEY_MASTER_V4_1 => {
Paul Crowleye1e17232022-08-08 16:17:18 -0700103 Ok((strongbox, DenyLaterStrategy::EarlyBootOnly))
Janis Danisevskis5c748212021-05-17 17:13:56 -0700104 }
Paul Crowleye1e17232022-08-08 16:17:18 -0700105 _ => Ok((tee, DenyLaterStrategy::MaxUsesPerBoot)),
Janis Danisevskis5c748212021-05-17 17:13:56 -0700106 }
107 }
108}
Paul Crowley44c02da2021-04-08 17:04:43 +0000109
Paul Crowley44c02da2021-04-08 17:04:43 +0000110/// This is not thread safe; caller must hold a lock before calling.
111/// In practice the caller is SuperKeyManager and the lock is the
112/// Mutex on its internal state.
113pub fn get_level_zero_key(db: &mut KeystoreDB) -> Result<ZVec> {
Paul Crowleye1e17232022-08-08 16:17:18 -0700114 let (km_dev, deny_later_strategy) = get_level_zero_key_km_and_strategy()
Janis Danisevskis5c748212021-05-17 17:13:56 -0700115 .context("In get_level_zero_key: get preferred KM instance failed")?;
Paul Crowleye1e17232022-08-08 16:17:18 -0700116 log::info!(
117 "In get_level_zero_key: security_level={:?}, deny_later_strategy={:?}",
118 km_dev.security_level(),
119 deny_later_strategy
120 );
121 let required_security_level = km_dev.security_level();
122 let required_param: KmKeyParameter = match deny_later_strategy {
123 DenyLaterStrategy::EarlyBootOnly => KeyParameterValue::EarlyBootOnly,
124 DenyLaterStrategy::MaxUsesPerBoot => KeyParameterValue::MaxUsesPerBoot(1),
125 }
126 .into();
127 let params = vec![
Paul Crowley44c02da2021-04-08 17:04:43 +0000128 KeyParameterValue::Algorithm(Algorithm::HMAC).into(),
129 KeyParameterValue::Digest(Digest::SHA_2_256).into(),
130 KeyParameterValue::KeySize(256).into(),
131 KeyParameterValue::MinMacLength(256).into(),
132 KeyParameterValue::KeyPurpose(KeyPurpose::SIGN).into(),
Paul Crowleyeb964cf2021-04-19 18:14:15 -0700133 KeyParameterValue::NoAuthRequired.into(),
Paul Crowleye1e17232022-08-08 16:17:18 -0700134 required_param.clone(),
Paul Crowley44c02da2021-04-08 17:04:43 +0000135 ];
Janis Danisevskis5c748212021-05-17 17:13:56 -0700136
Paul Crowleye1e17232022-08-08 16:17:18 -0700137 let key_desc = KeyMintDevice::internal_descriptor("boot_level_key".to_string());
Paul Crowley44c02da2021-04-08 17:04:43 +0000138 let (key_id_guard, key_entry) = km_dev
Janis Danisevskis0cabd712021-05-25 11:07:10 -0700139 .lookup_or_generate_key(db, &key_desc, KeyType::Client, &params, |key_characteristics| {
Janis Danisevskisacebfa22021-05-25 10:56:10 -0700140 key_characteristics.iter().any(|kc| {
Paul Crowleye1e17232022-08-08 16:17:18 -0700141 if kc.securityLevel != required_security_level {
142 log::error!(
143 "In get_level_zero_key: security level expected={:?} got={:?}",
144 required_security_level,
145 kc.securityLevel
146 );
147 return false;
Janis Danisevskisacebfa22021-05-25 10:56:10 -0700148 }
Paul Crowleye1e17232022-08-08 16:17:18 -0700149 if !kc.authorizations.iter().any(|a| a == &required_param) {
150 log::error!(
151 "In get_level_zero_key: required param absent {:?}",
152 required_param
153 );
154 return false;
155 }
156 true
Janis Danisevskisacebfa22021-05-25 10:56:10 -0700157 })
158 })
Paul Crowley44c02da2021-04-08 17:04:43 +0000159 .context("In get_level_zero_key: lookup_or_generate_key failed")?;
160
Janis Danisevskise2d774b2021-05-27 10:39:47 -0700161 let params = [
162 KeyParameterValue::MacLength(256).into(),
163 KeyParameterValue::Digest(Digest::SHA_2_256).into(),
164 ];
Paul Crowley44c02da2021-04-08 17:04:43 +0000165 let level_zero_key = km_dev
166 .use_key_in_one_step(
167 db,
Paul Crowley618869e2021-04-08 20:30:54 -0700168 &key_id_guard,
Paul Crowley44c02da2021-04-08 17:04:43 +0000169 &key_entry,
170 KeyPurpose::SIGN,
171 &params,
Paul Crowley618869e2021-04-08 20:30:54 -0700172 None,
Paul Crowley44c02da2021-04-08 17:04:43 +0000173 b"Create boot level key",
174 )
175 .context("In get_level_zero_key: use_key_in_one_step failed")?;
176 // TODO: this is rather unsatisfactory, we need a better way to handle
177 // sensitive binder returns.
178 let level_zero_key = ZVec::try_from(level_zero_key)
179 .context("In get_level_zero_key: conversion to ZVec failed")?;
180 Ok(level_zero_key)
181}
182
183/// Holds the key for the current boot level, and a cache of future keys generated as required.
184/// When the boot level advances, keys prior to the current boot level are securely dropped.
185pub struct BootLevelKeyCache {
186 /// Least boot level currently accessible, if any is.
187 current: usize,
188 /// Invariant: cache entry *i*, if it exists, holds the HKDF key for boot level
189 /// *i* + `current`. If the cache is non-empty it can be grown forwards, but it cannot be
190 /// grown backwards, so keys below `current` are inaccessible.
191 /// `cache.clear()` makes all keys inaccessible.
192 cache: VecDeque<ZVec>,
193}
194
195impl BootLevelKeyCache {
196 const HKDF_ADVANCE: &'static [u8] = b"Advance KDF one step";
197 const HKDF_AES: &'static [u8] = b"Generate AES-256-GCM key";
198 const HKDF_KEY_SIZE: usize = 32;
199
200 /// Initialize the cache with the level zero key.
201 pub fn new(level_zero_key: ZVec) -> Self {
202 let mut cache: VecDeque<ZVec> = VecDeque::new();
203 cache.push_back(level_zero_key);
204 Self { current: 0, cache }
205 }
206
207 /// Report whether the key for the given level can be inferred.
208 pub fn level_accessible(&self, boot_level: usize) -> bool {
209 // If the requested boot level is lower than the current boot level
210 // or if we have reached the end (`cache.empty()`) we can't retrieve
211 // the boot key.
212 boot_level >= self.current && !self.cache.is_empty()
213 }
214
215 /// Get the HKDF key for boot level `boot_level`. The key for level *i*+1
216 /// is calculated from the level *i* key using `hkdf_expand`.
217 fn get_hkdf_key(&mut self, boot_level: usize) -> Result<Option<&ZVec>> {
218 if !self.level_accessible(boot_level) {
219 return Ok(None);
220 }
221 // `self.cache.len()` represents the first entry not in the cache,
222 // so `self.current + self.cache.len()` is the first boot level not in the cache.
223 let first_not_cached = self.current + self.cache.len();
224
225 // Grow the cache forwards until it contains the desired boot level.
226 for _level in first_not_cached..=boot_level {
227 // We check at the start that cache is non-empty and future iterations only push,
228 // so this must unwrap.
229 let highest_key = self.cache.back().unwrap();
230 let next_key = hkdf_expand(Self::HKDF_KEY_SIZE, highest_key, Self::HKDF_ADVANCE)
231 .context("In BootLevelKeyCache::get_hkdf_key: Advancing key one step")?;
232 self.cache.push_back(next_key);
233 }
234
235 // If we reach this point, we should have a key at index boot_level - current.
236 Ok(Some(self.cache.get(boot_level - self.current).unwrap()))
237 }
238
239 /// Drop keys prior to the given boot level, while retaining the ability to generate keys for
240 /// that level and later.
241 pub fn advance_boot_level(&mut self, new_boot_level: usize) -> Result<()> {
242 if !self.level_accessible(new_boot_level) {
243 log::error!(
244 concat!(
245 "In BootLevelKeyCache::advance_boot_level: ",
246 "Failed to advance boot level to {}, current is {}, cache size {}"
247 ),
248 new_boot_level,
249 self.current,
250 self.cache.len()
251 );
252 return Ok(());
253 }
254
255 // We `get` the new boot level for the side effect of advancing the cache to a point
256 // where the new boot level is present.
257 self.get_hkdf_key(new_boot_level)
258 .context("In BootLevelKeyCache::advance_boot_level: Advancing cache")?;
259
260 // Then we split the queue at the index of the new boot level and discard the front,
261 // keeping only the keys with the current boot level or higher.
262 self.cache = self.cache.split_off(new_boot_level - self.current);
263
264 // The new cache has the new boot level at index 0, so we set `current` to
265 // `new_boot_level`.
266 self.current = new_boot_level;
267
268 Ok(())
269 }
270
271 /// Drop all keys, effectively raising the current boot level to infinity; no keys can
272 /// be inferred from this point on.
273 pub fn finish(&mut self) {
274 self.cache.clear();
275 }
276
277 fn expand_key(
278 &mut self,
279 boot_level: usize,
280 out_len: usize,
281 info: &[u8],
282 ) -> Result<Option<ZVec>> {
283 self.get_hkdf_key(boot_level)
284 .context("In BootLevelKeyCache::expand_key: Looking up HKDF key")?
285 .map(|k| hkdf_expand(out_len, k, info))
286 .transpose()
287 .context("In BootLevelKeyCache::expand_key: Calling hkdf_expand")
288 }
289
290 /// Return the AES-256-GCM key for the current boot level.
291 pub fn aes_key(&mut self, boot_level: usize) -> Result<Option<ZVec>> {
292 self.expand_key(boot_level, AES_256_KEY_LENGTH, BootLevelKeyCache::HKDF_AES)
293 .context("In BootLevelKeyCache::aes_key: expand_key failed")
294 }
295}
296
297#[cfg(test)]
298mod test {
299 use super::*;
300
301 #[test]
302 fn test_output_is_consistent() -> Result<()> {
303 let initial_key = b"initial key";
304 let mut blkc = BootLevelKeyCache::new(ZVec::try_from(initial_key as &[u8])?);
Chris Wailes3877f292021-07-26 19:24:18 -0700305 assert!(blkc.level_accessible(0));
306 assert!(blkc.level_accessible(9));
307 assert!(blkc.level_accessible(10));
308 assert!(blkc.level_accessible(100));
Paul Crowley44c02da2021-04-08 17:04:43 +0000309 let v0 = blkc.aes_key(0).unwrap().unwrap();
310 let v10 = blkc.aes_key(10).unwrap().unwrap();
311 assert_eq!(Some(&v0), blkc.aes_key(0)?.as_ref());
312 assert_eq!(Some(&v10), blkc.aes_key(10)?.as_ref());
313 blkc.advance_boot_level(5)?;
Chris Wailes3877f292021-07-26 19:24:18 -0700314 assert!(!blkc.level_accessible(0));
315 assert!(blkc.level_accessible(9));
316 assert!(blkc.level_accessible(10));
317 assert!(blkc.level_accessible(100));
Paul Crowley44c02da2021-04-08 17:04:43 +0000318 assert_eq!(None, blkc.aes_key(0)?);
319 assert_eq!(Some(&v10), blkc.aes_key(10)?.as_ref());
320 blkc.advance_boot_level(10)?;
Chris Wailes3877f292021-07-26 19:24:18 -0700321 assert!(!blkc.level_accessible(0));
322 assert!(!blkc.level_accessible(9));
323 assert!(blkc.level_accessible(10));
324 assert!(blkc.level_accessible(100));
Paul Crowley44c02da2021-04-08 17:04:43 +0000325 assert_eq!(None, blkc.aes_key(0)?);
326 assert_eq!(Some(&v10), blkc.aes_key(10)?.as_ref());
327 blkc.advance_boot_level(0)?;
Chris Wailes3877f292021-07-26 19:24:18 -0700328 assert!(!blkc.level_accessible(0));
329 assert!(!blkc.level_accessible(9));
330 assert!(blkc.level_accessible(10));
331 assert!(blkc.level_accessible(100));
Paul Crowley44c02da2021-04-08 17:04:43 +0000332 assert_eq!(None, blkc.aes_key(0)?);
333 assert_eq!(Some(v10), blkc.aes_key(10)?);
334 blkc.finish();
Chris Wailes3877f292021-07-26 19:24:18 -0700335 assert!(!blkc.level_accessible(0));
336 assert!(!blkc.level_accessible(9));
337 assert!(!blkc.level_accessible(10));
338 assert!(!blkc.level_accessible(100));
Paul Crowley44c02da2021-04-08 17:04:43 +0000339 assert_eq!(None, blkc.aes_key(0)?);
340 assert_eq!(None, blkc.aes_key(10)?);
341 Ok(())
342 }
343}