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
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";