keystore: eliminate redundant key stretching
Since the Keystore password is a high-entropy synthetic password, key
stretching is not required. Therefore, improve the performance of
encrypting and decrypting Keystore user super keys by using HKDF instead
of 8192-iteration PBKDF2. PBKDF2 continues to be used for decrypting
old keys, when AES-GCM decryption using the HKDF-derived key fails.
Bug: 296464083
Bug: 314391626
Test: atest -p --include-subdirs system/security/keystore2
Test: Upgraded a device and verified the old super keys can still be
decrypted.
Test: Verified via logcat that super key creation got faster.
Change-Id: Ib7976671ecf886e6308b66e6b1fdfb4b21346afb
diff --git a/keystore2/src/super_key.rs b/keystore2/src/super_key.rs
index b9f43ed..9c17ccb 100644
--- a/keystore2/src/super_key.rs
+++ b/keystore2/src/super_key.rs
@@ -532,11 +532,17 @@
(Some(&EncryptedBy::Password), Some(salt), Some(iv), Some(tag)) => {
// Note that password encryption is AES no matter the value of algorithm.
let key = pw
- .derive_key_pbkdf2(salt, AES_256_KEY_LENGTH)
- .context(ks_err!("Failed to generate key from password."))?;
+ .derive_key_hkdf(salt, AES_256_KEY_LENGTH)
+ .context(ks_err!("Failed to derive key from password."))?;
- aes_gcm_decrypt(blob, iv, tag, &key)
- .context(ks_err!("Failed to decrypt key blob."))?
+ aes_gcm_decrypt(blob, iv, tag, &key).or_else(|_e| {
+ // Handle old key stored before the switch to HKDF.
+ let key = pw
+ .derive_key_pbkdf2(salt, AES_256_KEY_LENGTH)
+ .context(ks_err!("Failed to derive key from password (PBKDF2)."))?;
+ aes_gcm_decrypt(blob, iv, tag, &key)
+ .context(ks_err!("Failed to decrypt key blob."))
+ })?
}
(enc_by, salt, iv, tag) => {
return Err(Error::Rc(ResponseCode::VALUE_CORRUPTED)).context(ks_err!(
@@ -563,14 +569,20 @@
}
/// Encrypts the super key from a key derived from the password, before storing in the database.
+ /// This does not stretch the password; i.e., it assumes that the password is a high-entropy
+ /// synthetic password, not a low-entropy user provided password.
pub fn encrypt_with_password(
super_key: &[u8],
pw: &Password,
) -> Result<(Vec<u8>, BlobMetaData)> {
let salt = generate_salt().context("In encrypt_with_password: Failed to generate salt.")?;
- let derived_key = pw
- .derive_key_pbkdf2(&salt, AES_256_KEY_LENGTH)
- .context(ks_err!("Failed to derive password."))?;
+ let derived_key = if android_security_flags::fix_unlocked_device_required_keys_v2() {
+ pw.derive_key_hkdf(&salt, AES_256_KEY_LENGTH)
+ .context(ks_err!("Failed to derive key from password."))?
+ } else {
+ pw.derive_key_pbkdf2(&salt, AES_256_KEY_LENGTH)
+ .context(ks_err!("Failed to derive password."))?
+ };
let mut metadata = BlobMetaData::new();
metadata.add(BlobMetaEntry::EncryptedBy(EncryptedBy::Password));
metadata.add(BlobMetaEntry::Salt(salt));