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/crypto/lib.rs b/keystore2/src/crypto/lib.rs
index 234dede..09b84ec 100644
--- a/keystore2/src/crypto/lib.rs
+++ b/keystore2/src/crypto/lib.rs
@@ -172,7 +172,7 @@
     }
 }
 
-/// Represents a "password" that can be used to key the PBKDF2 algorithm.
+/// A high-entropy synthetic password from which an AES key may be derived.
 pub enum Password<'a> {
     /// Borrow an existing byte array
     Ref(&'a [u8]),
@@ -194,20 +194,23 @@
         }
     }
 
-    /// Generate a key from the given password and salt.
-    /// The salt must be exactly 16 bytes long.
-    /// Two key sizes are accepted: 16 and 32 bytes.
-    pub fn derive_key_pbkdf2(&self, salt: &[u8], key_length: usize) -> Result<ZVec, Error> {
+    /// Derives a key from the given password and salt, using PBKDF2 with 8192 iterations.
+    ///
+    /// The salt length must be 16 bytes, and the output key length must be 16 or 32 bytes.
+    ///
+    /// This function exists only for backwards compatibility reasons.  Keystore now receives only
+    /// high-entropy synthetic passwords, which do not require key stretching.
+    pub fn derive_key_pbkdf2(&self, salt: &[u8], out_len: usize) -> Result<ZVec, Error> {
         if salt.len() != SALT_LENGTH {
             return Err(Error::InvalidSaltLength);
         }
-        match key_length {
+        match out_len {
             AES_128_KEY_LENGTH | AES_256_KEY_LENGTH => {}
             _ => return Err(Error::InvalidKeyLength),
         }
 
         let pw = self.get_key();
-        let mut result = ZVec::new(key_length)?;
+        let mut result = ZVec::new(out_len)?;
 
         // Safety: We checked that the salt is exactly 16 bytes long. The other pointers are valid,
         // and have matching lengths.
@@ -224,6 +227,13 @@
         Ok(result)
     }
 
+    /// Derives a key from the given high-entropy synthetic password and salt, using HKDF.
+    pub fn derive_key_hkdf(&self, salt: &[u8], out_len: usize) -> Result<ZVec, Error> {
+        let prk = hkdf_extract(self.get_key(), salt)?;
+        let info = [];
+        hkdf_expand(out_len, &prk, &info)
+    }
+
     /// Try to make another Password object with the same data.
     pub fn try_clone(&self) -> Result<Password<'static>, Error> {
         Ok(Password::Owned(ZVec::try_from(self.get_key())?))
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));