|  | /* | 
|  | * 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 <chrono> | 
|  |  | 
|  | #include <fcntl.h> | 
|  | #include <stdlib.h> | 
|  | #include <sys/stat.h> | 
|  | #include <sys/types.h> | 
|  | #include <unistd.h> | 
|  |  | 
|  | #include <android-base/logging.h> | 
|  | #include <android-base/stringprintf.h> | 
|  |  | 
|  | #include <cppbor.h> | 
|  | #include <cppbor_parse.h> | 
|  |  | 
|  | #include <android/hardware/identity/support/IdentityCredentialSupport.h> | 
|  |  | 
|  | #include "CredentialData.h" | 
|  | #include "Util.h" | 
|  |  | 
|  | namespace android { | 
|  | namespace security { | 
|  | namespace identity { | 
|  |  | 
|  | using std::optional; | 
|  |  | 
|  | string CredentialData::calculateCredentialFileName(const string& dataPath, uid_t ownerUid, | 
|  | const string& name) { | 
|  | return android::base::StringPrintf( | 
|  | "%s/%d-%s", dataPath.c_str(), (int)ownerUid, | 
|  | android::hardware::identity::support::encodeHex(name).c_str()); | 
|  | } | 
|  |  | 
|  | CredentialData::CredentialData(const string& dataPath, uid_t ownerUid, const string& name) | 
|  | : dataPath_(dataPath), ownerUid_(ownerUid), name_(name), secureUserId_(0) { | 
|  | fileName_ = calculateCredentialFileName(dataPath_, ownerUid_, name_); | 
|  | } | 
|  |  | 
|  | void CredentialData::setSecureUserId(int64_t secureUserId) { | 
|  | secureUserId_ = secureUserId; | 
|  | } | 
|  |  | 
|  | void CredentialData::setCredentialData(const vector<uint8_t>& credentialData) { | 
|  | credentialData_ = credentialData; | 
|  | } | 
|  |  | 
|  | void CredentialData::setAttestationCertificate(const vector<uint8_t>& attestationCertificate) { | 
|  | attestationCertificate_ = attestationCertificate; | 
|  | } | 
|  |  | 
|  | void CredentialData::addSecureAccessControlProfile( | 
|  | const SecureAccessControlProfile& secureAccessControlProfile) { | 
|  | secureAccessControlProfiles_.push_back(secureAccessControlProfile); | 
|  | } | 
|  |  | 
|  | void CredentialData::addEntryData(const string& namespaceName, const string& entryName, | 
|  | const EntryData& data) { | 
|  | idToEncryptedChunks_[namespaceName + ":" + entryName] = data; | 
|  | } | 
|  |  | 
|  | bool CredentialData::saveToDisk() const { | 
|  | cppbor::Map map; | 
|  |  | 
|  | map.add("secureUserId", secureUserId_); | 
|  |  | 
|  | map.add("credentialData", credentialData_); | 
|  |  | 
|  | map.add("attestationCertificate", attestationCertificate_); | 
|  |  | 
|  | cppbor::Array sacpArray; | 
|  | for (const SecureAccessControlProfile& sacp : secureAccessControlProfiles_) { | 
|  | cppbor::Array array; | 
|  | array.add(sacp.id); | 
|  | array.add(sacp.readerCertificate.encodedCertificate); | 
|  | array.add(sacp.userAuthenticationRequired); | 
|  | array.add(sacp.timeoutMillis); | 
|  | array.add(sacp.secureUserId); | 
|  | vector<uint8_t> mac = sacp.mac; | 
|  | array.add(mac); | 
|  | sacpArray.add(std::move(array)); | 
|  | } | 
|  | map.add("secureAccessControlProfiles", std::move(sacpArray)); | 
|  |  | 
|  | cppbor::Map encryptedBlobsMap; | 
|  | for (auto const& [nsAndName, entryData] : idToEncryptedChunks_) { | 
|  | cppbor::Array encryptedChunkArray; | 
|  | for (const vector<uint8_t>& encryptedChunk : entryData.encryptedChunks) { | 
|  | encryptedChunkArray.add(encryptedChunk); | 
|  | } | 
|  | cppbor::Array entryDataArray; | 
|  | entryDataArray.add(entryData.size); | 
|  | cppbor::Array idsArray; | 
|  | for (int32_t id : entryData.accessControlProfileIds) { | 
|  | idsArray.add(id); | 
|  | } | 
|  | entryDataArray.add(std::move(idsArray)); | 
|  | entryDataArray.add(std::move(encryptedChunkArray)); | 
|  | encryptedBlobsMap.add(nsAndName, std::move(entryDataArray)); | 
|  | } | 
|  | map.add("entryData", std::move(encryptedBlobsMap)); | 
|  | map.add("authKeyCount", keyCount_); | 
|  | map.add("maxUsesPerAuthKey", maxUsesPerKey_); | 
|  | map.add("minValidTimeMillis", minValidTimeMillis_); | 
|  |  | 
|  | 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)); | 
|  |  | 
|  | vector<uint8_t> credentialData = map.encode(); | 
|  |  | 
|  | return fileSetContents(fileName_, credentialData); | 
|  | } | 
|  |  | 
|  | optional<SecureAccessControlProfile> parseSacp(const cppbor::Item& item) { | 
|  | const cppbor::Array* array = item.asArray(); | 
|  | if (array == nullptr || array->size() < 6) { | 
|  | LOG(ERROR) << "The SACP CBOR is not an array with at least six elements (size=" | 
|  | << (array != nullptr ? array->size() : -1) << ")"; | 
|  | return {}; | 
|  | } | 
|  | const cppbor::Int* itemId = ((*array)[0])->asInt(); | 
|  | const cppbor::Bstr* itemReaderCertificate = ((*array)[1])->asBstr(); | 
|  | const cppbor::Simple* simple = ((*array)[2])->asSimple(); | 
|  | const cppbor::Bool* itemUserAuthenticationRequired = | 
|  | (simple != nullptr ? (simple->asBool()) : nullptr); | 
|  | const cppbor::Int* itemTimeoutMillis = ((*array)[3])->asInt(); | 
|  | const cppbor::Int* itesecureUserId_ = ((*array)[4])->asInt(); | 
|  | const cppbor::Bstr* itemMac = ((*array)[5])->asBstr(); | 
|  | if (itemId == nullptr || itemReaderCertificate == nullptr || | 
|  | itemUserAuthenticationRequired == nullptr || itemTimeoutMillis == nullptr || | 
|  | itesecureUserId_ == nullptr || itemMac == nullptr) { | 
|  | LOG(ERROR) << "One or more items SACP array in CBOR is of wrong type"; | 
|  | return {}; | 
|  | } | 
|  | SecureAccessControlProfile sacp; | 
|  | sacp.id = itemId->value(); | 
|  | sacp.readerCertificate.encodedCertificate = itemReaderCertificate->value(); | 
|  | sacp.userAuthenticationRequired = itemUserAuthenticationRequired->value(); | 
|  | sacp.timeoutMillis = itemTimeoutMillis->value(); | 
|  | sacp.secureUserId = itesecureUserId_->value(); | 
|  | sacp.mac = itemMac->value(); | 
|  | return sacp; | 
|  | } | 
|  |  | 
|  | optional<AuthKeyData> parseAuthKeyData(const cppbor::Item& item) { | 
|  | const cppbor::Array* array = item.asArray(); | 
|  | if (array == nullptr || array->size() < 6) { | 
|  | LOG(ERROR) << "The AuthKeyData CBOR is not an array with at least six elements"; | 
|  | return {}; | 
|  | } | 
|  | const cppbor::Bstr* itemCertificate = ((*array)[0])->asBstr(); | 
|  | const cppbor::Bstr* itemKeyBlob = ((*array)[1])->asBstr(); | 
|  | const cppbor::Bstr* itemStaticAuthenticationData = ((*array)[2])->asBstr(); | 
|  | const cppbor::Bstr* itemPendingCertificate = ((*array)[3])->asBstr(); | 
|  | const cppbor::Bstr* itemPendingKeyBlob = ((*array)[4])->asBstr(); | 
|  | const cppbor::Int* itemUseCount = ((*array)[5])->asInt(); | 
|  | if (itemCertificate == nullptr || itemKeyBlob == nullptr || | 
|  | itemStaticAuthenticationData == nullptr || itemPendingCertificate == nullptr || | 
|  | itemPendingKeyBlob == nullptr || itemUseCount == nullptr) { | 
|  | 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(); | 
|  | authKeyData.useCount = itemUseCount->value(); | 
|  | return authKeyData; | 
|  | } | 
|  |  | 
|  | vector<int32_t> parseAccessControlProfileIds(const cppbor::Item& item) { | 
|  | const cppbor::Array* array = item.asArray(); | 
|  | if (array == nullptr) { | 
|  | LOG(ERROR) << "The accessControlProfileIds member is not an array"; | 
|  | return {}; | 
|  | } | 
|  |  | 
|  | vector<int32_t> accessControlProfileIds; | 
|  | for (size_t n = 0; n < array->size(); n++) { | 
|  | const cppbor::Int* itemInt = ((*array)[n])->asInt(); | 
|  | if (itemInt == nullptr) { | 
|  | LOG(ERROR) << "An item in the accessControlProfileIds array is not a bstr"; | 
|  | return {}; | 
|  | } | 
|  | accessControlProfileIds.push_back(itemInt->value()); | 
|  | } | 
|  | return accessControlProfileIds; | 
|  | } | 
|  |  | 
|  | optional<vector<vector<uint8_t>>> parseEncryptedChunks(const cppbor::Item& item) { | 
|  | const cppbor::Array* array = item.asArray(); | 
|  | if (array == nullptr) { | 
|  | LOG(ERROR) << "The encryptedChunks member is not an array"; | 
|  | return {}; | 
|  | } | 
|  |  | 
|  | vector<vector<uint8_t>> encryptedChunks; | 
|  | for (size_t n = 0; n < array->size(); n++) { | 
|  | const cppbor::Bstr* itemBstr = ((*array)[n])->asBstr(); | 
|  | if (itemBstr == nullptr) { | 
|  | LOG(ERROR) << "An item in the encryptedChunks array is not a bstr"; | 
|  | return {}; | 
|  | } | 
|  | encryptedChunks.push_back(itemBstr->value()); | 
|  | } | 
|  | return encryptedChunks; | 
|  | } | 
|  |  | 
|  | bool CredentialData::loadFromDisk() { | 
|  | // Reset all data. | 
|  | credentialData_.clear(); | 
|  | attestationCertificate_.clear(); | 
|  | secureAccessControlProfiles_.clear(); | 
|  | idToEncryptedChunks_.clear(); | 
|  | authKeyDatas_.clear(); | 
|  | keyCount_ = 0; | 
|  | maxUsesPerKey_ = 1; | 
|  | minValidTimeMillis_ = 0; | 
|  |  | 
|  | optional<vector<uint8_t>> data = fileGetContents(fileName_); | 
|  | if (!data) { | 
|  | LOG(ERROR) << "Error loading data"; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | auto [item, _ /* newPos */, message] = cppbor::parse(data.value()); | 
|  | if (item == nullptr) { | 
|  | LOG(ERROR) << "Data loaded from " << fileName_ << " is not valid CBOR: " << message; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | const cppbor::Map* map = item->asMap(); | 
|  | if (map == nullptr) { | 
|  | LOG(ERROR) << "Top-level item is not a map"; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | for (size_t n = 0; n < map->size(); n++) { | 
|  | auto& [keyItem, valueItem] = (*map)[n]; | 
|  | const cppbor::Tstr* tstr = keyItem->asTstr(); | 
|  | if (tstr == nullptr) { | 
|  | LOG(ERROR) << "Key item in top-level map is not a tstr"; | 
|  | return false; | 
|  | } | 
|  | const string& key = tstr->value(); | 
|  |  | 
|  | if (key == "secureUserId") { | 
|  | const cppbor::Int* number = valueItem->asInt(); | 
|  | if (number == nullptr) { | 
|  | LOG(ERROR) << "Value for secureUserId is not a number"; | 
|  | return false; | 
|  | } | 
|  | secureUserId_ = number->value(); | 
|  | } else if (key == "credentialData") { | 
|  | const cppbor::Bstr* valueBstr = valueItem->asBstr(); | 
|  | if (valueBstr == nullptr) { | 
|  | LOG(ERROR) << "Value for credentialData is not a bstr"; | 
|  | return false; | 
|  | } | 
|  | credentialData_ = valueBstr->value(); | 
|  | } else if (key == "attestationCertificate") { | 
|  | const cppbor::Bstr* valueBstr = valueItem->asBstr(); | 
|  | if (valueBstr == nullptr) { | 
|  | LOG(ERROR) << "Value for attestationCertificate is not a bstr"; | 
|  | return false; | 
|  | } | 
|  | attestationCertificate_ = valueBstr->value(); | 
|  | } else if (key == "secureAccessControlProfiles") { | 
|  | const cppbor::Array* array = valueItem->asArray(); | 
|  | if (array == nullptr) { | 
|  | LOG(ERROR) << "Value for attestationCertificate is not an array"; | 
|  | return false; | 
|  | } | 
|  | for (size_t m = 0; m < array->size(); m++) { | 
|  | const std::unique_ptr<cppbor::Item>& item = (*array)[m]; | 
|  | optional<SecureAccessControlProfile> sacp = parseSacp(*item); | 
|  | if (!sacp) { | 
|  | LOG(ERROR) << "Error parsing SecureAccessControlProfile"; | 
|  | return false; | 
|  | } | 
|  | secureAccessControlProfiles_.push_back(sacp.value()); | 
|  | } | 
|  |  | 
|  | } else if (key == "entryData") { | 
|  | const cppbor::Map* map = valueItem->asMap(); | 
|  | if (map == nullptr) { | 
|  | LOG(ERROR) << "Value for encryptedChunks is not an map"; | 
|  | return false; | 
|  | } | 
|  | for (size_t m = 0; m < map->size(); m++) { | 
|  | auto& [ecKeyItem, ecValueItem] = (*map)[m]; | 
|  | const cppbor::Tstr* ecTstr = ecKeyItem->asTstr(); | 
|  | if (ecTstr == nullptr) { | 
|  | LOG(ERROR) << "Key item in encryptedChunks map is not a tstr"; | 
|  | return false; | 
|  | } | 
|  | const string& ecId = ecTstr->value(); | 
|  |  | 
|  | const cppbor::Array* ecEntryArrayItem = ecValueItem->asArray(); | 
|  | if (ecEntryArrayItem == nullptr || ecEntryArrayItem->size() < 3) { | 
|  | LOG(ERROR) << "Value item in encryptedChunks map is an array with at least two " | 
|  | "elements"; | 
|  | return false; | 
|  | } | 
|  | const cppbor::Int* ecEntrySizeItem = (*ecEntryArrayItem)[0]->asInt(); | 
|  | if (ecEntrySizeItem == nullptr) { | 
|  | LOG(ERROR) << "Entry size not a number"; | 
|  | return false; | 
|  | } | 
|  | uint64_t entrySize = ecEntrySizeItem->value(); | 
|  |  | 
|  | optional<vector<int32_t>> accessControlProfileIds = | 
|  | parseAccessControlProfileIds(*(*ecEntryArrayItem)[1]); | 
|  | if (!accessControlProfileIds) { | 
|  | LOG(ERROR) << "Error parsing access control profile ids"; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | optional<vector<vector<uint8_t>>> encryptedChunks = | 
|  | parseEncryptedChunks(*(*ecEntryArrayItem)[2]); | 
|  | if (!encryptedChunks) { | 
|  | LOG(ERROR) << "Error parsing encrypted chunks"; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | EntryData data; | 
|  | data.size = entrySize; | 
|  | data.accessControlProfileIds = accessControlProfileIds.value(); | 
|  | data.encryptedChunks = encryptedChunks.value(); | 
|  | idToEncryptedChunks_[ecId] = data; | 
|  | } | 
|  |  | 
|  | } else if (key == "authKeyData") { | 
|  | const cppbor::Array* array = valueItem->asArray(); | 
|  | if (array == nullptr) { | 
|  | LOG(ERROR) << "Value for authData is not an array"; | 
|  | return false; | 
|  | } | 
|  | for (size_t m = 0; m < array->size(); m++) { | 
|  | const std::unique_ptr<cppbor::Item>& item = (*array)[m]; | 
|  | optional<AuthKeyData> authKeyData = parseAuthKeyData(*item); | 
|  | if (!authKeyData) { | 
|  | LOG(ERROR) << "Error parsing AuthKeyData"; | 
|  | return false; | 
|  | } | 
|  | authKeyDatas_.push_back(authKeyData.value()); | 
|  | } | 
|  |  | 
|  | } else if (key == "authKeyCount") { | 
|  | const cppbor::Int* number = valueItem->asInt(); | 
|  | if (number == nullptr) { | 
|  | LOG(ERROR) << "Value for authKeyCount is not a number"; | 
|  | return false; | 
|  | } | 
|  | keyCount_ = number->value(); | 
|  |  | 
|  | } else if (key == "maxUsesPerAuthKey") { | 
|  | const cppbor::Int* number = valueItem->asInt(); | 
|  | if (number == nullptr) { | 
|  | LOG(ERROR) << "Value for maxUsesPerAuthKey is not a number"; | 
|  | return false; | 
|  | } | 
|  | maxUsesPerKey_ = number->value(); | 
|  |  | 
|  | } else if (key == "minValidTimeMillis") { | 
|  | const cppbor::Int* number = valueItem->asInt(); | 
|  | if (number == nullptr) { | 
|  | LOG(ERROR) << "Value for minValidTimeMillis is not a number"; | 
|  | return false; | 
|  | } | 
|  | minValidTimeMillis_ = number->value(); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (credentialData_.size() == 0) { | 
|  | LOG(ERROR) << "Missing credentialData"; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if (attestationCertificate_.size() == 0) { | 
|  | LOG(ERROR) << "Missing attestationCertificate"; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if (size_t(keyCount_) != authKeyDatas_.size()) { | 
|  | LOG(ERROR) << "keyCount_=" << keyCount_ | 
|  | << " != authKeyDatas_.size()=" << authKeyDatas_.size(); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | const vector<uint8_t>& CredentialData::getCredentialData() const { | 
|  | return credentialData_; | 
|  | } | 
|  |  | 
|  | int64_t CredentialData::getSecureUserId() { | 
|  | return secureUserId_; | 
|  | } | 
|  |  | 
|  | const vector<uint8_t>& CredentialData::getAttestationCertificate() const { | 
|  | return attestationCertificate_; | 
|  | } | 
|  |  | 
|  | const vector<SecureAccessControlProfile>& CredentialData::getSecureAccessControlProfiles() const { | 
|  | return secureAccessControlProfiles_; | 
|  | } | 
|  |  | 
|  | bool CredentialData::hasEntryData(const string& namespaceName, const string& entryName) const { | 
|  | string id = namespaceName + ":" + entryName; | 
|  | auto iter = idToEncryptedChunks_.find(id); | 
|  | if (iter == idToEncryptedChunks_.end()) { | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | optional<EntryData> CredentialData::getEntryData(const string& namespaceName, | 
|  | const string& entryName) const { | 
|  | string id = namespaceName + ":" + entryName; | 
|  | auto iter = idToEncryptedChunks_.find(id); | 
|  | if (iter == idToEncryptedChunks_.end()) { | 
|  | return {}; | 
|  | } | 
|  | return iter->second; | 
|  | } | 
|  |  | 
|  | bool CredentialData::deleteCredential() { | 
|  | if (unlink(fileName_.c_str()) != 0) { | 
|  | PLOG(ERROR) << "Error deleting " << fileName_; | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | optional<bool> CredentialData::credentialExists(const string& dataPath, uid_t ownerUid, | 
|  | const string& name) { | 
|  | struct stat statbuf; | 
|  | string filename = calculateCredentialFileName(dataPath, ownerUid, name); | 
|  | if (stat(filename.c_str(), &statbuf) != 0) { | 
|  | if (errno == ENOENT) { | 
|  | return false; | 
|  | } | 
|  | PLOG(ERROR) << "Error getting information about " << filename; | 
|  | return {}; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // --- | 
|  |  | 
|  | void CredentialData::setAvailableAuthenticationKeys(int keyCount, int maxUsesPerKey, | 
|  | int64_t minValidTimeMillis) { | 
|  | keyCount_ = keyCount; | 
|  | maxUsesPerKey_ = maxUsesPerKey; | 
|  | minValidTimeMillis_ = minValidTimeMillis; | 
|  |  | 
|  | // If growing the number of auth keys (prevKeyCount < keyCount_ case) we'll add | 
|  | // new AuthKeyData structs to |authKeyDatas_| and each struct will have empty |certificate| | 
|  | // and |pendingCertificate| fields. Those will be filled out when the | 
|  | // getAuthKeysNeedingCertification() is called. | 
|  | // | 
|  | // If shrinking, we'll just delete the AuthKeyData structs at the end. There's nothing | 
|  | // else to do, the HAL doesn't need to know we're nuking these authentication keys. | 
|  | // | 
|  | // Therefore, in either case it's as simple as just resizing the vector. | 
|  | authKeyDatas_.resize(keyCount_); | 
|  | } | 
|  |  | 
|  | const vector<AuthKeyData>& CredentialData::getAuthKeyDatas() const { | 
|  | return authKeyDatas_; | 
|  | } | 
|  |  | 
|  | tuple<int /* keyCount */, int /*maxUsersPerKey */, int64_t /* minValidTimeMillis */> | 
|  | CredentialData::getAvailableAuthenticationKeys() const { | 
|  | return std::make_tuple(keyCount_, maxUsesPerKey_, minValidTimeMillis_); | 
|  | } | 
|  |  | 
|  | AuthKeyData* CredentialData::findAuthKey_(bool allowUsingExhaustedKeys, | 
|  | bool allowUsingExpiredKeys) { | 
|  | AuthKeyData* candidate = nullptr; | 
|  |  | 
|  | time_t now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); | 
|  | int64_t nowMilliSeconds; | 
|  | if (__builtin_mul_overflow(int64_t(now), int64_t(1000), &nowMilliSeconds)) { | 
|  | LOG(ERROR) << "Overflow converting " << now << " to milliseconds"; | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | int n = 0; | 
|  | 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; | 
|  | } | 
|  | } | 
|  | n++; | 
|  | } | 
|  |  | 
|  | if (candidate == nullptr) { | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | if (candidate->useCount >= maxUsesPerKey_ && !allowUsingExhaustedKeys) { | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | return candidate; | 
|  | } | 
|  |  | 
|  | const AuthKeyData* CredentialData::selectAuthKey(bool allowUsingExhaustedKeys, | 
|  | bool allowUsingExpiredKeys, | 
|  | bool incrementUsageCount) { | 
|  | 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; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (incrementUsageCount) { | 
|  | candidate->useCount += 1; | 
|  | } | 
|  | return candidate; | 
|  | } | 
|  |  | 
|  | optional<vector<vector<uint8_t>>> | 
|  | CredentialData::getAuthKeysNeedingCertification(const sp<IIdentityCredential>& halBinder) { | 
|  |  | 
|  | vector<vector<uint8_t>> keysNeedingCert; | 
|  |  | 
|  | time_t now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); | 
|  | int64_t nowMilliseconds; | 
|  | if (__builtin_mul_overflow(int64_t(now), int64_t(1000), &nowMilliseconds)) { | 
|  | LOG(ERROR) << "Overflow converting " << now << " to milliseconds"; | 
|  | return {}; | 
|  | } | 
|  |  | 
|  | for (AuthKeyData& data : authKeyDatas_) { | 
|  | bool keyExceedUseCount = (data.useCount >= maxUsesPerKey_); | 
|  | int64_t expirationDateAdjusted = data.expirationDateMillisSinceEpoch - minValidTimeMillis_; | 
|  | bool keyBeyondAdjustedExpirationDate = (nowMilliseconds > expirationDateAdjusted); | 
|  | bool newKeyNeeded = | 
|  | (data.certificate.size() == 0) || keyExceedUseCount || keyBeyondAdjustedExpirationDate; | 
|  | bool certificationPending = (data.pendingCertificate.size() > 0); | 
|  | if (newKeyNeeded && !certificationPending) { | 
|  | vector<uint8_t> signingKeyBlob; | 
|  | Certificate signingKeyCertificate; | 
|  | if (!halBinder->generateSigningKeyPair(&signingKeyBlob, &signingKeyCertificate) | 
|  | .isOk()) { | 
|  | LOG(ERROR) << "Error generating signing key-pair"; | 
|  | return {}; | 
|  | } | 
|  | data.pendingCertificate = signingKeyCertificate.encodedCertificate; | 
|  | data.pendingKeyBlob = signingKeyBlob; | 
|  | certificationPending = true; | 
|  | } | 
|  |  | 
|  | if (certificationPending) { | 
|  | keysNeedingCert.push_back(data.pendingCertificate); | 
|  | } | 
|  | } | 
|  | return keysNeedingCert; | 
|  | } | 
|  |  | 
|  | 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(); | 
|  | data.useCount = 0; | 
|  | return true; | 
|  | } | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | }  // namespace identity | 
|  | }  // namespace security | 
|  | }  // namespace android |