summaryrefslogtreecommitdiff
path: root/identity
diff options
context:
space:
mode:
authorDavid Zeuthen <zeuthen@google.com>2020-10-16 11:50:13 -0400
committerDavid Zeuthen <zeuthen@google.com>2021-01-22 18:37:03 -0500
commit472e6c8e18c800b0b650bd8697e17ce65c1f3608 (patch)
tree471569d2a83c6aa0bbc43328f05d0e2cd51ee28b /identity
parent507171614b09405ec1660ed7726d6869745b128c (diff)
downloadsecurity-472e6c8e18c800b0b650bd8697e17ce65c1f3608.tar.gz
Credstore changes for Android 12
- Add Credential.proveOwership() - Add Credential.deleteWithChallenge() - Add Credential.updateCredential() - Add Credential.storeStaticAuthenticationDataWithExpirationDate() - Store this on disk. For entries stored without this parameter assume they never expire. - Add allowUsingExpiredKeys to Credential.selectAuthKey() and Credential.getEntries() - Unless set to true, never select an expired key - Introduce ERROR_NOT_SUPPORTED and return this if HAL does not support operation Bug: 170146643 Test: atest android.security.identity.cts Change-Id: Ic5dafc6498c9c59b82942def9d348d974f008589
Diffstat (limited to 'identity')
-rw-r--r--identity/Android.bp5
-rw-r--r--identity/Credential.cpp269
-rw-r--r--identity/Credential.h29
-rw-r--r--identity/CredentialData.cpp64
-rw-r--r--identity/CredentialData.h8
-rw-r--r--identity/CredentialStore.cpp16
-rw-r--r--identity/CredentialStore.h1
-rw-r--r--identity/WritableCredential.cpp43
-rw-r--r--identity/WritableCredential.h18
-rw-r--r--identity/binder/android/security/identity/ICredential.aidl20
-rw-r--r--identity/binder/android/security/identity/ICredentialStore.aidl1
11 files changed, 404 insertions, 70 deletions
diff --git a/identity/Android.bp b/identity/Android.bp
index c0f16359..dd61930b 100644
--- a/identity/Android.bp
+++ b/identity/Android.bp
@@ -5,6 +5,7 @@ cc_defaults {
// "-Werror",
"-Wextra",
"-Wunused",
+ "-Wno-deprecated-declarations",
],
sanitize: {
misc_undefined : ["integer"],
@@ -38,8 +39,8 @@ cc_binary {
"libkeystore-attestation-application-id",
],
static_libs: [
- "android.hardware.identity-cpp",
- "android.hardware.keymaster-cpp",
+ "android.hardware.identity-unstable-cpp",
+ "android.hardware.keymaster-unstable-cpp",
"libcppbor",
]
}
diff --git a/identity/Credential.cpp b/identity/Credential.cpp
index 28ba752e..4a2bae17 100644
--- a/identity/Credential.cpp
+++ b/identity/Credential.cpp
@@ -36,6 +36,7 @@
#include "Credential.h"
#include "CredentialData.h"
#include "Util.h"
+#include "WritableCredential.h"
namespace android {
namespace security {
@@ -47,6 +48,8 @@ using std::tuple;
using android::security::keystore::IKeystoreService;
+using ::android::hardware::identity::IWritableIdentityCredential;
+
using ::android::hardware::identity::support::ecKeyPairGetPkcs12;
using ::android::hardware::identity::support::ecKeyPairGetPrivateKey;
using ::android::hardware::identity::support::ecKeyPairGetPublicKey;
@@ -58,25 +61,26 @@ using AidlHardwareAuthToken = android::hardware::keymaster::HardwareAuthToken;
using AidlVerificationToken = android::hardware::keymaster::VerificationToken;
Credential::Credential(CipherSuite cipherSuite, const std::string& dataPath,
- const std::string& credentialName)
- : cipherSuite_(cipherSuite), dataPath_(dataPath), credentialName_(credentialName) {}
+ const std::string& credentialName, uid_t callingUid,
+ HardwareInformation hwInfo, sp<IIdentityCredentialStore> halStoreBinder,
+ int halApiVersion)
+ : cipherSuite_(cipherSuite), dataPath_(dataPath), credentialName_(credentialName),
+ callingUid_(callingUid), hwInfo_(std::move(hwInfo)), halStoreBinder_(halStoreBinder),
+ halApiVersion_(halApiVersion) {}
Credential::~Credential() {}
-Status Credential::loadCredential(sp<IIdentityCredentialStore> halStoreBinder) {
- uid_t callingUid = android::IPCThreadState::self()->getCallingUid();
- sp<CredentialData> data = new CredentialData(dataPath_, callingUid, credentialName_);
+Status Credential::ensureOrReplaceHalBinder() {
+ sp<CredentialData> data = new CredentialData(dataPath_, callingUid_, credentialName_);
if (!data->loadFromDisk()) {
LOG(ERROR) << "Error loading data for credential";
return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
"Error loading data for credential");
}
- data_ = data;
-
sp<IIdentityCredential> halBinder;
Status status =
- halStoreBinder->getCredential(cipherSuite_, data_->getCredentialData(), &halBinder);
+ 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) {
@@ -87,21 +91,33 @@ Status Credential::loadCredential(sp<IIdentityCredentialStore> halStoreBinder) {
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) {
- *_aidl_return = data_->getAttestationCertificate();
+ sp<CredentialData> data = new CredentialData(dataPath_, callingUid_, credentialName_);
+ if (!data->loadFromDisk()) {
+ LOG(ERROR) << "Error loading data for credential";
+ return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+ "Error loading data for credential");
+ }
+ *_aidl_return = data->getAttestationCertificate();
return Status::ok();
}
// Returns operation handle
-Status Credential::selectAuthKey(bool allowUsingExhaustedKeys, int64_t* _aidl_return) {
+Status Credential::selectAuthKey(bool allowUsingExhaustedKeys, bool allowUsingExpiredKeys,
+ int64_t* _aidl_return) {
+ sp<CredentialData> data = new CredentialData(dataPath_, callingUid_, credentialName_);
+ if (!data->loadFromDisk()) {
+ LOG(ERROR) << "Error loading data for credential";
+ return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+ "Error loading data for credential");
+ }
- selectedAuthKey_ = data_->selectAuthKey(allowUsingExhaustedKeys);
+ selectedAuthKey_ = data->selectAuthKey(allowUsingExhaustedKeys, allowUsingExpiredKeys);
if (selectedAuthKey_ == nullptr) {
return Status::fromServiceSpecificError(
ICredentialStore::ERROR_NO_AUTHENTICATION_KEY_AVAILABLE,
@@ -174,16 +190,23 @@ Status Credential::getEntries(const vector<uint8_t>& requestMessage,
const vector<RequestNamespaceParcel>& requestNamespaces,
const vector<uint8_t>& sessionTranscript,
const vector<uint8_t>& readerSignature, bool allowUsingExhaustedKeys,
- GetEntriesResultParcel* _aidl_return) {
+ bool allowUsingExpiredKeys, GetEntriesResultParcel* _aidl_return) {
GetEntriesResultParcel ret;
+ sp<CredentialData> data = new CredentialData(dataPath_, callingUid_, credentialName_);
+ if (!data->loadFromDisk()) {
+ LOG(ERROR) << "Error loading data for credential";
+ return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+ "Error loading data for credential");
+ }
+
// Calculate requestCounts ahead of time and be careful not to include
// elements that don't exist.
//
// Also go through and figure out which access control profiles to include
// in the startRetrieval() call.
vector<int32_t> requestCounts;
- const vector<SecureAccessControlProfile>& allProfiles = data_->getSecureAccessControlProfiles();
+ const vector<SecureAccessControlProfile>& allProfiles = data->getSecureAccessControlProfiles();
// We don't support ACP identifiers which isn't in the range 0 to 31. This
// guarantee exists so it's feasible to implement the TA part of an Identity
@@ -202,13 +225,13 @@ Status Credential::getEntries(const vector<uint8_t>& requestMessage,
for (const RequestNamespaceParcel& rns : requestNamespaces) {
size_t numEntriesInNsToRequest = 0;
for (const RequestEntryParcel& rep : rns.entries) {
- if (data_->hasEntryData(rns.namespaceName, rep.name)) {
+ if (data->hasEntryData(rns.namespaceName, rep.name)) {
numEntriesInNsToRequest++;
}
- optional<EntryData> data = data_->getEntryData(rns.namespaceName, rep.name);
- if (data) {
- for (int32_t id : data.value().accessControlProfileIds) {
+ optional<EntryData> eData = data->getEntryData(rns.namespaceName, rep.name);
+ if (eData) {
+ for (int32_t id : eData.value().accessControlProfileIds) {
if (id < 0 || id >= 32) {
LOG(ERROR) << "Invalid accessControlProfileId " << id << " for "
<< rns.namespaceName << ": " << rep.name;
@@ -282,7 +305,7 @@ Status Credential::getEntries(const vector<uint8_t>& requestMessage,
if (userAuthNeeded) {
vector<uint8_t> authTokenBytes;
vector<uint8_t> verificationTokenBytes;
- if (!getTokensFromKeystore(selectedChallenge_, data_->getSecureUserId(),
+ if (!getTokensFromKeystore(selectedChallenge_, data->getSecureUserId(),
authTokenMaxAgeMillis, authTokenBytes, verificationTokenBytes)) {
LOG(ERROR) << "Error getting tokens from keystore";
return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
@@ -331,7 +354,7 @@ Status Credential::getEntries(const vector<uint8_t>& requestMessage,
const AuthKeyData* authKey = selectedAuthKey_;
if (sessionTranscript.size() > 0) {
if (authKey == nullptr) {
- authKey = data_->selectAuthKey(allowUsingExhaustedKeys);
+ authKey = data->selectAuthKey(allowUsingExhaustedKeys, allowUsingExpiredKeys);
if (authKey == nullptr) {
return Status::fromServiceSpecificError(
ICredentialStore::ERROR_NO_AUTHENTICATION_KEY_AVAILABLE,
@@ -351,7 +374,7 @@ Status Credential::getEntries(const vector<uint8_t>& requestMessage,
RequestNamespace ns;
ns.namespaceName = rns.namespaceName;
for (const RequestEntryParcel& rep : rns.entries) {
- optional<EntryData> entryData = data_->getEntryData(rns.namespaceName, rep.name);
+ optional<EntryData> entryData = data->getEntryData(rns.namespaceName, rep.name);
if (entryData) {
RequestDataItem di;
di.name = rep.name;
@@ -406,16 +429,16 @@ Status Credential::getEntries(const vector<uint8_t>& requestMessage,
ResultEntryParcel resultEntryParcel;
resultEntryParcel.name = rep.name;
- optional<EntryData> data = data_->getEntryData(rns.namespaceName, rep.name);
- if (!data) {
+ optional<EntryData> eData = data->getEntryData(rns.namespaceName, rep.name);
+ if (!eData) {
resultEntryParcel.status = STATUS_NO_SUCH_ENTRY;
resultNamespaceParcel.entries.push_back(resultEntryParcel);
continue;
}
status =
- halBinder_->startRetrieveEntryValue(rns.namespaceName, rep.name, data.value().size,
- data.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) {
@@ -441,7 +464,7 @@ Status Credential::getEntries(const vector<uint8_t>& requestMessage,
}
vector<uint8_t> value;
- for (const auto& encryptedChunk : data.value().encryptedChunks) {
+ for (const auto& encryptedChunk : eData.value().encryptedChunks) {
vector<uint8_t> chunk;
status = halBinder_->retrieveEntryValue(encryptedChunk, &chunk);
if (!status.isOk()) {
@@ -467,7 +490,7 @@ Status Credential::getEntries(const vector<uint8_t>& requestMessage,
// Ensure useCount is updated on disk.
if (authKey != nullptr) {
- if (!data_->saveToDisk()) {
+ if (!data->saveToDisk()) {
return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
"Error saving data");
}
@@ -479,11 +502,46 @@ Status Credential::getEntries(const vector<uint8_t>& requestMessage,
Status Credential::deleteCredential(vector<uint8_t>* _aidl_return) {
vector<uint8_t> proofOfDeletionSignature;
+
+ sp<CredentialData> data = new CredentialData(dataPath_, callingUid_, credentialName_);
+ if (!data->loadFromDisk()) {
+ LOG(ERROR) << "Error loading data for credential";
+ return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+ "Error loading data for credential");
+ }
+
Status status = halBinder_->deleteCredential(&proofOfDeletionSignature);
if (!status.isOk()) {
return halStatusToGenericError(status);
}
- if (!data_->deleteCredential()) {
+ if (!data->deleteCredential()) {
+ return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+ "Error deleting credential data on disk");
+ }
+ *_aidl_return = proofOfDeletionSignature;
+ return Status::ok();
+}
+
+Status Credential::deleteWithChallenge(const vector<uint8_t>& challenge,
+ vector<uint8_t>* _aidl_return) {
+ if (halApiVersion_ < 3) {
+ return Status::fromServiceSpecificError(ICredentialStore::ERROR_NOT_SUPPORTED,
+ "Not implemented by HAL");
+ }
+ vector<uint8_t> proofOfDeletionSignature;
+
+ sp<CredentialData> data = new CredentialData(dataPath_, callingUid_, credentialName_);
+ if (!data->loadFromDisk()) {
+ LOG(ERROR) << "Error loading data for credential";
+ return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+ "Error loading data for credential");
+ }
+
+ Status status = halBinder_->deleteCredentialWithChallenge(challenge, &proofOfDeletionSignature);
+ if (!status.isOk()) {
+ return halStatusToGenericError(status);
+ }
+ if (!data->deleteCredential()) {
return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
"Error deleting credential data on disk");
}
@@ -491,6 +549,20 @@ Status Credential::deleteCredential(vector<uint8_t>* _aidl_return) {
return Status::ok();
}
+Status Credential::proveOwnership(const vector<uint8_t>& challenge, vector<uint8_t>* _aidl_return) {
+ if (halApiVersion_ < 3) {
+ return Status::fromServiceSpecificError(ICredentialStore::ERROR_NOT_SUPPORTED,
+ "Not implemented by HAL");
+ }
+ vector<uint8_t> proofOfOwnershipSignature;
+ Status status = halBinder_->proveOwnership(challenge, &proofOfOwnershipSignature);
+ if (!status.isOk()) {
+ return halStatusToGenericError(status);
+ }
+ *_aidl_return = proofOfOwnershipSignature;
+ return Status::ok();
+}
+
Status Credential::createEphemeralKeyPair(vector<uint8_t>* _aidl_return) {
vector<uint8_t> keyPair;
Status status = halBinder_->createEphemeralKeyPair(&keyPair);
@@ -522,8 +594,14 @@ Status Credential::setReaderEphemeralPublicKey(const vector<uint8_t>& publicKey)
}
Status Credential::setAvailableAuthenticationKeys(int32_t keyCount, int32_t maxUsesPerKey) {
- data_->setAvailableAuthenticationKeys(keyCount, maxUsesPerKey);
- if (!data_->saveToDisk()) {
+ sp<CredentialData> data = new CredentialData(dataPath_, callingUid_, credentialName_);
+ if (!data->loadFromDisk()) {
+ LOG(ERROR) << "Error loading data for credential";
+ return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+ "Error loading data for credential");
+ }
+ data->setAvailableAuthenticationKeys(keyCount, maxUsesPerKey);
+ if (!data->saveToDisk()) {
return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
"Error saving data");
}
@@ -531,8 +609,14 @@ Status Credential::setAvailableAuthenticationKeys(int32_t keyCount, int32_t maxU
}
Status Credential::getAuthKeysNeedingCertification(vector<AuthKeyParcel>* _aidl_return) {
+ sp<CredentialData> data = new CredentialData(dataPath_, callingUid_, credentialName_);
+ if (!data->loadFromDisk()) {
+ LOG(ERROR) << "Error loading data for credential";
+ return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+ "Error loading data for credential");
+ }
optional<vector<vector<uint8_t>>> keysNeedingCert =
- data_->getAuthKeysNeedingCertification(halBinder_);
+ data->getAuthKeysNeedingCertification(halBinder_);
if (!keysNeedingCert) {
return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
"Error getting auth keys neededing certification");
@@ -543,7 +627,7 @@ Status Credential::getAuthKeysNeedingCertification(vector<AuthKeyParcel>* _aidl_
authKeyParcel.x509cert = key;
authKeyParcels.push_back(authKeyParcel);
}
- if (!data_->saveToDisk()) {
+ if (!data->saveToDisk()) {
return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
"Error saving data");
}
@@ -553,13 +637,48 @@ Status Credential::getAuthKeysNeedingCertification(vector<AuthKeyParcel>* _aidl_
Status Credential::storeStaticAuthenticationData(const AuthKeyParcel& authenticationKey,
const vector<uint8_t>& staticAuthData) {
- if (!data_->storeStaticAuthenticationData(authenticationKey.x509cert, staticAuthData)) {
+ sp<CredentialData> data = new CredentialData(dataPath_, callingUid_, credentialName_);
+ if (!data->loadFromDisk()) {
+ LOG(ERROR) << "Error loading data for credential";
+ return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+ "Error loading data for credential");
+ }
+ if (!data->storeStaticAuthenticationData(authenticationKey.x509cert,
+ std::numeric_limits<int64_t>::max(), staticAuthData)) {
return Status::fromServiceSpecificError(
ICredentialStore::ERROR_AUTHENTICATION_KEY_NOT_FOUND,
"Error finding authentication key to store static "
"authentication data for");
}
- if (!data_->saveToDisk()) {
+ if (!data->saveToDisk()) {
+ return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+ "Error saving data");
+ }
+ return Status::ok();
+}
+
+Status
+Credential::storeStaticAuthenticationDataWithExpiration(const AuthKeyParcel& authenticationKey,
+ int64_t expirationDateMillisSinceEpoch,
+ const vector<uint8_t>& staticAuthData) {
+ if (halApiVersion_ < 3) {
+ return Status::fromServiceSpecificError(ICredentialStore::ERROR_NOT_SUPPORTED,
+ "Not implemented by HAL");
+ }
+ sp<CredentialData> data = new CredentialData(dataPath_, callingUid_, credentialName_);
+ if (!data->loadFromDisk()) {
+ LOG(ERROR) << "Error loading data for credential";
+ return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+ "Error loading data for credential");
+ }
+ if (!data->storeStaticAuthenticationData(authenticationKey.x509cert,
+ expirationDateMillisSinceEpoch, staticAuthData)) {
+ return Status::fromServiceSpecificError(
+ ICredentialStore::ERROR_AUTHENTICATION_KEY_NOT_FOUND,
+ "Error finding authentication key to store static "
+ "authentication data for");
+ }
+ if (!data->saveToDisk()) {
return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
"Error saving data");
}
@@ -567,7 +686,13 @@ Status Credential::storeStaticAuthenticationData(const AuthKeyParcel& authentica
}
Status Credential::getAuthenticationDataUsageCount(vector<int32_t>* _aidl_return) {
- const vector<AuthKeyData>& authKeyDatas = data_->getAuthKeyDatas();
+ sp<CredentialData> data = new CredentialData(dataPath_, callingUid_, credentialName_);
+ if (!data->loadFromDisk()) {
+ LOG(ERROR) << "Error loading data for credential";
+ return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+ "Error loading data for credential");
+ }
+ const vector<AuthKeyData>& authKeyDatas = data->getAuthKeyDatas();
vector<int32_t> ret;
for (const AuthKeyData& authKeyData : authKeyDatas) {
ret.push_back(authKeyData.useCount);
@@ -576,6 +701,80 @@ Status Credential::getAuthenticationDataUsageCount(vector<int32_t>* _aidl_return
return Status::ok();
}
+optional<string> extractDocType(const vector<uint8_t>& credentialData) {
+ auto [item, _ /* newPos */, message] = cppbor::parse(credentialData);
+ if (item == nullptr) {
+ LOG(ERROR) << "CredentialData is not valid CBOR: " << message;
+ return {};
+ }
+ const cppbor::Array* array = item->asArray();
+ if (array == nullptr || array->size() < 1) {
+ LOG(ERROR) << "CredentialData array with at least one element";
+ return {};
+ }
+ const cppbor::Tstr* tstr = ((*array)[0])->asTstr();
+ if (tstr == nullptr) {
+ LOG(ERROR) << "First item in CredentialData is not a string";
+ return {};
+ }
+ return tstr->value();
+}
+
+Status Credential::update(sp<IWritableCredential>* _aidl_return) {
+ if (halApiVersion_ < 3) {
+ return Status::fromServiceSpecificError(ICredentialStore::ERROR_NOT_SUPPORTED,
+ "Not implemented by HAL");
+ }
+ sp<CredentialData> data = new CredentialData(dataPath_, callingUid_, credentialName_);
+ if (!data->loadFromDisk()) {
+ LOG(ERROR) << "Error loading data for credential";
+ return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+ "Error loading data for credential");
+ }
+
+ sp<IWritableIdentityCredential> halWritableCredential;
+ Status status = halBinder_->updateCredential(&halWritableCredential);
+ if (!status.isOk()) {
+ return halStatusToGenericError(status);
+ }
+
+ optional<string> docType = extractDocType(data->getCredentialData());
+ if (!docType) {
+ return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+ "Unable to extract DocType from CredentialData");
+ }
+
+ // NOTE: The caller is expected to call WritableCredential::personalize() which will
+ // write brand new data to disk, specifically it will overwrite any data already
+ // have _including_ authentication keys.
+ //
+ // It is because of this we need to set the CredentialKey certificate chain,
+ // keyCount, and maxUsesPerKey below.
+ sp<WritableCredential> writableCredential =
+ new WritableCredential(dataPath_, credentialName_, docType.value(), true, hwInfo_,
+ halWritableCredential, halApiVersion_);
+
+ writableCredential->setAttestationCertificate(data->getAttestationCertificate());
+ auto [keyCount, maxUsesPerKey] = data->getAvailableAuthenticationKeys();
+ writableCredential->setAvailableAuthenticationKeys(keyCount, maxUsesPerKey);
+
+ // Because its data has changed, we need to reconnect to the HAL when the
+ // credential has been updated... otherwise the remote object will have
+ // stale data for future calls (e.g. getAuthKeysNeedingCertification().
+ //
+ // The joys and pitfalls of mutable objects...
+ //
+ writableCredential->setCredentialUpdatedCallback([this] {
+ Status status = this->ensureOrReplaceHalBinder();
+ if (!status.isOk()) {
+ LOG(ERROR) << "Error loading credential";
+ }
+ });
+
+ *_aidl_return = writableCredential;
+ return Status::ok();
+}
+
} // namespace identity
} // namespace security
} // namespace android
diff --git a/identity/Credential.h b/identity/Credential.h
index e2880d98..7f085150 100644
--- a/identity/Credential.h
+++ b/identity/Credential.h
@@ -36,6 +36,7 @@ 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::RequestDataItem;
@@ -43,10 +44,12 @@ using ::android::hardware::identity::RequestNamespace;
class Credential : public BnCredential {
public:
- Credential(CipherSuite cipherSuite, const string& dataPath, const string& credentialName);
+ Credential(CipherSuite cipherSuite, const string& dataPath, const string& credentialName,
+ uid_t callingUid, HardwareInformation hwInfo,
+ sp<IIdentityCredentialStore> halStoreBinder, int halApiVersion);
~Credential();
- Status loadCredential(sp<IIdentityCredentialStore> halStoreBinder);
+ Status ensureOrReplaceHalBinder();
// ICredential overrides
Status createEphemeralKeyPair(vector<uint8_t>* _aidl_return) override;
@@ -55,33 +58,47 @@ class Credential : public BnCredential {
Status deleteCredential(vector<uint8_t>* _aidl_return) override;
+ Status deleteWithChallenge(const vector<uint8_t>& challenge,
+ vector<uint8_t>* _aidl_return) override;
+
+ Status proveOwnership(const vector<uint8_t>& challenge, vector<uint8_t>* _aidl_return) override;
+
Status getCredentialKeyCertificateChain(vector<uint8_t>* _aidl_return) override;
- Status selectAuthKey(bool allowUsingExhaustedKeys, int64_t* _aidl_return) override;
+ Status selectAuthKey(bool allowUsingExhaustedKeys, bool allowUsingExpiredKeys,
+ 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,
- GetEntriesResultParcel* _aidl_return) override;
+ bool allowUsingExpiredKeys, GetEntriesResultParcel* _aidl_return) override;
Status setAvailableAuthenticationKeys(int32_t keyCount, int32_t maxUsesPerKey) override;
Status getAuthKeysNeedingCertification(vector<AuthKeyParcel>* _aidl_return) override;
Status storeStaticAuthenticationData(const AuthKeyParcel& authenticationKey,
const vector<uint8_t>& staticAuthData) override;
+ Status
+ storeStaticAuthenticationDataWithExpiration(const AuthKeyParcel& authenticationKey,
+ int64_t expirationDateMillisSinceEpoch,
+ const vector<uint8_t>& staticAuthData) override;
Status getAuthenticationDataUsageCount(vector<int32_t>* _aidl_return) override;
+ Status update(sp<IWritableCredential>* _aidl_return) override;
+
private:
CipherSuite cipherSuite_;
string dataPath_;
string credentialName_;
+ uid_t callingUid_;
+ HardwareInformation hwInfo_;
+ sp<IIdentityCredentialStore> halStoreBinder_;
const AuthKeyData* selectedAuthKey_ = nullptr;
uint64_t selectedChallenge_ = 0;
- sp<CredentialData> data_;
-
sp<IIdentityCredential> halBinder_;
+ int halApiVersion_;
ssize_t
calcExpectedDeviceNameSpacesSize(const vector<uint8_t>& requestMessage,
diff --git a/identity/CredentialData.cpp b/identity/CredentialData.cpp
index b4e6641d..96c436a8 100644
--- a/identity/CredentialData.cpp
+++ b/identity/CredentialData.cpp
@@ -16,6 +16,8 @@
#define LOG_TAG "CredentialData"
+#include <chrono>
+
#include <fcntl.h>
#include <stdlib.h>
#include <sys/stat.h>
@@ -119,12 +121,15 @@ bool CredentialData::saveToDisk() const {
cppbor::Array authKeyDatasArray;
for (const AuthKeyData& data : authKeyDatas_) {
cppbor::Array array;
+ // Fields 0-6 was in the original version in Android 11
array.add(data.certificate);
array.add(data.keyBlob);
array.add(data.staticAuthenticationData);
array.add(data.pendingCertificate);
array.add(data.pendingKeyBlob);
array.add(data.useCount);
+ // Field 7 was added in Android 12
+ array.add(data.expirationDateMillisSinceEpoch);
authKeyDatasArray.add(std::move(array));
}
map.add("authKeyData", std::move(authKeyDatasArray));
@@ -183,9 +188,17 @@ optional<AuthKeyData> parseAuthKeyData(const cppbor::Item& item) {
LOG(ERROR) << "One or more items in AuthKeyData array in CBOR is of wrong type";
return {};
}
+ // expirationDateMillisSinceEpoch was added as the 7th element for Android 12. If not
+ // present, default to longest possible expiration date.
+ int64_t expirationDateMillisSinceEpoch = INT64_MAX;
+ if (array->size() >= 7) {
+ const cppbor::Int* itemExpirationDateMillisSinceEpoch = ((*array)[6])->asInt();
+ expirationDateMillisSinceEpoch = itemExpirationDateMillisSinceEpoch->value();
+ }
AuthKeyData authKeyData;
authKeyData.certificate = itemCertificate->value();
authKeyData.keyBlob = itemKeyBlob->value();
+ authKeyData.expirationDateMillisSinceEpoch = expirationDateMillisSinceEpoch;
authKeyData.staticAuthenticationData = itemStaticAuthenticationData->value();
authKeyData.pendingCertificate = itemPendingCertificate->value();
authKeyData.pendingKeyBlob = itemPendingKeyBlob->value();
@@ -232,7 +245,6 @@ optional<vector<vector<uint8_t>>> parseEncryptedChunks(const cppbor::Item& item)
}
bool CredentialData::loadFromDisk() {
-
// Reset all data.
credentialData_.clear();
attestationCertificate_.clear();
@@ -487,16 +499,28 @@ const vector<AuthKeyData>& CredentialData::getAuthKeyDatas() const {
return authKeyDatas_;
}
-const AuthKeyData* CredentialData::selectAuthKey(bool allowUsingExhaustedKeys) {
+pair<int /* keyCount */, int /*maxUsersPerKey */> CredentialData::getAvailableAuthenticationKeys() {
+ return std::make_pair(keyCount_, maxUsesPerKey_);
+}
+
+AuthKeyData* CredentialData::findAuthKey_(bool allowUsingExhaustedKeys,
+ bool allowUsingExpiredKeys) {
AuthKeyData* candidate = nullptr;
+ int64_t nowMilliSeconds =
+ std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()) * 1000;
+
int n = 0;
- int candidateNum = -1;
for (AuthKeyData& data : authKeyDatas_) {
+ if (nowMilliSeconds > data.expirationDateMillisSinceEpoch) {
+ if (!allowUsingExpiredKeys) {
+ continue;
+ }
+ }
if (data.certificate.size() != 0) {
+ // Not expired, include in normal check
if (candidate == nullptr || data.useCount < candidate->useCount) {
candidate = &data;
- candidateNum = n;
}
}
n++;
@@ -510,6 +534,28 @@ const AuthKeyData* CredentialData::selectAuthKey(bool allowUsingExhaustedKeys) {
return nullptr;
}
+ return candidate;
+}
+
+const AuthKeyData* CredentialData::selectAuthKey(bool allowUsingExhaustedKeys,
+ bool allowUsingExpiredKeys) {
+ AuthKeyData* candidate;
+
+ // First try to find a un-expired key..
+ candidate = findAuthKey_(allowUsingExhaustedKeys, false);
+ if (candidate == nullptr) {
+ // That didn't work, there are no un-expired keys and we don't allow using expired keys.
+ if (!allowUsingExpiredKeys) {
+ return nullptr;
+ }
+
+ // See if there's an expired key then...
+ candidate = findAuthKey_(allowUsingExhaustedKeys, true);
+ if (candidate == nullptr) {
+ return nullptr;
+ }
+ }
+
candidate->useCount += 1;
return candidate;
}
@@ -519,8 +565,14 @@ CredentialData::getAuthKeysNeedingCertification(const sp<IIdentityCredential>& h
vector<vector<uint8_t>> keysNeedingCert;
+ int64_t nowMilliSeconds =
+ std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()) * 1000;
+
for (AuthKeyData& data : authKeyDatas_) {
- bool newKeyNeeded = (data.certificate.size() == 0) || (data.useCount >= maxUsesPerKey_);
+ bool keyExceedUseCount = (data.useCount >= maxUsesPerKey_);
+ bool keyBeyondExpirationDate = (nowMilliSeconds > data.expirationDateMillisSinceEpoch);
+ bool newKeyNeeded =
+ (data.certificate.size() == 0) || keyExceedUseCount || keyBeyondExpirationDate;
bool certificationPending = (data.pendingCertificate.size() > 0);
if (newKeyNeeded && !certificationPending) {
vector<uint8_t> signingKeyBlob;
@@ -543,11 +595,13 @@ CredentialData::getAuthKeysNeedingCertification(const sp<IIdentityCredential>& h
}
bool CredentialData::storeStaticAuthenticationData(const vector<uint8_t>& authenticationKey,
+ int64_t expirationDateMillisSinceEpoch,
const vector<uint8_t>& staticAuthData) {
for (AuthKeyData& data : authKeyDatas_) {
if (data.pendingCertificate == authenticationKey) {
data.certificate = data.pendingCertificate;
data.keyBlob = data.pendingKeyBlob;
+ data.expirationDateMillisSinceEpoch = expirationDateMillisSinceEpoch;
data.staticAuthenticationData = staticAuthData;
data.pendingCertificate.clear();
data.pendingKeyBlob.clear();
diff --git a/identity/CredentialData.h b/identity/CredentialData.h
index 79958281..b037997b 100644
--- a/identity/CredentialData.h
+++ b/identity/CredentialData.h
@@ -55,6 +55,7 @@ struct AuthKeyData {
vector<uint8_t> certificate;
vector<uint8_t> keyBlob;
+ int64_t expirationDateMillisSinceEpoch;
vector<uint8_t> staticAuthenticationData;
vector<uint8_t> pendingCertificate;
vector<uint8_t> pendingKeyBlob;
@@ -106,17 +107,22 @@ class CredentialData : public RefBase {
const vector<AuthKeyData>& getAuthKeyDatas() const;
+ pair<int /* keyCount */, int /*maxUsersPerKey */> getAvailableAuthenticationKeys();
+
// Returns |nullptr| if a suitable key cannot be found. Otherwise returns
// the authentication and increases its use-count.
- const AuthKeyData* selectAuthKey(bool allowUsingExhaustedKeys);
+ const AuthKeyData* selectAuthKey(bool allowUsingExhaustedKeys, bool allowUsingExpiredKeys);
optional<vector<vector<uint8_t>>>
getAuthKeysNeedingCertification(const sp<IIdentityCredential>& halBinder);
bool storeStaticAuthenticationData(const vector<uint8_t>& authenticationKey,
+ int64_t expirationDateMillisSinceEpoch,
const vector<uint8_t>& staticAuthData);
private:
+ AuthKeyData* findAuthKey_(bool allowUsingExhaustedKeys, bool allowUsingExpiredKeys);
+
// Set by constructor.
//
string dataPath_;
diff --git a/identity/CredentialStore.cpp b/identity/CredentialStore.cpp
index e3a825ba..f77294ee 100644
--- a/identity/CredentialStore.cpp
+++ b/identity/CredentialStore.cpp
@@ -41,11 +41,12 @@ bool CredentialStore::init() {
LOG(ERROR) << "Error getting hardware information: " << status.toString8();
return false;
}
+ halApiVersion_ = hal_->getInterfaceVersion();
- LOG(INFO) << "Connected to Identity Credential HAL with name '" << hwInfo_.credentialStoreName
- << "' authored by '" << hwInfo_.credentialStoreAuthorName << "' with chunk size "
- << hwInfo_.dataChunkSize << " and directoAccess set to "
- << (hwInfo_.isDirectAccess ? "true" : "false");
+ LOG(INFO) << "Connected to Identity Credential HAL with API version " << halApiVersion_
+ << " and name '" << hwInfo_.credentialStoreName << "' authored by '"
+ << hwInfo_.credentialStoreAuthorName << "' with chunk size " << hwInfo_.dataChunkSize
+ << " and directoAccess set to " << (hwInfo_.isDirectAccess ? "true" : "false");
return true;
}
@@ -89,7 +90,7 @@ Status CredentialStore::createCredential(const std::string& credentialName,
}
sp<IWritableCredential> writableCredential = new WritableCredential(
- dataPath_, credentialName, docType, hwInfo_.dataChunkSize, halWritableCredential);
+ dataPath_, credentialName, docType, false, hwInfo_, halWritableCredential, halApiVersion_);
*_aidl_return = writableCredential;
return Status::ok();
}
@@ -112,9 +113,10 @@ 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);
+ sp<Credential> credential = new Credential(CipherSuite(cipherSuite), dataPath_, credentialName,
+ callingUid, hwInfo_, hal_, halApiVersion_);
- Status loadStatus = credential->loadCredential(hal_);
+ Status loadStatus = credential->ensureOrReplaceHalBinder();
if (!loadStatus.isOk()) {
LOG(ERROR) << "Error loading credential";
} else {
diff --git a/identity/CredentialStore.h b/identity/CredentialStore.h
index 24b2b4db..15da4eba 100644
--- a/identity/CredentialStore.h
+++ b/identity/CredentialStore.h
@@ -57,6 +57,7 @@ class CredentialStore : public BnCredentialStore {
string dataPath_;
sp<IIdentityCredentialStore> hal_;
+ int halApiVersion_;
HardwareInformation hwInfo_;
};
diff --git a/identity/WritableCredential.cpp b/identity/WritableCredential.cpp
index a932dcf5..d0688b8d 100644
--- a/identity/WritableCredential.cpp
+++ b/identity/WritableCredential.cpp
@@ -39,13 +39,19 @@ using ::android::hardware::identity::SecureAccessControlProfile;
using ::android::hardware::identity::support::chunkVector;
WritableCredential::WritableCredential(const string& dataPath, const string& credentialName,
- const string& docType, size_t dataChunkSize,
- sp<IWritableIdentityCredential> halBinder)
- : dataPath_(dataPath), credentialName_(credentialName), docType_(docType),
- dataChunkSize_(dataChunkSize), halBinder_(halBinder) {}
+ const string& docType, bool isUpdate,
+ HardwareInformation hwInfo,
+ sp<IWritableIdentityCredential> halBinder, int halApiVersion)
+ : dataPath_(dataPath), credentialName_(credentialName), docType_(docType), isUpdate_(isUpdate),
+ hwInfo_(std::move(hwInfo)), halBinder_(halBinder), halApiVersion_(halApiVersion) {}
WritableCredential::~WritableCredential() {}
+void WritableCredential::setCredentialUpdatedCallback(
+ std::function<void()>&& onCredentialUpdatedCallback) {
+ onCredentialUpdatedCallback_ = onCredentialUpdatedCallback;
+}
+
Status WritableCredential::ensureAttestationCertificateExists(const vector<uint8_t>& challenge) {
if (!attestationCertificate_.empty()) {
return Status::ok();
@@ -79,7 +85,10 @@ Status WritableCredential::ensureAttestationCertificateExists(const vector<uint8
Status WritableCredential::getCredentialKeyCertificateChain(const vector<uint8_t>& challenge,
vector<uint8_t>* _aidl_return) {
-
+ if (isUpdate_) {
+ return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+ "Cannot be called for an update");
+ }
Status ensureStatus = ensureAttestationCertificateExists(challenge);
if (!ensureStatus.isOk()) {
return ensureStatus;
@@ -89,6 +98,15 @@ Status WritableCredential::getCredentialKeyCertificateChain(const vector<uint8_t
return Status::ok();
}
+void WritableCredential::setAttestationCertificate(const vector<uint8_t>& attestationCertificate) {
+ attestationCertificate_ = attestationCertificate;
+}
+
+void WritableCredential::setAvailableAuthenticationKeys(int keyCount, int maxUsesPerKey) {
+ keyCount_ = keyCount;
+ maxUsesPerKey_ = maxUsesPerKey;
+}
+
ssize_t WritableCredential::calcExpectedProofOfProvisioningSize(
const vector<AccessControlProfileParcel>& accessControlProfiles,
const vector<EntryNamespaceParcel>& entryNamespaces) {
@@ -149,9 +167,12 @@ Status
WritableCredential::personalize(const vector<AccessControlProfileParcel>& accessControlProfiles,
const vector<EntryNamespaceParcel>& entryNamespaces,
int64_t secureUserId, vector<uint8_t>* _aidl_return) {
- Status ensureStatus = ensureAttestationCertificateExists({0x00}); // Challenge cannot be empty.
- if (!ensureStatus.isOk()) {
- return ensureStatus;
+ if (!isUpdate_) {
+ Status ensureStatus =
+ ensureAttestationCertificateExists({0x00}); // Challenge cannot be empty.
+ if (!ensureStatus.isOk()) {
+ return ensureStatus;
+ }
}
uid_t callingUid = android::IPCThreadState::self()->getCallingUid();
@@ -203,7 +224,7 @@ WritableCredential::personalize(const vector<AccessControlProfileParcel>& access
for (const EntryNamespaceParcel& ensParcel : entryNamespaces) {
for (const EntryParcel& eParcel : ensParcel.entries) {
- vector<vector<uint8_t>> chunks = chunkVector(eParcel.value, dataChunkSize_);
+ vector<vector<uint8_t>> chunks = chunkVector(eParcel.value, hwInfo_.dataChunkSize);
vector<int32_t> ids;
std::copy(eParcel.accessControlProfileIds.begin(),
@@ -240,11 +261,15 @@ WritableCredential::personalize(const vector<AccessControlProfileParcel>& access
}
data.setCredentialData(credentialData);
+ data.setAvailableAuthenticationKeys(keyCount_, maxUsesPerKey_);
+
if (!data.saveToDisk()) {
return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
"Error saving credential data to disk");
}
+ onCredentialUpdatedCallback_();
+
*_aidl_return = proofOfProvisioningSignature;
return Status::ok();
}
diff --git a/identity/WritableCredential.h b/identity/WritableCredential.h
index eb63aca2..6ff31aec 100644
--- a/identity/WritableCredential.h
+++ b/identity/WritableCredential.h
@@ -29,6 +29,7 @@ namespace security {
namespace identity {
using ::android::binder::Status;
+using ::android::hardware::identity::HardwareInformation;
using ::android::hardware::identity::IWritableIdentityCredential;
using ::std::string;
using ::std::vector;
@@ -36,9 +37,15 @@ using ::std::vector;
class WritableCredential : public BnWritableCredential {
public:
WritableCredential(const string& dataPath, const string& credentialName, const string& docType,
- size_t dataChunkSize, sp<IWritableIdentityCredential> halBinder);
+ bool isUpdate, HardwareInformation hwInfo,
+ sp<IWritableIdentityCredential> halBinder, int halApiVersion);
~WritableCredential();
+ // Used when updating a credential
+ void setAttestationCertificate(const vector<uint8_t>& attestationCertificate);
+ void setAvailableAuthenticationKeys(int keyCount, int maxUsesPerKey);
+ void setCredentialUpdatedCallback(std::function<void()>&& onCredentialUpdatedCallback);
+
// IWritableCredential overrides
Status getCredentialKeyCertificateChain(const vector<uint8_t>& challenge,
vector<uint8_t>* _aidl_return) override;
@@ -51,9 +58,16 @@ class WritableCredential : public BnWritableCredential {
string dataPath_;
string credentialName_;
string docType_;
- size_t dataChunkSize_;
+ bool isUpdate_;
+ HardwareInformation hwInfo_;
sp<IWritableIdentityCredential> halBinder_;
+ int halApiVersion_;
+
vector<uint8_t> attestationCertificate_;
+ int keyCount_ = 0;
+ int maxUsesPerKey_ = 1;
+
+ std::function<void()> onCredentialUpdatedCallback_ = []() {};
ssize_t calcExpectedProofOfProvisioningSize(
const vector<AccessControlProfileParcel>& accessControlProfiles,
diff --git a/identity/binder/android/security/identity/ICredential.aidl b/identity/binder/android/security/identity/ICredential.aidl
index 7bd0df79..2165810f 100644
--- a/identity/binder/android/security/identity/ICredential.aidl
+++ b/identity/binder/android/security/identity/ICredential.aidl
@@ -16,6 +16,8 @@
package android.security.identity;
+import android.security.identity.IWritableCredential;
+
import android.security.identity.RequestNamespaceParcel;
import android.security.identity.GetEntriesResultParcel;
import android.security.identity.AuthKeyParcel;
@@ -40,23 +42,35 @@ interface ICredential {
void setReaderEphemeralPublicKey(in byte[] publicKey);
byte[] deleteCredential();
+ byte[] deleteWithChallenge(in byte[] challenge);
+
+ byte[] proveOwnership(in byte[] challenge);
byte[] getCredentialKeyCertificateChain();
- long selectAuthKey(in boolean allowUsingExhaustedKeys);
+ long selectAuthKey(in boolean allowUsingExhaustedKeys,
+ in boolean allowUsingExpiredKeys);
GetEntriesResultParcel getEntries(in byte[] requestMessage,
in RequestNamespaceParcel[] requestNamespaces,
in byte[] sessionTranscript,
in byte[] readerSignature,
- in boolean allowUsingExhaustedKeys);
+ in boolean allowUsingExhaustedKeys,
+ in boolean allowUsingExpiredKeys);
void setAvailableAuthenticationKeys(in int keyCount, in int maxUsesPerKey);
AuthKeyParcel[] getAuthKeysNeedingCertification();
- void storeStaticAuthenticationData(in AuthKeyParcel authenticationKey, in byte[] staticAuthData);
+ void storeStaticAuthenticationData(in AuthKeyParcel authenticationKey,
+ in byte[] staticAuthData);
+
+ void storeStaticAuthenticationDataWithExpiration(in AuthKeyParcel authenticationKey,
+ in long expirationDateMillisSinceEpoch,
+ in byte[] staticAuthData);
int[] getAuthenticationDataUsageCount();
+
+ IWritableCredential update();
}
diff --git a/identity/binder/android/security/identity/ICredentialStore.aidl b/identity/binder/android/security/identity/ICredentialStore.aidl
index 10398312..8357f47b 100644
--- a/identity/binder/android/security/identity/ICredentialStore.aidl
+++ b/identity/binder/android/security/identity/ICredentialStore.aidl
@@ -39,6 +39,7 @@ interface ICredentialStore {
const int ERROR_AUTHENTICATION_KEY_NOT_FOUND = 9;
const int ERROR_INVALID_ITEMS_REQUEST_MESSAGE = 10;
const int ERROR_SESSION_TRANSCRIPT_MISMATCH = 11;
+ const int ERROR_NOT_SUPPORTED = 12;
SecurityHardwareInfoParcel getSecurityHardwareInfo();