summaryrefslogtreecommitdiff
path: root/identity
diff options
context:
space:
mode:
authorDavid Zeuthen <zeuthen@google.com>2021-09-11 13:52:17 -0400
committerDavid Zeuthen <zeuthen@google.com>2022-01-10 17:38:04 -0500
commit045a2c87e0f4450efb7c0c34125b2f06684b4f2d (patch)
tree9aee00ef7c8f7c6c75c6dc353ec5226a8143c34c /identity
parentedeefe007974927be5c12a6fe7120789ac11de8b (diff)
downloadsecurity-045a2c87e0f4450efb7c0c34125b2f06684b4f2d.tar.gz
identity: Add multi-document presentation support.
Also fix a bug so the same AuthKey is used for several getEntries() calls on a credential. This matches the behavior in the Jetpack. Bug: 197965513 Test: New CTS tests and new screen in CtsVerifier Change-Id: I344f44b5655f0977ee650b518ce669e3c8a7b47a
Diffstat (limited to 'identity')
-rw-r--r--identity/Android.bp7
-rw-r--r--identity/Credential.cpp237
-rw-r--r--identity/Credential.h17
-rw-r--r--identity/CredentialData.cpp7
-rw-r--r--identity/CredentialData.h3
-rw-r--r--identity/CredentialStore.cpp26
-rw-r--r--identity/CredentialStore.h10
-rw-r--r--identity/Session.cpp101
-rw-r--r--identity/Session.h77
-rw-r--r--identity/binder/android/security/identity/ICredential.aidl6
-rw-r--r--identity/binder/android/security/identity/ICredentialStore.aidl4
-rw-r--r--identity/binder/android/security/identity/ISession.aidl34
12 files changed, 456 insertions, 73 deletions
diff --git a/identity/Android.bp b/identity/Android.bp
index ddb43353..7b0503a5 100644
--- a/identity/Android.bp
+++ b/identity/Android.bp
@@ -36,6 +36,7 @@ cc_binary {
"WritableCredential.cpp",
"Credential.cpp",
"CredentialData.cpp",
+ "Session.cpp",
"Util.cpp",
],
init_rc: ["credstore.rc"],
@@ -52,10 +53,11 @@ cc_binary {
"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 @@ filegroup {
"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 7c75d8a2..c67fe4a3 100644
--- a/identity/Credential.cpp
+++ b/identity/Credential.cpp
@@ -70,10 +70,10 @@ using ::aidl::android::security::authorization::IKeystoreAuthorization;
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 @@ Status Credential::ensureOrReplaceHalBinder() {
"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 @@ Status Credential::getCredentialKeyCertificateChain(std::vector<uint8_t>* _aidl_
// 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 @@ Status Credential::selectAuthKey(bool allowUsingExhaustedKeys, bool allowUsingEx
// 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 @@ bool Credential::ensureChallenge() {
}
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 @@ Status Credential::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) {
+ bool allowUsingExpiredKeys, bool incrementUsageCount,
+ GetEntriesResultParcel* _aidl_return) {
GetEntriesResultParcel ret;
sp<CredentialData> data = new CredentialData(dataPath_, callingUid_, credentialName_);
@@ -228,6 +257,28 @@ Status Credential::getEntries(const vector<uint8_t>& requestMessage,
"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 @@ Status Credential::getEntries(const vector<uint8_t>& requestMessage,
}
}
- // 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.
- //
- // 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.
+ 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.
//
- 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 @@ Status Credential::getEntries(const vector<uint8_t>& requestMessage,
}
// 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 Credential::getEntries(const vector<uint8_t>& requestMessage,
}
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 @@ Status Credential::getEntries(const vector<uint8_t>& requestMessage,
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 @@ Status Credential::getEntries(const vector<uint8_t>& requestMessage,
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::getEntries(const vector<uint8_t>& requestMessage,
}
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 @@ Status Credential::deleteWithChallenge(const vector<uint8_t>& challenge,
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 @@ Status Credential::proveOwnership(const vector<uint8_t>& challenge, vector<uint8
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::proveOwnership(const vector<uint8_t>& challenge, vector<uint8
}
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::createEphemeralKeyPair(vector<uint8_t>* _aidl_return) {
}
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::setReaderEphemeralPublicKey(const vector<uint8_t>& publicKey)
}
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::setAvailableAuthenticationKeys(int32_t keyCount, int32_t maxU
}
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::getAuthKeysNeedingCertification(vector<AuthKeyParcel>* _aidl_
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 @@ Credential::storeStaticAuthenticationDataWithExpiration(const AuthKeyParcel& aut
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 @@ Credential::storeStaticAuthenticationDataWithExpiration(const AuthKeyParcel& aut
}
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 @@ Status Credential::update(sp<IWritableCredential>* _aidl_return) {
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 a76f3cc0..0906fea2 100644
--- a/identity/Credential.h
+++ b/identity/Credential.h
@@ -39,6 +39,7 @@ 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;
@@ -46,7 +47,8 @@ class Credential : public BnCredential {
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 @@ class Credential : public BnCredential {
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 @@ class Credential : public BnCredential {
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 74b995d8..2189f909 100644
--- a/identity/CredentialData.cpp
+++ b/identity/CredentialData.cpp
@@ -538,7 +538,8 @@ AuthKeyData* CredentialData::findAuthKey_(bool allowUsingExhaustedKeys,
}
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 @@ const AuthKeyData* CredentialData::selectAuthKey(bool allowUsingExhaustedKeys,
}
}
- candidate->useCount += 1;
+ if (incrementUsageCount) {
+ candidate->useCount += 1;
+ }
return candidate;
}
diff --git a/identity/CredentialData.h b/identity/CredentialData.h
index 24b55d3d..e240e473 100644
--- a/identity/CredentialData.h
+++ b/identity/CredentialData.h
@@ -111,7 +111,8 @@ class CredentialData : public RefBase {
// 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 071cf247..61a91253 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 @@ Status CredentialStore::createCredential(const std::string& credentialName,
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 @@ Status CredentialStore::getCredentialByName(const std::string& credentialName, i
// 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 @@ Status CredentialStore::getCredentialByName(const std::string& credentialName, i
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 15da4eba..f2aa506f 100644
--- a/identity/CredentialStore.h
+++ b/identity/CredentialStore.h
@@ -30,12 +30,14 @@ namespace identity {
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 @@ class CredentialStore : public BnCredentialStore {
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 @@ class CredentialStore : public BnCredentialStore {
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 00000000..98ba3d3a
--- /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 00000000..116c2fd1
--- /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 2165810f..e6a9fae0 100644
--- a/identity/binder/android/security/identity/ICredential.aidl
+++ b/identity/binder/android/security/identity/ICredential.aidl
@@ -49,14 +49,16 @@ interface ICredential {
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 8357f47b..39b5e5f6 100644
--- a/identity/binder/android/security/identity/ICredentialStore.aidl
+++ b/identity/binder/android/security/identity/ICredentialStore.aidl
@@ -19,6 +19,7 @@ package android.security.identity;
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 @@ interface ICredentialStore {
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 00000000..2139ec1c
--- /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);
+}