045a2c87e0
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
620 lines
23 KiB
C++
620 lines
23 KiB
C++
/*
|
|
* 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_);
|
|
|
|
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;
|
|
|
|
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();
|
|
}
|
|
}
|
|
|
|
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) {
|
|
keyCount_ = keyCount;
|
|
maxUsesPerKey_ = maxUsesPerKey;
|
|
|
|
// 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_;
|
|
}
|
|
|
|
pair<int /* keyCount */, int /*maxUsersPerKey */> CredentialData::getAvailableAuthenticationKeys() {
|
|
return std::make_pair(keyCount_, maxUsesPerKey_);
|
|
}
|
|
|
|
AuthKeyData* CredentialData::findAuthKey_(bool allowUsingExhaustedKeys,
|
|
bool allowUsingExpiredKeys) {
|
|
AuthKeyData* candidate = nullptr;
|
|
|
|
int64_t nowMilliSeconds =
|
|
std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()) * 1000;
|
|
|
|
int n = 0;
|
|
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;
|
|
|
|
int64_t nowMilliSeconds =
|
|
std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()) * 1000;
|
|
|
|
for (AuthKeyData& data : authKeyDatas_) {
|
|
bool keyExceedUseCount = (data.useCount >= maxUsesPerKey_);
|
|
bool keyBeyondExpirationDate = (nowMilliSeconds > data.expirationDateMillisSinceEpoch);
|
|
bool newKeyNeeded =
|
|
(data.certificate.size() == 0) || keyExceedUseCount || keyBeyondExpirationDate;
|
|
bool certificationPending = (data.pendingCertificate.size() > 0);
|
|
if (newKeyNeeded && !certificationPending) {
|
|
vector<uint8_t> signingKeyBlob;
|
|
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
|