| Paul Crowley | 44c02da | 2021-04-08 17:04:43 +0000 | [diff] [blame] | 1 | // 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 |  | 
| Shaquille Johnson | 9da2e1c | 2022-09-19 12:39:01 +0000 | [diff] [blame] | 17 | use crate::ks_err; | 
| Janis Danisevskis | 0cabd71 | 2021-05-25 11:07:10 -0700 | [diff] [blame] | 18 | use crate::{ | 
|  | 19 | database::{KeyType, KeystoreDB}, | 
|  | 20 | key_parameter::KeyParameterValue, | 
|  | 21 | raw_device::KeyMintDevice, | 
|  | 22 | }; | 
| Paul Crowley | 44c02da | 2021-04-08 17:04:43 +0000 | [diff] [blame] | 23 | use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{ | 
| Janis Danisevskis | acebfa2 | 2021-05-25 10:56:10 -0700 | [diff] [blame] | 24 | Algorithm::Algorithm, Digest::Digest, KeyParameter::KeyParameter as KmKeyParameter, | 
| Paul Crowley | e1e1723 | 2022-08-08 16:17:18 -0700 | [diff] [blame] | 25 | KeyPurpose::KeyPurpose, SecurityLevel::SecurityLevel, | 
| Paul Crowley | 44c02da | 2021-04-08 17:04:43 +0000 | [diff] [blame] | 26 | }; | 
| Paul Crowley | 44c02da | 2021-04-08 17:04:43 +0000 | [diff] [blame] | 27 | use anyhow::{Context, Result}; | 
| Paul Crowley | 44c02da | 2021-04-08 17:04:43 +0000 | [diff] [blame] | 28 | use keystore2_crypto::{hkdf_expand, ZVec, AES_256_KEY_LENGTH}; | 
|  | 29 | use std::{collections::VecDeque, convert::TryFrom}; | 
|  | 30 |  | 
| Paul Crowley | e1e1723 | 2022-08-08 16:17:18 -0700 | [diff] [blame] | 31 | /// Strategies used to prevent later boot stages from using the KM key that protects the level 0 | 
|  | 32 | /// key | 
|  | 33 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | 
|  | 34 | enum DenyLaterStrategy { | 
|  | 35 | /// set MaxUsesPerBoot to 1. This is much less secure, since the attacker can replace the key | 
|  | 36 | /// itself, and therefore create artifacts which appear to come from early boot. | 
|  | 37 | MaxUsesPerBoot, | 
|  | 38 | /// set the EarlyBootOnly property. This property is only supported in KM from 4.1 on, but | 
|  | 39 | /// it ensures that the level 0 key was genuinely created in early boot | 
|  | 40 | EarlyBootOnly, | 
|  | 41 | } | 
|  | 42 |  | 
| Paul Crowley | f71fe8a | 2022-08-22 13:02:38 -0700 | [diff] [blame] | 43 | /// Generally the L0 KM and strategy are chosen by probing KM versions in TEE and Strongbox. | 
|  | 44 | /// However, once a device is launched the KM and strategy must never change, even if the | 
|  | 45 | /// KM version in TEE or Strongbox is updated. Setting this property at build time using | 
|  | 46 | /// `PRODUCT_VENDOR_PROPERTIES` means that the strategy can be fixed no matter what versions | 
|  | 47 | /// of KM are present. | 
|  | 48 | const PROPERTY_NAME: &str = "ro.keystore.boot_level_key.strategy"; | 
|  | 49 |  | 
|  | 50 | fn lookup_level_zero_km_and_strategy() -> Result<Option<(SecurityLevel, DenyLaterStrategy)>> { | 
| Shaquille Johnson | 9da2e1c | 2022-09-19 12:39:01 +0000 | [diff] [blame] | 51 | let property_val = rustutils::system_properties::read(PROPERTY_NAME) | 
|  | 52 | .with_context(|| ks_err!("property read failed: {}", PROPERTY_NAME))?; | 
| Paul Crowley | f71fe8a | 2022-08-22 13:02:38 -0700 | [diff] [blame] | 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 Crowley | e1e1723 | 2022-08-08 16:17:18 -0700 | [diff] [blame] | 86 | fn get_level_zero_key_km_and_strategy() -> Result<(KeyMintDevice, DenyLaterStrategy)> { | 
| Paul Crowley | f71fe8a | 2022-08-22 13:02:38 -0700 | [diff] [blame] | 87 | if let Some((level, strategy)) = lookup_level_zero_km_and_strategy()? { | 
|  | 88 | return Ok(( | 
| Shaquille Johnson | 9da2e1c | 2022-09-19 12:39:01 +0000 | [diff] [blame] | 89 | KeyMintDevice::get(level).context(ks_err!("Get KM instance failed."))?, | 
| Paul Crowley | f71fe8a | 2022-08-22 13:02:38 -0700 | [diff] [blame] | 90 | strategy, | 
|  | 91 | )); | 
|  | 92 | } | 
| Janis Danisevskis | 5c74821 | 2021-05-17 17:13:56 -0700 | [diff] [blame] | 93 | let tee = KeyMintDevice::get(SecurityLevel::TRUSTED_ENVIRONMENT) | 
| Shaquille Johnson | 9da2e1c | 2022-09-19 12:39:01 +0000 | [diff] [blame] | 94 | .context(ks_err!("Get TEE instance failed."))?; | 
| Janis Danisevskis | 5c74821 | 2021-05-17 17:13:56 -0700 | [diff] [blame] | 95 | if tee.version() >= KeyMintDevice::KEY_MASTER_V4_1 { | 
| Paul Crowley | e1e1723 | 2022-08-08 16:17:18 -0700 | [diff] [blame] | 96 | Ok((tee, DenyLaterStrategy::EarlyBootOnly)) | 
| Janis Danisevskis | 5c74821 | 2021-05-17 17:13:56 -0700 | [diff] [blame] | 97 | } else { | 
| Paul Crowley | e1e1723 | 2022-08-08 16:17:18 -0700 | [diff] [blame] | 98 | match KeyMintDevice::get_or_none(SecurityLevel::STRONGBOX) | 
| Shaquille Johnson | 9da2e1c | 2022-09-19 12:39:01 +0000 | [diff] [blame] | 99 | .context(ks_err!("Get Strongbox instance failed."))? | 
| Paul Crowley | e1e1723 | 2022-08-08 16:17:18 -0700 | [diff] [blame] | 100 | { | 
| Janis Danisevskis | 5c74821 | 2021-05-17 17:13:56 -0700 | [diff] [blame] | 101 | Some(strongbox) if strongbox.version() >= KeyMintDevice::KEY_MASTER_V4_1 => { | 
| Paul Crowley | e1e1723 | 2022-08-08 16:17:18 -0700 | [diff] [blame] | 102 | Ok((strongbox, DenyLaterStrategy::EarlyBootOnly)) | 
| Janis Danisevskis | 5c74821 | 2021-05-17 17:13:56 -0700 | [diff] [blame] | 103 | } | 
| Paul Crowley | e1e1723 | 2022-08-08 16:17:18 -0700 | [diff] [blame] | 104 | _ => Ok((tee, DenyLaterStrategy::MaxUsesPerBoot)), | 
| Janis Danisevskis | 5c74821 | 2021-05-17 17:13:56 -0700 | [diff] [blame] | 105 | } | 
|  | 106 | } | 
|  | 107 | } | 
| Paul Crowley | 44c02da | 2021-04-08 17:04:43 +0000 | [diff] [blame] | 108 |  | 
| Paul Crowley | 44c02da | 2021-04-08 17:04:43 +0000 | [diff] [blame] | 109 | /// This is not thread safe; caller must hold a lock before calling. | 
|  | 110 | /// In practice the caller is SuperKeyManager and the lock is the | 
|  | 111 | /// Mutex on its internal state. | 
|  | 112 | pub fn get_level_zero_key(db: &mut KeystoreDB) -> Result<ZVec> { | 
| Paul Crowley | e1e1723 | 2022-08-08 16:17:18 -0700 | [diff] [blame] | 113 | let (km_dev, deny_later_strategy) = get_level_zero_key_km_and_strategy() | 
| Shaquille Johnson | 9da2e1c | 2022-09-19 12:39:01 +0000 | [diff] [blame] | 114 | .context(ks_err!("get preferred KM instance failed"))?; | 
| Paul Crowley | e1e1723 | 2022-08-08 16:17:18 -0700 | [diff] [blame] | 115 | log::info!( | 
|  | 116 | "In get_level_zero_key: security_level={:?}, deny_later_strategy={:?}", | 
|  | 117 | km_dev.security_level(), | 
|  | 118 | deny_later_strategy | 
|  | 119 | ); | 
|  | 120 | let required_security_level = km_dev.security_level(); | 
|  | 121 | let required_param: KmKeyParameter = match deny_later_strategy { | 
|  | 122 | DenyLaterStrategy::EarlyBootOnly => KeyParameterValue::EarlyBootOnly, | 
|  | 123 | DenyLaterStrategy::MaxUsesPerBoot => KeyParameterValue::MaxUsesPerBoot(1), | 
|  | 124 | } | 
|  | 125 | .into(); | 
|  | 126 | let params = vec![ | 
| Paul Crowley | 44c02da | 2021-04-08 17:04:43 +0000 | [diff] [blame] | 127 | KeyParameterValue::Algorithm(Algorithm::HMAC).into(), | 
|  | 128 | KeyParameterValue::Digest(Digest::SHA_2_256).into(), | 
|  | 129 | KeyParameterValue::KeySize(256).into(), | 
|  | 130 | KeyParameterValue::MinMacLength(256).into(), | 
|  | 131 | KeyParameterValue::KeyPurpose(KeyPurpose::SIGN).into(), | 
| Paul Crowley | eb964cf | 2021-04-19 18:14:15 -0700 | [diff] [blame] | 132 | KeyParameterValue::NoAuthRequired.into(), | 
| Paul Crowley | e1e1723 | 2022-08-08 16:17:18 -0700 | [diff] [blame] | 133 | required_param.clone(), | 
| Paul Crowley | 44c02da | 2021-04-08 17:04:43 +0000 | [diff] [blame] | 134 | ]; | 
| Janis Danisevskis | 5c74821 | 2021-05-17 17:13:56 -0700 | [diff] [blame] | 135 |  | 
| Paul Crowley | e1e1723 | 2022-08-08 16:17:18 -0700 | [diff] [blame] | 136 | let key_desc = KeyMintDevice::internal_descriptor("boot_level_key".to_string()); | 
| Paul Crowley | 44c02da | 2021-04-08 17:04:43 +0000 | [diff] [blame] | 137 | let (key_id_guard, key_entry) = km_dev | 
| Janis Danisevskis | 0cabd71 | 2021-05-25 11:07:10 -0700 | [diff] [blame] | 138 | .lookup_or_generate_key(db, &key_desc, KeyType::Client, ¶ms, |key_characteristics| { | 
| Janis Danisevskis | acebfa2 | 2021-05-25 10:56:10 -0700 | [diff] [blame] | 139 | key_characteristics.iter().any(|kc| { | 
| Paul Crowley | e1e1723 | 2022-08-08 16:17:18 -0700 | [diff] [blame] | 140 | if kc.securityLevel != required_security_level { | 
|  | 141 | log::error!( | 
|  | 142 | "In get_level_zero_key: security level expected={:?} got={:?}", | 
|  | 143 | required_security_level, | 
|  | 144 | kc.securityLevel | 
|  | 145 | ); | 
|  | 146 | return false; | 
| Janis Danisevskis | acebfa2 | 2021-05-25 10:56:10 -0700 | [diff] [blame] | 147 | } | 
| Paul Crowley | e1e1723 | 2022-08-08 16:17:18 -0700 | [diff] [blame] | 148 | if !kc.authorizations.iter().any(|a| a == &required_param) { | 
|  | 149 | log::error!( | 
|  | 150 | "In get_level_zero_key: required param absent {:?}", | 
|  | 151 | required_param | 
|  | 152 | ); | 
|  | 153 | return false; | 
|  | 154 | } | 
|  | 155 | true | 
| Janis Danisevskis | acebfa2 | 2021-05-25 10:56:10 -0700 | [diff] [blame] | 156 | }) | 
|  | 157 | }) | 
| Shaquille Johnson | 9da2e1c | 2022-09-19 12:39:01 +0000 | [diff] [blame] | 158 | .context(ks_err!("lookup_or_generate_key failed"))?; | 
| Paul Crowley | 44c02da | 2021-04-08 17:04:43 +0000 | [diff] [blame] | 159 |  | 
| Janis Danisevskis | e2d774b | 2021-05-27 10:39:47 -0700 | [diff] [blame] | 160 | let params = [ | 
|  | 161 | KeyParameterValue::MacLength(256).into(), | 
|  | 162 | KeyParameterValue::Digest(Digest::SHA_2_256).into(), | 
|  | 163 | ]; | 
| Paul Crowley | 44c02da | 2021-04-08 17:04:43 +0000 | [diff] [blame] | 164 | let level_zero_key = km_dev | 
|  | 165 | .use_key_in_one_step( | 
|  | 166 | db, | 
| Paul Crowley | 618869e | 2021-04-08 20:30:54 -0700 | [diff] [blame] | 167 | &key_id_guard, | 
| Paul Crowley | 44c02da | 2021-04-08 17:04:43 +0000 | [diff] [blame] | 168 | &key_entry, | 
|  | 169 | KeyPurpose::SIGN, | 
|  | 170 | ¶ms, | 
| Paul Crowley | 618869e | 2021-04-08 20:30:54 -0700 | [diff] [blame] | 171 | None, | 
| Paul Crowley | 44c02da | 2021-04-08 17:04:43 +0000 | [diff] [blame] | 172 | b"Create boot level key", | 
|  | 173 | ) | 
| Shaquille Johnson | 9da2e1c | 2022-09-19 12:39:01 +0000 | [diff] [blame] | 174 | .context(ks_err!("use_key_in_one_step failed"))?; | 
| Paul Crowley | 44c02da | 2021-04-08 17:04:43 +0000 | [diff] [blame] | 175 | // TODO: this is rather unsatisfactory, we need a better way to handle | 
|  | 176 | // sensitive binder returns. | 
| Shaquille Johnson | 9da2e1c | 2022-09-19 12:39:01 +0000 | [diff] [blame] | 177 | let level_zero_key = | 
|  | 178 | ZVec::try_from(level_zero_key).context(ks_err!("conversion to ZVec failed"))?; | 
| Paul Crowley | 44c02da | 2021-04-08 17:04:43 +0000 | [diff] [blame] | 179 | Ok(level_zero_key) | 
|  | 180 | } | 
|  | 181 |  | 
|  | 182 | /// Holds the key for the current boot level, and a cache of future keys generated as required. | 
|  | 183 | /// When the boot level advances, keys prior to the current boot level are securely dropped. | 
|  | 184 | pub struct BootLevelKeyCache { | 
|  | 185 | /// Least boot level currently accessible, if any is. | 
|  | 186 | current: usize, | 
|  | 187 | /// Invariant: cache entry *i*, if it exists, holds the HKDF key for boot level | 
|  | 188 | /// *i* + `current`. If the cache is non-empty it can be grown forwards, but it cannot be | 
|  | 189 | /// grown backwards, so keys below `current` are inaccessible. | 
|  | 190 | /// `cache.clear()` makes all keys inaccessible. | 
|  | 191 | cache: VecDeque<ZVec>, | 
|  | 192 | } | 
|  | 193 |  | 
|  | 194 | impl BootLevelKeyCache { | 
|  | 195 | const HKDF_ADVANCE: &'static [u8] = b"Advance KDF one step"; | 
|  | 196 | const HKDF_AES: &'static [u8] = b"Generate AES-256-GCM key"; | 
|  | 197 | const HKDF_KEY_SIZE: usize = 32; | 
|  | 198 |  | 
|  | 199 | /// Initialize the cache with the level zero key. | 
|  | 200 | pub fn new(level_zero_key: ZVec) -> Self { | 
|  | 201 | let mut cache: VecDeque<ZVec> = VecDeque::new(); | 
|  | 202 | cache.push_back(level_zero_key); | 
|  | 203 | Self { current: 0, cache } | 
|  | 204 | } | 
|  | 205 |  | 
|  | 206 | /// Report whether the key for the given level can be inferred. | 
|  | 207 | pub fn level_accessible(&self, boot_level: usize) -> bool { | 
|  | 208 | // If the requested boot level is lower than the current boot level | 
|  | 209 | // or if we have reached the end (`cache.empty()`) we can't retrieve | 
|  | 210 | // the boot key. | 
|  | 211 | boot_level >= self.current && !self.cache.is_empty() | 
|  | 212 | } | 
|  | 213 |  | 
|  | 214 | /// Get the HKDF key for boot level `boot_level`. The key for level *i*+1 | 
|  | 215 | /// is calculated from the level *i* key using `hkdf_expand`. | 
|  | 216 | fn get_hkdf_key(&mut self, boot_level: usize) -> Result<Option<&ZVec>> { | 
|  | 217 | if !self.level_accessible(boot_level) { | 
|  | 218 | return Ok(None); | 
|  | 219 | } | 
|  | 220 | // `self.cache.len()` represents the first entry not in the cache, | 
|  | 221 | // so `self.current + self.cache.len()` is the first boot level not in the cache. | 
|  | 222 | let first_not_cached = self.current + self.cache.len(); | 
|  | 223 |  | 
|  | 224 | // Grow the cache forwards until it contains the desired boot level. | 
|  | 225 | for _level in first_not_cached..=boot_level { | 
|  | 226 | // We check at the start that cache is non-empty and future iterations only push, | 
|  | 227 | // so this must unwrap. | 
|  | 228 | let highest_key = self.cache.back().unwrap(); | 
|  | 229 | let next_key = hkdf_expand(Self::HKDF_KEY_SIZE, highest_key, Self::HKDF_ADVANCE) | 
| Shaquille Johnson | 9da2e1c | 2022-09-19 12:39:01 +0000 | [diff] [blame] | 230 | .context(ks_err!("Advancing key one step"))?; | 
| Paul Crowley | 44c02da | 2021-04-08 17:04:43 +0000 | [diff] [blame] | 231 | self.cache.push_back(next_key); | 
|  | 232 | } | 
|  | 233 |  | 
|  | 234 | // If we reach this point, we should have a key at index boot_level - current. | 
|  | 235 | Ok(Some(self.cache.get(boot_level - self.current).unwrap())) | 
|  | 236 | } | 
|  | 237 |  | 
|  | 238 | /// Drop keys prior to the given boot level, while retaining the ability to generate keys for | 
|  | 239 | /// that level and later. | 
|  | 240 | pub fn advance_boot_level(&mut self, new_boot_level: usize) -> Result<()> { | 
|  | 241 | if !self.level_accessible(new_boot_level) { | 
|  | 242 | log::error!( | 
| Shaquille Johnson | 9da2e1c | 2022-09-19 12:39:01 +0000 | [diff] [blame] | 243 | "Failed to advance boot level to {}, current is {}, cache size {}", | 
| Paul Crowley | 44c02da | 2021-04-08 17:04:43 +0000 | [diff] [blame] | 244 | new_boot_level, | 
|  | 245 | self.current, | 
|  | 246 | self.cache.len() | 
|  | 247 | ); | 
|  | 248 | return Ok(()); | 
|  | 249 | } | 
|  | 250 |  | 
|  | 251 | // We `get` the new boot level for the side effect of advancing the cache to a point | 
|  | 252 | // where the new boot level is present. | 
| Shaquille Johnson | 9da2e1c | 2022-09-19 12:39:01 +0000 | [diff] [blame] | 253 | self.get_hkdf_key(new_boot_level).context(ks_err!("Advancing cache"))?; | 
| Paul Crowley | 44c02da | 2021-04-08 17:04:43 +0000 | [diff] [blame] | 254 |  | 
|  | 255 | // Then we split the queue at the index of the new boot level and discard the front, | 
|  | 256 | // keeping only the keys with the current boot level or higher. | 
|  | 257 | self.cache = self.cache.split_off(new_boot_level - self.current); | 
|  | 258 |  | 
|  | 259 | // The new cache has the new boot level at index 0, so we set `current` to | 
|  | 260 | // `new_boot_level`. | 
|  | 261 | self.current = new_boot_level; | 
|  | 262 |  | 
|  | 263 | Ok(()) | 
|  | 264 | } | 
|  | 265 |  | 
|  | 266 | /// Drop all keys, effectively raising the current boot level to infinity; no keys can | 
|  | 267 | /// be inferred from this point on. | 
|  | 268 | pub fn finish(&mut self) { | 
|  | 269 | self.cache.clear(); | 
|  | 270 | } | 
|  | 271 |  | 
|  | 272 | fn expand_key( | 
|  | 273 | &mut self, | 
|  | 274 | boot_level: usize, | 
|  | 275 | out_len: usize, | 
|  | 276 | info: &[u8], | 
|  | 277 | ) -> Result<Option<ZVec>> { | 
|  | 278 | self.get_hkdf_key(boot_level) | 
| Shaquille Johnson | 9da2e1c | 2022-09-19 12:39:01 +0000 | [diff] [blame] | 279 | .context(ks_err!("Looking up HKDF key"))? | 
| Paul Crowley | 44c02da | 2021-04-08 17:04:43 +0000 | [diff] [blame] | 280 | .map(|k| hkdf_expand(out_len, k, info)) | 
|  | 281 | .transpose() | 
| Shaquille Johnson | 9da2e1c | 2022-09-19 12:39:01 +0000 | [diff] [blame] | 282 | .context(ks_err!("Calling hkdf_expand")) | 
| Paul Crowley | 44c02da | 2021-04-08 17:04:43 +0000 | [diff] [blame] | 283 | } | 
|  | 284 |  | 
|  | 285 | /// Return the AES-256-GCM key for the current boot level. | 
|  | 286 | pub fn aes_key(&mut self, boot_level: usize) -> Result<Option<ZVec>> { | 
|  | 287 | self.expand_key(boot_level, AES_256_KEY_LENGTH, BootLevelKeyCache::HKDF_AES) | 
| Shaquille Johnson | 9da2e1c | 2022-09-19 12:39:01 +0000 | [diff] [blame] | 288 | .context(ks_err!("expand_key failed")) | 
| Paul Crowley | 44c02da | 2021-04-08 17:04:43 +0000 | [diff] [blame] | 289 | } | 
|  | 290 | } | 
|  | 291 |  | 
|  | 292 | #[cfg(test)] | 
|  | 293 | mod test { | 
|  | 294 | use super::*; | 
|  | 295 |  | 
|  | 296 | #[test] | 
|  | 297 | fn test_output_is_consistent() -> Result<()> { | 
|  | 298 | let initial_key = b"initial key"; | 
|  | 299 | let mut blkc = BootLevelKeyCache::new(ZVec::try_from(initial_key as &[u8])?); | 
| Chris Wailes | 3877f29 | 2021-07-26 19:24:18 -0700 | [diff] [blame] | 300 | assert!(blkc.level_accessible(0)); | 
|  | 301 | assert!(blkc.level_accessible(9)); | 
|  | 302 | assert!(blkc.level_accessible(10)); | 
|  | 303 | assert!(blkc.level_accessible(100)); | 
| Paul Crowley | 44c02da | 2021-04-08 17:04:43 +0000 | [diff] [blame] | 304 | let v0 = blkc.aes_key(0).unwrap().unwrap(); | 
|  | 305 | let v10 = blkc.aes_key(10).unwrap().unwrap(); | 
|  | 306 | assert_eq!(Some(&v0), blkc.aes_key(0)?.as_ref()); | 
|  | 307 | assert_eq!(Some(&v10), blkc.aes_key(10)?.as_ref()); | 
|  | 308 | blkc.advance_boot_level(5)?; | 
| Chris Wailes | 3877f29 | 2021-07-26 19:24:18 -0700 | [diff] [blame] | 309 | assert!(!blkc.level_accessible(0)); | 
|  | 310 | assert!(blkc.level_accessible(9)); | 
|  | 311 | assert!(blkc.level_accessible(10)); | 
|  | 312 | assert!(blkc.level_accessible(100)); | 
| Paul Crowley | 44c02da | 2021-04-08 17:04:43 +0000 | [diff] [blame] | 313 | assert_eq!(None, blkc.aes_key(0)?); | 
|  | 314 | assert_eq!(Some(&v10), blkc.aes_key(10)?.as_ref()); | 
|  | 315 | blkc.advance_boot_level(10)?; | 
| Chris Wailes | 3877f29 | 2021-07-26 19:24:18 -0700 | [diff] [blame] | 316 | assert!(!blkc.level_accessible(0)); | 
|  | 317 | assert!(!blkc.level_accessible(9)); | 
|  | 318 | assert!(blkc.level_accessible(10)); | 
|  | 319 | assert!(blkc.level_accessible(100)); | 
| Paul Crowley | 44c02da | 2021-04-08 17:04:43 +0000 | [diff] [blame] | 320 | assert_eq!(None, blkc.aes_key(0)?); | 
|  | 321 | assert_eq!(Some(&v10), blkc.aes_key(10)?.as_ref()); | 
|  | 322 | blkc.advance_boot_level(0)?; | 
| Chris Wailes | 3877f29 | 2021-07-26 19:24:18 -0700 | [diff] [blame] | 323 | assert!(!blkc.level_accessible(0)); | 
|  | 324 | assert!(!blkc.level_accessible(9)); | 
|  | 325 | assert!(blkc.level_accessible(10)); | 
|  | 326 | assert!(blkc.level_accessible(100)); | 
| Paul Crowley | 44c02da | 2021-04-08 17:04:43 +0000 | [diff] [blame] | 327 | assert_eq!(None, blkc.aes_key(0)?); | 
|  | 328 | assert_eq!(Some(v10), blkc.aes_key(10)?); | 
|  | 329 | blkc.finish(); | 
| Chris Wailes | 3877f29 | 2021-07-26 19:24:18 -0700 | [diff] [blame] | 330 | assert!(!blkc.level_accessible(0)); | 
|  | 331 | assert!(!blkc.level_accessible(9)); | 
|  | 332 | assert!(!blkc.level_accessible(10)); | 
|  | 333 | assert!(!blkc.level_accessible(100)); | 
| Paul Crowley | 44c02da | 2021-04-08 17:04:43 +0000 | [diff] [blame] | 334 | assert_eq!(None, blkc.aes_key(0)?); | 
|  | 335 | assert_eq!(None, blkc.aes_key(10)?); | 
|  | 336 | Ok(()) | 
|  | 337 | } | 
|  | 338 | } |