Port to binderized keymaster HAL
This patch ports keystore to the HIDL based binderized keymaster HAL.
Keystore has no more dependencies on legacy keymaster headers, and
therefore data structures, constant declarations, or enums. All
keymaster related data structures and enums used by keystore are the
once defined by the HIDL based keymaster HAL definition. In the process
of porting, keystore underwent some changes:
* Keystore got a new implementation of AuthorizationSet that is fully
based on the new HIDL data structures. Key parameters are now either
organised as AuthorizationSets or hidl_vec<KeyParameter>. (Formerly,
this was a mixture of keymaster's AuthorizationSet,
std::vec<keymaster_key_param_t>, and keymaster_key_param_set_t.) The
former is used for memory management and provides algorithms for
assembling, joining, and subtracting sets of parameters. The latter
is used as wire format for the HAL IPC; it can wrap the memory owned
by an AuthorizationSet for this purpose. The AuthorizationSet is
accompanied by a new implementation of type safe functions for
creating and accessing tagged key parameters,
Authorizations (keystore/keymaster_tags.h).
* A new type (KSSReturnCode) was introduced that wraps keystore service
response codes. Keystore has two sets of error codes. ErrorCode
errors are less than 0 and use 0 as success value. ResponseCode
errors are greater than zero and use 1 as success value. This patch
changes ResponseCode to be an enum class so that is no longer
assignable to int without a cast. The new return type can only be
initialized by ResponseCode or ErrorCode and when accessed as int32_t,
which happens on serialization when the response is send to a client,
the success values are coalesced onto 1 as expected by the
clients. KSSreturnCode is also comparable to ResponseCode and
ErrorCode, and the predicate isOk() returns true if it was initialized
with either ErrorCode::OK (0) or ReponseCode::NO_ERROR (1).
* A bug was fixed, that caused the keystore verify function to return
success, regardless of the input, internal errors, or lack of
permissions.
* The marshalling code in IKeystoreService.cpp was rewritten. For data
structures that are known to keymaster, the client facing side of
keystore uses HIDL based data structures as (target) source
for (un)marshaling to avoid further conversion. hidl_vecs are used to
wrap parcel memory without copying and taking ownership where
possible.
* Explicit use of malloc is reduced (malloc was required by the C nature
of the old HAL). The new implementations avoid explicit use of
malloc/new and waive the use of pointers for return values. Instead,
functions return by value objects that take ownership of secondary
memory allocations where required.
Test: runtest --path=cts/tests/tests/keystore/src/android/keystore/cts
Bug: 32020919
Change-Id: I59d3a0f4a6bdf6bb3bbf791ad8827c463effa286
diff --git a/keystore/keymaster_enforcement.cpp b/keystore/keymaster_enforcement.cpp
new file mode 100644
index 0000000..117f048
--- /dev/null
+++ b/keystore/keymaster_enforcement.cpp
@@ -0,0 +1,598 @@
+/*
+ * Copyright (C) 2014 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 "keystore"
+
+#include "keymaster_enforcement.h"
+
+#include <assert.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <string.h>
+
+#include <openssl/evp.h>
+
+#include <cutils/log.h>
+#include <hardware/hw_auth_token.h>
+#include <list>
+
+namespace keystore {
+
+class AccessTimeMap {
+ public:
+ explicit AccessTimeMap(uint32_t max_size) : max_size_(max_size) {}
+
+ /* If the key is found, returns true and fills \p last_access_time. If not found returns
+ * false. */
+ bool LastKeyAccessTime(km_id_t keyid, uint32_t* last_access_time) const;
+
+ /* Updates the last key access time with the currentTime parameter. Adds the key if
+ * needed, returning false if key cannot be added because list is full. */
+ bool UpdateKeyAccessTime(km_id_t keyid, uint32_t current_time, uint32_t timeout);
+
+ private:
+ struct AccessTime {
+ km_id_t keyid;
+ uint32_t access_time;
+ uint32_t timeout;
+ };
+ std::list<AccessTime> last_access_list_;
+ const uint32_t max_size_;
+};
+
+class AccessCountMap {
+ public:
+ explicit AccessCountMap(uint32_t max_size) : max_size_(max_size) {}
+
+ /* If the key is found, returns true and fills \p count. If not found returns
+ * false. */
+ bool KeyAccessCount(km_id_t keyid, uint32_t* count) const;
+
+ /* Increments key access count, adding an entry if the key has never been used. Returns
+ * false if the list has reached maximum size. */
+ bool IncrementKeyAccessCount(km_id_t keyid);
+
+ private:
+ struct AccessCount {
+ km_id_t keyid;
+ uint64_t access_count;
+ };
+ std::list<AccessCount> access_count_list_;
+ const uint32_t max_size_;
+};
+
+bool is_public_key_algorithm(const AuthorizationSet& auth_set) {
+ auto algorithm = auth_set.GetTagValue(TAG_ALGORITHM);
+ return algorithm.isOk() &&
+ (algorithm.value() == Algorithm::RSA || algorithm.value() == Algorithm::EC);
+}
+
+static ErrorCode authorized_purpose(const KeyPurpose purpose, const AuthorizationSet& auth_set) {
+ switch (purpose) {
+ case KeyPurpose::VERIFY:
+ case KeyPurpose::ENCRYPT:
+ case KeyPurpose::SIGN:
+ case KeyPurpose::DECRYPT:
+ if (auth_set.Contains(TAG_PURPOSE, purpose)) return ErrorCode::OK;
+ return ErrorCode::INCOMPATIBLE_PURPOSE;
+
+ default:
+ return ErrorCode::UNSUPPORTED_PURPOSE;
+ }
+}
+
+inline bool is_origination_purpose(KeyPurpose purpose) {
+ return purpose == KeyPurpose::ENCRYPT || purpose == KeyPurpose::SIGN;
+}
+
+inline bool is_usage_purpose(KeyPurpose purpose) {
+ return purpose == KeyPurpose::DECRYPT || purpose == KeyPurpose::VERIFY;
+}
+
+KeymasterEnforcement::KeymasterEnforcement(uint32_t max_access_time_map_size,
+ uint32_t max_access_count_map_size)
+ : access_time_map_(new (std::nothrow) AccessTimeMap(max_access_time_map_size)),
+ access_count_map_(new (std::nothrow) AccessCountMap(max_access_count_map_size)) {}
+
+KeymasterEnforcement::~KeymasterEnforcement() {
+ delete access_time_map_;
+ delete access_count_map_;
+}
+
+ErrorCode KeymasterEnforcement::AuthorizeOperation(const KeyPurpose purpose, const km_id_t keyid,
+ const AuthorizationSet& auth_set,
+ const AuthorizationSet& operation_params,
+ uint64_t op_handle, bool is_begin_operation) {
+ if (is_public_key_algorithm(auth_set)) {
+ switch (purpose) {
+ case KeyPurpose::ENCRYPT:
+ case KeyPurpose::VERIFY:
+ /* Public key operations are always authorized. */
+ return ErrorCode::OK;
+
+ case KeyPurpose::DECRYPT:
+ case KeyPurpose::SIGN:
+ case KeyPurpose::DERIVE_KEY:
+ break;
+ case KeyPurpose::WRAP_KEY:
+ return ErrorCode::INCOMPATIBLE_PURPOSE;
+ };
+ };
+
+ if (is_begin_operation)
+ return AuthorizeBegin(purpose, keyid, auth_set, operation_params);
+ else
+ return AuthorizeUpdateOrFinish(auth_set, operation_params, op_handle);
+}
+
+// For update and finish the only thing to check is user authentication, and then only if it's not
+// timeout-based.
+ErrorCode KeymasterEnforcement::AuthorizeUpdateOrFinish(const AuthorizationSet& auth_set,
+ const AuthorizationSet& operation_params,
+ uint64_t op_handle) {
+ int auth_type_index = -1;
+ for (size_t pos = 0; pos < auth_set.size(); ++pos) {
+ switch (auth_set[pos].tag) {
+ case Tag::NO_AUTH_REQUIRED:
+ case Tag::AUTH_TIMEOUT:
+ // If no auth is required or if auth is timeout-based, we have nothing to check.
+ return ErrorCode::OK;
+
+ case Tag::USER_AUTH_TYPE:
+ auth_type_index = pos;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ // Note that at this point we should be able to assume that authentication is required, because
+ // authentication is required if KM_TAG_NO_AUTH_REQUIRED is absent. However, there are legacy
+ // keys which have no authentication-related tags, so we assume that absence is equivalent to
+ // presence of KM_TAG_NO_AUTH_REQUIRED.
+ //
+ // So, if we found KM_TAG_USER_AUTH_TYPE or if we find KM_TAG_USER_SECURE_ID then authentication
+ // is required. If we find neither, then we assume authentication is not required and return
+ // success.
+ bool authentication_required = (auth_type_index != -1);
+ for (auto& param : auth_set) {
+ auto user_secure_id = authorizationValue(TAG_USER_SECURE_ID, param);
+ if (user_secure_id.isOk()) {
+ authentication_required = true;
+ int auth_timeout_index = -1;
+ if (AuthTokenMatches(auth_set, operation_params, user_secure_id.value(),
+ auth_type_index, auth_timeout_index, op_handle,
+ false /* is_begin_operation */))
+ return ErrorCode::OK;
+ }
+ }
+
+ if (authentication_required) return ErrorCode::KEY_USER_NOT_AUTHENTICATED;
+
+ return ErrorCode::OK;
+}
+
+ErrorCode KeymasterEnforcement::AuthorizeBegin(const KeyPurpose purpose, const km_id_t keyid,
+ const AuthorizationSet& auth_set,
+ const AuthorizationSet& operation_params) {
+ // Find some entries that may be needed to handle KM_TAG_USER_SECURE_ID
+ int auth_timeout_index = -1;
+ int auth_type_index = -1;
+ int no_auth_required_index = -1;
+ for (size_t pos = 0; pos < auth_set.size(); ++pos) {
+ switch (auth_set[pos].tag) {
+ case Tag::AUTH_TIMEOUT:
+ auth_timeout_index = pos;
+ break;
+ case Tag::USER_AUTH_TYPE:
+ auth_type_index = pos;
+ break;
+ case Tag::NO_AUTH_REQUIRED:
+ no_auth_required_index = pos;
+ break;
+ default:
+ break;
+ }
+ }
+
+ ErrorCode error = authorized_purpose(purpose, auth_set);
+ if (error != ErrorCode::OK) return error;
+
+ // If successful, and if key has a min time between ops, this will be set to the time limit
+ uint32_t min_ops_timeout = UINT32_MAX;
+
+ bool update_access_count = false;
+ bool caller_nonce_authorized_by_key = false;
+ bool authentication_required = false;
+ bool auth_token_matched = false;
+
+ for (auto& param : auth_set) {
+
+ // KM_TAG_PADDING_OLD and KM_TAG_DIGEST_OLD aren't actually members of the enum, so we can't
+ // switch on them. There's nothing to validate for them, though, so just ignore them.
+ if (int32_t(param.tag) == KM_TAG_PADDING_OLD || int32_t(param.tag) == KM_TAG_DIGEST_OLD)
+ continue;
+
+ switch (param.tag) {
+
+ case Tag::ACTIVE_DATETIME: {
+ auto date = authorizationValue(TAG_ACTIVE_DATETIME, param);
+ if (date.isOk() && !activation_date_valid(date.value()))
+ return ErrorCode::KEY_NOT_YET_VALID;
+ break;
+ }
+ case Tag::ORIGINATION_EXPIRE_DATETIME: {
+ auto date = authorizationValue(TAG_ORIGINATION_EXPIRE_DATETIME, param);
+ if (is_origination_purpose(purpose) && date.isOk() &&
+ expiration_date_passed(date.value()))
+ return ErrorCode::KEY_EXPIRED;
+ break;
+ }
+ case Tag::USAGE_EXPIRE_DATETIME: {
+ auto date = authorizationValue(TAG_USAGE_EXPIRE_DATETIME, param);
+ if (is_usage_purpose(purpose) && date.isOk() && expiration_date_passed(date.value()))
+ return ErrorCode::KEY_EXPIRED;
+ break;
+ }
+ case Tag::MIN_SECONDS_BETWEEN_OPS: {
+ auto min_ops_timeout = authorizationValue(TAG_MIN_SECONDS_BETWEEN_OPS, param);
+ if (min_ops_timeout.isOk() && !MinTimeBetweenOpsPassed(min_ops_timeout.value(), keyid))
+ return ErrorCode::KEY_RATE_LIMIT_EXCEEDED;
+ break;
+ }
+ case Tag::MAX_USES_PER_BOOT: {
+ auto max_users = authorizationValue(TAG_MAX_USES_PER_BOOT, param);
+ update_access_count = true;
+ if (max_users.isOk() && !MaxUsesPerBootNotExceeded(keyid, max_users.value()))
+ return ErrorCode::KEY_MAX_OPS_EXCEEDED;
+ break;
+ }
+ case Tag::USER_SECURE_ID:
+ if (no_auth_required_index != -1) {
+ // Key has both KM_TAG_USER_SECURE_ID and KM_TAG_NO_AUTH_REQUIRED
+ return ErrorCode::INVALID_KEY_BLOB;
+ }
+
+ if (auth_timeout_index != -1) {
+ auto secure_id = authorizationValue(TAG_USER_SECURE_ID, param);
+ authentication_required = true;
+ if (secure_id.isOk() &&
+ AuthTokenMatches(auth_set, operation_params, secure_id.value(), auth_type_index,
+ auth_timeout_index, 0 /* op_handle */,
+ true /* is_begin_operation */))
+ auth_token_matched = true;
+ }
+ break;
+
+ case Tag::CALLER_NONCE:
+ caller_nonce_authorized_by_key = true;
+ break;
+
+ /* Tags should never be in key auths. */
+ case Tag::INVALID:
+ case Tag::AUTH_TOKEN:
+ case Tag::ROOT_OF_TRUST:
+ case Tag::APPLICATION_DATA:
+ case Tag::ATTESTATION_CHALLENGE:
+ case Tag::ATTESTATION_APPLICATION_ID:
+ return ErrorCode::INVALID_KEY_BLOB;
+
+ /* Tags used for cryptographic parameters in keygen. Nothing to enforce. */
+ case Tag::PURPOSE:
+ case Tag::ALGORITHM:
+ case Tag::KEY_SIZE:
+ case Tag::BLOCK_MODE:
+ case Tag::DIGEST:
+ case Tag::MAC_LENGTH:
+ case Tag::PADDING:
+ case Tag::NONCE:
+ case Tag::MIN_MAC_LENGTH:
+ case Tag::KDF:
+ case Tag::EC_CURVE:
+
+ /* Tags not used for operations. */
+ case Tag::BLOB_USAGE_REQUIREMENTS:
+ case Tag::EXPORTABLE:
+
+ /* Algorithm specific parameters not used for access control. */
+ case Tag::RSA_PUBLIC_EXPONENT:
+ case Tag::ECIES_SINGLE_HASH_MODE:
+
+ /* Informational tags. */
+ case Tag::CREATION_DATETIME:
+ case Tag::ORIGIN:
+ case Tag::ROLLBACK_RESISTANT:
+
+ /* Tags handled when KM_TAG_USER_SECURE_ID is handled */
+ case Tag::NO_AUTH_REQUIRED:
+ case Tag::USER_AUTH_TYPE:
+ case Tag::AUTH_TIMEOUT:
+
+ /* Tag to provide data to operations. */
+ case Tag::ASSOCIATED_DATA:
+
+ /* Tags that are implicitly verified by secure side */
+ case Tag::ALL_APPLICATIONS:
+ case Tag::APPLICATION_ID:
+ case Tag::OS_VERSION:
+ case Tag::OS_PATCHLEVEL:
+
+ /* Ignored pending removal */
+ case Tag::USER_ID:
+ case Tag::ALL_USERS:
+
+ /* TODO(swillden): Handle these */
+ case Tag::INCLUDE_UNIQUE_ID:
+ case Tag::UNIQUE_ID:
+ case Tag::RESET_SINCE_ID_ROTATION:
+ case Tag::ALLOW_WHILE_ON_BODY:
+ break;
+
+ case Tag::BOOTLOADER_ONLY:
+ return ErrorCode::INVALID_KEY_BLOB;
+ }
+ }
+
+ if (authentication_required && !auth_token_matched) {
+ ALOGE("Auth required but no matching auth token found");
+ return ErrorCode::KEY_USER_NOT_AUTHENTICATED;
+ }
+
+ if (!caller_nonce_authorized_by_key && is_origination_purpose(purpose) &&
+ operation_params.Contains(Tag::NONCE))
+ return ErrorCode::CALLER_NONCE_PROHIBITED;
+
+ if (min_ops_timeout != UINT32_MAX) {
+ if (!access_time_map_) {
+ ALOGE("Rate-limited keys table not allocated. Rate-limited keys disabled");
+ return ErrorCode::MEMORY_ALLOCATION_FAILED;
+ }
+
+ if (!access_time_map_->UpdateKeyAccessTime(keyid, get_current_time(), min_ops_timeout)) {
+ ALOGE("Rate-limited keys table full. Entries will time out.");
+ return ErrorCode::TOO_MANY_OPERATIONS;
+ }
+ }
+
+ if (update_access_count) {
+ if (!access_count_map_) {
+ ALOGE("Usage-count limited keys tabel not allocated. Count-limited keys disabled");
+ return ErrorCode::MEMORY_ALLOCATION_FAILED;
+ }
+
+ if (!access_count_map_->IncrementKeyAccessCount(keyid)) {
+ ALOGE("Usage count-limited keys table full, until reboot.");
+ return ErrorCode::TOO_MANY_OPERATIONS;
+ }
+ }
+
+ return ErrorCode::OK;
+}
+
+class EvpMdCtx {
+ public:
+ EvpMdCtx() { EVP_MD_CTX_init(&ctx_); }
+ ~EvpMdCtx() { EVP_MD_CTX_cleanup(&ctx_); }
+
+ EVP_MD_CTX* get() { return &ctx_; }
+
+ private:
+ EVP_MD_CTX ctx_;
+};
+
+/* static */
+bool KeymasterEnforcement::CreateKeyId(const hidl_vec<uint8_t>& key_blob, km_id_t* keyid) {
+ EvpMdCtx ctx;
+
+ uint8_t hash[EVP_MAX_MD_SIZE];
+ unsigned int hash_len;
+ if (EVP_DigestInit_ex(ctx.get(), EVP_sha256(), nullptr /* ENGINE */) &&
+ EVP_DigestUpdate(ctx.get(), &key_blob[0], key_blob.size()) &&
+ EVP_DigestFinal_ex(ctx.get(), hash, &hash_len)) {
+ assert(hash_len >= sizeof(*keyid));
+ memcpy(keyid, hash, sizeof(*keyid));
+ return true;
+ }
+
+ return false;
+}
+
+bool KeymasterEnforcement::MinTimeBetweenOpsPassed(uint32_t min_time_between, const km_id_t keyid) {
+ if (!access_time_map_) return false;
+
+ uint32_t last_access_time;
+ if (!access_time_map_->LastKeyAccessTime(keyid, &last_access_time)) return true;
+ return min_time_between <= static_cast<int64_t>(get_current_time()) - last_access_time;
+}
+
+bool KeymasterEnforcement::MaxUsesPerBootNotExceeded(const km_id_t keyid, uint32_t max_uses) {
+ if (!access_count_map_) return false;
+
+ uint32_t key_access_count;
+ if (!access_count_map_->KeyAccessCount(keyid, &key_access_count)) return true;
+ return key_access_count < max_uses;
+}
+
+template <typename IntType, uint32_t byteOrder> struct choose_hton;
+
+template <typename IntType> struct choose_hton<IntType, __ORDER_LITTLE_ENDIAN__> {
+ inline static IntType hton(const IntType& value) {
+ IntType result = 0;
+ const unsigned char* inbytes = reinterpret_cast<const unsigned char*>(&value);
+ unsigned char* outbytes = reinterpret_cast<unsigned char*>(&result);
+ for (int i = sizeof(IntType) - 1; i >= 0; --i) {
+ *(outbytes++) = inbytes[i];
+ }
+ return result;
+ }
+};
+
+template <typename IntType> struct choose_hton<IntType, __ORDER_BIG_ENDIAN__> {
+ inline static IntType hton(const IntType& value) { return value; }
+};
+
+template <typename IntType> inline IntType hton(const IntType& value) {
+ return choose_hton<IntType, __BYTE_ORDER__>::hton(value);
+}
+
+template <typename IntType> inline IntType ntoh(const IntType& value) {
+ // same operation and hton
+ return choose_hton<IntType, __BYTE_ORDER__>::hton(value);
+}
+
+bool KeymasterEnforcement::AuthTokenMatches(const AuthorizationSet& auth_set,
+ const AuthorizationSet& operation_params,
+ const uint64_t user_secure_id,
+ const int auth_type_index, const int auth_timeout_index,
+ const uint64_t op_handle,
+ bool is_begin_operation) const {
+ assert(auth_type_index < static_cast<int>(auth_set.size()));
+ assert(auth_timeout_index < static_cast<int>(auth_set.size()));
+
+ auto auth_token_blob = operation_params.GetTagValue(TAG_AUTH_TOKEN);
+ if (!auth_token_blob.isOk()) {
+ ALOGE("Authentication required, but auth token not provided");
+ return false;
+ }
+
+ if (auth_token_blob.value().size() != sizeof(hw_auth_token_t)) {
+ ALOGE("Bug: Auth token is the wrong size (%zu expected, %zu found)",
+ sizeof(hw_auth_token_t), auth_token_blob.value().size());
+ return false;
+ }
+
+ hw_auth_token_t auth_token;
+ memcpy(&auth_token, &auth_token_blob.value()[0], sizeof(hw_auth_token_t));
+ if (auth_token.version != HW_AUTH_TOKEN_VERSION) {
+ ALOGE("Bug: Auth token is the version %hhu (or is not an auth token). Expected %d",
+ auth_token.version, HW_AUTH_TOKEN_VERSION);
+ return false;
+ }
+
+ if (!ValidateTokenSignature(auth_token)) {
+ ALOGE("Auth token signature invalid");
+ return false;
+ }
+
+ if (auth_timeout_index == -1 && op_handle && op_handle != auth_token.challenge) {
+ ALOGE("Auth token has the challenge %" PRIu64 ", need %" PRIu64, auth_token.challenge,
+ op_handle);
+ return false;
+ }
+
+ if (user_secure_id != auth_token.user_id && user_secure_id != auth_token.authenticator_id) {
+ ALOGI("Auth token SIDs %" PRIu64 " and %" PRIu64 " do not match key SID %" PRIu64,
+ auth_token.user_id, auth_token.authenticator_id, user_secure_id);
+ return false;
+ }
+
+ if (auth_type_index < 0 || auth_type_index > static_cast<int>(auth_set.size())) {
+ ALOGE("Auth required but no auth type found");
+ return false;
+ }
+
+ assert(auth_set[auth_type_index].tag == KM_TAG_USER_AUTH_TYPE);
+ auto key_auth_type_mask = authorizationValue(TAG_USER_AUTH_TYPE, auth_set[auth_type_index]);
+ if (!key_auth_type_mask.isOk()) return false;
+
+ uint32_t token_auth_type = ntoh(auth_token.authenticator_type);
+ if ((uint32_t(key_auth_type_mask.value()) & token_auth_type) == 0) {
+ ALOGE("Key requires match of auth type mask 0%uo, but token contained 0%uo",
+ key_auth_type_mask.value(), token_auth_type);
+ return false;
+ }
+
+ if (auth_timeout_index != -1 && is_begin_operation) {
+ assert(auth_set[auth_timeout_index].tag == KM_TAG_AUTH_TIMEOUT);
+ auto auth_token_timeout =
+ authorizationValue(TAG_AUTH_TIMEOUT, auth_set[auth_timeout_index]);
+ if (!auth_token_timeout.isOk()) return false;
+
+ if (auth_token_timed_out(auth_token, auth_token_timeout.value())) {
+ ALOGE("Auth token has timed out");
+ return false;
+ }
+ }
+
+ // Survived the whole gauntlet. We have authentage!
+ return true;
+}
+
+bool AccessTimeMap::LastKeyAccessTime(km_id_t keyid, uint32_t* last_access_time) const {
+ for (auto& entry : last_access_list_)
+ if (entry.keyid == keyid) {
+ *last_access_time = entry.access_time;
+ return true;
+ }
+ return false;
+}
+
+bool AccessTimeMap::UpdateKeyAccessTime(km_id_t keyid, uint32_t current_time, uint32_t timeout) {
+ for (auto iter = last_access_list_.begin(); iter != last_access_list_.end();) {
+ if (iter->keyid == keyid) {
+ iter->access_time = current_time;
+ return true;
+ }
+
+ // Expire entry if possible.
+ assert(current_time >= iter->access_time);
+ if (current_time - iter->access_time >= iter->timeout)
+ iter = last_access_list_.erase(iter);
+ else
+ ++iter;
+ }
+
+ if (last_access_list_.size() >= max_size_) return false;
+
+ AccessTime new_entry;
+ new_entry.keyid = keyid;
+ new_entry.access_time = current_time;
+ new_entry.timeout = timeout;
+ last_access_list_.push_front(new_entry);
+ return true;
+}
+
+bool AccessCountMap::KeyAccessCount(km_id_t keyid, uint32_t* count) const {
+ for (auto& entry : access_count_list_)
+ if (entry.keyid == keyid) {
+ *count = entry.access_count;
+ return true;
+ }
+ return false;
+}
+
+bool AccessCountMap::IncrementKeyAccessCount(km_id_t keyid) {
+ for (auto& entry : access_count_list_)
+ if (entry.keyid == keyid) {
+ // Note that the 'if' below will always be true because KM_TAG_MAX_USES_PER_BOOT is a
+ // uint32_t, and as soon as entry.access_count reaches the specified maximum value
+ // operation requests will be rejected and access_count won't be incremented any more.
+ // And, besides, UINT64_MAX is huge. But we ensure that it doesn't wrap anyway, out of
+ // an abundance of caution.
+ if (entry.access_count < UINT64_MAX) ++entry.access_count;
+ return true;
+ }
+
+ if (access_count_list_.size() >= max_size_) return false;
+
+ AccessCount new_entry;
+ new_entry.keyid = keyid;
+ new_entry.access_count = 1;
+ access_count_list_.push_front(new_entry);
+ return true;
+}
+}; /* namespace keystore */