diff options
author | David Zeuthen <zeuthen@google.com> | 2021-09-11 13:52:17 -0400 |
---|---|---|
committer | David Zeuthen <zeuthen@google.com> | 2022-01-10 17:38:04 -0500 |
commit | 045a2c87e0f4450efb7c0c34125b2f06684b4f2d (patch) | |
tree | 9aee00ef7c8f7c6c75c6dc353ec5226a8143c34c /identity | |
parent | edeefe007974927be5c12a6fe7120789ac11de8b (diff) | |
download | security-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.bp | 7 | ||||
-rw-r--r-- | identity/Credential.cpp | 237 | ||||
-rw-r--r-- | identity/Credential.h | 17 | ||||
-rw-r--r-- | identity/CredentialData.cpp | 7 | ||||
-rw-r--r-- | identity/CredentialData.h | 3 | ||||
-rw-r--r-- | identity/CredentialStore.cpp | 26 | ||||
-rw-r--r-- | identity/CredentialStore.h | 10 | ||||
-rw-r--r-- | identity/Session.cpp | 101 | ||||
-rw-r--r-- | identity/Session.h | 77 | ||||
-rw-r--r-- | identity/binder/android/security/identity/ICredential.aidl | 6 | ||||
-rw-r--r-- | identity/binder/android/security/identity/ICredentialStore.aidl | 4 | ||||
-rw-r--r-- | identity/binder/android/security/identity/ISession.aidl | 34 |
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); +} |