Merge "[keystore2] Fix binder import path"
diff --git a/diced/Android.bp b/diced/Android.bp
index db0268c..525828e 100644
--- a/diced/Android.bp
+++ b/diced/Android.bp
@@ -124,6 +124,7 @@
rust_binary {
name: "diced",
srcs: ["src/diced_main.rs"],
+ prefer_rlib: true,
rustlibs: [
"android.hardware.security.dice-V1-rust",
"libandroid_logger",
diff --git a/diced/aidl/Android.bp b/diced/aidl/Android.bp
index 2fedc3c..b8d0c7e 100644
--- a/diced/aidl/Android.bp
+++ b/diced/aidl/Android.bp
@@ -33,6 +33,10 @@
},
rust: {
enabled: true,
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.compos",
+ ],
},
ndk: {
enabled: true,
diff --git a/diced/src/diced_client_test.rs b/diced/src/diced_client_test.rs
index 3f5b68f..3915508 100644
--- a/diced/src/diced_client_test.rs
+++ b/diced/src/diced_client_test.rs
@@ -18,7 +18,6 @@
};
use android_security_dice::aidl::android::security::dice::IDiceMaintenance::IDiceMaintenance;
use android_security_dice::aidl::android::security::dice::IDiceNode::IDiceNode;
-use anyhow::Result;
use binder::Strong;
use diced_open_dice_cbor as dice;
use nix::libc::uid_t;
@@ -49,15 +48,12 @@
let input_values = diced_sample_inputs::get_input_values_vector();
let former = node.derive(&[]).expect("Trying to call derive.");
let latter = node.derive(&input_values).expect("Trying to call derive with input values.");
- let artifacts = diced_utils::ResidentArtifacts::new(
- former.cdiAttest[..].try_into().unwrap(),
- former.cdiSeal[..].try_into().unwrap(),
- &former.bcc.data,
- )
- .unwrap();
+ let artifacts =
+ diced_utils::ResidentArtifacts::new(&former.cdiAttest, &former.cdiSeal, &former.bcc.data)
+ .unwrap();
let input_values: Vec<diced_utils::InputValues> =
- input_values.iter().map(|v| v.try_into()).collect::<Result<_>>().unwrap();
+ input_values.iter().map(|v| v.into()).collect();
let artifacts =
artifacts.execute_steps(input_values.iter().map(|v| v as &dyn dice::InputValues)).unwrap();
@@ -101,7 +97,7 @@
.unwrap();
let input_values: Vec<diced_utils::InputValues> =
- input_values.iter().map(|v| v.try_into()).collect::<Result<_>>().unwrap();
+ input_values.iter().map(|v| v.into()).collect();
let artifacts =
artifacts.execute_steps(input_values.iter().map(|v| v as &dyn dice::InputValues)).unwrap();
@@ -119,15 +115,15 @@
fn client_input_values(uid: uid_t) -> BinderInputValues {
BinderInputValues {
- codeHash: vec![0; dice::HASH_SIZE],
+ codeHash: [0; dice::HASH_SIZE],
config: BinderConfig {
desc: dice::bcc::format_config_descriptor(Some(&format!("{}", uid)), None, true)
.unwrap(),
},
- authorityHash: vec![0; dice::HASH_SIZE],
+ authorityHash: [0; dice::HASH_SIZE],
authorityDescriptor: None,
mode: BinderMode::NORMAL,
- hidden: vec![0; dice::HIDDEN_SIZE],
+ hidden: [0; dice::HIDDEN_SIZE],
}
}
@@ -164,12 +160,8 @@
.unwrap();
let client = [client];
- let input_values: Vec<diced_utils::InputValues> = input_values
- .iter()
- .chain(client.iter())
- .map(|v| v.try_into())
- .collect::<Result<_>>()
- .unwrap();
+ let input_values: Vec<diced_utils::InputValues> =
+ input_values.iter().chain(client.iter()).map(|v| v.into()).collect();
let artifacts =
artifacts.execute_steps(input_values.iter().map(|v| v as &dyn dice::InputValues)).unwrap();
diff --git a/diced/src/hal_node.rs b/diced/src/hal_node.rs
index 6bc9c4a..01a7577 100644
--- a/diced/src/hal_node.rs
+++ b/diced/src/hal_node.rs
@@ -188,7 +188,7 @@
run_forked(move || {
let artifacts = artifacts.with_artifacts(|a| ResidentArtifacts::new_from(a))?;
let input_values: Vec<utils::InputValues> =
- input_values.iter().map(|v| v.try_into()).collect::<Result<_>>()?;
+ input_values.iter().map(|v| v.into()).collect();
let artifacts = artifacts
.execute_steps(input_values.iter().map(|v| v as &dyn dice::InputValues))
.context("In ResidentHal::get_effective_artifacts:")?;
@@ -280,7 +280,7 @@
let new_artifacts =
artifacts_clone.with_artifacts(|a| ResidentArtifacts::new_from(a))?;
let input_values: Vec<utils::InputValues> =
- input_values.iter().map(|v| v.try_into()).collect::<Result<_>>()?;
+ input_values.iter().map(|v| v.into()).collect();
let new_artifacts = new_artifacts
.execute_steps(input_values.iter().map(|v| v as &dyn dice::InputValues))
@@ -383,17 +383,21 @@
Ok(BinderInputValues {
codeHash: dice_ctx
.hash(code.as_bytes())
- .context("In make_input_values: code hash failed.")?,
+ .context("In make_input_values: code hash failed.")?
+ .as_slice()
+ .try_into()?,
config: BinderConfig {
desc: dice::bcc::format_config_descriptor(Some(config_name), None, true)
.context("In make_input_values: Failed to format config descriptor.")?,
},
authorityHash: dice_ctx
.hash(authority.as_bytes())
- .context("In make_input_values: authority hash failed.")?,
+ .context("In make_input_values: authority hash failed.")?
+ .as_slice()
+ .try_into()?,
authorityDescriptor: None,
mode: BinderMode::NORMAL,
- hidden: vec![0; dice::HIDDEN_SIZE],
+ hidden: [0; dice::HIDDEN_SIZE],
})
}
@@ -411,8 +415,7 @@
make_input_values("component 3 code", "component 3", "component 3 authority")?,
];
- let input_values: Vec<utils::InputValues> =
- input_values.iter().map(|v| v.try_into()).collect::<Result<_>>()?;
+ let input_values: Vec<utils::InputValues> = input_values.iter().map(|v| v.into()).collect();
let new_artifacts =
artifacts.execute_steps(input_values.iter().map(|v| v as &dyn dice::InputValues))?;
diff --git a/diced/src/lib.rs b/diced/src/lib.rs
index 520f333..9594977 100644
--- a/diced/src/lib.rs
+++ b/diced/src/lib.rs
@@ -98,14 +98,14 @@
fn client_input_values(uid: uid_t) -> Result<BinderInputValues> {
Ok(BinderInputValues {
- codeHash: vec![0; dice::HASH_SIZE],
+ codeHash: [0; dice::HASH_SIZE],
config: BinderConfig {
desc: dice::bcc::format_config_descriptor(Some(&format!("{}", uid)), None, true)
.context("In client_input_values: failed to format config descriptor")?,
},
- authorityHash: vec![0; dice::HASH_SIZE],
+ authorityHash: [0; dice::HASH_SIZE],
authorityDescriptor: None,
- hidden: vec![0; dice::HIDDEN_SIZE],
+ hidden: [0; dice::HIDDEN_SIZE],
mode: Mode::NORMAL,
})
}
diff --git a/diced/src/resident_node.rs b/diced/src/resident_node.rs
index 5fe4dc9..99a6dc9 100644
--- a/diced/src/resident_node.rs
+++ b/diced/src/resident_node.rs
@@ -66,8 +66,8 @@
.map(|v| v.iter())
.unwrap_or_else(|| client_arr.iter())
.chain(input_values.iter())
- .map(|v| v.try_into())
- .collect::<Result<_>>()?;
+ .map(|v| v.into())
+ .collect();
artifacts
.execute_steps(input_values.iter().map(|v| v as &dyn dice::InputValues))
diff --git a/diced/src/sample_inputs.rs b/diced/src/sample_inputs.rs
index f76ebc9..93897a6 100644
--- a/diced/src/sample_inputs.rs
+++ b/diced/src/sample_inputs.rs
@@ -134,7 +134,7 @@
hidden: &[u8; dice::HIDDEN_SIZE],
) -> Result<BinderInputValues> {
Ok(BinderInputValues {
- codeHash: code_hash.to_vec(),
+ codeHash: *code_hash,
config: BinderConfig {
desc: dice::bcc::format_config_descriptor(
Some(config_name),
@@ -143,9 +143,9 @@
)
.context("In make_input_values: Failed to format config descriptor.")?,
},
- authorityHash: authority_hash.to_vec(),
+ authorityHash: *authority_hash,
authorityDescriptor: None,
- hidden: hidden.to_vec(),
+ hidden: *hidden,
mode,
})
}
diff --git a/diced/src/utils.rs b/diced/src/utils.rs
index 3d3db55..03e8969 100644
--- a/diced/src/utils.rs
+++ b/diced/src/utils.rs
@@ -18,41 +18,20 @@
Bcc::Bcc, BccHandover::BccHandover, InputValues::InputValues as BinderInputValues,
Mode::Mode as BinderMode,
};
-use anyhow::{anyhow, Context, Result};
+use anyhow::{Context, Result};
use dice::ContextImpl;
use diced_open_dice_cbor as dice;
use keystore2_crypto::ZVec;
-use std::convert::{TryFrom, TryInto};
+use std::convert::TryInto;
/// This new type wraps a reference to BinderInputValues and implements the open dice
/// InputValues trait.
#[derive(Debug)]
pub struct InputValues<'a>(&'a BinderInputValues);
-impl<'a> TryFrom<&'a BinderInputValues> for InputValues<'a> {
- type Error = anyhow::Error;
-
- fn try_from(input_values: &'a BinderInputValues) -> Result<InputValues<'a>> {
- if input_values.codeHash.len() != dice::HASH_SIZE {
- return Err(anyhow!(format!(
- "In try_from: Code hash has invalid size: {}",
- input_values.codeHash.len()
- )));
- }
- if input_values.authorityHash.len() != dice::HASH_SIZE {
- return Err(anyhow!(format!(
- "In try_from: Authority hash has invalid size: {}",
- input_values.authorityHash.len()
- )));
- }
- if input_values.hidden.len() != dice::HIDDEN_SIZE {
- return Err(anyhow!(format!(
- "In try_from: Hidden has invalid size: {}",
- input_values.hidden.len()
- )));
- }
-
- Ok(Self(input_values))
+impl<'a> From<&'a BinderInputValues> for InputValues<'a> {
+ fn from(input_values: &'a BinderInputValues) -> InputValues<'a> {
+ Self(input_values)
}
}
@@ -69,8 +48,7 @@
impl dice::InputValues for InputValues<'_> {
fn code_hash(&self) -> &[u8; dice::HASH_SIZE] {
- // If `self` was created using try_from the length was checked and this cannot panic.
- self.0.codeHash.as_slice().try_into().unwrap()
+ &self.0.codeHash
}
fn config(&self) -> dice::Config {
@@ -78,8 +56,7 @@
}
fn authority_hash(&self) -> &[u8; dice::HASH_SIZE] {
- // If `self` was created using try_from the length was checked and this cannot panic.
- self.0.authorityHash.as_slice().try_into().unwrap()
+ &self.0.authorityHash
}
fn authority_descriptor(&self) -> Option<&[u8]> {
@@ -98,7 +75,7 @@
fn hidden(&self) -> &[u8; dice::HIDDEN_SIZE] {
// If `self` was created using try_from the length was checked and this cannot panic.
- self.0.hidden.as_slice().try_into().unwrap()
+ &self.0.hidden
}
}
@@ -109,11 +86,7 @@
cdi_seal: &[u8; dice::CDI_SIZE],
bcc: &[u8],
) -> Result<BccHandover> {
- Ok(BccHandover {
- cdiAttest: cdi_attest.to_vec(),
- cdiSeal: cdi_seal.to_vec(),
- bcc: Bcc { data: bcc.to_vec() },
- })
+ Ok(BccHandover { cdiAttest: *cdi_attest, cdiSeal: *cdi_seal, bcc: Bcc { data: bcc.to_vec() } })
}
/// ResidentArtifacts stores a set of dice artifacts comprising CDI_ATTEST, CDI_SEAL,
@@ -406,92 +379,3 @@
}
}
}
-
-#[cfg(test)]
-mod test {
- use super::*;
- use android_hardware_security_dice::aidl::android::hardware::security::dice::{
- Config::Config as BinderConfig, InputValues::InputValues as BinderInputValues,
- };
- use dice::InputValues as DiceInputValues;
- use diced_open_dice_cbor as dice;
-
- static CODE_HASH_TEST_VECTOR: [u8; dice::HASH_SIZE] = [1u8; dice::HASH_SIZE];
- static CONFIG_DESCRIPTOR_TEST_VECTOR: &[u8] = &[3, 2, 1];
- static AUTHORITY_HASH_TEST_VECTOR: [u8; dice::HASH_SIZE] = [3u8; dice::HASH_SIZE];
- static AUTHORITY_DESCRIPTOR_TEST_VECTOR: &[u8] = &[1, 2, 3];
- static HIDDEN_TEST_VECTOR: [u8; dice::HIDDEN_SIZE] = [4u8; dice::HIDDEN_SIZE];
-
- #[test]
- fn try_from_input_values_binder() {
- let input_values_good = BinderInputValues {
- codeHash: CODE_HASH_TEST_VECTOR.to_vec(),
- config: BinderConfig { desc: CONFIG_DESCRIPTOR_TEST_VECTOR.to_vec() },
- authorityHash: AUTHORITY_HASH_TEST_VECTOR.to_vec(),
- authorityDescriptor: Some(AUTHORITY_DESCRIPTOR_TEST_VECTOR.to_vec()),
- mode: BinderMode::NORMAL,
- hidden: HIDDEN_TEST_VECTOR.to_vec(),
- };
-
- let converted_input_values: InputValues =
- (&input_values_good).try_into().expect("This should succeed.");
- assert_eq!(*converted_input_values.code_hash(), CODE_HASH_TEST_VECTOR);
- assert_eq!(
- converted_input_values.config(),
- dice::Config::Descriptor(CONFIG_DESCRIPTOR_TEST_VECTOR)
- );
- assert_eq!(*converted_input_values.authority_hash(), AUTHORITY_HASH_TEST_VECTOR);
- assert_eq!(
- converted_input_values.authority_descriptor(),
- Some(AUTHORITY_DESCRIPTOR_TEST_VECTOR)
- );
- assert_eq!(converted_input_values.mode(), dice::Mode::Normal);
- assert_eq!(*converted_input_values.hidden(), HIDDEN_TEST_VECTOR);
-
- // One more time without authority descriptor.
- let input_values_still_good_without_authority_descriptor =
- BinderInputValues { authorityDescriptor: None, ..input_values_good.clone() };
-
- let converted_input_values: InputValues =
- (&input_values_still_good_without_authority_descriptor)
- .try_into()
- .expect("This should succeed.");
- assert_eq!(*converted_input_values.code_hash(), CODE_HASH_TEST_VECTOR);
- assert_eq!(
- converted_input_values.config(),
- dice::Config::Descriptor(CONFIG_DESCRIPTOR_TEST_VECTOR)
- );
- assert_eq!(*converted_input_values.authority_hash(), AUTHORITY_HASH_TEST_VECTOR);
- assert_eq!(converted_input_values.authority_descriptor(), None);
- assert_eq!(converted_input_values.mode(), dice::Mode::Normal);
- assert_eq!(*converted_input_values.hidden(), HIDDEN_TEST_VECTOR);
-
- // Now check the failure cases.
- // Wrong sized codeHash.
- let input_values_bad_code_hash = BinderInputValues {
- codeHash: vec![1u8; dice::HASH_SIZE + 1],
- ..input_values_good.clone()
- };
-
- InputValues::try_from(&input_values_bad_code_hash)
- .expect_err("Conversion of input values with wrong sized code hash succeeded.");
-
- // Wrong sized authority hash.
- let input_values_bad_authority_hash = BinderInputValues {
- authorityHash: vec![1u8; dice::HASH_SIZE + 1],
- ..input_values_good.clone()
- };
-
- InputValues::try_from(&input_values_bad_authority_hash)
- .expect_err("Conversion of input values with wrong sized authority hash succeeded.");
-
- // Wrong sized hidden.
- let input_values_bad_hidden = BinderInputValues {
- authorityHash: vec![1u8; dice::HASH_SIZE + 1],
- ..input_values_good.clone()
- };
-
- InputValues::try_from(&input_values_bad_hidden)
- .expect_err("Conversion of input values with wrong sized hidden succeeded.");
- }
-}
diff --git a/identity/Android.bp b/identity/Android.bp
index ddb4335..7b0503a 100644
--- a/identity/Android.bp
+++ b/identity/Android.bp
@@ -36,6 +36,7 @@
"WritableCredential.cpp",
"Credential.cpp",
"CredentialData.cpp",
+ "Session.cpp",
"Util.cpp",
],
init_rc: ["credstore.rc"],
@@ -52,10 +53,11 @@
"libkeymaster4support",
"libkeystore-attestation-application-id",
"android.security.authorization-ndk",
+ "libutilscallstack",
],
static_libs: [
- "android.hardware.identity-V3-cpp",
- "android.hardware.keymaster-V3-cpp",
+ "android.hardware.identity-V4-cpp",
+ "android.hardware.keymaster-V4-cpp",
"libcppbor_external",
],
}
@@ -77,6 +79,7 @@
"binder/android/security/identity/AuthKeyParcel.aidl",
"binder/android/security/identity/SecurityHardwareInfoParcel.aidl",
"binder/android/security/identity/ICredentialStoreFactory.aidl",
+ "binder/android/security/identity/ISession.aidl",
],
path: "binder",
}
diff --git a/identity/Credential.cpp b/identity/Credential.cpp
index 7c75d8a..c67fe4a 100644
--- a/identity/Credential.cpp
+++ b/identity/Credential.cpp
@@ -70,10 +70,10 @@
Credential::Credential(CipherSuite cipherSuite, const std::string& dataPath,
const std::string& credentialName, uid_t callingUid,
HardwareInformation hwInfo, sp<IIdentityCredentialStore> halStoreBinder,
- int halApiVersion)
+ sp<IPresentationSession> halSessionBinder, int halApiVersion)
: cipherSuite_(cipherSuite), dataPath_(dataPath), credentialName_(credentialName),
callingUid_(callingUid), hwInfo_(std::move(hwInfo)), halStoreBinder_(halStoreBinder),
- halApiVersion_(halApiVersion) {}
+ halSessionBinder_(halSessionBinder), halApiVersion_(halApiVersion) {}
Credential::~Credential() {}
@@ -85,25 +85,40 @@
"Error loading data for credential");
}
- sp<IIdentityCredential> halBinder;
- Status status =
- halStoreBinder_->getCredential(cipherSuite_, data->getCredentialData(), &halBinder);
- if (!status.isOk() && status.exceptionCode() == binder::Status::EX_SERVICE_SPECIFIC) {
- int code = status.serviceSpecificErrorCode();
- if (code == IIdentityCredentialStore::STATUS_CIPHER_SUITE_NOT_SUPPORTED) {
- return halStatusToError(status, ICredentialStore::ERROR_CIPHER_SUITE_NOT_SUPPORTED);
+ // If we're in a session we explicitly don't get the binder to IIdentityCredential until
+ // it's used in getEntries() which is the only method call allowed for sessions.
+ //
+ // Why? This is because we want to throw the IIdentityCredential object away as soon as it's
+ // used because the HAL only guarantees a single IIdentityCredential object alive at a time
+ // and in a session there may be multiple credentials in play and we want to do multiple
+ // getEntries() calls on all of them.
+ //
+
+ if (!halSessionBinder_) {
+ sp<IIdentityCredential> halBinder;
+ Status status =
+ halStoreBinder_->getCredential(cipherSuite_, data->getCredentialData(), &halBinder);
+ if (!status.isOk() && status.exceptionCode() == binder::Status::EX_SERVICE_SPECIFIC) {
+ int code = status.serviceSpecificErrorCode();
+ if (code == IIdentityCredentialStore::STATUS_CIPHER_SUITE_NOT_SUPPORTED) {
+ return halStatusToError(status, ICredentialStore::ERROR_CIPHER_SUITE_NOT_SUPPORTED);
+ }
}
+ if (!status.isOk()) {
+ LOG(ERROR) << "Error getting HAL binder";
+ return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC);
+ }
+ halBinder_ = halBinder;
}
- if (!status.isOk()) {
- LOG(ERROR) << "Error getting HAL binder";
- return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC);
- }
- halBinder_ = halBinder;
return Status::ok();
}
Status Credential::getCredentialKeyCertificateChain(std::vector<uint8_t>* _aidl_return) {
+ if (halSessionBinder_) {
+ return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+ "Cannot be used with session");
+ }
sp<CredentialData> data = new CredentialData(dataPath_, callingUid_, credentialName_);
if (!data->loadFromDisk()) {
LOG(ERROR) << "Error loading data for credential";
@@ -116,7 +131,11 @@
// Returns operation handle
Status Credential::selectAuthKey(bool allowUsingExhaustedKeys, bool allowUsingExpiredKeys,
- int64_t* _aidl_return) {
+ bool incrementUsageCount, int64_t* _aidl_return) {
+ if (halSessionBinder_) {
+ return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+ "Cannot be used with session");
+ }
sp<CredentialData> data = new CredentialData(dataPath_, callingUid_, credentialName_);
if (!data->loadFromDisk()) {
LOG(ERROR) << "Error loading data for credential";
@@ -127,7 +146,7 @@
// We just check if a key is available, we actually don't store it since we
// don't keep CredentialData around between binder calls.
const AuthKeyData* authKey =
- data->selectAuthKey(allowUsingExhaustedKeys, allowUsingExpiredKeys);
+ data->selectAuthKey(allowUsingExhaustedKeys, allowUsingExpiredKeys, incrementUsageCount);
if (authKey == nullptr) {
return Status::fromServiceSpecificError(
ICredentialStore::ERROR_NO_AUTHENTICATION_KEY_AVAILABLE,
@@ -148,10 +167,19 @@
}
int64_t challenge;
- Status status = halBinder_->createAuthChallenge(&challenge);
- if (!status.isOk()) {
- LOG(ERROR) << "Error getting challenge: " << status.exceptionMessage();
- return false;
+ // If we're in a session, the challenge is selected by the session
+ if (halSessionBinder_) {
+ Status status = halSessionBinder_->getAuthChallenge(&challenge);
+ if (!status.isOk()) {
+ LOG(ERROR) << "Error getting challenge from session: " << status.exceptionMessage();
+ return false;
+ }
+ } else {
+ Status status = halBinder_->createAuthChallenge(&challenge);
+ if (!status.isOk()) {
+ LOG(ERROR) << "Error getting challenge: " << status.exceptionMessage();
+ return false;
+ }
}
if (challenge == 0) {
LOG(ERROR) << "Returned challenge is 0 (bug in HAL or TA)";
@@ -218,7 +246,8 @@
const vector<RequestNamespaceParcel>& requestNamespaces,
const vector<uint8_t>& sessionTranscript,
const vector<uint8_t>& readerSignature, bool allowUsingExhaustedKeys,
- bool allowUsingExpiredKeys, GetEntriesResultParcel* _aidl_return) {
+ bool allowUsingExpiredKeys, bool incrementUsageCount,
+ GetEntriesResultParcel* _aidl_return) {
GetEntriesResultParcel ret;
sp<CredentialData> data = new CredentialData(dataPath_, callingUid_, credentialName_);
@@ -228,6 +257,28 @@
"Error loading data for credential");
}
+ // If used in a session, get the binder on demand...
+ //
+ sp<IIdentityCredential> halBinder = halBinder_;
+ if (halSessionBinder_) {
+ if (halBinder) {
+ LOG(ERROR) << "Unexpected HAL binder for session";
+ return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+ "Unexpected HAL binder for session");
+ }
+ Status status = halSessionBinder_->getCredential(data->getCredentialData(), &halBinder);
+ if (!status.isOk() && status.exceptionCode() == binder::Status::EX_SERVICE_SPECIFIC) {
+ int code = status.serviceSpecificErrorCode();
+ if (code == IIdentityCredentialStore::STATUS_CIPHER_SUITE_NOT_SUPPORTED) {
+ return halStatusToError(status, ICredentialStore::ERROR_CIPHER_SUITE_NOT_SUPPORTED);
+ }
+ }
+ if (!status.isOk()) {
+ LOG(ERROR) << "Error getting HAL binder";
+ return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC);
+ }
+ }
+
// Calculate requestCounts ahead of time and be careful not to include
// elements that don't exist.
//
@@ -354,33 +405,40 @@
}
}
- // Note that the selectAuthKey() method is only called if a CryptoObject is involved at
- // the Java layer. So we could end up with no previously selected auth key and we may
- // need one.
+ // Reuse the same AuthKey over multiple getEntries() calls.
//
- const AuthKeyData* authKey =
- data->selectAuthKey(allowUsingExhaustedKeys, allowUsingExpiredKeys);
- if (authKey == nullptr) {
- // If no authKey is available, consider it an error only when a
- // SessionTranscript was provided.
+ bool updateUseCountOnDisk = false;
+ if (!selectedAuthKey_) {
+ // Note that the selectAuthKey() method is only called if a CryptoObject is involved at
+ // the Java layer. So we could end up with no previously selected auth key and we may
+ // need one.
//
- // We allow no SessionTranscript to be provided because it makes
- // the API simpler to deal with insofar it can be used without having
- // to generate any authentication keys.
- //
- // In this "no SessionTranscript is provided" mode we don't return
- // DeviceNameSpaces nor a MAC over DeviceAuthentication so we don't
- // need a device key.
- //
- if (sessionTranscript.size() > 0) {
- return Status::fromServiceSpecificError(
- ICredentialStore::ERROR_NO_AUTHENTICATION_KEY_AVAILABLE,
- "No suitable authentication key available and one is needed");
+ const AuthKeyData* authKey = data->selectAuthKey(
+ allowUsingExhaustedKeys, allowUsingExpiredKeys, incrementUsageCount);
+ if (authKey == nullptr) {
+ // If no authKey is available, consider it an error only when a
+ // SessionTranscript was provided.
+ //
+ // We allow no SessionTranscript to be provided because it makes
+ // the API simpler to deal with insofar it can be used without having
+ // to generate any authentication keys.
+ //
+ // In this "no SessionTranscript is provided" mode we don't return
+ // DeviceNameSpaces nor a MAC over DeviceAuthentication so we don't
+ // need a device key.
+ //
+ if (sessionTranscript.size() > 0) {
+ return Status::fromServiceSpecificError(
+ ICredentialStore::ERROR_NO_AUTHENTICATION_KEY_AVAILABLE,
+ "No suitable authentication key available and one is needed");
+ }
+ } else {
+ // We did find an authKey. Store its contents for future getEntries() calls.
+ updateUseCountOnDisk = true;
+ selectedAuthKeySigningKeyBlob_ = authKey->keyBlob;
+ selectedAuthKeyStaticAuthData_ = authKey->staticAuthenticationData;
}
- }
- vector<uint8_t> signingKeyBlob;
- if (authKey != nullptr) {
- signingKeyBlob = authKey->keyBlob;
+ selectedAuthKey_ = true;
}
// Pass the HAL enough information to allow calculating the size of
@@ -405,22 +463,22 @@
}
// This is not catastrophic, we might be dealing with a version 1 implementation which
// doesn't have this method.
- Status status = halBinder_->setRequestedNamespaces(halRequestNamespaces);
+ Status status = halBinder->setRequestedNamespaces(halRequestNamespaces);
if (!status.isOk()) {
LOG(INFO) << "Failed setting expected requested namespaces, assuming V1 HAL "
<< "and continuing";
}
// Pass the verification token. Failure is OK, this method isn't in the V1 HAL.
- status = halBinder_->setVerificationToken(aidlVerificationToken);
+ status = halBinder->setVerificationToken(aidlVerificationToken);
if (!status.isOk()) {
LOG(INFO) << "Failed setting verification token, assuming V1 HAL "
<< "and continuing";
}
- status =
- halBinder_->startRetrieval(selectedProfiles, aidlAuthToken, requestMessage, signingKeyBlob,
- sessionTranscript, readerSignature, requestCounts);
+ status = halBinder->startRetrieval(selectedProfiles, aidlAuthToken, requestMessage,
+ selectedAuthKeySigningKeyBlob_, sessionTranscript,
+ readerSignature, requestCounts);
if (!status.isOk() && status.exceptionCode() == binder::Status::EX_SERVICE_SPECIFIC) {
int code = status.serviceSpecificErrorCode();
if (code == IIdentityCredentialStore::STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND) {
@@ -453,8 +511,8 @@
}
status =
- halBinder_->startRetrieveEntryValue(rns.namespaceName, rep.name, eData.value().size,
- eData.value().accessControlProfileIds);
+ halBinder->startRetrieveEntryValue(rns.namespaceName, rep.name, eData.value().size,
+ eData.value().accessControlProfileIds);
if (!status.isOk() && status.exceptionCode() == binder::Status::EX_SERVICE_SPECIFIC) {
int code = status.serviceSpecificErrorCode();
if (code == IIdentityCredentialStore::STATUS_USER_AUTHENTICATION_FAILED) {
@@ -482,7 +540,7 @@
vector<uint8_t> value;
for (const auto& encryptedChunk : eData.value().encryptedChunks) {
vector<uint8_t> chunk;
- status = halBinder_->retrieveEntryValue(encryptedChunk, &chunk);
+ status = halBinder->retrieveEntryValue(encryptedChunk, &chunk);
if (!status.isOk()) {
return halStatusToGenericError(status);
}
@@ -496,16 +554,14 @@
ret.resultNamespaces.push_back(resultNamespaceParcel);
}
- status = halBinder_->finishRetrieval(&ret.mac, &ret.deviceNameSpaces);
+ status = halBinder->finishRetrieval(&ret.mac, &ret.deviceNameSpaces);
if (!status.isOk()) {
return halStatusToGenericError(status);
}
- if (authKey != nullptr) {
- ret.staticAuthenticationData = authKey->staticAuthenticationData;
- }
+ ret.staticAuthenticationData = selectedAuthKeyStaticAuthData_;
// Ensure useCount is updated on disk.
- if (authKey != nullptr) {
+ if (updateUseCountOnDisk) {
if (!data->saveToDisk()) {
return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
"Error saving data");
@@ -517,6 +573,11 @@
}
Status Credential::deleteCredential(vector<uint8_t>* _aidl_return) {
+ if (halSessionBinder_) {
+ return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+ "Cannot be used with session");
+ }
+
vector<uint8_t> proofOfDeletionSignature;
sp<CredentialData> data = new CredentialData(dataPath_, callingUid_, credentialName_);
@@ -544,6 +605,12 @@
return Status::fromServiceSpecificError(ICredentialStore::ERROR_NOT_SUPPORTED,
"Not implemented by HAL");
}
+
+ if (halSessionBinder_) {
+ return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+ "Cannot be used with session");
+ }
+
vector<uint8_t> proofOfDeletionSignature;
sp<CredentialData> data = new CredentialData(dataPath_, callingUid_, credentialName_);
@@ -570,6 +637,12 @@
return Status::fromServiceSpecificError(ICredentialStore::ERROR_NOT_SUPPORTED,
"Not implemented by HAL");
}
+
+ if (halSessionBinder_) {
+ return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+ "Cannot be used with session");
+ }
+
vector<uint8_t> proofOfOwnershipSignature;
Status status = halBinder_->proveOwnership(challenge, &proofOfOwnershipSignature);
if (!status.isOk()) {
@@ -580,19 +653,26 @@
}
Status Credential::createEphemeralKeyPair(vector<uint8_t>* _aidl_return) {
+ if (halSessionBinder_) {
+ return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+ "Cannot be used with session");
+ }
+
vector<uint8_t> keyPair;
Status status = halBinder_->createEphemeralKeyPair(&keyPair);
if (!status.isOk()) {
return halStatusToGenericError(status);
}
+ time_t nowSeconds = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
+ time_t validityNotBefore = nowSeconds;
+ time_t validityNotAfter = nowSeconds + 24 * 60 * 60;
optional<vector<uint8_t>> pkcs12Bytes = ecKeyPairGetPkcs12(keyPair,
"ephemeralKey", // Alias for key
"0", // Serial, as a decimal number
"Credstore", // Issuer
"Ephemeral Key", // Subject
- 0, // Validity Not Before
- 24 * 60 * 60); // Validity Not After
+ validityNotBefore, validityNotAfter);
if (!pkcs12Bytes) {
return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
"Error creating PKCS#12 structure for key pair");
@@ -602,6 +682,11 @@
}
Status Credential::setReaderEphemeralPublicKey(const vector<uint8_t>& publicKey) {
+ if (halSessionBinder_) {
+ return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+ "Cannot be used with session");
+ }
+
Status status = halBinder_->setReaderEphemeralPublicKey(publicKey);
if (!status.isOk()) {
return halStatusToGenericError(status);
@@ -610,6 +695,11 @@
}
Status Credential::setAvailableAuthenticationKeys(int32_t keyCount, int32_t maxUsesPerKey) {
+ if (halSessionBinder_) {
+ return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+ "Cannot be used with session");
+ }
+
sp<CredentialData> data = new CredentialData(dataPath_, callingUid_, credentialName_);
if (!data->loadFromDisk()) {
LOG(ERROR) << "Error loading data for credential";
@@ -625,6 +715,11 @@
}
Status Credential::getAuthKeysNeedingCertification(vector<AuthKeyParcel>* _aidl_return) {
+ if (halSessionBinder_) {
+ return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+ "Cannot be used with session");
+ }
+
sp<CredentialData> data = new CredentialData(dataPath_, callingUid_, credentialName_);
if (!data->loadFromDisk()) {
LOG(ERROR) << "Error loading data for credential";
@@ -653,6 +748,11 @@
Status Credential::storeStaticAuthenticationData(const AuthKeyParcel& authenticationKey,
const vector<uint8_t>& staticAuthData) {
+ if (halSessionBinder_) {
+ return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+ "Cannot be used with session");
+ }
+
sp<CredentialData> data = new CredentialData(dataPath_, callingUid_, credentialName_);
if (!data->loadFromDisk()) {
LOG(ERROR) << "Error loading data for credential";
@@ -681,6 +781,12 @@
return Status::fromServiceSpecificError(ICredentialStore::ERROR_NOT_SUPPORTED,
"Not implemented by HAL");
}
+
+ if (halSessionBinder_) {
+ return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+ "Cannot be used with session");
+ }
+
sp<CredentialData> data = new CredentialData(dataPath_, callingUid_, credentialName_);
if (!data->loadFromDisk()) {
LOG(ERROR) << "Error loading data for credential";
@@ -702,6 +808,11 @@
}
Status Credential::getAuthenticationDataUsageCount(vector<int32_t>* _aidl_return) {
+ if (halSessionBinder_) {
+ return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+ "Cannot be used with session");
+ }
+
sp<CredentialData> data = new CredentialData(dataPath_, callingUid_, credentialName_);
if (!data->loadFromDisk()) {
LOG(ERROR) << "Error loading data for credential";
@@ -741,6 +852,12 @@
return Status::fromServiceSpecificError(ICredentialStore::ERROR_NOT_SUPPORTED,
"Not implemented by HAL");
}
+
+ if (halSessionBinder_) {
+ return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+ "Cannot be used with session");
+ }
+
sp<CredentialData> data = new CredentialData(dataPath_, callingUid_, credentialName_);
if (!data->loadFromDisk()) {
LOG(ERROR) << "Error loading data for credential";
diff --git a/identity/Credential.h b/identity/Credential.h
index a76f3cc..0906fea 100644
--- a/identity/Credential.h
+++ b/identity/Credential.h
@@ -39,6 +39,7 @@
using ::android::hardware::identity::HardwareInformation;
using ::android::hardware::identity::IIdentityCredential;
using ::android::hardware::identity::IIdentityCredentialStore;
+using ::android::hardware::identity::IPresentationSession;
using ::android::hardware::identity::RequestDataItem;
using ::android::hardware::identity::RequestNamespace;
@@ -46,7 +47,8 @@
public:
Credential(CipherSuite cipherSuite, const string& dataPath, const string& credentialName,
uid_t callingUid, HardwareInformation hwInfo,
- sp<IIdentityCredentialStore> halStoreBinder, int halApiVersion);
+ sp<IIdentityCredentialStore> halStoreBinder,
+ sp<IPresentationSession> halSessionBinder, int halApiVersion);
~Credential();
Status ensureOrReplaceHalBinder();
@@ -67,13 +69,14 @@
Status getCredentialKeyCertificateChain(vector<uint8_t>* _aidl_return) override;
Status selectAuthKey(bool allowUsingExhaustedKeys, bool allowUsingExpiredKeys,
- int64_t* _aidl_return) override;
+ bool incrementUsageCount, int64_t* _aidl_return) override;
Status getEntries(const vector<uint8_t>& requestMessage,
const vector<RequestNamespaceParcel>& requestNamespaces,
const vector<uint8_t>& sessionTranscript,
const vector<uint8_t>& readerSignature, bool allowUsingExhaustedKeys,
- bool allowUsingExpiredKeys, GetEntriesResultParcel* _aidl_return) override;
+ bool allowUsingExpiredKeys, bool incrementUsageCount,
+ GetEntriesResultParcel* _aidl_return) override;
Status setAvailableAuthenticationKeys(int32_t keyCount, int32_t maxUsesPerKey) override;
Status getAuthKeysNeedingCertification(vector<AuthKeyParcel>* _aidl_return) override;
@@ -94,12 +97,20 @@
uid_t callingUid_;
HardwareInformation hwInfo_;
sp<IIdentityCredentialStore> halStoreBinder_;
+ sp<IPresentationSession> halSessionBinder_;
uint64_t selectedChallenge_ = 0;
sp<IIdentityCredential> halBinder_;
int halApiVersion_;
+ // This is used to cache the selected AuthKey to ensure the same AuthKey is used across
+ // multiple getEntries() calls.
+ //
+ bool selectedAuthKey_ = false;
+ vector<uint8_t> selectedAuthKeySigningKeyBlob_;
+ vector<uint8_t> selectedAuthKeyStaticAuthData_;
+
bool ensureChallenge();
ssize_t
diff --git a/identity/CredentialData.cpp b/identity/CredentialData.cpp
index 74b995d..2189f90 100644
--- a/identity/CredentialData.cpp
+++ b/identity/CredentialData.cpp
@@ -538,7 +538,8 @@
}
const AuthKeyData* CredentialData::selectAuthKey(bool allowUsingExhaustedKeys,
- bool allowUsingExpiredKeys) {
+ bool allowUsingExpiredKeys,
+ bool incrementUsageCount) {
AuthKeyData* candidate;
// First try to find a un-expired key..
@@ -556,7 +557,9 @@
}
}
- candidate->useCount += 1;
+ if (incrementUsageCount) {
+ candidate->useCount += 1;
+ }
return candidate;
}
diff --git a/identity/CredentialData.h b/identity/CredentialData.h
index 24b55d3..e240e47 100644
--- a/identity/CredentialData.h
+++ b/identity/CredentialData.h
@@ -111,7 +111,8 @@
// Returns |nullptr| if a suitable key cannot be found. Otherwise returns
// the authentication and increases its use-count.
- const AuthKeyData* selectAuthKey(bool allowUsingExhaustedKeys, bool allowUsingExpiredKeys);
+ const AuthKeyData* selectAuthKey(bool allowUsingExhaustedKeys, bool allowUsingExpiredKeys,
+ bool incrementUsageCount);
optional<vector<vector<uint8_t>>>
getAuthKeysNeedingCertification(const sp<IIdentityCredential>& halBinder);
diff --git a/identity/CredentialStore.cpp b/identity/CredentialStore.cpp
index 071cf24..61a9125 100644
--- a/identity/CredentialStore.cpp
+++ b/identity/CredentialStore.cpp
@@ -25,6 +25,7 @@
#include "Credential.h"
#include "CredentialData.h"
#include "CredentialStore.h"
+#include "Session.h"
#include "Util.h"
#include "WritableCredential.h"
@@ -95,7 +96,8 @@
return Status::ok();
}
-Status CredentialStore::getCredentialByName(const std::string& credentialName, int32_t cipherSuite,
+Status CredentialStore::getCredentialCommon(const std::string& credentialName, int32_t cipherSuite,
+ sp<IPresentationSession> halSessionBinder,
sp<ICredential>* _aidl_return) {
*_aidl_return = nullptr;
@@ -113,8 +115,9 @@
// Note: IdentityCredentialStore.java's CipherSuite enumeration and CipherSuite from the
// HAL is manually kept in sync. So this cast is safe.
- sp<Credential> credential = new Credential(CipherSuite(cipherSuite), dataPath_, credentialName,
- callingUid, hwInfo_, hal_, halApiVersion_);
+ sp<Credential> credential =
+ new Credential(CipherSuite(cipherSuite), dataPath_, credentialName, callingUid, hwInfo_,
+ hal_, halSessionBinder, halApiVersion_);
Status loadStatus = credential->ensureOrReplaceHalBinder();
if (!loadStatus.isOk()) {
@@ -125,6 +128,23 @@
return loadStatus;
}
+Status CredentialStore::getCredentialByName(const std::string& credentialName, int32_t cipherSuite,
+ sp<ICredential>* _aidl_return) {
+ return getCredentialCommon(credentialName, cipherSuite, nullptr, _aidl_return);
+}
+
+Status CredentialStore::createPresentationSession(int32_t cipherSuite, sp<ISession>* _aidl_return) {
+ sp<IPresentationSession> halPresentationSession;
+ Status status =
+ hal_->createPresentationSession(CipherSuite(cipherSuite), &halPresentationSession);
+ if (!status.isOk()) {
+ return halStatusToGenericError(status);
+ }
+
+ *_aidl_return = new Session(cipherSuite, halPresentationSession, this);
+ return Status::ok();
+}
+
} // namespace identity
} // namespace security
} // namespace android
diff --git a/identity/CredentialStore.h b/identity/CredentialStore.h
index 15da4eb..f2aa506 100644
--- a/identity/CredentialStore.h
+++ b/identity/CredentialStore.h
@@ -30,12 +30,14 @@
using ::android::sp;
using ::android::binder::Status;
+using ::std::optional;
using ::std::string;
using ::std::unique_ptr;
using ::std::vector;
using ::android::hardware::identity::HardwareInformation;
using ::android::hardware::identity::IIdentityCredentialStore;
+using ::android::hardware::identity::IPresentationSession;
class CredentialStore : public BnCredentialStore {
public:
@@ -44,6 +46,12 @@
bool init();
+ // Used by both getCredentialByName() and Session::getCredential()
+ //
+ Status getCredentialCommon(const string& credentialName, int32_t cipherSuite,
+ sp<IPresentationSession> halSessionBinder,
+ sp<ICredential>* _aidl_return);
+
// ICredentialStore overrides
Status getSecurityHardwareInfo(SecurityHardwareInfoParcel* _aidl_return) override;
@@ -53,6 +61,8 @@
Status getCredentialByName(const string& credentialName, int32_t cipherSuite,
sp<ICredential>* _aidl_return) override;
+ Status createPresentationSession(int32_t cipherSuite, sp<ISession>* _aidl_return) override;
+
private:
string dataPath_;
diff --git a/identity/Session.cpp b/identity/Session.cpp
new file mode 100644
index 0000000..98ba3d3
--- /dev/null
+++ b/identity/Session.cpp
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 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.
+ */
+
+#define LOG_TAG "credstore"
+
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/hardware/identity/support/IdentityCredentialSupport.h>
+
+#include <android/security/identity/ICredentialStore.h>
+#include <android/security/identity/ISession.h>
+
+#include "Session.h"
+#include "Util.h"
+
+namespace android {
+namespace security {
+namespace identity {
+
+using std::optional;
+
+using ::android::hardware::identity::IPresentationSession;
+using ::android::hardware::identity::IWritableIdentityCredential;
+
+using ::android::hardware::identity::support::ecKeyPairGetPkcs12;
+using ::android::hardware::identity::support::ecKeyPairGetPrivateKey;
+using ::android::hardware::identity::support::ecKeyPairGetPublicKey;
+using ::android::hardware::identity::support::hexdump;
+using ::android::hardware::identity::support::sha256;
+
+Status Session::getEphemeralKeyPair(vector<uint8_t>* _aidl_return) {
+ vector<uint8_t> keyPair;
+ Status status = halBinder_->getEphemeralKeyPair(&keyPair);
+ if (!status.isOk()) {
+ return halStatusToGenericError(status);
+ }
+ time_t nowSeconds = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
+ time_t validityNotBefore = nowSeconds;
+ time_t validityNotAfter = nowSeconds + 24 * 60 * 60;
+ optional<vector<uint8_t>> pkcs12Bytes = ecKeyPairGetPkcs12(keyPair,
+ "ephemeralKey", // Alias for key
+ "0", // Serial, as a decimal number
+ "Credstore", // Issuer
+ "Ephemeral Key", // Subject
+ validityNotBefore, validityNotAfter);
+ if (!pkcs12Bytes) {
+ return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+ "Error creating PKCS#12 structure for key pair");
+ }
+ *_aidl_return = pkcs12Bytes.value();
+ return Status::ok();
+}
+
+Status Session::setReaderEphemeralPublicKey(const vector<uint8_t>& publicKey) {
+ Status status = halBinder_->setReaderEphemeralPublicKey(publicKey);
+ if (!status.isOk()) {
+ return halStatusToGenericError(status);
+ }
+ return Status::ok();
+}
+
+Status Session::setSessionTranscript(const vector<uint8_t>& sessionTranscript) {
+ Status status = halBinder_->setSessionTranscript(sessionTranscript);
+ if (!status.isOk()) {
+ return halStatusToGenericError(status);
+ }
+ return Status::ok();
+}
+
+Status Session::getCredentialForPresentation(const string& credentialName,
+ sp<ICredential>* _aidl_return) {
+ return store_->getCredentialCommon(credentialName, cipherSuite_, halBinder_, _aidl_return);
+}
+
+Status Session::getAuthChallenge(int64_t* _aidl_return) {
+ *_aidl_return = 0;
+ int64_t authChallenge;
+ Status status = halBinder_->getAuthChallenge(&authChallenge);
+ if (!status.isOk()) {
+ return halStatusToGenericError(status);
+ }
+ *_aidl_return = authChallenge;
+ return Status::ok();
+}
+
+} // namespace identity
+} // namespace security
+} // namespace android
diff --git a/identity/Session.h b/identity/Session.h
new file mode 100644
index 0000000..116c2fd
--- /dev/null
+++ b/identity/Session.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 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.
+ */
+
+#ifndef SYSTEM_SECURITY_PRESENTATION_H_
+#define SYSTEM_SECURITY_PRESENTATION_H_
+
+#include <string>
+#include <vector>
+
+#include <android/security/identity/BnSession.h>
+
+#include <android/hardware/identity/IPresentationSession.h>
+
+#include <android/hardware/identity/IIdentityCredentialStore.h>
+
+#include "CredentialStore.h"
+
+namespace android {
+namespace security {
+namespace identity {
+
+using ::android::sp;
+using ::android::binder::Status;
+using ::std::string;
+using ::std::vector;
+
+using ::android::hardware::identity::CipherSuite;
+using ::android::hardware::identity::HardwareInformation;
+using ::android::hardware::identity::IIdentityCredential;
+using ::android::hardware::identity::IIdentityCredentialStore;
+using ::android::hardware::identity::IPresentationSession;
+using ::android::hardware::identity::RequestDataItem;
+using ::android::hardware::identity::RequestNamespace;
+
+class Session : public BnSession {
+ public:
+ Session(int32_t cipherSuite, sp<IPresentationSession> halBinder, sp<CredentialStore> store)
+ : cipherSuite_(cipherSuite), halBinder_(halBinder), store_(store) {}
+
+ bool initialize();
+
+ // ISession overrides
+ Status getEphemeralKeyPair(vector<uint8_t>* _aidl_return) override;
+
+ Status setReaderEphemeralPublicKey(const vector<uint8_t>& publicKey) override;
+
+ Status setSessionTranscript(const vector<uint8_t>& sessionTranscript) override;
+
+ Status getAuthChallenge(int64_t* _aidl_return) override;
+
+ Status getCredentialForPresentation(const string& credentialName,
+ sp<ICredential>* _aidl_return) override;
+
+ private:
+ int32_t cipherSuite_;
+ sp<IPresentationSession> halBinder_;
+ sp<CredentialStore> store_;
+};
+
+} // namespace identity
+} // namespace security
+} // namespace android
+
+#endif // SYSTEM_SECURITY_SESSION_H_
diff --git a/identity/binder/android/security/identity/ICredential.aidl b/identity/binder/android/security/identity/ICredential.aidl
index 2165810..e6a9fae 100644
--- a/identity/binder/android/security/identity/ICredential.aidl
+++ b/identity/binder/android/security/identity/ICredential.aidl
@@ -49,14 +49,16 @@
byte[] getCredentialKeyCertificateChain();
long selectAuthKey(in boolean allowUsingExhaustedKeys,
- in boolean allowUsingExpiredKeys);
+ in boolean allowUsingExpiredKeys,
+ in boolean incrementUsageCount);
GetEntriesResultParcel getEntries(in byte[] requestMessage,
in RequestNamespaceParcel[] requestNamespaces,
in byte[] sessionTranscript,
in byte[] readerSignature,
in boolean allowUsingExhaustedKeys,
- in boolean allowUsingExpiredKeys);
+ in boolean allowUsingExpiredKeys,
+ in boolean incrementUsageCount);
void setAvailableAuthenticationKeys(in int keyCount, in int maxUsesPerKey);
diff --git a/identity/binder/android/security/identity/ICredentialStore.aidl b/identity/binder/android/security/identity/ICredentialStore.aidl
index 8357f47..39b5e5f 100644
--- a/identity/binder/android/security/identity/ICredentialStore.aidl
+++ b/identity/binder/android/security/identity/ICredentialStore.aidl
@@ -19,6 +19,7 @@
import android.security.identity.IWritableCredential;
import android.security.identity.ICredential;
import android.security.identity.SecurityHardwareInfoParcel;
+import android.security.identity.ISession;
/**
* @hide
@@ -45,6 +46,9 @@
IWritableCredential createCredential(in @utf8InCpp String credentialName,
in @utf8InCpp String docType);
+
ICredential getCredentialByName(in @utf8InCpp String credentialName,
in int cipherSuite);
+
+ ISession createPresentationSession(in int cipherSuite);
}
diff --git a/identity/binder/android/security/identity/ISession.aidl b/identity/binder/android/security/identity/ISession.aidl
new file mode 100644
index 0000000..2139ec1
--- /dev/null
+++ b/identity/binder/android/security/identity/ISession.aidl
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2019, 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.
+ */
+
+package android.security.identity;
+
+import android.security.identity.ICredential;
+
+/**
+ * @hide
+ */
+interface ISession {
+ byte[] getEphemeralKeyPair();
+
+ long getAuthChallenge();
+
+ void setReaderEphemeralPublicKey(in byte[] publicKey);
+
+ void setSessionTranscript(in byte[] sessionTranscript);
+
+ ICredential getCredentialForPresentation(in @utf8InCpp String credentialName);
+}
diff --git a/ondevice-signing/VerityUtils.cpp b/ondevice-signing/VerityUtils.cpp
index 47cba00..24a46b9 100644
--- a/ondevice-signing/VerityUtils.cpp
+++ b/ondevice-signing/VerityUtils.cpp
@@ -283,9 +283,10 @@
}
Result<void> verifyAllFilesUsingCompOs(const std::string& directory_path,
- std::map<std::string, std::string> digests,
+ const std::map<std::string, std::string>& digests,
const SigningKey& signing_key) {
std::error_code ec;
+ size_t verified_count = 0;
auto it = std::filesystem::recursive_directory_iterator(directory_path, ec);
for (auto end = std::filesystem::recursive_directory_iterator(); it != end; it.increment(ec)) {
auto& path = it->path();
@@ -305,7 +306,9 @@
if (verity_digest.ok()) {
// The file is already in fs-verity. We need to make sure it was signed
// by CompOS, so we just check that it has the digest we expect.
- if (verity_digest.value() != compos_digest) {
+ if (verity_digest.value() == compos_digest) {
+ ++verified_count;
+ } else {
return Error() << "fs-verity digest does not match CompOS digest: " << path;
}
} else {
@@ -333,6 +336,7 @@
if (!enabled.ok()) {
return Error() << enabled.error();
}
+ ++verified_count;
}
} else if (it->is_directory()) {
// These are fine to ignore
@@ -346,6 +350,12 @@
return Error() << "Failed to iterate " << directory_path << ": " << ec.message();
}
+ // Make sure all the files we expected have been seen
+ if (verified_count != digests.size()) {
+ return Error() << "Verified " << verified_count << " files, but expected "
+ << digests.size();
+ }
+
return {};
}
diff --git a/ondevice-signing/include/VerityUtils.h b/ondevice-signing/include/VerityUtils.h
index 7715628..0559c35 100644
--- a/ondevice-signing/include/VerityUtils.h
+++ b/ondevice-signing/include/VerityUtils.h
@@ -34,6 +34,7 @@
android::base::Result<std::map<std::string, std::string>>
addFilesToVerityRecursive(const std::string& path, const SigningKey& key);
-android::base::Result<void> verifyAllFilesUsingCompOs(const std::string& directory_path,
- std::map<std::string, std::string> digests,
- const SigningKey& signing_key);
+android::base::Result<void>
+verifyAllFilesUsingCompOs(const std::string& directory_path,
+ const std::map<std::string, std::string>& digests,
+ const SigningKey& signing_key);
diff --git a/ondevice-signing/odsign_main.cpp b/ondevice-signing/odsign_main.cpp
index 0433a3e..a324857 100644
--- a/ondevice-signing/odsign_main.cpp
+++ b/ondevice-signing/odsign_main.cpp
@@ -39,7 +39,6 @@
using android::base::ErrnoError;
using android::base::Error;
-using android::base::GetProperty;
using android::base::Result;
using android::base::SetProperty;
@@ -149,11 +148,6 @@
return access(kCompOsVerifyPath, X_OK) == 0 && access(kKvmDevicePath, F_OK) == 0;
}
-bool isDebugBuild() {
- std::string build_type = GetProperty("ro.build.type", "");
- return build_type == "userdebug" || build_type == "eng";
-}
-
Result<void> verifyExistingRootCert(const SigningKey& key) {
if (access(kSigningKeyCert.c_str(), F_OK) < 0) {
return ErrnoError() << "Key certificate not found: " << kSigningKeyCert;
@@ -531,43 +525,46 @@
return art::odrefresh::ExitCode::kCompilationRequired;
}
- // Read the CompOS signature before checking, otherwise odrefresh will delete it
- // (And there's no point checking the artifacts if we don't have a valid signature.)
+ // Make sure the artifacts we have are genuinely produced by the current
+ // instance of CompOS.
auto compos_info = getComposInfo(compos_key);
if (!compos_info.ok()) {
LOG(WARNING) << compos_info.error();
} else {
- odrefresh_status = checkArtifacts();
- if (odrefresh_status != art::odrefresh::ExitCode::kOkay) {
- LOG(WARNING) << "Pending artifacts are not OK";
- return odrefresh_status;
- }
-
- // The artifacts appear to be up to date - but we haven't
- // verified that they are genuine yet.
-
std::map<std::string, std::string> compos_digests(compos_info->file_hashes().begin(),
compos_info->file_hashes().end());
auto status = verifyAllFilesUsingCompOs(kArtArtifactsDir, compos_digests, signing_key);
if (!status.ok()) {
- LOG(WARNING) << status.error();
+ LOG(WARNING) << "Faild to verify CompOS artifacts: " << status.error();
} else {
- auto persisted = persistDigests(compos_digests, signing_key);
-
- if (!persisted.ok()) {
- LOG(WARNING) << persisted.error();
- } else {
- LOG(INFO) << "Pending artifacts successfully verified.";
+ LOG(INFO) << "CompOS artifacts successfully verified.";
+ odrefresh_status = checkArtifacts();
+ if (odrefresh_status == art::odrefresh::ExitCode::kOkay) {
+ // We have digests of all the files, and they aren't going to change, so
+ // we can just sign them & save them now, and skip checking them later.
+ auto persisted = persistDigests(compos_digests, signing_key);
+ if (!persisted.ok()) {
+ LOG(ERROR) << persisted.error();
+ // Don't try to compile again - if we can't write the digests, things
+ // are pretty bad.
+ return art::odrefresh::ExitCode::kCleanupFailed;
+ }
+ LOG(INFO) << "Persisted CompOS digests.";
*digests_verified = true;
- return art::odrefresh::ExitCode::kOkay;
}
+ return odrefresh_status;
}
}
// We can't use the existing artifacts, so we will need to generate new
// ones.
- removeDirectory(kArtArtifactsDir);
+ if (removeDirectory(kArtArtifactsDir) == 0) {
+ // We have unsigned artifacts that we can't delete, so it's not safe to continue.
+ LOG(ERROR) << "Unable to delete invalid CompOS artifacts";
+ return art::odrefresh::ExitCode::kCleanupFailed;
+ }
+
return art::odrefresh::ExitCode::kCompilationRequired;
}
} // namespace
@@ -607,7 +604,7 @@
LOG(INFO) << "Device doesn't support fsverity. Falling back to full verification.";
}
- bool useCompOs = kUseCompOs && supportsFsVerity && compOsPresent() && isDebugBuild();
+ bool useCompOs = kUseCompOs && supportsFsVerity && compOsPresent();
if (supportsFsVerity) {
auto existing_cert = verifyExistingRootCert(*key);