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