Password is only used in PBKDF2
Ensure that the secret that LockSettingsService passes us, which we
somewhat inaccurately call a "password", is used only as input to PBKDF2
by wrapping it in a Password type.
Bug: 163866361
Test: keystore2_test
Change-Id: I5eb964cb9ffe97902dfeec17c328766f79aa5646
diff --git a/keystore2/src/authorization.rs b/keystore2/src/authorization.rs
index 5abb426..553746a 100644
--- a/keystore2/src/authorization.rs
+++ b/keystore2/src/authorization.rs
@@ -33,6 +33,7 @@
ResponseCode::ResponseCode as KsResponseCode };
use anyhow::{Context, Result};
use binder::IBinderInternal;
+use keystore2_crypto::Password;
use keystore2_selinux as selinux;
/// This is the Authorization error type, it wraps binder exceptions and the
@@ -128,10 +129,10 @@
&self,
lock_screen_event: LockScreenEvent,
user_id: i32,
- password: Option<&[u8]>,
+ password: Option<Password>,
) -> Result<()> {
match (lock_screen_event, password) {
- (LockScreenEvent::UNLOCK, Some(user_password)) => {
+ (LockScreenEvent::UNLOCK, Some(password)) => {
//This corresponds to the unlock() method in legacy keystore API.
//check permission
check_keystore_permission(KeystorePerm::unlock())
@@ -145,7 +146,7 @@
&LEGACY_MIGRATOR,
&SUPER_KEY,
user_id as u32,
- user_password,
+ &password,
)
})
.context("In on_lock_screen_event: Unlock with password.")?
@@ -213,7 +214,10 @@
user_id: i32,
password: Option<&[u8]>,
) -> BinderResult<()> {
- map_or_log_err(self.on_lock_screen_event(lock_screen_event, user_id, password), Ok)
+ map_or_log_err(
+ self.on_lock_screen_event(lock_screen_event, user_id, password.map(|pw| pw.into())),
+ Ok,
+ )
}
fn getAuthTokensForCredStore(
diff --git a/keystore2/src/crypto/lib.rs b/keystore2/src/crypto/lib.rs
index bd5906c..98e6eef 100644
--- a/keystore2/src/crypto/lib.rs
+++ b/keystore2/src/crypto/lib.rs
@@ -149,42 +149,68 @@
}
}
-/// Generates 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_from_password(
- pw: &[u8],
- salt: Option<&[u8]>,
- key_length: usize,
-) -> Result<ZVec, Error> {
- let salt: *const u8 = match salt {
- Some(s) => {
- if s.len() != SALT_LENGTH {
- return Err(Error::InvalidSaltLength);
- }
- s.as_ptr()
- }
- None => std::ptr::null(),
- };
+/// Represents a "password" that can be used to key the PBKDF2 algorithm.
+pub enum Password<'a> {
+ /// Borrow an existing byte array
+ Ref(&'a [u8]),
+ /// Use an owned ZVec to store the key
+ Owned(ZVec),
+}
- match key_length {
- AES_128_KEY_LENGTH | AES_256_KEY_LENGTH => {}
- _ => return Err(Error::InvalidKeyLength),
+impl<'a> From<&'a [u8]> for Password<'a> {
+ fn from(pw: &'a [u8]) -> Self {
+ Self::Ref(pw)
+ }
+}
+
+impl<'a> Password<'a> {
+ fn get_key(&'a self) -> &'a [u8] {
+ match self {
+ Self::Ref(b) => b,
+ Self::Owned(z) => &*z,
+ }
}
- let mut result = ZVec::new(key_length)?;
+ /// 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(&self, salt: Option<&[u8]>, key_length: usize) -> Result<ZVec, Error> {
+ let pw = self.get_key();
- unsafe {
- generateKeyFromPassword(
- result.as_mut_ptr(),
- result.len(),
- pw.as_ptr() as *const std::os::raw::c_char,
- pw.len(),
- salt,
- )
- };
+ let salt: *const u8 = match salt {
+ Some(s) => {
+ if s.len() != SALT_LENGTH {
+ return Err(Error::InvalidSaltLength);
+ }
+ s.as_ptr()
+ }
+ None => std::ptr::null(),
+ };
- Ok(result)
+ match key_length {
+ AES_128_KEY_LENGTH | AES_256_KEY_LENGTH => {}
+ _ => return Err(Error::InvalidKeyLength),
+ }
+
+ let mut result = ZVec::new(key_length)?;
+
+ unsafe {
+ generateKeyFromPassword(
+ result.as_mut_ptr(),
+ result.len(),
+ pw.as_ptr() as *const std::os::raw::c_char,
+ pw.len(),
+ salt,
+ )
+ };
+
+ Ok(result)
+ }
+
+ /// 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())?))
+ }
}
/// Calls the boringssl HKDF_extract function.
diff --git a/keystore2/src/database.rs b/keystore2/src/database.rs
index 22ab02e..0e8b3d7 100644
--- a/keystore2/src/database.rs
+++ b/keystore2/src/database.rs
@@ -1357,15 +1357,11 @@
}
fn is_locked_error(e: &anyhow::Error) -> bool {
- matches!(e.root_cause().downcast_ref::<rusqlite::ffi::Error>(),
- Some(rusqlite::ffi::Error {
- code: rusqlite::ErrorCode::DatabaseBusy,
- ..
- })
- | Some(rusqlite::ffi::Error {
- code: rusqlite::ErrorCode::DatabaseLocked,
- ..
- }))
+ matches!(
+ e.root_cause().downcast_ref::<rusqlite::ffi::Error>(),
+ Some(rusqlite::ffi::Error { code: rusqlite::ErrorCode::DatabaseBusy, .. })
+ | Some(rusqlite::ffi::Error { code: rusqlite::ErrorCode::DatabaseLocked, .. })
+ )
}
/// Creates a new key entry and allocates a new randomized id for the new key.
@@ -4889,7 +4885,7 @@
#[test]
fn test_store_super_key() -> Result<()> {
let mut db = new_test_db()?;
- let pw = "xyzabc".as_bytes();
+ let pw: keystore2_crypto::Password = (&b"xyzabc"[..]).into();
let super_key = keystore2_crypto::generate_aes256_key()?;
let secret = String::from("keystore2 is great.");
let secret_bytes = secret.into_bytes();
diff --git a/keystore2/src/legacy_blob.rs b/keystore2/src/legacy_blob.rs
index 3fc77b7..5f40ece 100644
--- a/keystore2/src/legacy_blob.rs
+++ b/keystore2/src/legacy_blob.rs
@@ -26,7 +26,7 @@
SecurityLevel::SecurityLevel, Tag::Tag, TagType::TagType,
};
use anyhow::{Context, Result};
-use keystore2_crypto::{aes_gcm_decrypt, derive_key_from_password, ZVec};
+use keystore2_crypto::{aes_gcm_decrypt, Password, ZVec};
use std::collections::{HashMap, HashSet};
use std::{convert::TryInto, fs::File, path::Path, path::PathBuf};
use std::{
@@ -1036,7 +1036,7 @@
}
/// Load and decrypt legacy super key blob.
- pub fn load_super_key(&self, user_id: u32, pw: &[u8]) -> Result<Option<ZVec>> {
+ pub fn load_super_key(&self, user_id: u32, pw: &Password) -> Result<Option<ZVec>> {
let path = self.make_super_key_filename(user_id);
let blob = Self::read_generic_blob(&path)
.context("In load_super_key: While loading super key.")?;
@@ -1046,7 +1046,8 @@
Blob {
value: BlobValue::PwEncrypted { iv, tag, data, salt, key_size }, ..
} => {
- let key = derive_key_from_password(pw, Some(&salt), key_size)
+ let key = pw
+ .derive_key(Some(&salt), key_size)
.context("In load_super_key: Failed to derive key from password.")?;
let blob = aes_gcm_decrypt(&data, &iv, &tag, &key).context(
"In load_super_key: while trying to decrypt legacy super key blob.",
@@ -1294,7 +1295,7 @@
Some(&error::Error::Rc(ResponseCode::LOCKED))
);
- key_manager.unlock_user_key(&mut db, 0, PASSWORD, &legacy_blob_loader)?;
+ key_manager.unlock_user_key(&mut db, 0, &(PASSWORD.into()), &legacy_blob_loader)?;
if let (Some((Blob { flags, value: _ }, _params)), Some(cert), Some(chain)) =
legacy_blob_loader.load_by_uid_alias(10223, "authbound", Some(&key_manager))?
diff --git a/keystore2/src/legacy_migrator.rs b/keystore2/src/legacy_migrator.rs
index 1ae8719..7567070 100644
--- a/keystore2/src/legacy_migrator.rs
+++ b/keystore2/src/legacy_migrator.rs
@@ -29,9 +29,8 @@
};
use anyhow::{Context, Result};
use core::ops::Deref;
-use keystore2_crypto::ZVec;
+use keystore2_crypto::{Password, ZVec};
use std::collections::{HashMap, HashSet};
-use std::convert::TryInto;
use std::sync::atomic::{AtomicU8, Ordering};
use std::sync::mpsc::channel;
use std::sync::{Arc, Mutex};
@@ -334,7 +333,7 @@
pub fn with_try_migrate_super_key<F, T>(
&self,
user_id: u32,
- pw: &[u8],
+ pw: &Password,
mut key_accessor: F,
) -> Result<Option<T>>
where
@@ -345,12 +344,9 @@
Ok(None) => {}
Err(e) => return Err(e),
}
-
- let pw: ZVec = pw
- .try_into()
- .context("In with_try_migrate_super_key: copying the password into a zvec.")?;
+ let pw = pw.try_clone().context("In with_try_migrate_super_key: Cloning password.")?;
let result = self.do_serialized(move |migrator_state| {
- migrator_state.check_and_migrate_super_key(user_id, pw)
+ migrator_state.check_and_migrate_super_key(user_id, &pw)
});
if let Some(result) = result {
@@ -550,7 +546,7 @@
}
}
- fn check_and_migrate_super_key(&mut self, user_id: u32, pw: ZVec) -> Result<()> {
+ fn check_and_migrate_super_key(&mut self, user_id: u32, pw: &Password) -> Result<()> {
if self.recently_migrated_super_key.contains(&user_id) {
return Ok(());
}
@@ -561,7 +557,7 @@
.context("In check_and_migrate_super_key: Trying to load legacy super key.")?
{
let (blob, blob_metadata) =
- crate::super_key::SuperKeyManager::encrypt_with_password(&super_key, &pw)
+ crate::super_key::SuperKeyManager::encrypt_with_password(&super_key, pw)
.context("In check_and_migrate_super_key: Trying to encrypt super key.")?;
self.db.store_super_key(user_id, &(&blob, &blob_metadata)).context(concat!(
diff --git a/keystore2/src/super_key.rs b/keystore2/src/super_key.rs
index 5ee685a..aa434d6 100644
--- a/keystore2/src/super_key.rs
+++ b/keystore2/src/super_key.rs
@@ -23,8 +23,8 @@
use android_system_keystore2::aidl::android::system::keystore2::Domain::Domain;
use anyhow::{Context, Result};
use keystore2_crypto::{
- aes_gcm_decrypt, aes_gcm_encrypt, derive_key_from_password, generate_aes256_key, generate_salt,
- ZVec, AES_256_KEY_LENGTH,
+ aes_gcm_decrypt, aes_gcm_encrypt, generate_aes256_key, generate_salt, Password, ZVec,
+ AES_256_KEY_LENGTH,
};
use std::ops::Deref;
use std::{
@@ -130,7 +130,7 @@
&self,
db: &mut KeystoreDB,
user: UserId,
- pw: &[u8],
+ pw: &Password,
legacy_blob_loader: &LegacyBlobLoader,
) -> Result<()> {
let (_, entry) = db
@@ -233,7 +233,7 @@
db: &mut KeystoreDB,
legacy_migrator: &LegacyMigrator,
user_id: u32,
- pw: &[u8],
+ pw: &Password,
) -> Result<UserState> {
let result = legacy_migrator
.with_try_migrate_super_key(user_id, pw, || db.load_super_key(user_id))
@@ -261,7 +261,7 @@
db: &mut KeystoreDB,
legacy_migrator: &LegacyMigrator,
user_id: u32,
- pw: Option<&[u8]>,
+ pw: Option<&Password>,
) -> Result<UserState> {
let super_key_exists_in_db =
Self::super_key_exists_in_db_for_user(db, legacy_migrator, user_id).context(
@@ -296,7 +296,7 @@
&self,
user_id: u32,
entry: KeyEntry,
- pw: &[u8],
+ pw: &Password,
) -> Result<SuperKey> {
let super_key = Self::extract_super_key_from_key_entry(entry, pw).context(
"In populate_cache_from_super_key_blob. Failed to extract super key from key entry",
@@ -306,7 +306,7 @@
}
/// Extracts super key from the entry loaded from the database
- pub fn extract_super_key_from_key_entry(entry: KeyEntry, pw: &[u8]) -> Result<SuperKey> {
+ pub fn extract_super_key_from_key_entry(entry: KeyEntry, pw: &Password) -> Result<SuperKey> {
if let Some((blob, metadata)) = entry.key_blob_info() {
let key = match (
metadata.encrypted_by(),
@@ -315,9 +315,9 @@
metadata.aead_tag(),
) {
(Some(&EncryptedBy::Password), Some(salt), Some(iv), Some(tag)) => {
- let key = derive_key_from_password(pw, Some(salt), AES_256_KEY_LENGTH).context(
- "In extract_super_key_from_key_entry: Failed to generate key from password.",
- )?;
+ let key = pw.derive_key(Some(salt), AES_256_KEY_LENGTH).context(
+ "In extract_super_key_from_key_entry: Failed to generate key from password.",
+ )?;
aes_gcm_decrypt(blob, iv, tag, &key).context(
"In extract_super_key_from_key_entry: Failed to decrypt key blob.",
@@ -344,9 +344,13 @@
}
/// Encrypts the super key from a key derived from the password, before storing in the database.
- pub fn encrypt_with_password(super_key: &[u8], pw: &[u8]) -> Result<(Vec<u8>, BlobMetaData)> {
+ 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 = derive_key_from_password(pw, Some(&salt), AES_256_KEY_LENGTH)
+ let derived_key = pw
+ .derive_key(Some(&salt), AES_256_KEY_LENGTH)
.context("In encrypt_with_password: Failed to derive password.")?;
let mut metadata = BlobMetaData::new();
metadata.add(BlobMetaEntry::EncryptedBy(EncryptedBy::Password));
@@ -514,7 +518,7 @@
legacy_migrator: &LegacyMigrator,
skm: &SuperKeyManager,
user_id: u32,
- password: Option<&[u8]>,
+ password: Option<&Password>,
) -> Result<UserState> {
match skm.get_per_boot_key_by_user_id(user_id) {
Some(super_key) => {
@@ -548,7 +552,7 @@
legacy_migrator: &LegacyMigrator,
skm: &SuperKeyManager,
user_id: u32,
- password: &[u8],
+ password: &Password,
) -> Result<UserState> {
match skm.get_per_boot_key_by_user_id(user_id) {
Some(super_key) => {
diff --git a/keystore2/src/user_manager.rs b/keystore2/src/user_manager.rs
index 123f3a1..0cc2e92 100644
--- a/keystore2/src/user_manager.rs
+++ b/keystore2/src/user_manager.rs
@@ -29,6 +29,7 @@
use android_system_keystore2::aidl::android::system::keystore2::ResponseCode::ResponseCode;
use anyhow::{Context, Result};
use binder::{IBinderInternal, Strong};
+use keystore2_crypto::Password;
/// This struct is defined to implement the aforementioned AIDL interface.
/// As of now, it is an empty struct.
@@ -42,7 +43,7 @@
Ok(result)
}
- fn on_user_password_changed(user_id: i32, password: Option<&[u8]>) -> Result<()> {
+ fn on_user_password_changed(user_id: i32, password: Option<Password>) -> Result<()> {
//Check permission. Function should return if this failed. Therefore having '?' at the end
//is very important.
check_keystore_permission(KeystorePerm::change_password())
@@ -55,7 +56,7 @@
&LEGACY_MIGRATOR,
&SUPER_KEY,
user_id as u32,
- password,
+ password.as_ref(),
)
})
.context("In on_user_password_changed.")?
@@ -121,7 +122,7 @@
impl IKeystoreMaintenance for Maintenance {
fn onUserPasswordChanged(&self, user_id: i32, password: Option<&[u8]>) -> BinderResult<()> {
- map_or_log_err(Self::on_user_password_changed(user_id, password), Ok)
+ map_or_log_err(Self::on_user_password_changed(user_id, password.map(|pw| pw.into())), Ok)
}
fn onUserAdded(&self, user_id: i32) -> BinderResult<()> {