Merge "Moving set_requesting_sid to new_binder method."
diff --git a/keystore2/Android.bp b/keystore2/Android.bp
index af177be..dfc1db0 100644
--- a/keystore2/Android.bp
+++ b/keystore2/Android.bp
@@ -54,6 +54,8 @@
"librand",
"librusqlite",
"libstatslog_rust",
+ "libstatslog_rust_header",
+ "libstatspull_rust",
"libthiserror",
],
shared_libs: [
diff --git a/keystore2/aaid/Android.bp b/keystore2/aaid/Android.bp
index d27fdf6..c04ce51 100644
--- a/keystore2/aaid/Android.bp
+++ b/keystore2/aaid/Android.bp
@@ -39,8 +39,8 @@
bindgen_flags: [
"--size_t-is-usize",
- "--whitelist-function=aaid_keystore_attestation_id",
- "--whitelist-var=KEY_ATTESTATION_APPLICATION_ID_MAX_SIZE",
+ "--allowlist-function=aaid_keystore_attestation_id",
+ "--allowlist-var=KEY_ATTESTATION_APPLICATION_ID_MAX_SIZE",
],
}
diff --git a/keystore2/apc_compat/Android.bp b/keystore2/apc_compat/Android.bp
index 9519c8e..bf21675 100644
--- a/keystore2/apc_compat/Android.bp
+++ b/keystore2/apc_compat/Android.bp
@@ -41,12 +41,12 @@
source_stem: "bindings",
bindgen_flags: [
- "--whitelist-function=tryGetUserConfirmationService",
- "--whitelist-function=promptUserConfirmation",
- "--whitelist-function=abortUserConfirmation",
- "--whitelist-function=closeUserConfirmationService",
- "--whitelist-var=INVALID_SERVICE_HANDLE",
- "--whitelist-var=APC_COMPAT_.*",
+ "--allowlist-function=tryGetUserConfirmationService",
+ "--allowlist-function=promptUserConfirmation",
+ "--allowlist-function=abortUserConfirmation",
+ "--allowlist-function=closeUserConfirmationService",
+ "--allowlist-var=INVALID_SERVICE_HANDLE",
+ "--allowlist-var=APC_COMPAT_.*",
],
}
diff --git a/keystore2/src/authorization.rs b/keystore2/src/authorization.rs
index 0a8fe25..ec1edff 100644
--- a/keystore2/src/authorization.rs
+++ b/keystore2/src/authorization.rs
@@ -118,7 +118,7 @@
}
fn add_auth_token(&self, auth_token: &HardwareAuthToken) -> Result<()> {
- //check keystore permission
+ // Check keystore permission.
check_keystore_permission(KeystorePerm::add_auth()).context("In add_auth_token.")?;
ENFORCEMENTS.add_auth_token(auth_token.clone())?;
@@ -133,8 +133,8 @@
) -> Result<()> {
match (lock_screen_event, password) {
(LockScreenEvent::UNLOCK, Some(password)) => {
- //This corresponds to the unlock() method in legacy keystore API.
- //check permission
+ // This corresponds to the unlock() method in legacy keystore API.
+ // check permission
check_keystore_permission(KeystorePerm::unlock())
.context("In on_lock_screen_event: Unlock with password.")?;
ENFORCEMENTS.set_device_locked(user_id, false);
@@ -201,7 +201,7 @@
check_keystore_permission(KeystorePerm::get_auth_token())
.context("In get_auth_tokens_for_credstore.")?;
- // if the challenge is zero, return error
+ // If the challenge is zero, return error
if challenge == 0 {
return Err(Error::Rc(ResponseCode::INVALID_ARGUMENT))
.context("In get_auth_tokens_for_credstore. Challenge can not be zero.");
diff --git a/keystore2/src/boot_level_keys.rs b/keystore2/src/boot_level_keys.rs
index 3d33a26..dd69ed7 100644
--- a/keystore2/src/boot_level_keys.rs
+++ b/keystore2/src/boot_level_keys.rs
@@ -224,6 +224,7 @@
KeyParameterValue::KeySize(256).into(),
KeyParameterValue::MinMacLength(256).into(),
KeyParameterValue::KeyPurpose(KeyPurpose::SIGN).into(),
+ KeyParameterValue::NoAuthRequired.into(),
KeyParameterValue::MaxUsesPerBoot(1).into(),
];
// We use TRUSTED_ENVIRONMENT here because it is the authority on when
diff --git a/keystore2/src/crypto/Android.bp b/keystore2/src/crypto/Android.bp
index 21c9b74..3ba47cd 100644
--- a/keystore2/src/crypto/Android.bp
+++ b/keystore2/src/crypto/Android.bp
@@ -59,27 +59,27 @@
shared_libs: ["libcrypto"],
bindgen_flags: [
"--size_t-is-usize",
- "--whitelist-function", "randomBytes",
- "--whitelist-function", "AES_gcm_encrypt",
- "--whitelist-function", "AES_gcm_decrypt",
- "--whitelist-function", "CreateKeyId",
- "--whitelist-function", "generateKeyFromPassword",
- "--whitelist-function", "HKDFExtract",
- "--whitelist-function", "HKDFExpand",
- "--whitelist-function", "ECDHComputeKey",
- "--whitelist-function", "ECKEYGenerateKey",
- "--whitelist-function", "ECKEYMarshalPrivateKey",
- "--whitelist-function", "ECKEYParsePrivateKey",
- "--whitelist-function", "EC_KEY_get0_public_key",
- "--whitelist-function", "ECPOINTPoint2Oct",
- "--whitelist-function", "ECPOINTOct2Point",
- "--whitelist-function", "EC_KEY_free",
- "--whitelist-function", "EC_POINT_free",
- "--whitelist-function", "extractSubjectFromCertificate",
- "--whitelist-type", "EC_KEY",
- "--whitelist-type", "EC_POINT",
- "--whitelist-var", "EC_MAX_BYTES",
- "--whitelist-var", "EVP_MAX_MD_SIZE",
+ "--allowlist-function", "randomBytes",
+ "--allowlist-function", "AES_gcm_encrypt",
+ "--allowlist-function", "AES_gcm_decrypt",
+ "--allowlist-function", "CreateKeyId",
+ "--allowlist-function", "generateKeyFromPassword",
+ "--allowlist-function", "HKDFExtract",
+ "--allowlist-function", "HKDFExpand",
+ "--allowlist-function", "ECDHComputeKey",
+ "--allowlist-function", "ECKEYGenerateKey",
+ "--allowlist-function", "ECKEYMarshalPrivateKey",
+ "--allowlist-function", "ECKEYParsePrivateKey",
+ "--allowlist-function", "EC_KEY_get0_public_key",
+ "--allowlist-function", "ECPOINTPoint2Oct",
+ "--allowlist-function", "ECPOINTOct2Point",
+ "--allowlist-function", "EC_KEY_free",
+ "--allowlist-function", "EC_POINT_free",
+ "--allowlist-function", "extractSubjectFromCertificate",
+ "--allowlist-type", "EC_KEY",
+ "--allowlist-type", "EC_POINT",
+ "--allowlist-var", "EC_MAX_BYTES",
+ "--allowlist-var", "EVP_MAX_MD_SIZE",
],
cflags: ["-DBORINGSSL_NO_CXX"],
}
diff --git a/keystore2/src/database.rs b/keystore2/src/database.rs
index c35956f..32e2c98 100644
--- a/keystore2/src/database.rs
+++ b/keystore2/src/database.rs
@@ -72,6 +72,9 @@
use android_security_remoteprovisioning::aidl::android::security::remoteprovisioning::{
AttestationPoolStatus::AttestationPoolStatus,
};
+use statslog_rust::keystore2_storage_stats::{
+ Keystore2StorageStats, StorageType as StatsdStorageType,
+};
use keystore2_crypto::ZVec;
use lazy_static::lazy_static;
@@ -829,6 +832,9 @@
const UNASSIGNED_KEY_ID: i64 = -1i64;
const PERBOOT_DB_FILE_NAME: &'static str = &"file:perboot.sqlite?mode=memory&cache=shared";
+ /// Name of the file that holds the cross-boot persistent database.
+ pub const PERSISTENT_DB_FILENAME: &'static str = &"persistent.sqlite";
+
/// This creates a PerBootDbKeepAlive object to keep the per boot database alive.
pub fn keep_perboot_db_alive() -> Result<PerBootDbKeepAlive> {
let conn = Connection::open_in_memory()
@@ -847,7 +853,7 @@
pub fn new(db_root: &Path, gc: Option<Gc>) -> Result<Self> {
// Build the path to the sqlite file.
let mut persistent_path = db_root.to_path_buf();
- persistent_path.push("persistent.sqlite");
+ persistent_path.push(Self::PERSISTENT_DB_FILENAME);
// Now convert them to strings prefixed with "file:"
let mut persistent_path_str = "file:".to_owned();
@@ -1042,6 +1048,100 @@
Ok(conn)
}
+ fn do_table_size_query(
+ &mut self,
+ storage_type: StatsdStorageType,
+ query: &str,
+ params: &[&str],
+ ) -> Result<Keystore2StorageStats> {
+ let (total, unused) = self.with_transaction(TransactionBehavior::Deferred, |tx| {
+ tx.query_row(query, params, |row| Ok((row.get(0)?, row.get(1)?)))
+ .with_context(|| {
+ format!("get_storage_stat: Error size of storage type {}", storage_type as i32)
+ })
+ .no_gc()
+ })?;
+ Ok(Keystore2StorageStats { storage_type, size: total, unused_size: unused })
+ }
+
+ fn get_total_size(&mut self) -> Result<Keystore2StorageStats> {
+ self.do_table_size_query(
+ StatsdStorageType::Database,
+ "SELECT page_count * page_size, freelist_count * page_size
+ FROM pragma_page_count('persistent'),
+ pragma_page_size('persistent'),
+ persistent.pragma_freelist_count();",
+ &[],
+ )
+ }
+
+ fn get_table_size(
+ &mut self,
+ storage_type: StatsdStorageType,
+ schema: &str,
+ table: &str,
+ ) -> Result<Keystore2StorageStats> {
+ self.do_table_size_query(
+ storage_type,
+ "SELECT pgsize,unused FROM dbstat(?1)
+ WHERE name=?2 AND aggregate=TRUE;",
+ &[schema, table],
+ )
+ }
+
+ /// Fetches a storage statisitics atom for a given storage type. For storage
+ /// types that map to a table, information about the table's storage is
+ /// returned. Requests for storage types that are not DB tables return None.
+ pub fn get_storage_stat(
+ &mut self,
+ storage_type: StatsdStorageType,
+ ) -> Result<Keystore2StorageStats> {
+ match storage_type {
+ StatsdStorageType::Database => self.get_total_size(),
+ StatsdStorageType::KeyEntry => {
+ self.get_table_size(storage_type, "persistent", "keyentry")
+ }
+ StatsdStorageType::KeyEntryIdIndex => {
+ self.get_table_size(storage_type, "persistent", "keyentry_id_index")
+ }
+ StatsdStorageType::KeyEntryDomainNamespaceIndex => {
+ self.get_table_size(storage_type, "persistent", "keyentry_domain_namespace_index")
+ }
+ StatsdStorageType::BlobEntry => {
+ self.get_table_size(storage_type, "persistent", "blobentry")
+ }
+ StatsdStorageType::BlobEntryKeyEntryIdIndex => {
+ self.get_table_size(storage_type, "persistent", "blobentry_keyentryid_index")
+ }
+ StatsdStorageType::KeyParameter => {
+ self.get_table_size(storage_type, "persistent", "keyparameter")
+ }
+ StatsdStorageType::KeyParameterKeyEntryIdIndex => {
+ self.get_table_size(storage_type, "persistent", "keyparameter_keyentryid_index")
+ }
+ StatsdStorageType::KeyMetadata => {
+ self.get_table_size(storage_type, "persistent", "keymetadata")
+ }
+ StatsdStorageType::KeyMetadataKeyEntryIdIndex => {
+ self.get_table_size(storage_type, "persistent", "keymetadata_keyentryid_index")
+ }
+ StatsdStorageType::Grant => self.get_table_size(storage_type, "persistent", "grant"),
+ StatsdStorageType::AuthToken => {
+ self.get_table_size(storage_type, "perboot", "authtoken")
+ }
+ StatsdStorageType::BlobMetadata => {
+ self.get_table_size(storage_type, "persistent", "blobmetadata")
+ }
+ StatsdStorageType::BlobMetadataBlobEntryIdIndex => {
+ self.get_table_size(storage_type, "persistent", "blobmetadata_blobentryid_index")
+ }
+ _ => Err(anyhow::Error::msg(format!(
+ "Unsupported storage type: {}",
+ storage_type as i32
+ ))),
+ }
+ }
+
/// This function is intended to be used by the garbage collector.
/// It deletes the blob given by `blob_id_to_delete`. It then tries to find a superseded
/// key blob that might need special handling by the garbage collector.
@@ -3072,6 +3172,8 @@
use rusqlite::NO_PARAMS;
use rusqlite::{Error, TransactionBehavior};
use std::cell::RefCell;
+ use std::collections::BTreeMap;
+ use std::fmt::Write;
use std::sync::atomic::{AtomicU8, Ordering};
use std::sync::Arc;
use std::thread;
@@ -5172,4 +5274,192 @@
assert_eq!(secret_bytes, &*decrypted_secret_bytes);
Ok(())
}
+
+ fn get_valid_statsd_storage_types() -> Vec<StatsdStorageType> {
+ vec![
+ StatsdStorageType::KeyEntry,
+ StatsdStorageType::KeyEntryIdIndex,
+ StatsdStorageType::KeyEntryDomainNamespaceIndex,
+ StatsdStorageType::BlobEntry,
+ StatsdStorageType::BlobEntryKeyEntryIdIndex,
+ StatsdStorageType::KeyParameter,
+ StatsdStorageType::KeyParameterKeyEntryIdIndex,
+ StatsdStorageType::KeyMetadata,
+ StatsdStorageType::KeyMetadataKeyEntryIdIndex,
+ StatsdStorageType::Grant,
+ StatsdStorageType::AuthToken,
+ StatsdStorageType::BlobMetadata,
+ StatsdStorageType::BlobMetadataBlobEntryIdIndex,
+ ]
+ }
+
+ /// Perform a simple check to ensure that we can query all the storage types
+ /// that are supported by the DB. Check for reasonable values.
+ #[test]
+ fn test_query_all_valid_table_sizes() -> Result<()> {
+ const PAGE_SIZE: i64 = 4096;
+
+ let mut db = new_test_db()?;
+
+ for t in get_valid_statsd_storage_types() {
+ let stat = db.get_storage_stat(t)?;
+ assert!(stat.size >= PAGE_SIZE);
+ assert!(stat.size >= stat.unused_size);
+ }
+
+ Ok(())
+ }
+
+ fn get_storage_stats_map(db: &mut KeystoreDB) -> BTreeMap<i32, Keystore2StorageStats> {
+ get_valid_statsd_storage_types()
+ .into_iter()
+ .map(|t| (t as i32, db.get_storage_stat(t).unwrap()))
+ .collect()
+ }
+
+ fn assert_storage_increased(
+ db: &mut KeystoreDB,
+ increased_storage_types: Vec<StatsdStorageType>,
+ baseline: &mut BTreeMap<i32, Keystore2StorageStats>,
+ ) {
+ for storage in increased_storage_types {
+ // Verify the expected storage increased.
+ let new = db.get_storage_stat(storage).unwrap();
+ let storage = storage as i32;
+ let old = &baseline[&storage];
+ assert!(new.size >= old.size, "{}: {} >= {}", storage, new.size, old.size);
+ assert!(
+ new.unused_size <= old.unused_size,
+ "{}: {} <= {}",
+ storage,
+ new.unused_size,
+ old.unused_size
+ );
+
+ // Update the baseline with the new value so that it succeeds in the
+ // later comparison.
+ baseline.insert(storage, new);
+ }
+
+ // Get an updated map of the storage and verify there were no unexpected changes.
+ let updated_stats = get_storage_stats_map(db);
+ assert_eq!(updated_stats.len(), baseline.len());
+
+ for &k in baseline.keys() {
+ let stringify = |map: &BTreeMap<i32, Keystore2StorageStats>| -> String {
+ let mut s = String::new();
+ for &k in map.keys() {
+ writeln!(&mut s, " {}: {}, {}", &k, map[&k].size, map[&k].unused_size)
+ .expect("string concat failed");
+ }
+ s
+ };
+
+ assert!(
+ updated_stats[&k].size == baseline[&k].size
+ && updated_stats[&k].unused_size == baseline[&k].unused_size,
+ "updated_stats:\n{}\nbaseline:\n{}",
+ stringify(&updated_stats),
+ stringify(&baseline)
+ );
+ }
+ }
+
+ #[test]
+ fn test_verify_key_table_size_reporting() -> Result<()> {
+ let mut db = new_test_db()?;
+ let mut working_stats = get_storage_stats_map(&mut db);
+
+ let key_id = db.create_key_entry(&Domain::APP, &42, &KEYSTORE_UUID)?;
+ assert_storage_increased(
+ &mut db,
+ vec![
+ StatsdStorageType::KeyEntry,
+ StatsdStorageType::KeyEntryIdIndex,
+ StatsdStorageType::KeyEntryDomainNamespaceIndex,
+ ],
+ &mut working_stats,
+ );
+
+ let mut blob_metadata = BlobMetaData::new();
+ blob_metadata.add(BlobMetaEntry::EncryptedBy(EncryptedBy::Password));
+ db.set_blob(&key_id, SubComponentType::KEY_BLOB, Some(TEST_KEY_BLOB), None)?;
+ assert_storage_increased(
+ &mut db,
+ vec![
+ StatsdStorageType::BlobEntry,
+ StatsdStorageType::BlobEntryKeyEntryIdIndex,
+ StatsdStorageType::BlobMetadata,
+ StatsdStorageType::BlobMetadataBlobEntryIdIndex,
+ ],
+ &mut working_stats,
+ );
+
+ let params = make_test_params(None);
+ db.insert_keyparameter(&key_id, ¶ms)?;
+ assert_storage_increased(
+ &mut db,
+ vec![StatsdStorageType::KeyParameter, StatsdStorageType::KeyParameterKeyEntryIdIndex],
+ &mut working_stats,
+ );
+
+ let mut metadata = KeyMetaData::new();
+ metadata.add(KeyMetaEntry::CreationDate(DateTime::from_millis_epoch(123456789)));
+ db.insert_key_metadata(&key_id, &metadata)?;
+ assert_storage_increased(
+ &mut db,
+ vec![StatsdStorageType::KeyMetadata, StatsdStorageType::KeyMetadataKeyEntryIdIndex],
+ &mut working_stats,
+ );
+
+ let mut sum = 0;
+ for stat in working_stats.values() {
+ sum += stat.size;
+ }
+ let total = db.get_storage_stat(StatsdStorageType::Database)?.size;
+ assert!(sum <= total, "Expected sum <= total. sum: {}, total: {}", sum, total);
+
+ Ok(())
+ }
+
+ #[test]
+ fn test_verify_auth_table_size_reporting() -> Result<()> {
+ let mut db = new_test_db()?;
+ let mut working_stats = get_storage_stats_map(&mut db);
+ db.insert_auth_token(&HardwareAuthToken {
+ challenge: 123,
+ userId: 456,
+ authenticatorId: 789,
+ authenticatorType: kmhw_authenticator_type::ANY,
+ timestamp: Timestamp { milliSeconds: 10 },
+ mac: b"mac".to_vec(),
+ })?;
+ assert_storage_increased(&mut db, vec![StatsdStorageType::AuthToken], &mut working_stats);
+ Ok(())
+ }
+
+ #[test]
+ fn test_verify_grant_table_size_reporting() -> Result<()> {
+ const OWNER: i64 = 1;
+ let mut db = new_test_db()?;
+ make_test_key_entry(&mut db, Domain::APP, OWNER, TEST_ALIAS, None)?;
+
+ let mut working_stats = get_storage_stats_map(&mut db);
+ db.grant(
+ &KeyDescriptor {
+ domain: Domain::APP,
+ nspace: 0,
+ alias: Some(TEST_ALIAS.to_string()),
+ blob: None,
+ },
+ OWNER as u32,
+ 123,
+ key_perm_set![KeyPerm::use_()],
+ |_, _| Ok(()),
+ )?;
+
+ assert_storage_increased(&mut db, vec![StatsdStorageType::Grant], &mut working_stats);
+
+ Ok(())
+ }
}
diff --git a/keystore2/src/enforcements.rs b/keystore2/src/enforcements.rs
index 7993c88..378b72f 100644
--- a/keystore2/src/enforcements.rs
+++ b/keystore2/src/enforcements.rs
@@ -61,47 +61,54 @@
state: AuthRequestState,
/// This need to be set to Some to fulfill a AuthRequestState::OpAuth or
/// AuthRequestState::TimeStampedOpAuth.
- hat: Option<HardwareAuthToken>,
+ hat: Mutex<Option<HardwareAuthToken>>,
}
+unsafe impl Sync for AuthRequest {}
+
impl AuthRequest {
- fn op_auth() -> Arc<Mutex<Self>> {
- Arc::new(Mutex::new(Self { state: AuthRequestState::OpAuth, hat: None }))
+ fn op_auth() -> Arc<Self> {
+ Arc::new(Self { state: AuthRequestState::OpAuth, hat: Mutex::new(None) })
}
- fn timestamped_op_auth(receiver: Receiver<Result<TimeStampToken, Error>>) -> Arc<Mutex<Self>> {
- Arc::new(Mutex::new(Self {
+ fn timestamped_op_auth(receiver: Receiver<Result<TimeStampToken, Error>>) -> Arc<Self> {
+ Arc::new(Self {
state: AuthRequestState::TimeStampedOpAuth(receiver),
- hat: None,
- }))
+ hat: Mutex::new(None),
+ })
}
fn timestamp(
hat: HardwareAuthToken,
receiver: Receiver<Result<TimeStampToken, Error>>,
- ) -> Arc<Mutex<Self>> {
- Arc::new(Mutex::new(Self { state: AuthRequestState::TimeStamp(receiver), hat: Some(hat) }))
+ ) -> Arc<Self> {
+ Arc::new(Self { state: AuthRequestState::TimeStamp(receiver), hat: Mutex::new(Some(hat)) })
}
- fn add_auth_token(&mut self, hat: HardwareAuthToken) {
- self.hat = Some(hat)
+ fn add_auth_token(&self, hat: HardwareAuthToken) {
+ *self.hat.lock().unwrap() = Some(hat)
}
- fn get_auth_tokens(&mut self) -> Result<(HardwareAuthToken, Option<TimeStampToken>)> {
- match (&self.state, self.hat.is_some()) {
- (AuthRequestState::OpAuth, true) => Ok((self.hat.take().unwrap(), None)),
- (AuthRequestState::TimeStampedOpAuth(recv), true)
- | (AuthRequestState::TimeStamp(recv), true) => {
+ fn get_auth_tokens(&self) -> Result<(HardwareAuthToken, Option<TimeStampToken>)> {
+ let hat = self
+ .hat
+ .lock()
+ .unwrap()
+ .take()
+ .ok_or(Error::Km(ErrorCode::KEY_USER_NOT_AUTHENTICATED))
+ .context("In get_auth_tokens: No operation auth token received.")?;
+
+ let tst = match &self.state {
+ AuthRequestState::TimeStampedOpAuth(recv) | AuthRequestState::TimeStamp(recv) => {
let result = recv.recv().context("In get_auth_tokens: Sender disconnected.")?;
- let tst = result.context(concat!(
+ Some(result.context(concat!(
"In get_auth_tokens: Worker responded with error ",
"from generating timestamp token."
- ))?;
- Ok((self.hat.take().unwrap(), Some(tst)))
+ ))?)
}
- (_, false) => Err(Error::Km(ErrorCode::KEY_USER_NOT_AUTHENTICATED))
- .context("In get_auth_tokens: No operation auth token received."),
- }
+ AuthRequestState::OpAuth => None,
+ };
+ Ok((hat, tst))
}
}
@@ -127,7 +134,7 @@
/// We block on timestamp tokens, because we can always make progress on these requests.
/// The per-op auth tokens might never come, which means we fail if the client calls
/// update or finish before we got a per-op auth token.
- Waiting(Arc<Mutex<AuthRequest>>),
+ Waiting(Arc<AuthRequest>),
/// In this state we have gotten all of the required tokens, we just cache them to
/// be used when the operation progresses.
Token(HardwareAuthToken, Option<TimeStampToken>),
@@ -169,9 +176,15 @@
const CLEANUP_PERIOD: u8 = 25;
pub fn add_auth_token(&self, hat: HardwareAuthToken) {
- let mut map = self.map_and_cleanup_counter.lock().unwrap();
- let (ref mut map, _) = *map;
- if let Some((_, recv)) = map.remove_entry(&hat.challenge) {
+ let recv = {
+ // Limit the scope of the mutex guard, so that it is not held while the auth token is
+ // added.
+ let mut map = self.map_and_cleanup_counter.lock().unwrap();
+ let (ref mut map, _) = *map;
+ map.remove_entry(&hat.challenge)
+ };
+
+ if let Some((_, recv)) = recv {
recv.add_auth_token(hat);
}
}
@@ -191,7 +204,7 @@
}
#[derive(Debug)]
-struct TokenReceiver(Weak<Mutex<AuthRequest>>);
+struct TokenReceiver(Weak<AuthRequest>);
impl TokenReceiver {
fn is_obsolete(&self) -> bool {
@@ -200,8 +213,7 @@
fn add_auth_token(&self, hat: HardwareAuthToken) {
if let Some(state_arc) = self.0.upgrade() {
- let mut state = state_arc.lock().unwrap();
- state.add_auth_token(hat);
+ state_arc.add_auth_token(hat);
}
}
}
@@ -326,8 +338,7 @@
/// tokens into the DeferredAuthState::Token state for future use.
fn get_auth_tokens(&mut self) -> Result<(Option<HardwareAuthToken>, Option<TimeStampToken>)> {
let deferred_tokens = if let DeferredAuthState::Waiting(ref auth_request) = self.state {
- let mut state = auth_request.lock().unwrap();
- Some(state.get_auth_tokens().context("In AuthInfo::get_auth_tokens.")?)
+ Some(auth_request.get_auth_tokens().context("In AuthInfo::get_auth_tokens.")?)
} else {
None
};
diff --git a/keystore2/src/error.rs b/keystore2/src/error.rs
index 465dcfa..d1b2ffb 100644
--- a/keystore2/src/error.rs
+++ b/keystore2/src/error.rs
@@ -140,7 +140,7 @@
/// This function should be used by Keystore service calls to translate error conditions
/// into service specific exceptions.
///
-/// All error conditions get logged by this function.
+/// All error conditions get logged by this function, except for KEY_NOT_FOUND error.
///
/// All `Error::Rc(x)` and `Error::Km(x)` variants get mapped onto a service specific error
/// code of x. This is possible because KeyMint `ErrorCode` errors are always negative and
@@ -174,7 +174,13 @@
map_err_with(
result,
|e| {
- log::error!("{:?}", e);
+ // Make the key not found errors silent.
+ if !matches!(
+ e.root_cause().downcast_ref::<Error>(),
+ Some(Error::Rc(ResponseCode::KEY_NOT_FOUND))
+ ) {
+ log::error!("{:?}", e);
+ }
e
},
handle_ok,
diff --git a/keystore2/src/keystore2_main.rs b/keystore2/src/keystore2_main.rs
index b95add2..4d4a718 100644
--- a/keystore2/src/keystore2_main.rs
+++ b/keystore2/src/keystore2_main.rs
@@ -17,6 +17,7 @@
use keystore2::entropy;
use keystore2::globals::ENFORCEMENTS;
use keystore2::maintenance::Maintenance;
+use keystore2::metrics;
use keystore2::remote_provisioning::RemoteProvisioningService;
use keystore2::service::KeystoreService;
use keystore2::{apc::ApcManager, shared_secret_negotiation};
@@ -135,6 +136,13 @@
},
);
+ std::thread::spawn(|| {
+ match metrics::register_pull_metrics_callbacks() {
+ Err(e) => error!("register_pull_metrics_callbacks failed: {:?}.", e),
+ _ => info!("Pull metrics callbacks successfully registered."),
+ };
+ });
+
info!("Successfully registered Keystore 2.0 service.");
info!("Joining thread pool now.");
diff --git a/keystore2/src/legacy_blob.rs b/keystore2/src/legacy_blob.rs
index a3e440b..e631356 100644
--- a/keystore2/src/legacy_blob.rs
+++ b/keystore2/src/legacy_blob.rs
@@ -686,10 +686,18 @@
let user_id = uid_to_android_user(uid);
path.push(format!("user_{}", user_id));
let uid_str = uid.to_string();
- let dir =
- Self::with_retry_interrupted(|| fs::read_dir(path.as_path())).with_context(|| {
- format!("In list_vpn_profiles: Failed to open legacy blob database. {:?}", path)
- })?;
+ let dir = match Self::with_retry_interrupted(|| fs::read_dir(path.as_path())) {
+ Ok(dir) => dir,
+ Err(e) => match e.kind() {
+ ErrorKind::NotFound => return Ok(Default::default()),
+ _ => {
+ return Err(e).context(format!(
+ "In list_vpn_profiles: Failed to open legacy blob database. {:?}",
+ path
+ ))
+ }
+ },
+ };
let mut result: Vec<String> = Vec::new();
for entry in dir {
let file_name =
@@ -1370,4 +1378,14 @@
Ok(())
}
+
+ #[test]
+ fn list_vpn_profiles_on_non_existing_user() -> Result<()> {
+ let temp_dir = TempDir::new("list_vpn_profiles_on_non_existing_user")?;
+ let legacy_blob_loader = LegacyBlobLoader::new(temp_dir.path());
+
+ assert!(legacy_blob_loader.list_vpn_profiles(20)?.is_empty());
+
+ Ok(())
+ }
}
diff --git a/keystore2/src/legacy_migrator.rs b/keystore2/src/legacy_migrator.rs
index e5bcae4..5e0d573 100644
--- a/keystore2/src/legacy_migrator.rs
+++ b/keystore2/src/legacy_migrator.rs
@@ -420,7 +420,7 @@
.context("In list_uid: Trying to list legacy entries.")
}
- /// This is a key migration request that can run in the migrator thread. This should
+ /// This is a key migration request that must run in the migrator thread. This must
/// be passed to do_serialized.
fn check_and_migrate(&mut self, uid: u32, mut key: KeyDescriptor) -> Result<()> {
let alias = key.alias.clone().ok_or_else(|| {
diff --git a/keystore2/src/metrics.rs b/keystore2/src/metrics.rs
index c5dd582..5b66307 100644
--- a/keystore2/src/metrics.rs
+++ b/keystore2/src/metrics.rs
@@ -13,7 +13,9 @@
// limitations under the License.
//! This module provides convenience functions for keystore2 logging.
+use crate::async_task::AsyncTask;
use crate::error::get_error_code;
+use crate::globals::DB;
use crate::key_parameter::KeyParameterValue as KsKeyParamValue;
use crate::operation::Outcome;
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
@@ -22,15 +24,28 @@
KeyParameter::KeyParameter, KeyPurpose::KeyPurpose, PaddingMode::PaddingMode,
SecurityLevel::SecurityLevel,
};
-use statslog_rust::keystore2_key_creation_event_reported::{
- Algorithm as StatsdAlgorithm, EcCurve as StatsdEcCurve, KeyOrigin as StatsdKeyOrigin,
- Keystore2KeyCreationEventReported, SecurityLevel as StatsdKeyCreationSecurityLevel,
- UserAuthType as StatsdUserAuthType,
+use anyhow::Result;
+use keystore2_system_property::PropertyWatcher;
+use lazy_static::lazy_static;
+use statslog_rust::{
+ keystore2_key_creation_event_reported::{
+ Algorithm as StatsdAlgorithm, EcCurve as StatsdEcCurve, KeyOrigin as StatsdKeyOrigin,
+ Keystore2KeyCreationEventReported, SecurityLevel as StatsdKeyCreationSecurityLevel,
+ UserAuthType as StatsdUserAuthType,
+ },
+ keystore2_key_operation_event_reported::{
+ Keystore2KeyOperationEventReported, Outcome as StatsdOutcome, Purpose as StatsdKeyPurpose,
+ SecurityLevel as StatsdKeyOperationSecurityLevel,
+ },
+ keystore2_storage_stats::StorageType as StatsdStorageType,
};
-use statslog_rust::keystore2_key_operation_event_reported::{
- Keystore2KeyOperationEventReported, Outcome as StatsdOutcome, Purpose as StatsdKeyPurpose,
- SecurityLevel as StatsdKeyOperationSecurityLevel,
-};
+use statslog_rust_header::Atoms;
+use statspull_rust::{set_pull_atom_callback, StatsPullResult};
+
+lazy_static! {
+ /// Background thread which handles logging via statsd
+ static ref STATSD_LOGS_HANDLER: AsyncTask = Default::default();
+}
fn create_default_key_creation_atom() -> Keystore2KeyCreationEventReported {
// If a value is not present, fields represented by bitmaps and i32 fields
@@ -75,19 +90,18 @@
pub fn log_key_creation_event_stats<U>(
sec_level: SecurityLevel,
key_params: &[KeyParameter],
- result: &anyhow::Result<U>,
+ result: &Result<U>,
) {
let key_creation_event_stats =
construct_key_creation_event_stats(sec_level, key_params, result);
- let logging_result = key_creation_event_stats.stats_write();
+ STATSD_LOGS_HANDLER.queue_lo(move |_| {
+ let logging_result = key_creation_event_stats.stats_write();
- if let Err(e) = logging_result {
- log::error!(
- "In log_key_creation_event_stats. Error in logging key creation event. {:?}",
- e
- );
- }
+ if let Err(e) = logging_result {
+ log::error!("Error in logging key creation event in the async task. {:?}", e);
+ }
+ });
}
/// Log key operation events via statsd API.
@@ -106,20 +120,19 @@
key_upgraded,
);
- let logging_result = key_operation_event_stats.stats_write();
+ STATSD_LOGS_HANDLER.queue_lo(move |_| {
+ let logging_result = key_operation_event_stats.stats_write();
- if let Err(e) = logging_result {
- log::error!(
- "In log_key_operation_event_stats. Error in logging key operation event. {:?}",
- e
- );
- }
+ if let Err(e) = logging_result {
+ log::error!("Error in logging key operation event in the async task. {:?}", e);
+ }
+ });
}
fn construct_key_creation_event_stats<U>(
sec_level: SecurityLevel,
key_params: &[KeyParameter],
- result: &anyhow::Result<U>,
+ result: &Result<U>,
) -> Keystore2KeyCreationEventReported {
let mut key_creation_event_atom = create_default_key_creation_atom();
@@ -375,6 +388,55 @@
}
bitmap
}
+
+/// Registers pull metrics callbacks
+pub fn register_pull_metrics_callbacks() -> Result<()> {
+ // Before registering the callbacks with statsd, we have to wait for the system to finish
+ // booting up. This avoids possible races that may occur at startup. For example, statsd
+ // depends on a companion service, and if registration happens too soon it will fail since
+ // the companion service isn't up yet.
+ let mut watcher = PropertyWatcher::new("sys.boot_completed")?;
+ loop {
+ watcher.wait()?;
+ let value = watcher.read(|_name, value| Ok(value.trim().to_string()));
+ if value? == "1" {
+ set_pull_atom_callback(Atoms::Keystore2StorageStats, None, pull_metrics_callback);
+ break;
+ }
+ }
+ Ok(())
+}
+
+fn pull_metrics_callback() -> StatsPullResult {
+ let mut result = StatsPullResult::new();
+ let mut append = |stat| {
+ match stat {
+ Ok(s) => result.push(Box::new(s)),
+ Err(error) => {
+ log::error!("pull_metrics_callback: Error getting storage stat: {}", error)
+ }
+ };
+ };
+ DB.with(|db| {
+ let mut db = db.borrow_mut();
+ append(db.get_storage_stat(StatsdStorageType::Database));
+ append(db.get_storage_stat(StatsdStorageType::KeyEntry));
+ append(db.get_storage_stat(StatsdStorageType::KeyEntryIdIndex));
+ append(db.get_storage_stat(StatsdStorageType::KeyEntryDomainNamespaceIndex));
+ append(db.get_storage_stat(StatsdStorageType::BlobEntry));
+ append(db.get_storage_stat(StatsdStorageType::BlobEntryKeyEntryIdIndex));
+ append(db.get_storage_stat(StatsdStorageType::KeyParameter));
+ append(db.get_storage_stat(StatsdStorageType::KeyParameterKeyEntryIdIndex));
+ append(db.get_storage_stat(StatsdStorageType::KeyMetadata));
+ append(db.get_storage_stat(StatsdStorageType::KeyMetadataKeyEntryIdIndex));
+ append(db.get_storage_stat(StatsdStorageType::Grant));
+ append(db.get_storage_stat(StatsdStorageType::AuthToken));
+ append(db.get_storage_stat(StatsdStorageType::BlobMetadata));
+ append(db.get_storage_stat(StatsdStorageType::BlobMetadataBlobEntryIdIndex));
+ });
+ result
+}
+
/// Enum defining the bit position for each padding mode. Since padding mode can be repeatable, it
/// is represented using a bitmap.
#[allow(non_camel_case_types)]
diff --git a/keystore2/src/vintf/Android.bp b/keystore2/src/vintf/Android.bp
index feec8ae..3ab0ec5 100644
--- a/keystore2/src/vintf/Android.bp
+++ b/keystore2/src/vintf/Android.bp
@@ -53,11 +53,11 @@
shared_libs: ["libvintf"],
bindgen_flags: [
"--size_t-is-usize",
- "--whitelist-function", "getHalNames",
- "--whitelist-function", "getHalNamesAndVersions",
- "--whitelist-function", "getHidlInstances",
- "--whitelist-function", "getAidlInstances",
- "--whitelist-function", "freeNames",
+ "--allowlist-function", "getHalNames",
+ "--allowlist-function", "getHalNamesAndVersions",
+ "--allowlist-function", "getHidlInstances",
+ "--allowlist-function", "getAidlInstances",
+ "--allowlist-function", "freeNames",
],
}
diff --git a/keystore2/system_property/Android.bp b/keystore2/system_property/Android.bp
index 5a13c90..9e7b056 100644
--- a/keystore2/system_property/Android.bp
+++ b/keystore2/system_property/Android.bp
@@ -29,9 +29,9 @@
bindgen_flags: [
"--size_t-is-usize",
- "--whitelist-function=__system_property_find",
- "--whitelist-function=__system_property_read_callback",
- "--whitelist-function=__system_property_wait",
+ "--allowlist-function=__system_property_find",
+ "--allowlist-function=__system_property_read_callback",
+ "--allowlist-function=__system_property_wait",
],
}
diff --git a/keystore2/system_property/lib.rs b/keystore2/system_property/lib.rs
index f14cf0e..be13c88 100644
--- a/keystore2/system_property/lib.rs
+++ b/keystore2/system_property/lib.rs
@@ -15,8 +15,9 @@
//! This crate provides the PropertyWatcher type, which watches for changes
//! in Android system properties.
+use keystore2_system_property_bindgen::prop_info as PropInfo;
use std::os::raw::c_char;
-use std::ptr::null_mut;
+use std::ptr::null;
use std::{
ffi::{c_void, CStr, CString},
str::Utf8Error,
@@ -56,27 +57,34 @@
/// as `keystore.boot_level`; it can report the current value of this
/// property, or wait for it to change.
pub struct PropertyWatcher {
- prop_info: *const keystore2_system_property_bindgen::prop_info,
+ prop_name: CString,
+ prop_info: *const PropInfo,
serial: keystore2_system_property_bindgen::__uint32_t,
}
impl PropertyWatcher {
/// Create a PropertyWatcher for the named system property.
pub fn new(name: &str) -> Result<Self> {
- let cstr = CString::new(name)?;
- // Unsafe FFI call. We generate the CStr in this function
- // and so ensure it is valid during call.
- // Returned pointer is valid for the lifetime of the program.
- let prop_info =
- unsafe { keystore2_system_property_bindgen::__system_property_find(cstr.as_ptr()) };
- if prop_info.is_null() {
- Err(PropertyWatcherError::SystemPropertyAbsent)
+ Ok(Self { prop_name: CString::new(name)?, prop_info: null(), serial: 0 })
+ }
+
+ // Lazy-initializing accessor for self.prop_info.
+ fn get_prop_info(&mut self) -> Option<*const PropInfo> {
+ if self.prop_info.is_null() {
+ // Unsafe required for FFI call. Input and output are both const.
+ // The returned pointer is valid for the lifetime of the program.
+ self.prop_info = unsafe {
+ keystore2_system_property_bindgen::__system_property_find(self.prop_name.as_ptr())
+ };
+ }
+ if self.prop_info.is_null() {
+ None
} else {
- Ok(Self { prop_info, serial: 0 })
+ Some(self.prop_info)
}
}
- fn read_raw(&self, mut f: impl FnOnce(Option<&CStr>, Option<&CStr>)) {
+ fn read_raw(prop_info: *const PropInfo, mut f: impl FnOnce(Option<&CStr>, Option<&CStr>)) {
// Unsafe function converts values passed to us by
// __system_property_read_callback to Rust form
// and pass them to inner callback.
@@ -98,7 +106,7 @@
// to a void pointer, and unwrap it in our callback.
unsafe {
keystore2_system_property_bindgen::__system_property_read_callback(
- self.prop_info,
+ prop_info,
Some(callback),
&mut f as *mut _ as *mut c_void,
)
@@ -108,12 +116,14 @@
/// Call the passed function, passing it the name and current value
/// of this system property. See documentation for
/// `__system_property_read_callback` for details.
- pub fn read<T, F>(&self, f: F) -> Result<T>
+ /// Returns an error if the property is empty or doesn't exist.
+ pub fn read<T, F>(&mut self, f: F) -> Result<T>
where
F: FnOnce(&str, &str) -> anyhow::Result<T>,
{
+ let prop_info = self.get_prop_info().ok_or(PropertyWatcherError::SystemPropertyAbsent)?;
let mut result = Err(PropertyWatcherError::ReadCallbackNotCalled);
- self.read_raw(|name, value| {
+ Self::read_raw(prop_info, |name, value| {
// use a wrapping closure as an erzatz try block.
result = (|| {
let name = name.ok_or(PropertyWatcherError::MissingCString)?.to_str()?;
@@ -124,10 +134,43 @@
result
}
+ // Waits for the property that self is watching to be created. Returns immediately if the
+ // property already exists.
+ fn wait_for_property_creation(&mut self) -> Result<()> {
+ let mut global_serial = 0;
+ loop {
+ match self.get_prop_info() {
+ Some(_) => return Ok(()),
+ None => {
+ // Unsafe call for FFI. The function modifies only global_serial, and has
+ // no side-effects.
+ if !unsafe {
+ // Wait for a global serial number change, then try again. On success,
+ // the function will update global_serial with the last version seen.
+ keystore2_system_property_bindgen::__system_property_wait(
+ null(),
+ global_serial,
+ &mut global_serial,
+ null(),
+ )
+ } {
+ return Err(PropertyWatcherError::WaitFailed);
+ }
+ }
+ }
+ }
+ }
+
/// Wait for the system property to change. This
/// records the serial number of the last change, so
/// race conditions are avoided.
pub fn wait(&mut self) -> Result<()> {
+ // If the property is null, then wait for it to be created. Subsequent waits will
+ // skip this step and wait for our specific property to change.
+ if self.prop_info.is_null() {
+ return self.wait_for_property_creation();
+ }
+
let mut new_serial = self.serial;
// Unsafe block to call __system_property_wait.
// All arguments are private to PropertyWatcher so we
@@ -137,7 +180,7 @@
self.prop_info,
self.serial,
&mut new_serial,
- null_mut(),
+ null(),
)
} {
return Err(PropertyWatcherError::WaitFailed);
diff --git a/keystore2/vpnprofilestore/lib.rs b/keystore2/vpnprofilestore/lib.rs
index e7340a0..d92e045 100644
--- a/keystore2/vpnprofilestore/lib.rs
+++ b/keystore2/vpnprofilestore/lib.rs
@@ -175,7 +175,7 @@
/// This function should be used by vpnprofilestore service calls to translate error conditions
/// into service specific exceptions.
///
-/// All error conditions get logged by this function.
+/// All error conditions get logged by this function, except for ERROR_PROFILE_NOT_FOUND error.
///
/// `Error::Error(x)` variants get mapped onto a service specific error code of `x`.
///
@@ -190,12 +190,16 @@
{
result.map_or_else(
|e| {
- log::error!("{:#?}", e);
let root_cause = e.root_cause();
- let rc = match root_cause.downcast_ref::<Error>() {
- Some(Error::Error(e)) => *e,
- Some(Error::Binder(_, _)) | None => ERROR_SYSTEM_ERROR,
+ let (rc, log_error) = match root_cause.downcast_ref::<Error>() {
+ // Make the profile not found errors silent.
+ Some(Error::Error(ERROR_PROFILE_NOT_FOUND)) => (ERROR_PROFILE_NOT_FOUND, false),
+ Some(Error::Error(e)) => (*e, true),
+ Some(Error::Binder(_, _)) | None => (ERROR_SYSTEM_ERROR, true),
};
+ if log_error {
+ log::error!("{:?}", e);
+ }
Err(BinderStatus::new_service_specific_error(rc, None))
},
handle_ok,
diff --git a/ondevice-signing/KeystoreKey.cpp b/ondevice-signing/KeystoreKey.cpp
index 9b5e505..96e369a 100644
--- a/ondevice-signing/KeystoreKey.cpp
+++ b/ondevice-signing/KeystoreKey.cpp
@@ -151,8 +151,25 @@
KeyEntryResponse keyEntryResponse;
LOG(INFO) << "Trying to retrieve existing keystore key...";
status = mService->getKeyEntry(descriptor, &keyEntryResponse);
- if (!status.isOk()) {
- LOG(INFO) << "Existing keystore key not found, creating new key";
+ bool keyValid = false;
+
+ if (status.isOk()) {
+ // Make sure this is an early boot key
+ for (const auto& auth : keyEntryResponse.metadata.authorizations) {
+ if (auth.keyParameter.tag == Tag::MAX_BOOT_LEVEL) {
+ if (auth.keyParameter.value.get<KeyParameterValue::integer>() == kOdsignBootLevel) {
+ keyValid = true;
+ break;
+ }
+ }
+ }
+ if (!keyValid) {
+ LOG(WARNING) << "Found invalid keystore key without MAX_BOOT_LEVEL tag";
+ }
+ }
+
+ if (!keyValid) {
+ LOG(INFO) << "Existing keystore key not found or invalid, creating new key";
auto newKeyStatus = createNewKey(descriptor);
if (!newKeyStatus.ok()) {
LOG(ERROR) << "Failed to create new key";
diff --git a/provisioner/Android.bp b/provisioner/Android.bp
index d3f06fe..12a21d1 100644
--- a/provisioner/Android.bp
+++ b/provisioner/Android.bp
@@ -51,3 +51,19 @@
"android.security.provisioner-java",
],
}
+
+cc_binary {
+ name: "rkp_factory_extraction_tool",
+ srcs: ["rkp_factory_extraction_tool.cpp"],
+ shared_libs: [
+ "android.hardware.security.keymint-V1-ndk_platform",
+ "libbinder",
+ "libbinder_ndk",
+ "libcppbor_external",
+ "libcppcose_rkp",
+ "libcrypto",
+ "liblog",
+ "libvintf",
+ ],
+ //export_include_dirs: ["include"],
+}
diff --git a/provisioner/rkp_factory_extraction_tool.cpp b/provisioner/rkp_factory_extraction_tool.cpp
new file mode 100644
index 0000000..d4842b1
--- /dev/null
+++ b/provisioner/rkp_factory_extraction_tool.cpp
@@ -0,0 +1,166 @@
+/*
+ * Copyright 2021 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.
+ */
+
+#include <string>
+#include <vector>
+
+#include <aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.h>
+#include <android/binder_manager.h>
+#include <cppbor.h>
+#include <keymaster/cppcose/cppcose.h>
+#include <log/log.h>
+#include <vintf/VintfObject.h>
+
+using std::set;
+using std::string;
+using std::vector;
+
+using aidl::android::hardware::security::keymint::DeviceInfo;
+using aidl::android::hardware::security::keymint::IRemotelyProvisionedComponent;
+using aidl::android::hardware::security::keymint::MacedPublicKey;
+using aidl::android::hardware::security::keymint::ProtectedData;
+
+using android::vintf::HalManifest;
+using android::vintf::VintfObject;
+
+using namespace cppbor;
+using namespace cppcose;
+
+namespace {
+
+const string kPackage = "android.hardware.security.keymint";
+const string kInterface = "IRemotelyProvisionedComponent";
+const string kFormattedName = kPackage + "." + kInterface + "/";
+
+ErrMsgOr<vector<uint8_t>> generateEekChain(size_t length, const vector<uint8_t>& eekId) {
+ auto eekChain = cppbor::Array();
+
+ vector<uint8_t> prevPrivKey;
+ for (size_t i = 0; i < length - 1; ++i) {
+ vector<uint8_t> pubKey(ED25519_PUBLIC_KEY_LEN);
+ vector<uint8_t> privKey(ED25519_PRIVATE_KEY_LEN);
+
+ ED25519_keypair(pubKey.data(), privKey.data());
+
+ // The first signing key is self-signed.
+ if (prevPrivKey.empty()) prevPrivKey = privKey;
+
+ auto coseSign1 = constructCoseSign1(prevPrivKey,
+ cppbor::Map() /* payload CoseKey */
+ .add(CoseKey::KEY_TYPE, OCTET_KEY_PAIR)
+ .add(CoseKey::ALGORITHM, EDDSA)
+ .add(CoseKey::CURVE, ED25519)
+ .add(CoseKey::PUBKEY_X, pubKey)
+ .canonicalize()
+ .encode(),
+ {} /* AAD */);
+ if (!coseSign1) return coseSign1.moveMessage();
+ eekChain.add(coseSign1.moveValue());
+
+ prevPrivKey = privKey;
+ }
+
+ vector<uint8_t> pubKey(X25519_PUBLIC_VALUE_LEN);
+ vector<uint8_t> privKey(X25519_PRIVATE_KEY_LEN);
+ X25519_keypair(pubKey.data(), privKey.data());
+
+ auto coseSign1 = constructCoseSign1(prevPrivKey,
+ cppbor::Map() /* payload CoseKey */
+ .add(CoseKey::KEY_TYPE, OCTET_KEY_PAIR)
+ .add(CoseKey::KEY_ID, eekId)
+ .add(CoseKey::ALGORITHM, ECDH_ES_HKDF_256)
+ .add(CoseKey::CURVE, cppcose::X25519)
+ .add(CoseKey::PUBKEY_X, pubKey)
+ .canonicalize()
+ .encode(),
+ {} /* AAD */);
+ if (!coseSign1) return coseSign1.moveMessage();
+ eekChain.add(coseSign1.moveValue());
+
+ return eekChain.encode();
+}
+
+std::vector<uint8_t> getChallenge() {
+ return std::vector<uint8_t>(0);
+}
+
+std::vector<uint8_t> composeCertificateRequest(ProtectedData&& protectedData,
+ DeviceInfo&& deviceInfo) {
+ Array emptyMacedKeysToSign;
+ emptyMacedKeysToSign
+ .add(std::vector<uint8_t>(0)) // empty protected headers as bstr
+ .add(Map()) // empty unprotected headers
+ .add(Null()) // nil for the payload
+ .add(std::vector<uint8_t>(0)); // empty tag as bstr
+ Array certificateRequest;
+ certificateRequest.add(EncodedItem(std::move(deviceInfo.deviceInfo)))
+ .add(getChallenge()) // fake challenge
+ .add(EncodedItem(std::move(protectedData.protectedData)))
+ .add(std::move(emptyMacedKeysToSign));
+ return certificateRequest.encode();
+}
+
+int32_t errorMsg(string name) {
+ std::cerr << "Failed for rkp instance: " << name;
+ return -1;
+}
+
+} // namespace
+
+int main() {
+ std::shared_ptr<const HalManifest> manifest = VintfObject::GetDeviceHalManifest();
+ set<string> rkpNames = manifest->getAidlInstances(kPackage, kInterface);
+ for (auto name : rkpNames) {
+ string fullName = kFormattedName + name;
+ if (!AServiceManager_isDeclared(fullName.c_str())) {
+ ALOGE("Could not find the following instance declared in the manifest: %s\n",
+ fullName.c_str());
+ return errorMsg(name);
+ }
+ AIBinder* rkpAiBinder = AServiceManager_getService(fullName.c_str());
+ ::ndk::SpAIBinder rkp_binder(rkpAiBinder);
+ auto rkp_service = IRemotelyProvisionedComponent::fromBinder(rkp_binder);
+ std::vector<uint8_t> keysToSignMac;
+ std::vector<MacedPublicKey> emptyKeys;
+
+ // Replace this eek chain generation with the actual production GEEK
+ std::vector<uint8_t> eekId(10); // replace with real KID later (EEK fingerprint)
+ auto eekOrErr = generateEekChain(3 /* chainlength */, eekId);
+ if (!eekOrErr) {
+ ALOGE("Failed to generate test EEK somehow: %s", eekOrErr.message().c_str());
+ return errorMsg(name);
+ }
+
+ std::vector<uint8_t> eek = eekOrErr.moveValue();
+ DeviceInfo deviceInfo;
+ ProtectedData protectedData;
+ if (rkp_service) {
+ ALOGE("extracting bundle");
+ ::ndk::ScopedAStatus status = rkp_service->generateCertificateRequest(
+ true /* testMode */, emptyKeys, eek, getChallenge(), &deviceInfo, &protectedData,
+ &keysToSignMac);
+ if (!status.isOk()) {
+ ALOGE("Bundle extraction failed. Error code: %d", status.getServiceSpecificError());
+ return errorMsg(name);
+ }
+ std::cout << "\n";
+ std::vector<uint8_t> certificateRequest =
+ composeCertificateRequest(std::move(protectedData), std::move(deviceInfo));
+ std::copy(certificateRequest.begin(), certificateRequest.end(),
+ std::ostream_iterator<char>(std::cout));
+ }
+ }
+}