diff options
Diffstat (limited to 'keystore2')
-rw-r--r-- | keystore2/Android.bp | 1 | ||||
-rw-r--r-- | keystore2/legacykeystore/lib.rs | 6 | ||||
-rw-r--r-- | keystore2/src/authorization.rs | 2 | ||||
-rw-r--r-- | keystore2/src/database.rs | 15 | ||||
-rw-r--r-- | keystore2/src/enforcements.rs | 4 | ||||
-rw-r--r-- | keystore2/src/km_compat.rs | 18 | ||||
-rw-r--r-- | keystore2/src/legacy_importer.rs | 8 | ||||
-rw-r--r-- | keystore2/src/lib.rs | 1 | ||||
-rw-r--r-- | keystore2/src/maintenance.rs | 8 | ||||
-rw-r--r-- | keystore2/src/metrics_store.rs | 7 | ||||
-rw-r--r-- | keystore2/src/security_level.rs | 4 | ||||
-rw-r--r-- | keystore2/src/service.rs | 24 | ||||
-rw-r--r-- | keystore2/src/super_key.rs | 284 | ||||
-rw-r--r-- | keystore2/src/sw_keyblob.rs | 812 |
14 files changed, 1027 insertions, 167 deletions
diff --git a/keystore2/Android.bp b/keystore2/Android.bp index cfac54dc..1c7eebe1 100644 --- a/keystore2/Android.bp +++ b/keystore2/Android.bp @@ -100,6 +100,7 @@ rust_test { defaults: ["libkeystore2_defaults"], rustlibs: [ "libandroid_logger", + "libhex", "libkeystore2_test_utils", "liblibsqlite3_sys", "libnix", diff --git a/keystore2/legacykeystore/lib.rs b/keystore2/legacykeystore/lib.rs index b826a657..55224f72 100644 --- a/keystore2/legacykeystore/lib.rs +++ b/keystore2/legacykeystore/lib.rs @@ -500,8 +500,10 @@ impl LegacyKeystore { ) -> Result<bool> { let blob = legacy_loader .read_legacy_keystore_entry(uid, alias, |ciphertext, iv, tag, _salt, _key_size| { - if let Some(key) = - SUPER_KEY.read().unwrap().get_per_boot_key_by_user_id(uid_to_android_user(uid)) + if let Some(key) = SUPER_KEY + .read() + .unwrap() + .get_after_first_unlock_key_by_user_id(uid_to_android_user(uid)) { key.decrypt(ciphertext, iv, tag) } else { diff --git a/keystore2/src/authorization.rs b/keystore2/src/authorization.rs index de7f19a6..f840189e 100644 --- a/keystore2/src/authorization.rs +++ b/keystore2/src/authorization.rs @@ -191,7 +191,7 @@ impl AuthorizationManager { ENFORCEMENTS.set_device_locked(user_id, true); let mut skm = SUPER_KEY.write().unwrap(); DB.with(|db| { - skm.lock_screen_lock_bound_key( + skm.lock_unlocked_device_required_keys( &mut db.borrow_mut(), user_id as u32, unlocking_sids.unwrap_or(&[]), diff --git a/keystore2/src/database.rs b/keystore2/src/database.rs index 3a2f50f6..08361b19 100644 --- a/keystore2/src/database.rs +++ b/keystore2/src/database.rs @@ -2855,7 +2855,7 @@ pub mod tests { }; use crate::key_perm_set; use crate::permission::{KeyPerm, KeyPermSet}; - use crate::super_key::{SuperKeyManager, USER_SUPER_KEY, SuperEncryptionAlgorithm, SuperKeyType}; + use crate::super_key::{SuperKeyManager, USER_AFTER_FIRST_UNLOCK_SUPER_KEY, SuperEncryptionAlgorithm, SuperKeyType}; use keystore2_test_utils::TempDir; use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{ HardwareAuthToken::HardwareAuthToken, @@ -4966,18 +4966,23 @@ pub mod tests { SuperKeyManager::encrypt_with_password(&super_key, &pw)?; db.store_super_key( 1, - &USER_SUPER_KEY, + &USER_AFTER_FIRST_UNLOCK_SUPER_KEY, &encrypted_super_key, &metadata, &KeyMetaData::new(), )?; // Check if super key exists. - assert!(db.key_exists(Domain::APP, 1, USER_SUPER_KEY.alias, KeyType::Super)?); + assert!(db.key_exists( + Domain::APP, + 1, + USER_AFTER_FIRST_UNLOCK_SUPER_KEY.alias, + KeyType::Super + )?); - let (_, key_entry) = db.load_super_key(&USER_SUPER_KEY, 1)?.unwrap(); + let (_, key_entry) = db.load_super_key(&USER_AFTER_FIRST_UNLOCK_SUPER_KEY, 1)?.unwrap(); let loaded_super_key = SuperKeyManager::extract_super_key_from_key_entry( - USER_SUPER_KEY.algorithm, + USER_AFTER_FIRST_UNLOCK_SUPER_KEY.algorithm, key_entry, &pw, None, diff --git a/keystore2/src/enforcements.rs b/keystore2/src/enforcements.rs index 3bf582f0..bb23b82e 100644 --- a/keystore2/src/enforcements.rs +++ b/keystore2/src/enforcements.rs @@ -774,10 +774,10 @@ impl Enforcements { Candidate { priority: 3, enc_type: SuperEncryptionType::BootLevel(*level) } } KeyParameterValue::UnlockedDeviceRequired if *domain == Domain::APP => { - Candidate { priority: 2, enc_type: SuperEncryptionType::ScreenLockBound } + Candidate { priority: 2, enc_type: SuperEncryptionType::UnlockedDeviceRequired } } KeyParameterValue::UserSecureID(_) if *domain == Domain::APP => { - Candidate { priority: 1, enc_type: SuperEncryptionType::LskfBound } + Candidate { priority: 1, enc_type: SuperEncryptionType::AfterFirstUnlock } } _ => Candidate { priority: 0, enc_type: SuperEncryptionType::None }, }; diff --git a/keystore2/src/km_compat.rs b/keystore2/src/km_compat.rs index 8eba02da..f8673fb3 100644 --- a/keystore2/src/km_compat.rs +++ b/keystore2/src/km_compat.rs @@ -200,6 +200,15 @@ where let _ = self.soft.earlyBootEnded(); self.real.earlyBootEnded() } + fn getRootOfTrustChallenge(&self) -> binder::Result<[u8; 16]> { + self.real.getRootOfTrustChallenge() + } + fn getRootOfTrust(&self, challenge: &[u8; 16]) -> binder::Result<Vec<u8>> { + self.real.getRootOfTrust(challenge) + } + fn sendRootOfTrust(&self, root_of_trust: &[u8]) -> binder::Result<()> { + self.real.sendRootOfTrust(root_of_trust) + } // For methods that emit keyblobs, check whether the underlying real device // supports the relevant parameters, and forward to the appropriate device. @@ -304,15 +313,6 @@ where KeyBlob::Wrapped(keyblob) => self.soft.getKeyCharacteristics(keyblob, app_id, app_data), } } - fn getRootOfTrustChallenge(&self) -> binder::Result<[u8; 16]> { - self.real.getRootOfTrustChallenge() - } - fn getRootOfTrust(&self, challenge: &[u8; 16]) -> binder::Result<Vec<u8>> { - self.real.getRootOfTrust(challenge) - } - fn sendRootOfTrust(&self, root_of_trust: &[u8]) -> binder::Result<()> { - self.real.sendRootOfTrust(root_of_trust) - } fn convertStorageKeyToEphemeral(&self, storage_keyblob: &[u8]) -> binder::Result<Vec<u8>> { // Storage keys should never be associated with a software emulated device. self.real.convertStorageKeyToEphemeral(storage_keyblob) diff --git a/keystore2/src/legacy_importer.rs b/keystore2/src/legacy_importer.rs index 159e9361..7dcb98d6 100644 --- a/keystore2/src/legacy_importer.rs +++ b/keystore2/src/legacy_importer.rs @@ -22,7 +22,7 @@ use crate::error::{map_km_error, Error}; use crate::key_parameter::{KeyParameter, KeyParameterValue}; use crate::ks_err; use crate::legacy_blob::{self, Blob, BlobValue, LegacyKeyCharacteristics}; -use crate::super_key::USER_SUPER_KEY; +use crate::super_key::USER_AFTER_FIRST_UNLOCK_SUPER_KEY; use crate::utils::{ key_characteristics_to_internal, uid_to_android_user, upgrade_keyblob_if_required_with, watchdog as wd, AesGcm, @@ -450,7 +450,7 @@ impl LegacyImporterState { match self .db - .load_super_key(&USER_SUPER_KEY, user_id) + .load_super_key(&USER_AFTER_FIRST_UNLOCK_SUPER_KEY, user_id) .context(ks_err!("Failed to load super key"))? { Some((_, entry)) => Ok(entry.id()), @@ -729,7 +729,7 @@ impl LegacyImporterState { self.db .store_super_key( user_id, - &USER_SUPER_KEY, + &USER_AFTER_FIRST_UNLOCK_SUPER_KEY, &blob, &blob_metadata, &KeyMetaData::new(), @@ -772,7 +772,7 @@ impl LegacyImporterState { let super_key_id = self .db - .load_super_key(&USER_SUPER_KEY, user_id) + .load_super_key(&USER_AFTER_FIRST_UNLOCK_SUPER_KEY, user_id) .context(ks_err!("Failed to load super key"))? .map(|(_, entry)| entry.id()); diff --git a/keystore2/src/lib.rs b/keystore2/src/lib.rs index 97948899..3233017c 100644 --- a/keystore2/src/lib.rs +++ b/keystore2/src/lib.rs @@ -49,6 +49,7 @@ mod audit_log; mod gc; mod km_compat; mod super_key; +mod sw_keyblob; #[cfg(feature = "watchdog")] mod watchdog; diff --git a/keystore2/src/maintenance.rs b/keystore2/src/maintenance.rs index f25233fd..ea48f4d4 100644 --- a/keystore2/src/maintenance.rs +++ b/keystore2/src/maintenance.rs @@ -77,12 +77,12 @@ impl Maintenance { if let Some(pw) = password.as_ref() { DB.with(|db| { - skm.unlock_screen_lock_bound_key(&mut db.borrow_mut(), user_id as u32, pw) + skm.unlock_unlocked_device_required_keys(&mut db.borrow_mut(), user_id as u32, pw) }) - .context(ks_err!("unlock_screen_lock_bound_key failed"))?; + .context(ks_err!("unlock_unlocked_device_required_keys failed"))?; } - if let UserState::LskfLocked = DB + if let UserState::BeforeFirstUnlock = DB .with(|db| skm.get_user_state(&mut db.borrow_mut(), &LEGACY_IMPORTER, user_id as u32)) .context(ks_err!("Could not get user state while changing password!"))? { @@ -217,7 +217,7 @@ impl Maintenance { let user_id = uid_to_android_user(calling_uid); - let super_key = SUPER_KEY.read().unwrap().get_per_boot_key_by_user_id(user_id); + let super_key = SUPER_KEY.read().unwrap().get_after_first_unlock_key_by_user_id(user_id); DB.with(|db| { let (key_id_guard, _) = LEGACY_IMPORTER diff --git a/keystore2/src/metrics_store.rs b/keystore2/src/metrics_store.rs index 6dca74a2..5a76d04e 100644 --- a/keystore2/src/metrics_store.rs +++ b/keystore2/src/metrics_store.rs @@ -119,15 +119,14 @@ impl MetricsStore { // It is ok to unwrap here since the mutex cannot be poisoned according to the way it is // used in this module. And the lock is not acquired by this thread before. let mut metrics_store_guard = self.metrics_store.lock().unwrap(); - let atom_count_map = metrics_store_guard.entry(atom_id).or_insert_with(HashMap::new); + let atom_count_map = metrics_store_guard.entry(atom_id).or_default(); if atom_count_map.len() < MetricsStore::SINGLE_ATOM_STORE_MAX_SIZE { let atom_count = atom_count_map.entry(atom).or_insert(0); *atom_count += 1; } else { // Insert an overflow atom - let overflow_atom_count_map = metrics_store_guard - .entry(AtomID::KEYSTORE2_ATOM_WITH_OVERFLOW) - .or_insert_with(HashMap::new); + let overflow_atom_count_map = + metrics_store_guard.entry(AtomID::KEYSTORE2_ATOM_WITH_OVERFLOW).or_default(); if overflow_atom_count_map.len() < MetricsStore::SINGLE_ATOM_STORE_MAX_SIZE { let overflow_atom = Keystore2AtomWithOverflow { atom_id }; diff --git a/keystore2/src/security_level.rs b/keystore2/src/security_level.rs index 44ca4c8f..6696113b 100644 --- a/keystore2/src/security_level.rs +++ b/keystore2/src/security_level.rs @@ -247,7 +247,7 @@ impl KeystoreSecurityLevel { let super_key = SUPER_KEY .read() .unwrap() - .get_per_boot_key_by_user_id(uid_to_android_user(caller_uid)); + .get_after_first_unlock_key_by_user_id(uid_to_android_user(caller_uid)); let (key_id_guard, mut key_entry) = DB .with::<_, Result<(KeyIdGuard, KeyEntry)>>(|db| { LEGACY_IMPORTER.with_try_import(key, caller_uid, super_key, || { @@ -733,7 +733,7 @@ impl KeystoreSecurityLevel { // Import_wrapped_key requires the rebind permission for the new key. check_key_permission(KeyPerm::Rebind, &key, &None).context(ks_err!())?; - let super_key = SUPER_KEY.read().unwrap().get_per_boot_key_by_user_id(user_id); + let super_key = SUPER_KEY.read().unwrap().get_after_first_unlock_key_by_user_id(user_id); let (wrapping_key_id_guard, mut wrapping_key_entry) = DB .with(|db| { diff --git a/keystore2/src/service.rs b/keystore2/src/service.rs index 7ba8cbc2..1459254e 100644 --- a/keystore2/src/service.rs +++ b/keystore2/src/service.rs @@ -126,8 +126,10 @@ impl KeystoreService { fn get_key_entry(&self, key: &KeyDescriptor) -> Result<KeyEntryResponse> { let caller_uid = ThreadState::get_calling_uid(); - let super_key = - SUPER_KEY.read().unwrap().get_per_boot_key_by_user_id(uid_to_android_user(caller_uid)); + let super_key = SUPER_KEY + .read() + .unwrap() + .get_after_first_unlock_key_by_user_id(uid_to_android_user(caller_uid)); let (key_id_guard, mut key_entry) = DB .with(|db| { @@ -181,8 +183,10 @@ impl KeystoreService { certificate_chain: Option<&[u8]>, ) -> Result<()> { let caller_uid = ThreadState::get_calling_uid(); - let super_key = - SUPER_KEY.read().unwrap().get_per_boot_key_by_user_id(uid_to_android_user(caller_uid)); + let super_key = SUPER_KEY + .read() + .unwrap() + .get_after_first_unlock_key_by_user_id(uid_to_android_user(caller_uid)); DB.with::<_, Result<()>>(|db| { let entry = match LEGACY_IMPORTER.with_try_import(key, caller_uid, super_key, || { @@ -315,8 +319,10 @@ impl KeystoreService { fn delete_key(&self, key: &KeyDescriptor) -> Result<()> { let caller_uid = ThreadState::get_calling_uid(); - let super_key = - SUPER_KEY.read().unwrap().get_per_boot_key_by_user_id(uid_to_android_user(caller_uid)); + let super_key = SUPER_KEY + .read() + .unwrap() + .get_after_first_unlock_key_by_user_id(uid_to_android_user(caller_uid)); DB.with(|db| { LEGACY_IMPORTER.with_try_import(key, caller_uid, super_key, || { @@ -337,8 +343,10 @@ impl KeystoreService { access_vector: permission::KeyPermSet, ) -> Result<KeyDescriptor> { let caller_uid = ThreadState::get_calling_uid(); - let super_key = - SUPER_KEY.read().unwrap().get_per_boot_key_by_user_id(uid_to_android_user(caller_uid)); + let super_key = SUPER_KEY + .read() + .unwrap() + .get_after_first_unlock_key_by_user_id(uid_to_android_user(caller_uid)); DB.with(|db| { LEGACY_IMPORTER.with_try_import(key, caller_uid, super_key, || { diff --git a/keystore2/src/super_key.rs b/keystore2/src/super_key.rs index 5ee4f173..128cf4ca 100644 --- a/keystore2/src/super_key.rs +++ b/keystore2/src/super_key.rs @@ -80,21 +80,22 @@ pub struct SuperKeyType<'a> { pub algorithm: SuperEncryptionAlgorithm, } -/// Key used for LskfLocked keys; the corresponding superencryption key is loaded in memory -/// when the user first unlocks, and remains in memory until the device reboots. -pub const USER_SUPER_KEY: SuperKeyType = +/// The user's AfterFirstUnlock super key. This super key is loaded into memory when the user first +/// unlocks the device, and it remains in memory until the device reboots. This is used to encrypt +/// keys that require user authentication but not an unlocked device. +pub const USER_AFTER_FIRST_UNLOCK_SUPER_KEY: SuperKeyType = SuperKeyType { alias: "USER_SUPER_KEY", algorithm: SuperEncryptionAlgorithm::Aes256Gcm }; -/// Key used for ScreenLockBound keys; the corresponding superencryption key is loaded in memory -/// each time the user enters their LSKF, and cleared from memory each time the device is locked. -/// Symmetric. -pub const USER_SCREEN_LOCK_BOUND_KEY: SuperKeyType = SuperKeyType { +/// The user's UnlockedDeviceRequired symmetric super key. This super key is loaded into memory each +/// time the user unlocks the device, and it is cleared from memory each time the user locks the +/// device. This is used to encrypt keys that use the UnlockedDeviceRequired key parameter. +pub const USER_UNLOCKED_DEVICE_REQUIRED_SYMMETRIC_SUPER_KEY: SuperKeyType = SuperKeyType { alias: "USER_SCREEN_LOCK_BOUND_KEY", algorithm: SuperEncryptionAlgorithm::Aes256Gcm, }; -/// Key used for ScreenLockBound keys; the corresponding superencryption key is loaded in memory -/// each time the user enters their LSKF, and cleared from memory each time the device is locked. -/// Asymmetric, so keys can be encrypted when the device is locked. -pub const USER_SCREEN_LOCK_BOUND_P521_KEY: SuperKeyType = SuperKeyType { +/// The user's UnlockedDeviceRequired asymmetric super key. This is used to allow, while the device +/// is locked, the creation of keys that use the UnlockedDeviceRequired key parameter. The private +/// part of this key is loaded and cleared when the symmetric key is loaded and cleared. +pub const USER_UNLOCKED_DEVICE_REQUIRED_P521_SUPER_KEY: SuperKeyType = SuperKeyType { alias: "USER_SCREEN_LOCK_BOUND_P521_KEY", algorithm: SuperEncryptionAlgorithm::EcdhP521, }; @@ -104,10 +105,10 @@ pub const USER_SCREEN_LOCK_BOUND_P521_KEY: SuperKeyType = SuperKeyType { pub enum SuperEncryptionType { /// Do not superencrypt this key. None, - /// Superencrypt with a key that remains in memory from first unlock to reboot. - LskfBound, - /// Superencrypt with a key cleared from memory when the device is locked. - ScreenLockBound, + /// Superencrypt with the AfterFirstUnlock super key. + AfterFirstUnlock, + /// Superencrypt with an UnlockedDeviceRequired super key. + UnlockedDeviceRequired, /// Superencrypt with a key based on the desired boot level BootLevel(i32), } @@ -224,33 +225,32 @@ impl LockedKey { } } -/// Keys for unlocking UNLOCKED_DEVICE_REQUIRED keys, as LockedKeys, complete with -/// a database descriptor for the encrypting key and the sids for the auth tokens -/// that can be used to decrypt it. +/// A user's UnlockedDeviceRequired super keys, encrypted with a biometric-bound key, and +/// information about that biometric-bound key. struct BiometricUnlock { - /// List of auth token SIDs that can be used to unlock these keys. + /// List of auth token SIDs that are accepted by the encrypting biometric-bound key. sids: Vec<i64>, - /// Database descriptor of key to use to unlock. + /// Key descriptor of the encrypting biometric-bound key. key_desc: KeyDescriptor, - /// Locked versions of the matching UserSuperKeys fields - screen_lock_bound: LockedKey, - screen_lock_bound_private: LockedKey, + /// The UnlockedDeviceRequired super keys, encrypted with a biometric-bound key. + symmetric: LockedKey, + private: LockedKey, } #[derive(Default)] struct UserSuperKeys { - /// The per boot key is used for LSKF binding of authentication bound keys. There is one - /// key per android user. The key is stored on flash encrypted with a key derived from a - /// secret, that is itself derived from the user's lock screen knowledge factor (LSKF). - /// When the user unlocks the device for the first time, this key is unlocked, i.e., decrypted, - /// and stays memory resident until the device reboots. - per_boot: Option<Arc<SuperKey>>, - /// The screen lock key works like the per boot key with the distinction that it is cleared - /// from memory when the screen lock is engaged. - screen_lock_bound: Option<Arc<SuperKey>>, - /// When the device is locked, screen-lock-bound keys can still be encrypted, using - /// ECDH public-key encryption. This field holds the decryption private key. - screen_lock_bound_private: Option<Arc<SuperKey>>, + /// The AfterFirstUnlock super key is used for LSKF binding of authentication bound keys. There + /// is one key per android user. The key is stored on flash encrypted with a key derived from a + /// secret, that is itself derived from the user's lock screen knowledge factor (LSKF). When the + /// user unlocks the device for the first time, this key is unlocked, i.e., decrypted, and stays + /// memory resident until the device reboots. + after_first_unlock: Option<Arc<SuperKey>>, + /// The UnlockedDeviceRequired symmetric super key works like the AfterFirstUnlock super key + /// with the distinction that it is cleared from memory when the device is locked. + unlocked_device_required_symmetric: Option<Arc<SuperKey>>, + /// When the device is locked, keys that use the UnlockedDeviceRequired key parameter can still + /// be created, using ECDH public-key encryption. This field holds the decryption private key. + unlocked_device_required_private: Option<Arc<SuperKey>>, /// Versions of the above two keys, locked behind a biometric. biometric_unlock: Option<BiometricUnlock>, } @@ -351,7 +351,7 @@ impl SuperKeyManager { self.data.user_keys.remove(&user); } - fn install_per_boot_key_for_user( + fn install_after_first_unlock_key_for_user( &mut self, user: UserId, super_key: Arc<SuperKey>, @@ -359,7 +359,7 @@ impl SuperKeyManager { self.data .add_key_to_key_index(&super_key) .context(ks_err!("add_key_to_key_index failed"))?; - self.data.user_keys.entry(user).or_default().per_boot = Some(super_key); + self.data.user_keys.entry(user).or_default().after_first_unlock = Some(super_key); Ok(()) } @@ -387,16 +387,21 @@ impl SuperKeyManager { }) } - pub fn get_per_boot_key_by_user_id( + /// Returns the AfterFirstUnlock superencryption key for the given user ID, or None if the user + /// has not yet unlocked the device since boot. + pub fn get_after_first_unlock_key_by_user_id( &self, user_id: UserId, ) -> Option<Arc<dyn AesGcm + Send + Sync>> { - self.get_per_boot_key_by_user_id_internal(user_id) + self.get_after_first_unlock_key_by_user_id_internal(user_id) .map(|sk| -> Arc<dyn AesGcm + Send + Sync> { sk }) } - fn get_per_boot_key_by_user_id_internal(&self, user_id: UserId) -> Option<Arc<SuperKey>> { - self.data.user_keys.get(&user_id).and_then(|e| e.per_boot.as_ref().cloned()) + fn get_after_first_unlock_key_by_user_id_internal( + &self, + user_id: UserId, + ) -> Option<Arc<SuperKey>> { + self.data.user_keys.get(&user_id).and_then(|e| e.after_first_unlock.as_ref().cloned()) } /// Check if a given key is super-encrypted, from its metadata. If so, unwrap the key using @@ -470,7 +475,12 @@ impl SuperKeyManager { user_id: UserId, ) -> Result<bool> { let key_in_db = db - .key_exists(Domain::APP, user_id as u64 as i64, USER_SUPER_KEY.alias, KeyType::Super) + .key_exists( + Domain::APP, + user_id as u64 as i64, + USER_AFTER_FIRST_UNLOCK_SUPER_KEY.alias, + KeyType::Super, + ) .context(ks_err!())?; if key_in_db { @@ -490,8 +500,8 @@ impl SuperKeyManager { ) -> Result<Arc<SuperKey>> { let super_key = Self::extract_super_key_from_key_entry(algorithm, entry, pw, None) .context(ks_err!("Failed to extract super key from key entry"))?; - self.install_per_boot_key_for_user(user_id, super_key.clone()) - .context(ks_err!("Failed to install per boot key for user!"))?; + self.install_after_first_unlock_key_for_user(user_id, super_key.clone()) + .context(ks_err!("Failed to install AfterFirstUnlock super key for user!"))?; Ok(super_key) } @@ -588,7 +598,8 @@ impl SuperKeyManager { // the KeystoreDB and encrypts the key_blob using ECDH encryption and marks the keyblob to be // re-encrypted with the symmetric super key on the first use. // - // This hybrid scheme allows lock-screen-bound keys to be added when the screen is locked. + // This hybrid scheme allows keys that use the UnlockedDeviceRequired key parameter to be + // created while the device is locked. fn encrypt_with_hybrid_super_key( key_blob: &[u8], symmetric_key: Option<&SuperKey>, @@ -597,8 +608,9 @@ impl SuperKeyManager { user_id: UserId, ) -> Result<(Vec<u8>, BlobMetaData)> { if let Some(super_key) = symmetric_key { - Self::encrypt_with_aes_super_key(key_blob, super_key) - .context(ks_err!("Failed to encrypt with ScreenLockBound super key.")) + Self::encrypt_with_aes_super_key(key_blob, super_key).context(ks_err!( + "Failed to encrypt with UnlockedDeviceRequired symmetric super key." + )) } else { // Symmetric key is not available, use public key encryption let loaded = db @@ -639,39 +651,40 @@ impl SuperKeyManager { ) -> Result<(Vec<u8>, BlobMetaData)> { match Enforcements::super_encryption_required(domain, key_parameters, flags) { SuperEncryptionType::None => Ok((key_blob.to_vec(), BlobMetaData::new())), - SuperEncryptionType::LskfBound => { - // Encrypt the given key blob with the user's per-boot super key. If the per-boot - // super key is not unlocked or the LSKF is not setup, an error is returned. + SuperEncryptionType::AfterFirstUnlock => { + // Encrypt the given key blob with the user's AfterFirstUnlock super key. If the + // user has not unlocked the device since boot or has no LSKF, an error is returned. match self .get_user_state(db, legacy_importer, user_id) .context(ks_err!("Failed to get user state for user {user_id}"))? { - UserState::LskfUnlocked(super_key) => Self::encrypt_with_aes_super_key( - key_blob, &super_key, - ) - .context(ks_err!("Failed to encrypt with LskfBound key for user {user_id}")), - UserState::LskfLocked => { + UserState::AfterFirstUnlock(super_key) => { + Self::encrypt_with_aes_super_key(key_blob, &super_key).context(ks_err!( + "Failed to encrypt with AfterFirstUnlock super key for user {user_id}" + )) + } + UserState::BeforeFirstUnlock => { Err(Error::Rc(ResponseCode::LOCKED)).context(ks_err!("Device is locked.")) } UserState::Uninitialized => Err(Error::Rc(ResponseCode::UNINITIALIZED)) .context(ks_err!("LSKF is not setup for user {user_id}")), } } - SuperEncryptionType::ScreenLockBound => { - let screen_lock_bound_symmetric_key = self + SuperEncryptionType::UnlockedDeviceRequired => { + let symmetric_key = self .data .user_keys .get(&user_id) - .and_then(|e| e.screen_lock_bound.as_ref()) + .and_then(|e| e.unlocked_device_required_symmetric.as_ref()) .map(|arc| arc.as_ref()); Self::encrypt_with_hybrid_super_key( key_blob, - screen_lock_bound_symmetric_key, - &USER_SCREEN_LOCK_BOUND_P521_KEY, + symmetric_key, + &USER_UNLOCKED_DEVICE_REQUIRED_P521_SUPER_KEY, db, user_id, ) - .context(ks_err!("Failed to encrypt with ScreenLockBound hybrid scheme.")) + .context(ks_err!("Failed to encrypt with UnlockedDeviceRequired hybrid scheme.")) } SuperEncryptionType::BootLevel(level) => { let key_id = SuperKeyIdentifier::BootLevel(level); @@ -764,43 +777,55 @@ impl SuperKeyManager { } } - /// Decrypt the screen-lock bound keys for this user using the password and store in memory. - pub fn unlock_screen_lock_bound_key( + /// Decrypt the UnlockedDeviceRequired super keys for this user using the password and store + /// them in memory. If these keys don't exist yet, create them. + pub fn unlock_unlocked_device_required_keys( &mut self, db: &mut KeystoreDB, user_id: UserId, password: &Password, ) -> Result<()> { - let (screen_lock_bound, screen_lock_bound_private) = self + let (symmetric, private) = self .data .user_keys .get(&user_id) - .map(|e| (e.screen_lock_bound.clone(), e.screen_lock_bound_private.clone())) + .map(|e| { + ( + e.unlocked_device_required_symmetric.clone(), + e.unlocked_device_required_private.clone(), + ) + }) .unwrap_or((None, None)); - if screen_lock_bound.is_some() && screen_lock_bound_private.is_some() { + if symmetric.is_some() && private.is_some() { // Already unlocked. return Ok(()); } - let aes = if let Some(screen_lock_bound) = screen_lock_bound { - // This is weird. If this point is reached only one of the screen locked keys was - // initialized. This should never happen. - screen_lock_bound + let aes = if let Some(symmetric) = symmetric { + // This is weird. If this point is reached only one of the UnlockedDeviceRequired super + // keys was initialized. This should never happen. + symmetric } else { - self.get_or_create_super_key(db, user_id, &USER_SCREEN_LOCK_BOUND_KEY, password, None) - .context(ks_err!("Trying to get or create symmetric key."))? + self.get_or_create_super_key( + db, + user_id, + &USER_UNLOCKED_DEVICE_REQUIRED_SYMMETRIC_SUPER_KEY, + password, + None, + ) + .context(ks_err!("Trying to get or create symmetric key."))? }; - let ecdh = if let Some(screen_lock_bound_private) = screen_lock_bound_private { - // This is weird. If this point is reached only one of the screen locked keys was - // initialized. This should never happen. - screen_lock_bound_private + let ecdh = if let Some(private) = private { + // This is weird. If this point is reached only one of the UnlockedDeviceRequired super + // keys was initialized. This should never happen. + private } else { self.get_or_create_super_key( db, user_id, - &USER_SCREEN_LOCK_BOUND_P521_KEY, + &USER_UNLOCKED_DEVICE_REQUIRED_P521_SUPER_KEY, password, Some(aes.clone()), ) @@ -810,24 +835,28 @@ impl SuperKeyManager { self.data.add_key_to_key_index(&aes)?; self.data.add_key_to_key_index(&ecdh)?; let entry = self.data.user_keys.entry(user_id).or_default(); - entry.screen_lock_bound = Some(aes); - entry.screen_lock_bound_private = Some(ecdh); + entry.unlocked_device_required_symmetric = Some(aes); + entry.unlocked_device_required_private = Some(ecdh); Ok(()) } - /// Wipe the screen-lock bound keys for this user from memory. - pub fn lock_screen_lock_bound_key( + /// Wipe the user's UnlockedDeviceRequired super keys from memory. + pub fn lock_unlocked_device_required_keys( &mut self, db: &mut KeystoreDB, user_id: UserId, unlocking_sids: &[i64], ) { - log::info!("Locking screen bound for user {} sids {:?}", user_id, unlocking_sids); + log::info!( + "Locking UnlockedDeviceRequired super keys for user {}; unlocking_sids={:?}", + user_id, + unlocking_sids + ); let entry = self.data.user_keys.entry(user_id).or_default(); if !unlocking_sids.is_empty() { if let (Some(aes), Some(ecdh)) = ( - entry.screen_lock_bound.as_ref().cloned(), - entry.screen_lock_bound_private.as_ref().cloned(), + entry.unlocked_device_required_symmetric.as_ref().cloned(), + entry.unlocked_device_required_private.as_ref().cloned(), ) { let res = (|| -> Result<()> { let key_desc = KeyMintDevice::internal_descriptor(format!( @@ -862,7 +891,7 @@ impl SuperKeyManager { KeyType::Client, /* TODO Should be Super b/189470584 */ |dev| { let _wp = wd::watch_millis( - "In lock_screen_lock_bound_key: calling importKey.", + "In lock_unlocked_device_required_keys: calling importKey.", 500, ); dev.importKey( @@ -876,20 +905,20 @@ impl SuperKeyManager { entry.biometric_unlock = Some(BiometricUnlock { sids: unlocking_sids.into(), key_desc, - screen_lock_bound: LockedKey::new(&encrypting_key, &aes)?, - screen_lock_bound_private: LockedKey::new(&encrypting_key, &ecdh)?, + symmetric: LockedKey::new(&encrypting_key, &aes)?, + private: LockedKey::new(&encrypting_key, &ecdh)?, }); Ok(()) })(); - // There is no reason to propagate an error here upwards. We must discard - // entry.screen_lock_bound* in any case. + // There is no reason to propagate an error here upwards. We must clear the keys + // from memory in any case. if let Err(e) = res { log::error!("Error setting up biometric unlock: {:#?}", e); } } } - entry.screen_lock_bound = None; - entry.screen_lock_bound_private = None; + entry.unlocked_device_required_symmetric = None; + entry.unlocked_device_required_private = None; } /// User has unlocked, not using a password. See if any of our stored auth tokens can be used @@ -919,7 +948,7 @@ impl SuperKeyManager { entry.auth_token().userId == sid || entry.auth_token().authenticatorId == sid }) { let res: Result<(Arc<SuperKey>, Arc<SuperKey>)> = (|| { - let slb = biometric.screen_lock_bound.decrypt( + let symmetric = biometric.symmetric.decrypt( db, &km_dev, &key_id_guard, @@ -927,22 +956,22 @@ impl SuperKeyManager { auth_token_entry.auth_token(), None, )?; - let slbp = biometric.screen_lock_bound_private.decrypt( + let private = biometric.private.decrypt( db, &km_dev, &key_id_guard, &key_entry, auth_token_entry.auth_token(), - Some(slb.clone()), + Some(symmetric.clone()), )?; - Ok((slb, slbp)) + Ok((symmetric, private)) })(); match res { - Ok((slb, slbp)) => { - entry.screen_lock_bound = Some(slb.clone()); - entry.screen_lock_bound_private = Some(slbp.clone()); - self.data.add_key_to_key_index(&slb)?; - self.data.add_key_to_key_index(&slbp)?; + Ok((symmetric, private)) => { + entry.unlocked_device_required_symmetric = Some(symmetric.clone()); + entry.unlocked_device_required_private = Some(private.clone()); + self.data.add_key_to_key_index(&symmetric)?; + self.data.add_key_to_key_index(&private)?; log::info!("Successfully unlocked user {user_id} with biometric {sid}",); return Ok(()); } @@ -972,8 +1001,8 @@ impl SuperKeyManager { legacy_importer: &LegacyImporter, user_id: UserId, ) -> Result<UserState> { - match self.get_per_boot_key_by_user_id_internal(user_id) { - Some(super_key) => Ok(UserState::LskfUnlocked(super_key)), + match self.get_after_first_unlock_key_by_user_id_internal(user_id) { + Some(super_key) => Ok(UserState::AfterFirstUnlock(super_key)), None => { // Check if a super key exists in the database or legacy database. // If so, return locked user state. @@ -981,7 +1010,7 @@ impl SuperKeyManager { .super_key_exists_in_db_for_user(db, legacy_importer, user_id) .context(ks_err!())? { - Ok(UserState::LskfLocked) + Ok(UserState::BeforeFirstUnlock) } else { Ok(UserState::Uninitialized) } @@ -1023,10 +1052,10 @@ impl SuperKeyManager { UserState::Uninitialized => { Err(Error::sys()).context(ks_err!("Tried to reset an uninitialized user!")) } - UserState::LskfLocked => { + UserState::BeforeFirstUnlock => { Err(Error::sys()).context(ks_err!("Tried to reset a locked user's password!")) } - UserState::LskfUnlocked(_) => { + UserState::AfterFirstUnlock(_) => { // Mark keys created on behalf of the user as unreferenced. legacy_importer .bulk_delete_user(user_id, true) @@ -1041,8 +1070,9 @@ impl SuperKeyManager { } } - /// If the user hasn't been initialized yet, then this function generates the user's super keys - /// and sets the user's state to LskfUnlocked. Otherwise this function returns an error. + /// If the user hasn't been initialized yet, then this function generates the user's + /// AfterFirstUnlock super key and sets the user's state to AfterFirstUnlock. Otherwise this + /// function returns an error. pub fn init_user( &mut self, db: &mut KeystoreDB, @@ -1052,7 +1082,7 @@ impl SuperKeyManager { ) -> Result<()> { log::info!("init_user(user={user_id})"); match self.get_user_state(db, legacy_importer, user_id)? { - UserState::LskfUnlocked(_) | UserState::LskfLocked => { + UserState::AfterFirstUnlock(_) | UserState::BeforeFirstUnlock => { Err(Error::sys()).context(ks_err!("Tried to re-init an initialized user!")) } UserState::Uninitialized => { @@ -1068,7 +1098,7 @@ impl SuperKeyManager { let key_entry = db .store_super_key( user_id, - &USER_SUPER_KEY, + &USER_AFTER_FIRST_UNLOCK_SUPER_KEY, &encrypted_super_key, &blob_metadata, &KeyMetaData::new(), @@ -1077,7 +1107,7 @@ impl SuperKeyManager { self.populate_cache_from_super_key_blob( user_id, - USER_SUPER_KEY.algorithm, + USER_AFTER_FIRST_UNLOCK_SUPER_KEY.algorithm, key_entry, password, ) @@ -1089,12 +1119,12 @@ impl SuperKeyManager { /// Unlocks the given user with the given password. /// - /// If the user is LskfLocked: - /// - Unlock the per_boot super key - /// - Unlock the screen_lock_bound super key + /// If the user state is BeforeFirstUnlock: + /// - Unlock the user's AfterFirstUnlock super key + /// - Unlock the user's UnlockedDeviceRequired super keys /// - /// If the user is LskfUnlocked: - /// - Unlock the screen_lock_bound super key only + /// If the user state is AfterFirstUnlock: + /// - Unlock the user's UnlockedDeviceRequired super keys only /// pub fn unlock_user( &mut self, @@ -1105,12 +1135,14 @@ impl SuperKeyManager { ) -> Result<()> { log::info!("unlock_user(user={user_id})"); match self.get_user_state(db, legacy_importer, user_id)? { - UserState::LskfUnlocked(_) => self.unlock_screen_lock_bound_key(db, user_id, password), + UserState::AfterFirstUnlock(_) => { + self.unlock_unlocked_device_required_keys(db, user_id, password) + } UserState::Uninitialized => { Err(Error::sys()).context(ks_err!("Tried to unlock an uninitialized user!")) } - UserState::LskfLocked => { - let alias = &USER_SUPER_KEY; + UserState::BeforeFirstUnlock => { + let alias = &USER_AFTER_FIRST_UNLOCK_SUPER_KEY; let result = legacy_importer .with_try_import_super_key(user_id, password, || { db.load_super_key(alias, user_id) @@ -1126,7 +1158,7 @@ impl SuperKeyManager { password, ) .context(ks_err!("Failed when unlocking user."))?; - self.unlock_screen_lock_bound_key(db, user_id, password) + self.unlock_unlocked_device_required_keys(db, user_id, password) } None => { Err(Error::sys()).context(ks_err!("Locked user does not have a super key!")) @@ -1141,12 +1173,12 @@ impl SuperKeyManager { /// For now, only three states are defined. More states may be added later. pub enum UserState { // The user has registered LSKF and has unlocked the device by entering PIN/Password, - // and hence the per-boot super key is available in the cache. - LskfUnlocked(Arc<SuperKey>), + // and hence the AfterFirstUnlock super key is available in the cache. + AfterFirstUnlock(Arc<SuperKey>), // The user has registered LSKF, but has not unlocked the device using password, after reboot. - // Hence the per-boot super-key(s) is not available in the cache. - // However, the encrypted super key is available in the database. - LskfLocked, + // Hence the AfterFirstUnlock and UnlockedDeviceRequired super keys are not available in the + // cache. However, they exist in the database in encrypted form. + BeforeFirstUnlock, // There's no user in the device for the given user id, or the user with the user id has not // setup LSKF. Uninitialized, @@ -1241,7 +1273,7 @@ mod tests { let user_state = skm.write().unwrap().get_user_state(keystore_db, legacy_importer, user_id).unwrap(); match user_state { - UserState::LskfUnlocked(_) => {} + UserState::AfterFirstUnlock(_) => {} _ => panic!("{}", err_msg), } } @@ -1256,7 +1288,7 @@ mod tests { let user_state = skm.write().unwrap().get_user_state(keystore_db, legacy_importer, user_id).unwrap(); match user_state { - UserState::LskfLocked => {} + UserState::BeforeFirstUnlock => {} _ => panic!("{}", err_msg), } } diff --git a/keystore2/src/sw_keyblob.rs b/keystore2/src/sw_keyblob.rs new file mode 100644 index 00000000..11a9b41c --- /dev/null +++ b/keystore2/src/sw_keyblob.rs @@ -0,0 +1,812 @@ +// Copyright 2023, The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Code for parsing software-backed keyblobs, as emitted by the C++ reference implementation of +//! KeyMint. + +#![allow(dead_code)] + +use crate::error::Error; +use crate::ks_err; +use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{ + Algorithm::Algorithm, BlockMode::BlockMode, Digest::Digest, EcCurve::EcCurve, + ErrorCode::ErrorCode, HardwareAuthenticatorType::HardwareAuthenticatorType, + KeyFormat::KeyFormat, KeyOrigin::KeyOrigin, KeyParameter::KeyParameter, + KeyParameterValue::KeyParameterValue, KeyPurpose::KeyPurpose, PaddingMode::PaddingMode, + Tag::Tag, TagType::TagType, +}; +use anyhow::Result; +use keystore2_crypto::hmac_sha256; +use std::mem::size_of; + +/// Root of trust value. +const SOFTWARE_ROOT_OF_TRUST: &[u8] = b"SW"; + +/// Error macro. +macro_rules! bloberr { + { $($arg:tt)+ } => { + anyhow::Error::new(Error::Km(ErrorCode::INVALID_KEY_BLOB)).context(ks_err!($($arg)+)) + }; +} + +/// Get the `KeyParameterValue` associated with a tag from a collection of `KeyParameter`s. +fn get_tag_value(params: &[KeyParameter], tag: Tag) -> Option<&KeyParameterValue> { + params.iter().find_map(|kp| if kp.tag == tag { Some(&kp.value) } else { None }) +} + +/// Get the [`TagType`] for a [`Tag`]. +fn tag_type(tag: &Tag) -> TagType { + TagType((tag.0 as u32 & 0xf0000000) as i32) +} + +/// Extract key material and combined key characteristics from a legacy authenticated keyblob. +pub fn export_key( + data: &[u8], + params: &[KeyParameter], +) -> Result<(KeyFormat, Vec<u8>, Vec<KeyParameter>)> { + let hidden = hidden_params(params, &[SOFTWARE_ROOT_OF_TRUST]); + let KeyBlob { key_material, hw_enforced, sw_enforced } = + KeyBlob::new_from_serialized(data, &hidden)?; + + let mut combined = hw_enforced; + combined.extend_from_slice(&sw_enforced); + + let algo_val = + get_tag_value(&combined, Tag::ALGORITHM).ok_or_else(|| bloberr!("No algorithm found!"))?; + + let format = match algo_val { + KeyParameterValue::Algorithm(Algorithm::AES) + | KeyParameterValue::Algorithm(Algorithm::TRIPLE_DES) + | KeyParameterValue::Algorithm(Algorithm::HMAC) => KeyFormat::RAW, + KeyParameterValue::Algorithm(Algorithm::RSA) + | KeyParameterValue::Algorithm(Algorithm::EC) => KeyFormat::PKCS8, + _ => return Err(bloberr!("Unexpected algorithm {:?}", algo_val)), + }; + Ok((format, key_material, combined)) +} + +/// Plaintext key blob, with key characteristics. +#[derive(PartialEq, Eq)] +struct KeyBlob { + /// Raw key material. + key_material: Vec<u8>, + /// Hardware-enforced key characteristics. + hw_enforced: Vec<KeyParameter>, + /// Software-enforced key characteristics. + sw_enforced: Vec<KeyParameter>, +} + +impl KeyBlob { + /// Key blob version. + const KEY_BLOB_VERSION: u8 = 0; + + /// Hard-coded HMAC key used for keyblob authentication. + const LEGACY_HMAC_KEY: &[u8] = b"IntegrityAssuredBlob0\0"; + + /// Size (in bytes) of appended MAC. + const MAC_LEN: usize = 8; + + /// Parse a serialized [`KeyBlob`]. + fn new_from_serialized(mut data: &[u8], hidden: &[KeyParameter]) -> Result<Self> { + // Keyblob needs to be at least long enough for: + // - version byte, + // - 4-byte len for key material + // - 4-byte len for hw_enforced params + // - 4-byte len for sw_enforced params + // - MAC tag. + if data.len() < (1 + 3 * size_of::<u32>() + Self::MAC_LEN) { + return Err(bloberr!("blob not long enough (len = {})", data.len())); + } + + // Check the HMAC in the last 8 bytes before doing anything else. + let mac = &data[data.len() - Self::MAC_LEN..]; + let computed_mac = Self::compute_hmac(&data[..data.len() - Self::MAC_LEN], hidden)?; + if mac != computed_mac { + return Err(bloberr!("invalid key blob")); + } + + let version = consume_u8(&mut data)?; + if version != Self::KEY_BLOB_VERSION { + return Err(bloberr!("unexpected blob version {}", version)); + } + let key_material = consume_vec(&mut data)?; + let hw_enforced = deserialize_params(&mut data)?; + let sw_enforced = deserialize_params(&mut data)?; + + // Should just be the (already-checked) MAC left. + let rest = &data[Self::MAC_LEN..]; + if !rest.is_empty() { + return Err(bloberr!("extra data (len {})", rest.len())); + } + Ok(KeyBlob { key_material, hw_enforced, sw_enforced }) + } + + /// Compute the authentication HMAC for a KeyBlob. This is built as: + /// HMAC-SHA256(HK, data || serialize(hidden)) + /// with HK = b"IntegrityAssuredBlob0\0". + fn compute_hmac(data: &[u8], hidden: &[KeyParameter]) -> Result<Vec<u8>> { + let hidden_data = serialize_params(hidden)?; + let mut combined = data.to_vec(); + combined.extend_from_slice(&hidden_data); + let mut tag = hmac_sha256(Self::LEGACY_HMAC_KEY, &combined)?; + tag.truncate(Self::MAC_LEN); + Ok(tag) + } +} + +/// Build the parameters that are used as the hidden input to HMAC calculations: +/// - `ApplicationId(data)` if present +/// - `ApplicationData(data)` if present +/// - (repeated) `RootOfTrust(rot)` where `rot` is a hardcoded piece of root of trust information. +fn hidden_params(params: &[KeyParameter], rots: &[&[u8]]) -> Vec<KeyParameter> { + let mut results = Vec::new(); + if let Some(app_id) = get_tag_value(params, Tag::APPLICATION_ID) { + results.push(KeyParameter { tag: Tag::APPLICATION_ID, value: app_id.clone() }); + } + if let Some(app_data) = get_tag_value(params, Tag::APPLICATION_DATA) { + results.push(KeyParameter { tag: Tag::APPLICATION_DATA, value: app_data.clone() }); + } + for rot in rots { + results.push(KeyParameter { + tag: Tag::ROOT_OF_TRUST, + value: KeyParameterValue::Blob(rot.to_vec()), + }); + } + results +} + +/// Retrieve a `u8` from the start of the given slice, if possible. +fn consume_u8(data: &mut &[u8]) -> Result<u8> { + match data.first() { + Some(b) => { + *data = &(*data)[1..]; + Ok(*b) + } + None => Err(bloberr!("failed to find 1 byte")), + } +} + +/// Move past a bool value from the start of the given slice, if possible. +/// Bool values should only be included if `true`, so fail if the value +/// is anything other than 1. +fn consume_bool(data: &mut &[u8]) -> Result<bool> { + let b = consume_u8(data)?; + if b == 0x01 { + Ok(true) + } else { + Err(bloberr!("bool value other than 1 encountered")) + } +} + +/// Retrieve a (host-ordered) `u32` from the start of the given slice, if possible. +fn consume_u32(data: &mut &[u8]) -> Result<u32> { + const LEN: usize = size_of::<u32>(); + if data.len() < LEN { + return Err(bloberr!("failed to find {LEN} bytes")); + } + let chunk: [u8; LEN] = data[..LEN].try_into().unwrap(); // safe: just checked + *data = &(*data)[LEN..]; + Ok(u32::from_ne_bytes(chunk)) +} + +/// Retrieve a (host-ordered) `i32` from the start of the given slice, if possible. +fn consume_i32(data: &mut &[u8]) -> Result<i32> { + const LEN: usize = size_of::<i32>(); + if data.len() < LEN { + return Err(bloberr!("failed to find {LEN} bytes")); + } + let chunk: [u8; LEN] = data[..LEN].try_into().unwrap(); // safe: just checked + *data = &(*data)[4..]; + Ok(i32::from_ne_bytes(chunk)) +} + +/// Retrieve a (host-ordered) `i64` from the start of the given slice, if possible. +fn consume_i64(data: &mut &[u8]) -> Result<i64> { + const LEN: usize = size_of::<i64>(); + if data.len() < LEN { + return Err(bloberr!("failed to find {LEN} bytes")); + } + let chunk: [u8; LEN] = data[..LEN].try_into().unwrap(); // safe: just checked + *data = &(*data)[LEN..]; + Ok(i64::from_ne_bytes(chunk)) +} + +/// Retrieve a vector of bytes from the start of the given slice, if possible, +/// with the length of the data expected to appear as a host-ordered `u32` prefix. +fn consume_vec(data: &mut &[u8]) -> Result<Vec<u8>> { + let len = consume_u32(data)? as usize; + if len > data.len() { + return Err(bloberr!("failed to find {} bytes", len)); + } + let result = data[..len].to_vec(); + *data = &(*data)[len..]; + Ok(result) +} + +/// Retrieve the contents of a tag of `TagType::Bytes`. The `data` parameter holds +/// the as-yet unparsed data, and a length and offset are read from this (and consumed). +/// This length and offset refer to a location in the combined `blob_data`; however, +/// the offset is expected to be the next unconsumed chunk of `blob_data`, as indicated +/// by `next_blob_offset` (which itself is updated as a result of consuming the data). +fn consume_blob( + data: &mut &[u8], + next_blob_offset: &mut usize, + blob_data: &[u8], +) -> Result<Vec<u8>> { + let data_len = consume_u32(data)? as usize; + let data_offset = consume_u32(data)? as usize; + // Expect the blob data to come from the next offset in the initial blob chunk. + if data_offset != *next_blob_offset { + return Err(bloberr!("got blob offset {} instead of {}", data_offset, next_blob_offset)); + } + if (data_offset + data_len) > blob_data.len() { + return Err(bloberr!( + "blob at offset [{}..{}+{}] goes beyond blob data size {}", + data_offset, + data_offset, + data_len, + blob_data.len(), + )); + } + + let slice = &blob_data[data_offset..data_offset + data_len]; + *next_blob_offset += data_len; + Ok(slice.to_vec()) +} + +/// Deserialize a collection of [`KeyParam`]s in legacy serialized format. The provided slice is +/// modified to contain the unconsumed part of the data. +fn deserialize_params(data: &mut &[u8]) -> Result<Vec<KeyParameter>> { + let blob_data_size = consume_u32(data)? as usize; + if blob_data_size > data.len() { + return Err(bloberr!( + "blob data size {} bigger than data (len={})", + blob_data_size, + data.len() + )); + } + + let blob_data = &data[..blob_data_size]; + let mut next_blob_offset = 0; + + // Move past the blob data. + *data = &data[blob_data_size..]; + + let param_count = consume_u32(data)? as usize; + let param_size = consume_u32(data)? as usize; + if param_size > data.len() { + return Err(bloberr!( + "size mismatch 4+{}+4+4+{} > {}", + blob_data_size, + param_size, + data.len() + )); + } + + let mut results = Vec::new(); + for _i in 0..param_count { + let tag_num = consume_u32(data)? as i32; + let tag = Tag(tag_num); + let value = match tag_type(&tag) { + TagType::INVALID => return Err(bloberr!("invalid tag {:?} encountered", tag)), + TagType::ENUM | TagType::ENUM_REP => { + let val = consume_i32(data)?; + match tag { + Tag::ALGORITHM => KeyParameterValue::Algorithm(Algorithm(val)), + Tag::BLOCK_MODE => KeyParameterValue::BlockMode(BlockMode(val)), + Tag::PADDING => KeyParameterValue::PaddingMode(PaddingMode(val)), + Tag::DIGEST | Tag::RSA_OAEP_MGF_DIGEST => { + KeyParameterValue::Digest(Digest(val)) + } + Tag::EC_CURVE => KeyParameterValue::EcCurve(EcCurve(val)), + Tag::ORIGIN => KeyParameterValue::Origin(KeyOrigin(val)), + Tag::PURPOSE => KeyParameterValue::KeyPurpose(KeyPurpose(val)), + Tag::USER_AUTH_TYPE => { + KeyParameterValue::HardwareAuthenticatorType(HardwareAuthenticatorType(val)) + } + _ => KeyParameterValue::Integer(val), + } + } + TagType::UINT | TagType::UINT_REP => KeyParameterValue::Integer(consume_i32(data)?), + TagType::ULONG | TagType::ULONG_REP => { + KeyParameterValue::LongInteger(consume_i64(data)?) + } + TagType::DATE => KeyParameterValue::DateTime(consume_i64(data)?), + TagType::BOOL => KeyParameterValue::BoolValue(consume_bool(data)?), + TagType::BIGNUM | TagType::BYTES => { + KeyParameterValue::Blob(consume_blob(data, &mut next_blob_offset, blob_data)?) + } + _ => return Err(bloberr!("unexpected tag type for {:?}", tag)), + }; + results.push(KeyParameter { tag, value }); + } + + Ok(results) +} + +/// Serialize a collection of [`KeyParameter`]s into a format that is compatible with previous +/// implementations: +/// +/// ```text +/// [0..4] Size B of `TagType::Bytes` data, in host order. +/// [4..4+B] (*) Concatenated contents of each `TagType::Bytes` tag. +/// [4+B..4+B+4] Count N of the number of parameters, in host order. +/// [8+B..8+B+4] Size Z of encoded parameters. +/// [12+B..12+B+Z] Serialized parameters one after another. +/// ``` +/// +/// Individual parameters are serialized in the last chunk as: +/// +/// ```text +/// [0..4] Tag number, in host order. +/// Followed by one of the following depending on the tag's `TagType`; all integers in host order: +/// [4..5] Bool value (`TagType::Bool`) +/// [4..8] i32 values (`TagType::Uint[Rep]`, `TagType::Enum[Rep]`) +/// [4..12] i64 values, in host order (`TagType::UlongRep`, `TagType::Date`) +/// [4..8] + [8..12] Size + offset of data in (*) above (`TagType::Bytes`, `TagType::Bignum`) +/// ``` +fn serialize_params(params: &[KeyParameter]) -> Result<Vec<u8>> { + // First 4 bytes are the length of the combined [`TagType::Bytes`] data; come back to set that + // in a moment. + let mut result = vec![0; 4]; + + // Next append the contents of all of the [`TagType::Bytes`] data. + let mut blob_size = 0u32; + for param in params { + let tag_type = tag_type(¶m.tag); + if let KeyParameterValue::Blob(v) = ¶m.value { + if tag_type != TagType::BIGNUM && tag_type != TagType::BYTES { + return Err(bloberr!("unexpected tag type for tag {:?} with blob", param.tag)); + } + result.extend_from_slice(v); + blob_size += v.len() as u32; + } + } + // Go back and fill in the combined blob length in native order at the start. + result[..4].clone_from_slice(&blob_size.to_ne_bytes()); + + result.extend_from_slice(&(params.len() as u32).to_ne_bytes()); + + let params_size_offset = result.len(); + result.extend_from_slice(&[0u8; 4]); // placeholder for size of elements + let first_param_offset = result.len(); + let mut blob_offset = 0u32; + for param in params { + result.extend_from_slice(&(param.tag.0 as u32).to_ne_bytes()); + match ¶m.value { + KeyParameterValue::Invalid(_v) => { + return Err(bloberr!("invalid tag found in {:?}", param)) + } + + // Enum-holding variants. + KeyParameterValue::Algorithm(v) => { + result.extend_from_slice(&(v.0 as u32).to_ne_bytes()) + } + KeyParameterValue::BlockMode(v) => { + result.extend_from_slice(&(v.0 as u32).to_ne_bytes()) + } + KeyParameterValue::PaddingMode(v) => { + result.extend_from_slice(&(v.0 as u32).to_ne_bytes()) + } + KeyParameterValue::Digest(v) => result.extend_from_slice(&(v.0 as u32).to_ne_bytes()), + KeyParameterValue::EcCurve(v) => result.extend_from_slice(&(v.0 as u32).to_ne_bytes()), + KeyParameterValue::Origin(v) => result.extend_from_slice(&(v.0 as u32).to_ne_bytes()), + KeyParameterValue::KeyPurpose(v) => { + result.extend_from_slice(&(v.0 as u32).to_ne_bytes()) + } + KeyParameterValue::HardwareAuthenticatorType(v) => { + result.extend_from_slice(&(v.0 as u32).to_ne_bytes()) + } + + // Value-holding variants. + KeyParameterValue::Integer(v) => result.extend_from_slice(&(*v as u32).to_ne_bytes()), + KeyParameterValue::BoolValue(_v) => result.push(0x01u8), + KeyParameterValue::LongInteger(v) | KeyParameterValue::DateTime(v) => { + result.extend_from_slice(&(*v as u64).to_ne_bytes()) + } + KeyParameterValue::Blob(v) => { + let blob_len = v.len() as u32; + result.extend_from_slice(&blob_len.to_ne_bytes()); + result.extend_from_slice(&blob_offset.to_ne_bytes()); + blob_offset += blob_len; + } + + _ => return Err(bloberr!("unknown value found in {:?}", param)), + } + } + let serialized_size = (result.len() - first_param_offset) as u32; + + // Go back and fill in the total serialized size. + result[params_size_offset..params_size_offset + 4] + .clone_from_slice(&serialized_size.to_ne_bytes()); + Ok(result) +} + +#[cfg(test)] +mod tests { + use super::*; + use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{ + Algorithm::Algorithm, BlockMode::BlockMode, Digest::Digest, EcCurve::EcCurve, + KeyOrigin::KeyOrigin, KeyParameter::KeyParameter, + KeyParameterValue::KeyParameterValue as KPV, KeyPurpose::KeyPurpose, + PaddingMode::PaddingMode, Tag::Tag, + }; + + macro_rules! expect_err { + ($result:expr, $err_msg:expr) => { + assert!( + $result.is_err(), + "Expected error containing '{}', got success {:?}", + $err_msg, + $result + ); + let err = $result.err(); + assert!( + format!("{:?}", err).contains($err_msg), + "Unexpected error {:?}, doesn't contain '{}'", + err, + $err_msg + ); + }; + } + + #[test] + fn test_consume_u8() { + let buffer = [1, 2]; + let mut data = &buffer[..]; + assert_eq!(1u8, consume_u8(&mut data).unwrap()); + assert_eq!(2u8, consume_u8(&mut data).unwrap()); + let result = consume_u8(&mut data); + expect_err!(result, "failed to find 1 byte"); + } + + #[test] + fn test_consume_u32() { + // All supported platforms are little-endian. + let buffer = [ + 0x01, 0x02, 0x03, 0x04, // little-endian u32 + 0x04, 0x03, 0x02, 0x01, // little-endian u32 + 0x11, 0x12, 0x13, + ]; + let mut data = &buffer[..]; + assert_eq!(0x04030201u32, consume_u32(&mut data).unwrap()); + assert_eq!(0x01020304u32, consume_u32(&mut data).unwrap()); + let result = consume_u32(&mut data); + expect_err!(result, "failed to find 4 bytes"); + } + + #[test] + fn test_consume_i64() { + // All supported platforms are little-endian. + let buffer = [ + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, // little-endian i64 + 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, // little-endian i64 + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + ]; + let mut data = &buffer[..]; + assert_eq!(0x0807060504030201i64, consume_i64(&mut data).unwrap()); + assert_eq!(0x0102030405060708i64, consume_i64(&mut data).unwrap()); + let result = consume_i64(&mut data); + expect_err!(result, "failed to find 8 bytes"); + } + + #[test] + fn test_consume_vec() { + let buffer = [ + 0x01, 0x00, 0x00, 0x00, 0xaa, // + 0x00, 0x00, 0x00, 0x00, // + 0x01, 0x00, 0x00, 0x00, 0xbb, // + 0x07, 0x00, 0x00, 0x00, 0xbb, // not enough data + ]; + let mut data = &buffer[..]; + assert_eq!(vec![0xaa], consume_vec(&mut data).unwrap()); + assert_eq!(Vec::<u8>::new(), consume_vec(&mut data).unwrap()); + assert_eq!(vec![0xbb], consume_vec(&mut data).unwrap()); + let result = consume_vec(&mut data); + expect_err!(result, "failed to find 7 bytes"); + + let buffer = [ + 0x01, 0x00, 0x00, // + ]; + let mut data = &buffer[..]; + let result = consume_vec(&mut data); + expect_err!(result, "failed to find 4 bytes"); + } + + #[test] + fn test_key_new_from_serialized() { + let hidden = hidden_params(&[], &[SOFTWARE_ROOT_OF_TRUST]); + // Test data originally generated by instrumenting Cuttlefish C++ KeyMint while running VTS + // tests. + let tests = [ + ( + concat!( + "0010000000d43c2f04f948521b81bdbf001310f5920000000000000000000000", + "00000000000c0000006400000002000010200000000300003080000000010000", + "2000000000010000200100000004000020020000000600002001000000be0200", + "1000000000c1020030b0ad0100c20200307b150300bd020060a8bb52407b0100", + "00ce02003011643401cf020030000000003b06b13ae6ae6671", + ), + KeyBlob { + key_material: hex::decode("d43c2f04f948521b81bdbf001310f592").unwrap(), + hw_enforced: vec![], + sw_enforced: vec![ + KeyParameter { tag: Tag::ALGORITHM, value: KPV::Algorithm(Algorithm::AES) }, + KeyParameter { tag: Tag::KEY_SIZE, value: KPV::Integer(128) }, + KeyParameter { + tag: Tag::PURPOSE, + value: KPV::KeyPurpose(KeyPurpose::ENCRYPT), + }, + KeyParameter { + tag: Tag::PURPOSE, + value: KPV::KeyPurpose(KeyPurpose::DECRYPT), + }, + KeyParameter { + tag: Tag::BLOCK_MODE, + value: KPV::BlockMode(BlockMode::CBC), + }, + KeyParameter { + tag: Tag::PADDING, + value: KPV::PaddingMode(PaddingMode::NONE), + }, + KeyParameter { tag: Tag::ORIGIN, value: KPV::Origin(KeyOrigin::GENERATED) }, + KeyParameter { tag: Tag::OS_VERSION, value: KPV::Integer(110000) }, + KeyParameter { tag: Tag::OS_PATCHLEVEL, value: KPV::Integer(202107) }, + KeyParameter { + tag: Tag::CREATION_DATETIME, + value: KPV::DateTime(1628871769000), + }, + KeyParameter { tag: Tag::VENDOR_PATCHLEVEL, value: KPV::Integer(20210705) }, + KeyParameter { tag: Tag::BOOT_PATCHLEVEL, value: KPV::Integer(0) }, + ], + }, + Some(KeyFormat::RAW), + ), + ( + concat!( + "00df0000003081dc020101044200b6ce876b947e263d61b8e3998d50dc0afb6b", + "a14e46ab7ca532fbe2a379b155d0a5bb99265402857b1601fb20be6c244bf654", + "e9e79413cd503eae3d9cf68ed24f47a00706052b81040023a181890381860004", + "006b840f0db0b12f074ab916c7773cfa7d42967c9e5b4fae09cf999f7e116d14", + "0743bdd028db0a3fcc670e721b9f00bc7fb70aa401c7d6de6582fc26962a29b7", + "45e30142e90685646661550344113aaf28bdee6cb02d19df1faab4398556a909", + "7d6f64b95209601a549389a311231c6cce78354f2cdbc3a904abf70686f5f0c3", + "b877984d000000000000000000000000000000000c0000006400000002000010", + "030000000a000010030000000100002002000000010000200300000005000020", + "000000000300003009020000be02001000000000c1020030b0ad0100c2020030", + "7b150300bd02006018d352407b010000ce02003011643401cf02003000000000", + "2f69002e55e9b0a3" + ), + KeyBlob { + key_material: hex::decode(concat!( + "3081dc020101044200b6ce876b947e263d61b8e3998d50dc0afb6ba14e46ab7c", + "a532fbe2a379b155d0a5bb99265402857b1601fb20be6c244bf654e9e79413cd", + "503eae3d9cf68ed24f47a00706052b81040023a181890381860004006b840f0d", + "b0b12f074ab916c7773cfa7d42967c9e5b4fae09cf999f7e116d140743bdd028", + "db0a3fcc670e721b9f00bc7fb70aa401c7d6de6582fc26962a29b745e30142e9", + "0685646661550344113aaf28bdee6cb02d19df1faab4398556a9097d6f64b952", + "09601a549389a311231c6cce78354f2cdbc3a904abf70686f5f0c3b877984d", + )) + .unwrap(), + hw_enforced: vec![], + sw_enforced: vec![ + KeyParameter { tag: Tag::ALGORITHM, value: KPV::Algorithm(Algorithm::EC) }, + KeyParameter { tag: Tag::EC_CURVE, value: KPV::EcCurve(EcCurve::P_521) }, + KeyParameter { + tag: Tag::PURPOSE, + value: KPV::KeyPurpose(KeyPurpose::SIGN), + }, + KeyParameter { + tag: Tag::PURPOSE, + value: KPV::KeyPurpose(KeyPurpose::VERIFY), + }, + KeyParameter { tag: Tag::DIGEST, value: KPV::Digest(Digest::NONE) }, + KeyParameter { tag: Tag::KEY_SIZE, value: KPV::Integer(521) }, + KeyParameter { tag: Tag::ORIGIN, value: KPV::Origin(KeyOrigin::GENERATED) }, + KeyParameter { tag: Tag::OS_VERSION, value: KPV::Integer(110000) }, + KeyParameter { tag: Tag::OS_PATCHLEVEL, value: KPV::Integer(202107) }, + KeyParameter { + tag: Tag::CREATION_DATETIME, + value: KPV::DateTime(1628871775000), + }, + KeyParameter { tag: Tag::VENDOR_PATCHLEVEL, value: KPV::Integer(20210705) }, + KeyParameter { tag: Tag::BOOT_PATCHLEVEL, value: KPV::Integer(0) }, + ], + }, + Some(KeyFormat::PKCS8), + ), + ( + concat!( + "0037000000541d4c440223650d5f51753c1abd80c725034485551e874d62327c", + "65f6247a057f1218bd6c8cd7d319103ddb823fc11fb6c2c7268b5acc00000000", + "0000000000000000000000000c00000064000000020000108000000003000030", + "b801000001000020020000000100002003000000050000200400000008000030", + "00010000be02001000000000c1020030b0ad0100c20200307b150300bd020060", + "00d752407b010000ce02003011643401cf0200300000000036e6986ffc45fbb0", + ), + KeyBlob { + key_material: hex::decode(concat!( + "541d4c440223650d5f51753c1abd80c725034485551e874d62327c65f6247a05", + "7f1218bd6c8cd7d319103ddb823fc11fb6c2c7268b5acc" + )) + .unwrap(), + hw_enforced: vec![], + sw_enforced: vec![ + KeyParameter { + tag: Tag::ALGORITHM, + value: KPV::Algorithm(Algorithm::HMAC), + }, + KeyParameter { tag: Tag::KEY_SIZE, value: KPV::Integer(440) }, + KeyParameter { + tag: Tag::PURPOSE, + value: KPV::KeyPurpose(KeyPurpose::SIGN), + }, + KeyParameter { + tag: Tag::PURPOSE, + value: KPV::KeyPurpose(KeyPurpose::VERIFY), + }, + KeyParameter { tag: Tag::DIGEST, value: KPV::Digest(Digest::SHA_2_256) }, + KeyParameter { tag: Tag::MIN_MAC_LENGTH, value: KPV::Integer(256) }, + KeyParameter { tag: Tag::ORIGIN, value: KPV::Origin(KeyOrigin::GENERATED) }, + KeyParameter { tag: Tag::OS_VERSION, value: KPV::Integer(110000) }, + KeyParameter { tag: Tag::OS_PATCHLEVEL, value: KPV::Integer(202107) }, + KeyParameter { + tag: Tag::CREATION_DATETIME, + value: KPV::DateTime(1628871776000), + }, + KeyParameter { tag: Tag::VENDOR_PATCHLEVEL, value: KPV::Integer(20210705) }, + KeyParameter { tag: Tag::BOOT_PATCHLEVEL, value: KPV::Integer(0) }, + ], + }, + Some(KeyFormat::RAW), + ), + ( + concat!( + "00a8040000308204a40201000282010100bc47b5c71116766669b91fa747df87", + "a1963df83956569d4ac232aeba8a246c0ec73bf606374a6d07f30c2162f97082", + "825c7c6e482a2841dfeaec1429d84e52c54a6b2f760dec952c9c44a3c3a80f31", + "c1ced84878edd4858059071c4d20d9ab0aae978bd68c1eb448e174a9736c3973", + "6838151642eda8215107375865a99a57f29467c74c40f37b0221b93ec3f4f22d", + "5337c8bf9245d56936196a92b1dea315ecce8785f9fa9b7d159ca207612cc0de", + "b0957d61dbba5d9bd38784f4fecbf233b04e686a340528665ecd03db8e8a09b2", + "540c84e45c4a99fb338b76bba7722856b5113341c349708937228f167d238ed8", + "efb9cc19547dd620f6a90d95f07e50bfe102030100010282010002f91b69d9af", + "59fe87421af9ba60f15c77f9c1c90effd6634332876f8ee5a116b126f55d3703", + "8bf9f588ae20c8d951d842e35c9ef35a7822d3ebf72c0b7c3e229b289ae2e178", + "a848e06d558c2e03d26871ee98a35f370d461ff1c4acc39d684de680a25ec88e", + "e610260e406c400bdeb2893b2d0330cb483e662fa5abd24c2b82143e85dfe30a", + "e7a31f8262da2903d882b35a34a26b699ff2d812bad4b126a0065ec0e101d73a", + "e6f8b29a9144eb83f54940a371fc7416c2c0370df6a41cb5391f17ba33239e1b", + "4217c8db50db5c6bf77ccf621354ecc652a4f7196054c254566fd7b3bc0f3817", + "d9380b190bd382aaffa37785759f285194c11a188bccde0e2e2902818100fb23", + "3335770c9f3cbd4b6ede5f12d03c449b1997bce06a8249bc3de99972fd0d0a63", + "3f7790d1011bf5eedee16fa45a9107a910656ecaee364ce9edb4369843be71f2", + "7a74852d6c7215a6cc60d9803bcac544922f806d8e5844e0ddd914bd78009490", + "4c2856d2b944fade3fb1d67d4a33fb7663a9ab660ab372c2e4868a0f45990281", + "8100bfecf2bb4012e880fd065a0b088f2d757af2878d3f1305f21ce7a7158458", + "18e01181ff06b2f406239fc50808ce3dbe7b68ec01174913c0f237feb3c8c7eb", + "0078b77fb5b8f214b72f6d3835b1a7ebe8b132feb6cb34ab09ce22b98160fc84", + "20fcbf48d1eee49f874e902f049b206a61a095f0405a4935e7c5e49757ab7b57", + "298902818100ec0049383e16f3716de5fc5b2677148efe5dceb02483b43399bd", + "3765559994a9f3900eed7a7e9e8f3b0eee0e660eca392e3cb736cae612f39e55", + "dad696d3821def10d1f8bbca52f5e6d8e7893ffbdcb491aafdc17bebf86f84d2", + "d8480ed07a7bf9209d20ef6e79429489d4cb7768281a2f7e32ec1830fd6f6332", + "38f521ba764902818100b2c3ce5751580b4e51df3fb175387f5c24b79040a4d6", + "603c6265f70018b441ff3aef7d8e4cd2f480ec0906f1c4c0481304e8861f9d46", + "93fa48e3a9abc362859eeb343e1c5507ac94b5439ce7ac04154a2fb886a4819b", + "2a57e18a2e131b412ac4a09b004766959cdf357745f003e272aab3de02e2d5bc", + "2af4ed75760858ab181902818061d19c2a8dcacde104b97f7c4fae11216157c1", + "c0a258d882984d12383a73dc56fe2ac93512bb321df9706ecdb2f70a44c949c4", + "340a9fae64a0646cf51f37c58c08bebde91667b3b2fa7c895f7983d4786c5526", + "1941b3654533b0598383ebbcffcdf28b6cf13d376e3a70b49b14d8d06e8563a2", + "47f56a337e3b9845b4f2b61356000000000000000000000000000000000d0000", + "007000000002000010010000000300003000080000c800005001000100000000", + "0001000020020000000100002003000000050000200000000006000020010000", + "00be02001000000000c1020030b0ad0100c20200307b150300bd020060a8bb52", + "407b010000ce02003011643401cf02003000000000544862e9c961e857", + ), + KeyBlob { + key_material: hex::decode(concat!( + "308204a40201000282010100bc47b5c71116766669b91fa747df87a1963df839", + "56569d4ac232aeba8a246c0ec73bf606374a6d07f30c2162f97082825c7c6e48", + "2a2841dfeaec1429d84e52c54a6b2f760dec952c9c44a3c3a80f31c1ced84878", + "edd4858059071c4d20d9ab0aae978bd68c1eb448e174a9736c39736838151642", + "eda8215107375865a99a57f29467c74c40f37b0221b93ec3f4f22d5337c8bf92", + "45d56936196a92b1dea315ecce8785f9fa9b7d159ca207612cc0deb0957d61db", + "ba5d9bd38784f4fecbf233b04e686a340528665ecd03db8e8a09b2540c84e45c", + "4a99fb338b76bba7722856b5113341c349708937228f167d238ed8efb9cc1954", + "7dd620f6a90d95f07e50bfe102030100010282010002f91b69d9af59fe87421a", + "f9ba60f15c77f9c1c90effd6634332876f8ee5a116b126f55d37038bf9f588ae", + "20c8d951d842e35c9ef35a7822d3ebf72c0b7c3e229b289ae2e178a848e06d55", + "8c2e03d26871ee98a35f370d461ff1c4acc39d684de680a25ec88ee610260e40", + "6c400bdeb2893b2d0330cb483e662fa5abd24c2b82143e85dfe30ae7a31f8262", + "da2903d882b35a34a26b699ff2d812bad4b126a0065ec0e101d73ae6f8b29a91", + "44eb83f54940a371fc7416c2c0370df6a41cb5391f17ba33239e1b4217c8db50", + "db5c6bf77ccf621354ecc652a4f7196054c254566fd7b3bc0f3817d9380b190b", + "d382aaffa37785759f285194c11a188bccde0e2e2902818100fb233335770c9f", + "3cbd4b6ede5f12d03c449b1997bce06a8249bc3de99972fd0d0a633f7790d101", + "1bf5eedee16fa45a9107a910656ecaee364ce9edb4369843be71f27a74852d6c", + "7215a6cc60d9803bcac544922f806d8e5844e0ddd914bd780094904c2856d2b9", + "44fade3fb1d67d4a33fb7663a9ab660ab372c2e4868a0f459902818100bfecf2", + "bb4012e880fd065a0b088f2d757af2878d3f1305f21ce7a715845818e01181ff", + "06b2f406239fc50808ce3dbe7b68ec01174913c0f237feb3c8c7eb0078b77fb5", + "b8f214b72f6d3835b1a7ebe8b132feb6cb34ab09ce22b98160fc8420fcbf48d1", + "eee49f874e902f049b206a61a095f0405a4935e7c5e49757ab7b572989028181", + "00ec0049383e16f3716de5fc5b2677148efe5dceb02483b43399bd3765559994", + "a9f3900eed7a7e9e8f3b0eee0e660eca392e3cb736cae612f39e55dad696d382", + "1def10d1f8bbca52f5e6d8e7893ffbdcb491aafdc17bebf86f84d2d8480ed07a", + "7bf9209d20ef6e79429489d4cb7768281a2f7e32ec1830fd6f633238f521ba76", + "4902818100b2c3ce5751580b4e51df3fb175387f5c24b79040a4d6603c6265f7", + "0018b441ff3aef7d8e4cd2f480ec0906f1c4c0481304e8861f9d4693fa48e3a9", + "abc362859eeb343e1c5507ac94b5439ce7ac04154a2fb886a4819b2a57e18a2e", + "131b412ac4a09b004766959cdf357745f003e272aab3de02e2d5bc2af4ed7576", + "0858ab181902818061d19c2a8dcacde104b97f7c4fae11216157c1c0a258d882", + "984d12383a73dc56fe2ac93512bb321df9706ecdb2f70a44c949c4340a9fae64", + "a0646cf51f37c58c08bebde91667b3b2fa7c895f7983d4786c55261941b36545", + "33b0598383ebbcffcdf28b6cf13d376e3a70b49b14d8d06e8563a247f56a337e", + "3b9845b4f2b61356", + )) + .unwrap(), + hw_enforced: vec![], + sw_enforced: vec![ + KeyParameter { tag: Tag::ALGORITHM, value: KPV::Algorithm(Algorithm::RSA) }, + KeyParameter { tag: Tag::KEY_SIZE, value: KPV::Integer(2048) }, + KeyParameter { + tag: Tag::RSA_PUBLIC_EXPONENT, + value: KPV::LongInteger(65537), + }, + KeyParameter { + tag: Tag::PURPOSE, + value: KPV::KeyPurpose(KeyPurpose::SIGN), + }, + KeyParameter { + tag: Tag::PURPOSE, + value: KPV::KeyPurpose(KeyPurpose::VERIFY), + }, + KeyParameter { tag: Tag::DIGEST, value: KPV::Digest(Digest::NONE) }, + KeyParameter { + tag: Tag::PADDING, + value: KPV::PaddingMode(PaddingMode::NONE), + }, + KeyParameter { tag: Tag::ORIGIN, value: KPV::Origin(KeyOrigin::GENERATED) }, + KeyParameter { tag: Tag::OS_VERSION, value: KPV::Integer(110000) }, + KeyParameter { tag: Tag::OS_PATCHLEVEL, value: KPV::Integer(202107) }, + KeyParameter { + tag: Tag::CREATION_DATETIME, + value: KPV::DateTime(1628871769000), + }, + KeyParameter { tag: Tag::VENDOR_PATCHLEVEL, value: KPV::Integer(20210705) }, + KeyParameter { tag: Tag::BOOT_PATCHLEVEL, value: KPV::Integer(0) }, + ], + }, + // No support for RSA keys in export_key(). + None, + ), + ]; + + for (input, want, want_format) in tests { + let input = hex::decode(input).unwrap(); + let got = KeyBlob::new_from_serialized(&input, &hidden).expect("invalid keyblob!"); + assert!(got == want); + + if let Some(want_format) = want_format { + let (got_format, _key_material, params) = + export_key(&input, &[]).expect("invalid keyblob!"); + assert_eq!(got_format, want_format); + // All the test cases are software-only keys. + assert_eq!(params, got.sw_enforced); + } + } + } +} |