/* * 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. */ #define LOG_TAG "credstore" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "Credential.h" #include "CredentialData.h" #include "Util.h" #include "WritableCredential.h" namespace android { namespace security { namespace identity { using std::optional; using std::promise; using std::tuple; 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::sha256; using android::hardware::keymaster::SecurityLevel; using android::hardware::keymaster::V4_0::HardwareAuthToken; using android::hardware::keymaster::V4_0::VerificationToken; using AidlHardwareAuthToken = android::hardware::keymaster::HardwareAuthToken; using AidlVerificationToken = android::hardware::keymaster::VerificationToken; using KeyMintAuthToken = ::aidl::android::hardware::security::keymint::HardwareAuthToken; using ::aidl::android::hardware::security::secureclock::TimeStampToken; using ::aidl::android::security::authorization::AuthorizationTokens; using ::aidl::android::security::authorization::IKeystoreAuthorization; Credential::Credential(CipherSuite cipherSuite, const std::string& dataPath, const std::string& credentialName, uid_t callingUid, HardwareInformation hwInfo, sp halStoreBinder, int halApiVersion) : cipherSuite_(cipherSuite), dataPath_(dataPath), credentialName_(credentialName), callingUid_(callingUid), hwInfo_(std::move(hwInfo)), halStoreBinder_(halStoreBinder), halApiVersion_(halApiVersion) {} Credential::~Credential() {} Status Credential::ensureOrReplaceHalBinder() { sp 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 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; return Status::ok(); } Status Credential::getCredentialKeyCertificateChain(std::vector* _aidl_return) { sp 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, bool allowUsingExpiredKeys, int64_t* _aidl_return) { sp 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"); } // 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); if (authKey == nullptr) { return Status::fromServiceSpecificError( ICredentialStore::ERROR_NO_AUTHENTICATION_KEY_AVAILABLE, "No suitable authentication key available"); } if (!ensureChallenge()) { return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC, "Error getting challenge (bug in HAL or TA)"); } *_aidl_return = selectedChallenge_; return Status::ok(); } bool Credential::ensureChallenge() { if (selectedChallenge_ != 0) { return true; } int64_t challenge; 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)"; return false; } selectedChallenge_ = challenge; return true; } // Returns false if an error occurred communicating with keystore. // bool getTokensFromKeystore2(uint64_t challenge, uint64_t secureUserId, unsigned int authTokenMaxAgeMillis, AidlHardwareAuthToken& aidlAuthToken, AidlVerificationToken& aidlVerificationToken) { // try to connect to IKeystoreAuthorization AIDL service first. AIBinder* authzAIBinder = AServiceManager_checkService("android.security.authorization"); ::ndk::SpAIBinder authzBinder(authzAIBinder); auto authzService = IKeystoreAuthorization::fromBinder(authzBinder); if (authzService) { AuthorizationTokens authzTokens; auto result = authzService->getAuthTokensForCredStore(challenge, secureUserId, authTokenMaxAgeMillis, &authzTokens); // Convert KeyMint auth token to KeyMaster authtoken, only if tokens are // returned if (result.isOk()) { KeyMintAuthToken keymintAuthToken = authzTokens.authToken; aidlAuthToken.challenge = keymintAuthToken.challenge; aidlAuthToken.userId = keymintAuthToken.userId; aidlAuthToken.authenticatorId = keymintAuthToken.authenticatorId; aidlAuthToken.authenticatorType = ::android::hardware::keymaster::HardwareAuthenticatorType( int32_t(keymintAuthToken.authenticatorType)); aidlAuthToken.timestamp.milliSeconds = keymintAuthToken.timestamp.milliSeconds; aidlAuthToken.mac = keymintAuthToken.mac; // Convert timestamp token to KeyMaster verification token TimeStampToken timestampToken = authzTokens.timestampToken; aidlVerificationToken.challenge = timestampToken.challenge; aidlVerificationToken.timestamp.milliSeconds = timestampToken.timestamp.milliSeconds; // Legacy verification tokens were always minted by TEE. aidlVerificationToken.securityLevel = SecurityLevel::TRUSTED_ENVIRONMENT; aidlVerificationToken.mac = timestampToken.mac; } else { if (result.getServiceSpecificError() == 0) { // Here we differentiate the errors occurred during communication // from the service specific errors. LOG(ERROR) << "Error getting tokens from keystore2: " << result.getDescription(); return false; } else { // Log the reason for not receiving auth tokens from keystore2. LOG(INFO) << "Auth tokens were not received due to: " << result.getDescription(); } } return true; } else { LOG(ERROR) << "Error connecting to IKeystoreAuthorization service"; return false; } } Status Credential::getEntries(const vector& requestMessage, const vector& requestNamespaces, const vector& sessionTranscript, const vector& readerSignature, bool allowUsingExhaustedKeys, bool allowUsingExpiredKeys, GetEntriesResultParcel* _aidl_return) { GetEntriesResultParcel ret; sp 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 requestCounts; const vector& 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 // Credential HAL implementation where the TA uses a 32-bit word to indicate // which profiles are authorized. for (const SecureAccessControlProfile& profile : allProfiles) { if (profile.id < 0 || profile.id >= 32) { return Status::fromServiceSpecificError( ICredentialStore::ERROR_GENERIC, "Invalid accessProfileId in profile (must be between 0 and 31)"); } } vector includeProfile(32); for (const RequestNamespaceParcel& rns : requestNamespaces) { size_t numEntriesInNsToRequest = 0; for (const RequestEntryParcel& rep : rns.entries) { if (data->hasEntryData(rns.namespaceName, rep.name)) { numEntriesInNsToRequest++; } optional 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; return Status::fromServiceSpecificError( ICredentialStore::ERROR_GENERIC, "Invalid accessProfileId in entry (must be between 0 and 31)"); } includeProfile[id] = true; } } } requestCounts.push_back(numEntriesInNsToRequest); } // Now that we know which profiles are needed, send only those to the // HAL. vector selectedProfiles; for (size_t n = 0; n < allProfiles.size(); n++) { if (includeProfile[allProfiles[n].id]) { selectedProfiles.push_back(allProfiles[n]); } } // Calculate the highest [1] non-zero timeout and if user-auth is needed // ... we need this to select an appropriate authToken. // // [1] : Why do we request the highest timeout and not the lowest? Well, we // return partial results in getEntries e.g. if some data elements // fail to authorize we'll still return the ones that did not fail. So // e.g. consider data elements A and B where A has an ACP with 60 // seconds and B has an ACP with 3600 seconds. In this case we'll be // fine with getting an authToken for e.g. 2400 seconds which would // mean returning only B. // bool userAuthNeeded = false; unsigned int authTokenMaxAgeMillis = 0; for (auto& profile : selectedProfiles) { if (profile.userAuthenticationRequired) { userAuthNeeded = true; if (profile.timeoutMillis > 0) { if (profile.timeoutMillis > authTokenMaxAgeMillis) { authTokenMaxAgeMillis = profile.timeoutMillis; } } } } // Reset tokens and only get them if they're actually needed, e.g. if user authentication // is needed in any of the access control profiles for data items being requested. // AidlHardwareAuthToken aidlAuthToken; AidlVerificationToken aidlVerificationToken; aidlAuthToken.challenge = 0; aidlAuthToken.userId = 0; aidlAuthToken.authenticatorId = 0; aidlAuthToken.authenticatorType = ::android::hardware::keymaster::HardwareAuthenticatorType::NONE; aidlAuthToken.timestamp.milliSeconds = 0; aidlAuthToken.mac.clear(); aidlVerificationToken.challenge = 0; aidlVerificationToken.timestamp.milliSeconds = 0; aidlVerificationToken.securityLevel = ::android::hardware::keymaster::SecurityLevel::SOFTWARE; aidlVerificationToken.mac.clear(); if (userAuthNeeded) { // If user authentication is needed, always get a challenge from the // HAL/TA since it'll need it to check the returned VerificationToken // for freshness. if (!ensureChallenge()) { return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC, "Error getting challenge (bug in HAL or TA)"); } // Note: if all selected profiles require auth-on-every-presentation // then authTokenMaxAgeMillis will be 0 (because timeoutMillis for each // profile is 0). Which means that keystore will only return an // AuthToken if its challenge matches what we pass, regardless of its // age. This is intended b/c the HAL/TA will check not care about // the age in this case, it only cares that the challenge matches. // // Otherwise, if one or more of the profiles is auth-with-a-timeout then // authTokenMaxAgeMillis will be set to the largest of those // timeouts. We'll get an AuthToken which satisfies this deadline if it // exists. This authToken _may_ have the requested challenge but it's // not a guarantee and it's also not required. // if (!getTokensFromKeystore2(selectedChallenge_, data->getSecureUserId(), authTokenMaxAgeMillis, aidlAuthToken, aidlVerificationToken)) { LOG(ERROR) << "Error getting tokens from keystore2"; return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC, "Error getting tokens from keystore2"); } } // 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. // 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. // if (sessionTranscript.size() > 0) { return Status::fromServiceSpecificError( ICredentialStore::ERROR_NO_AUTHENTICATION_KEY_AVAILABLE, "No suitable authentication key available and one is needed"); } } vector signingKeyBlob; if (authKey != nullptr) { signingKeyBlob = authKey->keyBlob; } // Pass the HAL enough information to allow calculating the size of // DeviceNameSpaces ahead of time. vector halRequestNamespaces; for (const RequestNamespaceParcel& rns : requestNamespaces) { RequestNamespace ns; ns.namespaceName = rns.namespaceName; for (const RequestEntryParcel& rep : rns.entries) { optional entryData = data->getEntryData(rns.namespaceName, rep.name); if (entryData) { RequestDataItem di; di.name = rep.name; di.size = entryData.value().size; di.accessControlProfileIds = entryData.value().accessControlProfileIds; ns.items.push_back(di); } } if (ns.items.size() > 0) { halRequestNamespaces.push_back(ns); } } // This is not catastrophic, we might be dealing with a version 1 implementation which // doesn't have this method. 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); if (!status.isOk()) { LOG(INFO) << "Failed setting verification token, assuming V1 HAL " << "and continuing"; } status = halBinder_->startRetrieval(selectedProfiles, aidlAuthToken, requestMessage, signingKeyBlob, 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) { return halStatusToError(status, ICredentialStore::ERROR_EPHEMERAL_PUBLIC_KEY_NOT_FOUND); } else if (code == IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED) { return halStatusToError(status, ICredentialStore::ERROR_INVALID_READER_SIGNATURE); } else if (code == IIdentityCredentialStore::STATUS_INVALID_ITEMS_REQUEST_MESSAGE) { return halStatusToError(status, ICredentialStore::ERROR_INVALID_ITEMS_REQUEST_MESSAGE); } else if (code == IIdentityCredentialStore::STATUS_SESSION_TRANSCRIPT_MISMATCH) { return halStatusToError(status, ICredentialStore::ERROR_SESSION_TRANSCRIPT_MISMATCH); } } if (!status.isOk()) { return halStatusToGenericError(status); } for (const RequestNamespaceParcel& rns : requestNamespaces) { ResultNamespaceParcel resultNamespaceParcel; resultNamespaceParcel.namespaceName = rns.namespaceName; for (const RequestEntryParcel& rep : rns.entries) { ResultEntryParcel resultEntryParcel; resultEntryParcel.name = rep.name; optional 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, 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) { resultEntryParcel.status = STATUS_USER_AUTHENTICATION_FAILED; resultNamespaceParcel.entries.push_back(resultEntryParcel); continue; } else if (code == IIdentityCredentialStore::STATUS_READER_AUTHENTICATION_FAILED) { resultEntryParcel.status = STATUS_READER_AUTHENTICATION_FAILED; resultNamespaceParcel.entries.push_back(resultEntryParcel); continue; } else if (code == IIdentityCredentialStore::STATUS_NOT_IN_REQUEST_MESSAGE) { resultEntryParcel.status = STATUS_NOT_IN_REQUEST_MESSAGE; resultNamespaceParcel.entries.push_back(resultEntryParcel); continue; } else if (code == IIdentityCredentialStore::STATUS_NO_ACCESS_CONTROL_PROFILES) { resultEntryParcel.status = STATUS_NO_ACCESS_CONTROL_PROFILES; resultNamespaceParcel.entries.push_back(resultEntryParcel); continue; } } if (!status.isOk()) { return halStatusToGenericError(status); } vector value; for (const auto& encryptedChunk : eData.value().encryptedChunks) { vector chunk; status = halBinder_->retrieveEntryValue(encryptedChunk, &chunk); if (!status.isOk()) { return halStatusToGenericError(status); } value.insert(value.end(), chunk.begin(), chunk.end()); } resultEntryParcel.status = STATUS_OK; resultEntryParcel.value = value; resultNamespaceParcel.entries.push_back(resultEntryParcel); } ret.resultNamespaces.push_back(resultNamespaceParcel); } status = halBinder_->finishRetrieval(&ret.mac, &ret.deviceNameSpaces); if (!status.isOk()) { return halStatusToGenericError(status); } if (authKey != nullptr) { ret.staticAuthenticationData = authKey->staticAuthenticationData; } // Ensure useCount is updated on disk. if (authKey != nullptr) { if (!data->saveToDisk()) { return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC, "Error saving data"); } } *_aidl_return = ret; return Status::ok(); } Status Credential::deleteCredential(vector* _aidl_return) { vector proofOfDeletionSignature; sp 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()) { return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC, "Error deleting credential data on disk"); } *_aidl_return = proofOfDeletionSignature; return Status::ok(); } Status Credential::deleteWithChallenge(const vector& challenge, vector* _aidl_return) { if (halApiVersion_ < 3) { return Status::fromServiceSpecificError(ICredentialStore::ERROR_NOT_SUPPORTED, "Not implemented by HAL"); } vector proofOfDeletionSignature; sp 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"); } *_aidl_return = proofOfDeletionSignature; return Status::ok(); } Status Credential::proveOwnership(const vector& challenge, vector* _aidl_return) { if (halApiVersion_ < 3) { return Status::fromServiceSpecificError(ICredentialStore::ERROR_NOT_SUPPORTED, "Not implemented by HAL"); } vector proofOfOwnershipSignature; Status status = halBinder_->proveOwnership(challenge, &proofOfOwnershipSignature); if (!status.isOk()) { return halStatusToGenericError(status); } *_aidl_return = proofOfOwnershipSignature; return Status::ok(); } Status Credential::createEphemeralKeyPair(vector* _aidl_return) { vector keyPair; Status status = halBinder_->createEphemeralKeyPair(&keyPair); if (!status.isOk()) { return halStatusToGenericError(status); } optional> 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 if (!pkcs12Bytes) { return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC, "Error creating PKCS#12 structure for key pair"); } *_aidl_return = pkcs12Bytes.value(); return Status::ok(); } Status Credential::setReaderEphemeralPublicKey(const vector& publicKey) { Status status = halBinder_->setReaderEphemeralPublicKey(publicKey); if (!status.isOk()) { return halStatusToGenericError(status); } return Status::ok(); } Status Credential::setAvailableAuthenticationKeys(int32_t keyCount, int32_t maxUsesPerKey) { sp 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"); } return Status::ok(); } Status Credential::getAuthKeysNeedingCertification(vector* _aidl_return) { sp 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>> keysNeedingCert = data->getAuthKeysNeedingCertification(halBinder_); if (!keysNeedingCert) { return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC, "Error getting auth keys neededing certification"); } vector authKeyParcels; for (const vector& key : keysNeedingCert.value()) { AuthKeyParcel authKeyParcel; authKeyParcel.x509cert = key; authKeyParcels.push_back(authKeyParcel); } if (!data->saveToDisk()) { return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC, "Error saving data"); } *_aidl_return = authKeyParcels; return Status::ok(); } Status Credential::storeStaticAuthenticationData(const AuthKeyParcel& authenticationKey, const vector& staticAuthData) { sp 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::max(), 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"); } return Status::ok(); } Status Credential::storeStaticAuthenticationDataWithExpiration(const AuthKeyParcel& authenticationKey, int64_t expirationDateMillisSinceEpoch, const vector& staticAuthData) { if (halApiVersion_ < 3) { return Status::fromServiceSpecificError(ICredentialStore::ERROR_NOT_SUPPORTED, "Not implemented by HAL"); } sp 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"); } return Status::ok(); } Status Credential::getAuthenticationDataUsageCount(vector* _aidl_return) { sp 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& authKeyDatas = data->getAuthKeyDatas(); vector ret; for (const AuthKeyData& authKeyData : authKeyDatas) { ret.push_back(authKeyData.useCount); } *_aidl_return = ret; return Status::ok(); } optional extractDocType(const vector& 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* _aidl_return) { if (halApiVersion_ < 3) { return Status::fromServiceSpecificError(ICredentialStore::ERROR_NOT_SUPPORTED, "Not implemented by HAL"); } sp 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 halWritableCredential; Status status = halBinder_->updateCredential(&halWritableCredential); if (!status.isOk()) { return halStatusToGenericError(status); } optional 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 = new WritableCredential( dataPath_, credentialName_, docType.value(), true, hwInfo_, halWritableCredential); writableCredential->setAttestationCertificate(data->getAttestationCertificate()); auto [keyCount, maxUsesPerKey] = data->getAvailableAuthenticationKeys(); writableCredential->setAvailableAuthenticationKeys(keyCount, maxUsesPerKey); // Because its data has changed, we need to replace the binder for the // IIdentityCredential when the credential has been updated... otherwise the // remote object will have stale data for future calls, for example // getAuthKeysNeedingCertification(). // // The way this is implemented is that setCredentialToReloadWhenUpdated() // instructs the WritableCredential to call writableCredentialPersonalized() // on |this|. // // writableCredential->setCredentialToReloadWhenUpdated(this); *_aidl_return = writableCredential; return Status::ok(); } void Credential::writableCredentialPersonalized() { Status status = ensureOrReplaceHalBinder(); if (!status.isOk()) { LOG(ERROR) << "Error reloading credential"; } } } // namespace identity } // namespace security } // namespace android