1489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden/* 2489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden * Copyright (C) 2015 The Android Open Source Project 3489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden * 4489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden * Licensed under the Apache License, Version 2.0 (the "License"); 5489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden * you may not use this file except in compliance with the License. 6489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden * You may obtain a copy of the License at 7489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden * 8489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden * http://www.apache.org/licenses/LICENSE-2.0 9489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden * 10489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden * Unless required by applicable law or agreed to in writing, software 11489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden * distributed under the License is distributed on an "AS IS" BASIS, 12489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden * See the License for the specific language governing permissions and 14489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden * limitations under the License. 15489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden */ 16489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden 17489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden#include "auth_token_table.h" 18489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden 19489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden#include <assert.h> 20489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden#include <time.h> 21489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden 22489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden#include <algorithm> 23489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden 240bdad21c560552ec324733fb5db734fec204a2e9Shawn Willden#include <keymaster/android_keymaster_utils.h> 25489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden#include <keymaster/logger.h> 26489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden 27489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willdennamespace keymaster { 28489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden 29489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden// 30489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden// Some trivial template wrappers around std algorithms, so they take containers not ranges. 31489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden// 32489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willdentemplate <typename Container, typename Predicate> 33489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willdentypename Container::iterator find_if(Container& container, Predicate pred) { 34489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden return std::find_if(container.begin(), container.end(), pred); 35489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden} 36489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden 37489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willdentemplate <typename Container, typename Predicate> 38489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willdentypename Container::iterator remove_if(Container& container, Predicate pred) { 39489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden return std::remove_if(container.begin(), container.end(), pred); 40489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden} 41489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden 42489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willdentemplate <typename Container> typename Container::iterator min_element(Container& container) { 43489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden return std::min_element(container.begin(), container.end()); 44489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden} 45489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden 46489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willdentime_t clock_gettime_raw() { 47489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden struct timespec time; 48489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden clock_gettime(CLOCK_MONOTONIC_RAW, &time); 49489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden return time.tv_sec; 50489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden} 51489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden 52489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willdenvoid AuthTokenTable::AddAuthenticationToken(const hw_auth_token_t* auth_token) { 53489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden Entry new_entry(auth_token, clock_function_()); 54489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden RemoveEntriesSupersededBy(new_entry); 55489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden if (entries_.size() >= max_entries_) { 56489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden LOG_W("Auth token table filled up; replacing oldest entry", 0); 57489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden *min_element(entries_) = std::move(new_entry); 58489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden } else { 59489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden entries_.push_back(std::move(new_entry)); 60489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden } 61489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden} 62489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden 63b2ffa420da26414379b31807eec76ec8c9f3b0a9Shawn Willdeninline bool is_secret_key_operation(keymaster_algorithm_t algorithm, keymaster_purpose_t purpose) { 64b2ffa420da26414379b31807eec76ec8c9f3b0a9Shawn Willden if ((algorithm != KM_ALGORITHM_RSA || algorithm != KM_ALGORITHM_EC)) 65b2ffa420da26414379b31807eec76ec8c9f3b0a9Shawn Willden return true; 66b2ffa420da26414379b31807eec76ec8c9f3b0a9Shawn Willden if (purpose == KM_PURPOSE_SIGN || purpose == KM_PURPOSE_DECRYPT) 67b2ffa420da26414379b31807eec76ec8c9f3b0a9Shawn Willden return true; 68b2ffa420da26414379b31807eec76ec8c9f3b0a9Shawn Willden return false; 69489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden} 70489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden 71b2ffa420da26414379b31807eec76ec8c9f3b0a9Shawn Willdeninline bool KeyRequiresAuthentication(const AuthorizationSet& key_info, 72b2ffa420da26414379b31807eec76ec8c9f3b0a9Shawn Willden keymaster_purpose_t purpose) { 73b2ffa420da26414379b31807eec76ec8c9f3b0a9Shawn Willden keymaster_algorithm_t algorithm = KM_ALGORITHM_AES; 74b2ffa420da26414379b31807eec76ec8c9f3b0a9Shawn Willden key_info.GetTagValue(TAG_ALGORITHM, &algorithm); 75b2ffa420da26414379b31807eec76ec8c9f3b0a9Shawn Willden return is_secret_key_operation(algorithm, purpose) && key_info.find(TAG_NO_AUTH_REQUIRED) == -1; 76b2ffa420da26414379b31807eec76ec8c9f3b0a9Shawn Willden} 77b2ffa420da26414379b31807eec76ec8c9f3b0a9Shawn Willden 78b2ffa420da26414379b31807eec76ec8c9f3b0a9Shawn Willdeninline bool KeyRequiresAuthPerOperation(const AuthorizationSet& key_info, 79b2ffa420da26414379b31807eec76ec8c9f3b0a9Shawn Willden keymaster_purpose_t purpose) { 80b2ffa420da26414379b31807eec76ec8c9f3b0a9Shawn Willden keymaster_algorithm_t algorithm = KM_ALGORITHM_AES; 81b2ffa420da26414379b31807eec76ec8c9f3b0a9Shawn Willden key_info.GetTagValue(TAG_ALGORITHM, &algorithm); 82b2ffa420da26414379b31807eec76ec8c9f3b0a9Shawn Willden return is_secret_key_operation(algorithm, purpose) && key_info.find(TAG_AUTH_TIMEOUT) == -1; 83489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden} 84489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden 85489dfe1f3dbdb9377debce826e37294d48fe6754Shawn WilldenAuthTokenTable::Error AuthTokenTable::FindAuthorization(const AuthorizationSet& key_info, 86b2ffa420da26414379b31807eec76ec8c9f3b0a9Shawn Willden keymaster_purpose_t purpose, 87489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden keymaster_operation_handle_t op_handle, 88489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden const hw_auth_token_t** found) { 89b2ffa420da26414379b31807eec76ec8c9f3b0a9Shawn Willden if (!KeyRequiresAuthentication(key_info, purpose)) 90489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden return AUTH_NOT_REQUIRED; 91489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden 92489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden hw_authenticator_type_t auth_type = HW_AUTH_NONE; 93489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden key_info.GetTagValue(TAG_USER_AUTH_TYPE, &auth_type); 94489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden 95489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden std::vector<uint64_t> key_sids; 96489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden ExtractSids(key_info, &key_sids); 97489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden 98b2ffa420da26414379b31807eec76ec8c9f3b0a9Shawn Willden if (KeyRequiresAuthPerOperation(key_info, purpose)) 99489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden return FindAuthPerOpAuthorization(key_sids, auth_type, op_handle, found); 100489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden else 101489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden return FindTimedAuthorization(key_sids, auth_type, key_info, found); 102489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden} 103489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden 104489dfe1f3dbdb9377debce826e37294d48fe6754Shawn WilldenAuthTokenTable::Error AuthTokenTable::FindAuthPerOpAuthorization( 105489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden const std::vector<uint64_t>& sids, hw_authenticator_type_t auth_type, 106489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden keymaster_operation_handle_t op_handle, const hw_auth_token_t** found) { 107489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden if (op_handle == 0) 108489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden return OP_HANDLE_REQUIRED; 109489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden 110489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden auto matching_op = find_if( 111489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden entries_, [&](Entry& e) { return e.token()->challenge == op_handle && !e.completed(); }); 112489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden 113489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden if (matching_op == entries_.end()) 114489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden return AUTH_TOKEN_NOT_FOUND; 115489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden 116489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden if (!matching_op->SatisfiesAuth(sids, auth_type)) 117489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden return AUTH_TOKEN_WRONG_SID; 118489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden 119489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden *found = matching_op->token(); 120489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden return OK; 121489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden} 122489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden 123489dfe1f3dbdb9377debce826e37294d48fe6754Shawn WilldenAuthTokenTable::Error AuthTokenTable::FindTimedAuthorization(const std::vector<uint64_t>& sids, 124489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden hw_authenticator_type_t auth_type, 125489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden const AuthorizationSet& key_info, 126489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden const hw_auth_token_t** found) { 127489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden Entry* newest_match = NULL; 128489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden for (auto& entry : entries_) 129489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden if (entry.SatisfiesAuth(sids, auth_type) && entry.is_newer_than(newest_match)) 130489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden newest_match = &entry; 131489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden 132489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden if (!newest_match) 133489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden return AUTH_TOKEN_NOT_FOUND; 134489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden 135489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden uint32_t timeout; 136489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden key_info.GetTagValue(TAG_AUTH_TIMEOUT, &timeout); 137489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden time_t now = clock_function_(); 138489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden if (static_cast<int64_t>(newest_match->time_received()) + timeout < static_cast<int64_t>(now)) 139489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden return AUTH_TOKEN_EXPIRED; 140489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden 141489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden newest_match->UpdateLastUse(now); 142489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden *found = newest_match->token(); 143489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden return OK; 144489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden} 145489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden 146489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willdenvoid AuthTokenTable::ExtractSids(const AuthorizationSet& key_info, std::vector<uint64_t>* sids) { 147489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden assert(sids); 148489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden for (auto& param : key_info) 149489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden if (param.tag == TAG_USER_SECURE_ID) 150489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden sids->push_back(param.long_integer); 151489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden} 152489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden 153489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willdenvoid AuthTokenTable::RemoveEntriesSupersededBy(const Entry& entry) { 154489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden entries_.erase(remove_if(entries_, [&](Entry& e) { return entry.Supersedes(e); }), 155489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden entries_.end()); 156489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden} 157489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden 158bbc7648d285f67b898d24d307b011fb676ba6643Chad Brubakervoid AuthTokenTable::Clear() { 159bbc7648d285f67b898d24d307b011fb676ba6643Chad Brubaker entries_.clear(); 160bbc7648d285f67b898d24d307b011fb676ba6643Chad Brubaker} 161bbc7648d285f67b898d24d307b011fb676ba6643Chad Brubaker 162489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willdenbool AuthTokenTable::IsSupersededBySomeEntry(const Entry& entry) { 163489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden return std::any_of(entries_.begin(), entries_.end(), 164489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden [&](Entry& e) { return e.Supersedes(entry); }); 165489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden} 166489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden 167489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willdenvoid AuthTokenTable::MarkCompleted(const keymaster_operation_handle_t op_handle) { 168489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden auto found = find_if(entries_, [&](Entry& e) { return e.token()->challenge == op_handle; }); 169489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden if (found == entries_.end()) 170489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden return; 171489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden 172489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden assert(!IsSupersededBySomeEntry(*found)); 173489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden found->mark_completed(); 174489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden 175489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden if (IsSupersededBySomeEntry(*found)) 176489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden entries_.erase(found); 177489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden} 178489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden 179489dfe1f3dbdb9377debce826e37294d48fe6754Shawn WilldenAuthTokenTable::Entry::Entry(const hw_auth_token_t* token, time_t current_time) 180489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden : token_(token), time_received_(current_time), last_use_(current_time), 181489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden operation_completed_(token_->challenge == 0) { 182489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden} 183489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden 184489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willdenuint32_t AuthTokenTable::Entry::timestamp_host_order() const { 185489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden return ntoh(token_->timestamp); 186489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden} 187489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden 188489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willdenhw_authenticator_type_t AuthTokenTable::Entry::authenticator_type() const { 189489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden hw_authenticator_type_t result = static_cast<hw_authenticator_type_t>( 190489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden ntoh(static_cast<uint32_t>(token_->authenticator_type))); 191489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden return result; 192489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden} 193489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden 194489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willdenbool AuthTokenTable::Entry::SatisfiesAuth(const std::vector<uint64_t>& sids, 195489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden hw_authenticator_type_t auth_type) { 196489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden for (auto sid : sids) 197489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden if ((sid == token_->authenticator_id) || 198489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden (sid == token_->user_id && (auth_type & authenticator_type()) != 0)) 199489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden return true; 200489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden return false; 201489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden} 202489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden 203489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willdenvoid AuthTokenTable::Entry::UpdateLastUse(time_t time) { 204489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden this->last_use_ = time; 205489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden} 206489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden 207489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willdenbool AuthTokenTable::Entry::Supersedes(const Entry& entry) const { 208489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden if (!entry.completed()) 209489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden return false; 210489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden 211489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden return (token_->user_id == entry.token_->user_id && 212489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden token_->authenticator_type == entry.token_->authenticator_type && 213489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden token_->authenticator_type == entry.token_->authenticator_type && 214489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden timestamp_host_order() > entry.timestamp_host_order()); 215489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden} 216489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden 217489dfe1f3dbdb9377debce826e37294d48fe6754Shawn Willden} // namespace keymaster 218