summaryrefslogtreecommitdiff
path: root/keystore2
diff options
context:
space:
mode:
Diffstat (limited to 'keystore2')
-rw-r--r--keystore2/Android.bp1
-rw-r--r--keystore2/legacykeystore/lib.rs6
-rw-r--r--keystore2/src/authorization.rs2
-rw-r--r--keystore2/src/database.rs15
-rw-r--r--keystore2/src/enforcements.rs4
-rw-r--r--keystore2/src/km_compat.rs18
-rw-r--r--keystore2/src/legacy_importer.rs8
-rw-r--r--keystore2/src/lib.rs1
-rw-r--r--keystore2/src/maintenance.rs8
-rw-r--r--keystore2/src/metrics_store.rs7
-rw-r--r--keystore2/src/security_level.rs4
-rw-r--r--keystore2/src/service.rs24
-rw-r--r--keystore2/src/super_key.rs284
-rw-r--r--keystore2/src/sw_keyblob.rs812
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(&param.tag);
+ if let KeyParameterValue::Blob(v) = &param.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 &param.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);
+ }
+ }
+ }
+}