| Shawn Willden | 489dfe1 | 2015-03-17 10:13:27 -0600 | [diff] [blame] | 1 | /* | 
|  | 2 | * Copyright (C) 2015 The Android Open Source Project | 
|  | 3 | * | 
|  | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | 5 | * you may not use this file except in compliance with the License. | 
|  | 6 | * You may obtain a copy of the License at | 
|  | 7 | * | 
|  | 8 | *      http://www.apache.org/licenses/LICENSE-2.0 | 
|  | 9 | * | 
|  | 10 | * Unless required by applicable law or agreed to in writing, software | 
|  | 11 | * distributed under the License is distributed on an "AS IS" BASIS, | 
|  | 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | 13 | * See the License for the specific language governing permissions and | 
|  | 14 | * limitations under the License. | 
|  | 15 | */ | 
|  | 16 |  | 
| Rubin Xu | ce99f58 | 2017-10-12 10:50:11 +0100 | [diff] [blame] | 17 | #define LOG_TAG "keystore" | 
|  | 18 |  | 
| Shawn Willden | 489dfe1 | 2015-03-17 10:13:27 -0600 | [diff] [blame] | 19 | #include "auth_token_table.h" | 
|  | 20 |  | 
|  | 21 | #include <assert.h> | 
|  | 22 | #include <time.h> | 
|  | 23 |  | 
|  | 24 | #include <algorithm> | 
|  | 25 |  | 
| Logan Chien | cdc813f | 2018-04-23 13:52:28 +0800 | [diff] [blame] | 26 | #include <log/log.h> | 
| Shawn Willden | 489dfe1 | 2015-03-17 10:13:27 -0600 | [diff] [blame] | 27 |  | 
| Janis Danisevskis | c7a9fa2 | 2016-10-13 18:43:45 +0100 | [diff] [blame] | 28 | namespace keystore { | 
|  | 29 |  | 
| Shawn Willden | d3ed3a2 | 2017-03-28 00:39:16 +0000 | [diff] [blame] | 30 | template <typename IntType, uint32_t byteOrder> struct choose_hton; | 
|  | 31 |  | 
|  | 32 | template <typename IntType> struct choose_hton<IntType, __ORDER_LITTLE_ENDIAN__> { | 
|  | 33 | inline static IntType hton(const IntType& value) { | 
|  | 34 | IntType result = 0; | 
|  | 35 | const unsigned char* inbytes = reinterpret_cast<const unsigned char*>(&value); | 
|  | 36 | unsigned char* outbytes = reinterpret_cast<unsigned char*>(&result); | 
|  | 37 | for (int i = sizeof(IntType) - 1; i >= 0; --i) { | 
|  | 38 | *(outbytes++) = inbytes[i]; | 
|  | 39 | } | 
|  | 40 | return result; | 
|  | 41 | } | 
|  | 42 | }; | 
|  | 43 |  | 
|  | 44 | template <typename IntType> struct choose_hton<IntType, __ORDER_BIG_ENDIAN__> { | 
|  | 45 | inline static IntType hton(const IntType& value) { return value; } | 
|  | 46 | }; | 
|  | 47 |  | 
|  | 48 | template <typename IntType> inline IntType hton(const IntType& value) { | 
|  | 49 | return choose_hton<IntType, __BYTE_ORDER__>::hton(value); | 
|  | 50 | } | 
|  | 51 |  | 
|  | 52 | template <typename IntType> inline IntType ntoh(const IntType& value) { | 
|  | 53 | // same operation and hton | 
|  | 54 | return choose_hton<IntType, __BYTE_ORDER__>::hton(value); | 
|  | 55 | } | 
|  | 56 |  | 
| Shawn Willden | 489dfe1 | 2015-03-17 10:13:27 -0600 | [diff] [blame] | 57 | // | 
|  | 58 | // Some trivial template wrappers around std algorithms, so they take containers not ranges. | 
|  | 59 | // | 
|  | 60 | template <typename Container, typename Predicate> | 
|  | 61 | typename Container::iterator find_if(Container& container, Predicate pred) { | 
|  | 62 | return std::find_if(container.begin(), container.end(), pred); | 
|  | 63 | } | 
|  | 64 |  | 
|  | 65 | template <typename Container, typename Predicate> | 
|  | 66 | typename Container::iterator remove_if(Container& container, Predicate pred) { | 
|  | 67 | return std::remove_if(container.begin(), container.end(), pred); | 
|  | 68 | } | 
|  | 69 |  | 
|  | 70 | template <typename Container> typename Container::iterator min_element(Container& container) { | 
|  | 71 | return std::min_element(container.begin(), container.end()); | 
|  | 72 | } | 
|  | 73 |  | 
|  | 74 | time_t clock_gettime_raw() { | 
|  | 75 | struct timespec time; | 
|  | 76 | clock_gettime(CLOCK_MONOTONIC_RAW, &time); | 
|  | 77 | return time.tv_sec; | 
|  | 78 | } | 
|  | 79 |  | 
| Shawn Willden | 0329a82 | 2017-12-04 13:55:14 -0700 | [diff] [blame] | 80 | void AuthTokenTable::AddAuthenticationToken(HardwareAuthToken&& auth_token) { | 
| Janis Danisevskis | 8f737ad | 2017-11-21 12:30:15 -0800 | [diff] [blame] | 81 | Entry new_entry(std::move(auth_token), clock_function_()); | 
| Shawn Willden | 0329a82 | 2017-12-04 13:55:14 -0700 | [diff] [blame] | 82 | // STOPSHIP: debug only, to be removed | 
|  | 83 | ALOGD("AddAuthenticationToken: timestamp = %llu, time_received = %lld", | 
|  | 84 | static_cast<unsigned long long>(new_entry.token().timestamp), | 
|  | 85 | static_cast<long long>(new_entry.time_received())); | 
| Rubin Xu | ce99f58 | 2017-10-12 10:50:11 +0100 | [diff] [blame] | 86 |  | 
| Janis Danisevskis | ff3d7f4 | 2018-10-08 07:15:09 -0700 | [diff] [blame] | 87 | std::lock_guard<std::mutex> lock(entries_mutex_); | 
| Shawn Willden | 489dfe1 | 2015-03-17 10:13:27 -0600 | [diff] [blame] | 88 | RemoveEntriesSupersededBy(new_entry); | 
|  | 89 | if (entries_.size() >= max_entries_) { | 
| Janis Danisevskis | c7a9fa2 | 2016-10-13 18:43:45 +0100 | [diff] [blame] | 90 | ALOGW("Auth token table filled up; replacing oldest entry"); | 
| Shawn Willden | 489dfe1 | 2015-03-17 10:13:27 -0600 | [diff] [blame] | 91 | *min_element(entries_) = std::move(new_entry); | 
|  | 92 | } else { | 
|  | 93 | entries_.push_back(std::move(new_entry)); | 
|  | 94 | } | 
|  | 95 | } | 
|  | 96 |  | 
| Janis Danisevskis | c7a9fa2 | 2016-10-13 18:43:45 +0100 | [diff] [blame] | 97 | inline bool is_secret_key_operation(Algorithm algorithm, KeyPurpose purpose) { | 
| Shawn Willden | 0329a82 | 2017-12-04 13:55:14 -0700 | [diff] [blame] | 98 | if ((algorithm != Algorithm::RSA && algorithm != Algorithm::EC)) return true; | 
|  | 99 | if (purpose == KeyPurpose::SIGN || purpose == KeyPurpose::DECRYPT) return true; | 
| Shawn Willden | b2ffa42 | 2015-06-17 12:18:55 -0600 | [diff] [blame] | 100 | return false; | 
| Shawn Willden | 489dfe1 | 2015-03-17 10:13:27 -0600 | [diff] [blame] | 101 | } | 
|  | 102 |  | 
| Janis Danisevskis | c7a9fa2 | 2016-10-13 18:43:45 +0100 | [diff] [blame] | 103 | inline bool KeyRequiresAuthentication(const AuthorizationSet& key_info, KeyPurpose purpose) { | 
|  | 104 | auto algorithm = defaultOr(key_info.GetTagValue(TAG_ALGORITHM), Algorithm::AES); | 
|  | 105 | return is_secret_key_operation(algorithm, purpose) && | 
|  | 106 | key_info.find(Tag::NO_AUTH_REQUIRED) == -1; | 
| Shawn Willden | b2ffa42 | 2015-06-17 12:18:55 -0600 | [diff] [blame] | 107 | } | 
|  | 108 |  | 
| Janis Danisevskis | c7a9fa2 | 2016-10-13 18:43:45 +0100 | [diff] [blame] | 109 | inline bool KeyRequiresAuthPerOperation(const AuthorizationSet& key_info, KeyPurpose purpose) { | 
|  | 110 | auto algorithm = defaultOr(key_info.GetTagValue(TAG_ALGORITHM), Algorithm::AES); | 
|  | 111 | return is_secret_key_operation(algorithm, purpose) && key_info.find(Tag::AUTH_TIMEOUT) == -1; | 
| Shawn Willden | 489dfe1 | 2015-03-17 10:13:27 -0600 | [diff] [blame] | 112 | } | 
|  | 113 |  | 
| Janis Danisevskis | ff3d7f4 | 2018-10-08 07:15:09 -0700 | [diff] [blame] | 114 | std::tuple<AuthTokenTable::Error, HardwareAuthToken> | 
|  | 115 | AuthTokenTable::FindAuthorization(const AuthorizationSet& key_info, KeyPurpose purpose, | 
|  | 116 | uint64_t op_handle) { | 
|  | 117 |  | 
|  | 118 | std::lock_guard<std::mutex> lock(entries_mutex_); | 
|  | 119 |  | 
|  | 120 | if (!KeyRequiresAuthentication(key_info, purpose)) return {AUTH_NOT_REQUIRED, {}}; | 
| Shawn Willden | 489dfe1 | 2015-03-17 10:13:27 -0600 | [diff] [blame] | 121 |  | 
| Janis Danisevskis | c7a9fa2 | 2016-10-13 18:43:45 +0100 | [diff] [blame] | 122 | auto auth_type = | 
|  | 123 | defaultOr(key_info.GetTagValue(TAG_USER_AUTH_TYPE), HardwareAuthenticatorType::NONE); | 
| Shawn Willden | 489dfe1 | 2015-03-17 10:13:27 -0600 | [diff] [blame] | 124 |  | 
|  | 125 | std::vector<uint64_t> key_sids; | 
|  | 126 | ExtractSids(key_info, &key_sids); | 
|  | 127 |  | 
| Shawn Willden | b2ffa42 | 2015-06-17 12:18:55 -0600 | [diff] [blame] | 128 | if (KeyRequiresAuthPerOperation(key_info, purpose)) | 
| Janis Danisevskis | ff3d7f4 | 2018-10-08 07:15:09 -0700 | [diff] [blame] | 129 | return FindAuthPerOpAuthorization(key_sids, auth_type, op_handle); | 
| Shawn Willden | 489dfe1 | 2015-03-17 10:13:27 -0600 | [diff] [blame] | 130 | else | 
| Janis Danisevskis | ff3d7f4 | 2018-10-08 07:15:09 -0700 | [diff] [blame] | 131 | return FindTimedAuthorization(key_sids, auth_type, key_info); | 
| Shawn Willden | 489dfe1 | 2015-03-17 10:13:27 -0600 | [diff] [blame] | 132 | } | 
|  | 133 |  | 
| Janis Danisevskis | ff3d7f4 | 2018-10-08 07:15:09 -0700 | [diff] [blame] | 134 | std::tuple<AuthTokenTable::Error, HardwareAuthToken> AuthTokenTable::FindAuthPerOpAuthorization( | 
|  | 135 | const std::vector<uint64_t>& sids, HardwareAuthenticatorType auth_type, uint64_t op_handle) { | 
|  | 136 | if (op_handle == 0) return {OP_HANDLE_REQUIRED, {}}; | 
| Shawn Willden | 489dfe1 | 2015-03-17 10:13:27 -0600 | [diff] [blame] | 137 |  | 
|  | 138 | auto matching_op = find_if( | 
| Janis Danisevskis | 8f737ad | 2017-11-21 12:30:15 -0800 | [diff] [blame] | 139 | entries_, [&](Entry& e) { return e.token().challenge == op_handle && !e.completed(); }); | 
| Shawn Willden | 489dfe1 | 2015-03-17 10:13:27 -0600 | [diff] [blame] | 140 |  | 
| Janis Danisevskis | ff3d7f4 | 2018-10-08 07:15:09 -0700 | [diff] [blame] | 141 | if (matching_op == entries_.end()) return {AUTH_TOKEN_NOT_FOUND, {}}; | 
| Shawn Willden | 489dfe1 | 2015-03-17 10:13:27 -0600 | [diff] [blame] | 142 |  | 
| Janis Danisevskis | ff3d7f4 | 2018-10-08 07:15:09 -0700 | [diff] [blame] | 143 | if (!matching_op->SatisfiesAuth(sids, auth_type)) return {AUTH_TOKEN_WRONG_SID, {}}; | 
| Shawn Willden | 489dfe1 | 2015-03-17 10:13:27 -0600 | [diff] [blame] | 144 |  | 
| Janis Danisevskis | ff3d7f4 | 2018-10-08 07:15:09 -0700 | [diff] [blame] | 145 | return {OK, matching_op->token()}; | 
| Shawn Willden | 489dfe1 | 2015-03-17 10:13:27 -0600 | [diff] [blame] | 146 | } | 
|  | 147 |  | 
| Janis Danisevskis | ff3d7f4 | 2018-10-08 07:15:09 -0700 | [diff] [blame] | 148 | std::tuple<AuthTokenTable::Error, HardwareAuthToken> | 
|  | 149 | AuthTokenTable::FindTimedAuthorization(const std::vector<uint64_t>& sids, | 
|  | 150 | HardwareAuthenticatorType auth_type, | 
|  | 151 | const AuthorizationSet& key_info) { | 
| Yi Kong | e353f25 | 2018-07-30 01:38:39 -0700 | [diff] [blame] | 152 | Entry* newest_match = nullptr; | 
| Shawn Willden | 489dfe1 | 2015-03-17 10:13:27 -0600 | [diff] [blame] | 153 | for (auto& entry : entries_) | 
|  | 154 | if (entry.SatisfiesAuth(sids, auth_type) && entry.is_newer_than(newest_match)) | 
|  | 155 | newest_match = &entry; | 
|  | 156 |  | 
| Janis Danisevskis | ff3d7f4 | 2018-10-08 07:15:09 -0700 | [diff] [blame] | 157 | if (!newest_match) return {AUTH_TOKEN_NOT_FOUND, {}}; | 
| Shawn Willden | 489dfe1 | 2015-03-17 10:13:27 -0600 | [diff] [blame] | 158 |  | 
| Janis Danisevskis | c7a9fa2 | 2016-10-13 18:43:45 +0100 | [diff] [blame] | 159 | auto timeout = defaultOr(key_info.GetTagValue(TAG_AUTH_TIMEOUT), 0); | 
|  | 160 |  | 
| Shawn Willden | 489dfe1 | 2015-03-17 10:13:27 -0600 | [diff] [blame] | 161 | time_t now = clock_function_(); | 
|  | 162 | if (static_cast<int64_t>(newest_match->time_received()) + timeout < static_cast<int64_t>(now)) | 
| Janis Danisevskis | ff3d7f4 | 2018-10-08 07:15:09 -0700 | [diff] [blame] | 163 | return {AUTH_TOKEN_EXPIRED, {}}; | 
| Shawn Willden | 489dfe1 | 2015-03-17 10:13:27 -0600 | [diff] [blame] | 164 |  | 
| Janis Danisevskis | c7a9fa2 | 2016-10-13 18:43:45 +0100 | [diff] [blame] | 165 | if (key_info.GetTagValue(TAG_ALLOW_WHILE_ON_BODY).isOk()) { | 
| Tucker Sylvestro | 0ab28b7 | 2016-08-05 18:02:47 -0400 | [diff] [blame] | 166 | if (static_cast<int64_t>(newest_match->time_received()) < | 
|  | 167 | static_cast<int64_t>(last_off_body_)) { | 
| Janis Danisevskis | ff3d7f4 | 2018-10-08 07:15:09 -0700 | [diff] [blame] | 168 | return {AUTH_TOKEN_EXPIRED, {}}; | 
| Tucker Sylvestro | 0ab28b7 | 2016-08-05 18:02:47 -0400 | [diff] [blame] | 169 | } | 
|  | 170 | } | 
|  | 171 |  | 
| Shawn Willden | 489dfe1 | 2015-03-17 10:13:27 -0600 | [diff] [blame] | 172 | newest_match->UpdateLastUse(now); | 
| Janis Danisevskis | ff3d7f4 | 2018-10-08 07:15:09 -0700 | [diff] [blame] | 173 | return {OK, newest_match->token()}; | 
| Shawn Willden | 489dfe1 | 2015-03-17 10:13:27 -0600 | [diff] [blame] | 174 | } | 
|  | 175 |  | 
| David Zeuthen | ab3e565 | 2019-10-28 13:32:48 -0400 | [diff] [blame] | 176 | std::tuple<AuthTokenTable::Error, HardwareAuthToken> | 
|  | 177 | AuthTokenTable::FindAuthorizationForCredstore(uint64_t challenge, uint64_t secureUserId, | 
|  | 178 | int64_t authTokenMaxAgeMillis) { | 
|  | 179 | std::vector<uint64_t> sids = {secureUserId}; | 
|  | 180 | HardwareAuthenticatorType auth_type = HardwareAuthenticatorType::ANY; | 
|  | 181 |  | 
|  | 182 | time_t now = clock_function_(); | 
|  | 183 |  | 
|  | 184 | // challenge-based - the authToken has to contain the given challenge. | 
|  | 185 | if (challenge != 0) { | 
|  | 186 | auto matching_op = find_if( | 
|  | 187 | entries_, [&](Entry& e) { return e.token().challenge == challenge && !e.completed(); }); | 
|  | 188 | if (matching_op == entries_.end()) { | 
|  | 189 | return {AUTH_TOKEN_NOT_FOUND, {}}; | 
|  | 190 | } | 
|  | 191 |  | 
|  | 192 | if (!matching_op->SatisfiesAuth(sids, auth_type)) { | 
|  | 193 | return {AUTH_TOKEN_WRONG_SID, {}}; | 
|  | 194 | } | 
|  | 195 |  | 
|  | 196 | if (authTokenMaxAgeMillis > 0) { | 
|  | 197 | if (static_cast<int64_t>(matching_op->time_received()) + authTokenMaxAgeMillis < | 
|  | 198 | static_cast<int64_t>(now)) { | 
|  | 199 | return {AUTH_TOKEN_EXPIRED, {}}; | 
|  | 200 | } | 
|  | 201 | } | 
|  | 202 |  | 
|  | 203 | return {OK, matching_op->token()}; | 
|  | 204 | } | 
|  | 205 |  | 
|  | 206 | // Otherwise, no challenge - any authToken younger than the specified maximum | 
|  | 207 | // age will do. | 
|  | 208 | Entry* newest_match = nullptr; | 
|  | 209 | for (auto& entry : entries_) { | 
|  | 210 | if (entry.SatisfiesAuth(sids, auth_type) && entry.is_newer_than(newest_match)) { | 
|  | 211 | newest_match = &entry; | 
|  | 212 | } | 
|  | 213 | } | 
|  | 214 |  | 
|  | 215 | if (newest_match == nullptr) { | 
|  | 216 | return {AUTH_TOKEN_NOT_FOUND, {}}; | 
|  | 217 | } | 
|  | 218 |  | 
|  | 219 | if (authTokenMaxAgeMillis > 0) { | 
|  | 220 | if (static_cast<int64_t>(newest_match->time_received()) + authTokenMaxAgeMillis < | 
|  | 221 | static_cast<int64_t>(now)) { | 
|  | 222 | return {AUTH_TOKEN_EXPIRED, {}}; | 
|  | 223 | } | 
|  | 224 | } | 
|  | 225 |  | 
|  | 226 | newest_match->UpdateLastUse(now); | 
|  | 227 | return {OK, newest_match->token()}; | 
|  | 228 | } | 
|  | 229 |  | 
| Shawn Willden | 489dfe1 | 2015-03-17 10:13:27 -0600 | [diff] [blame] | 230 | void AuthTokenTable::ExtractSids(const AuthorizationSet& key_info, std::vector<uint64_t>* sids) { | 
|  | 231 | assert(sids); | 
|  | 232 | for (auto& param : key_info) | 
| Janis Danisevskis | c7a9fa2 | 2016-10-13 18:43:45 +0100 | [diff] [blame] | 233 | if (param.tag == Tag::USER_SECURE_ID) | 
|  | 234 | sids->push_back(authorizationValue(TAG_USER_SECURE_ID, param).value()); | 
| Shawn Willden | 489dfe1 | 2015-03-17 10:13:27 -0600 | [diff] [blame] | 235 | } | 
|  | 236 |  | 
|  | 237 | void AuthTokenTable::RemoveEntriesSupersededBy(const Entry& entry) { | 
|  | 238 | entries_.erase(remove_if(entries_, [&](Entry& e) { return entry.Supersedes(e); }), | 
|  | 239 | entries_.end()); | 
|  | 240 | } | 
|  | 241 |  | 
| Tucker Sylvestro | 0ab28b7 | 2016-08-05 18:02:47 -0400 | [diff] [blame] | 242 | void AuthTokenTable::onDeviceOffBody() { | 
|  | 243 | last_off_body_ = clock_function_(); | 
|  | 244 | } | 
|  | 245 |  | 
| Chad Brubaker | bbc7648 | 2015-04-16 15:16:44 -0700 | [diff] [blame] | 246 | void AuthTokenTable::Clear() { | 
| Janis Danisevskis | ff3d7f4 | 2018-10-08 07:15:09 -0700 | [diff] [blame] | 247 | std::lock_guard<std::mutex> lock(entries_mutex_); | 
|  | 248 |  | 
| Chad Brubaker | bbc7648 | 2015-04-16 15:16:44 -0700 | [diff] [blame] | 249 | entries_.clear(); | 
|  | 250 | } | 
|  | 251 |  | 
| Janis Danisevskis | ff3d7f4 | 2018-10-08 07:15:09 -0700 | [diff] [blame] | 252 | size_t AuthTokenTable::size() const { | 
|  | 253 | std::lock_guard<std::mutex> lock(entries_mutex_); | 
|  | 254 | return entries_.size(); | 
|  | 255 | } | 
|  | 256 |  | 
| Shawn Willden | 489dfe1 | 2015-03-17 10:13:27 -0600 | [diff] [blame] | 257 | bool AuthTokenTable::IsSupersededBySomeEntry(const Entry& entry) { | 
|  | 258 | return std::any_of(entries_.begin(), entries_.end(), | 
|  | 259 | [&](Entry& e) { return e.Supersedes(entry); }); | 
|  | 260 | } | 
|  | 261 |  | 
| Janis Danisevskis | c7a9fa2 | 2016-10-13 18:43:45 +0100 | [diff] [blame] | 262 | void AuthTokenTable::MarkCompleted(const uint64_t op_handle) { | 
| Janis Danisevskis | ff3d7f4 | 2018-10-08 07:15:09 -0700 | [diff] [blame] | 263 | std::lock_guard<std::mutex> lock(entries_mutex_); | 
|  | 264 |  | 
| Janis Danisevskis | 8f737ad | 2017-11-21 12:30:15 -0800 | [diff] [blame] | 265 | auto found = find_if(entries_, [&](Entry& e) { return e.token().challenge == op_handle; }); | 
| Janis Danisevskis | c7a9fa2 | 2016-10-13 18:43:45 +0100 | [diff] [blame] | 266 | if (found == entries_.end()) return; | 
| Shawn Willden | 489dfe1 | 2015-03-17 10:13:27 -0600 | [diff] [blame] | 267 |  | 
|  | 268 | assert(!IsSupersededBySomeEntry(*found)); | 
|  | 269 | found->mark_completed(); | 
|  | 270 |  | 
| Janis Danisevskis | c7a9fa2 | 2016-10-13 18:43:45 +0100 | [diff] [blame] | 271 | if (IsSupersededBySomeEntry(*found)) entries_.erase(found); | 
| Shawn Willden | 489dfe1 | 2015-03-17 10:13:27 -0600 | [diff] [blame] | 272 | } | 
|  | 273 |  | 
| Shawn Willden | 0329a82 | 2017-12-04 13:55:14 -0700 | [diff] [blame] | 274 | AuthTokenTable::Entry::Entry(HardwareAuthToken&& token, time_t current_time) | 
| Janis Danisevskis | 8f737ad | 2017-11-21 12:30:15 -0800 | [diff] [blame] | 275 | : token_(std::move(token)), time_received_(current_time), last_use_(current_time), | 
| Shawn Willden | 0329a82 | 2017-12-04 13:55:14 -0700 | [diff] [blame] | 276 | operation_completed_(token_.challenge == 0) {} | 
| Shawn Willden | 489dfe1 | 2015-03-17 10:13:27 -0600 | [diff] [blame] | 277 |  | 
|  | 278 | bool AuthTokenTable::Entry::SatisfiesAuth(const std::vector<uint64_t>& sids, | 
| Janis Danisevskis | c7a9fa2 | 2016-10-13 18:43:45 +0100 | [diff] [blame] | 279 | HardwareAuthenticatorType auth_type) { | 
| Shawn Willden | 0329a82 | 2017-12-04 13:55:14 -0700 | [diff] [blame] | 280 | for (auto sid : sids) { | 
|  | 281 | if (SatisfiesAuth(sid, auth_type)) return true; | 
|  | 282 | } | 
| Shawn Willden | 489dfe1 | 2015-03-17 10:13:27 -0600 | [diff] [blame] | 283 | return false; | 
|  | 284 | } | 
|  | 285 |  | 
|  | 286 | void AuthTokenTable::Entry::UpdateLastUse(time_t time) { | 
|  | 287 | this->last_use_ = time; | 
|  | 288 | } | 
|  | 289 |  | 
|  | 290 | bool AuthTokenTable::Entry::Supersedes(const Entry& entry) const { | 
| Janis Danisevskis | c7a9fa2 | 2016-10-13 18:43:45 +0100 | [diff] [blame] | 291 | if (!entry.completed()) return false; | 
| Shawn Willden | 489dfe1 | 2015-03-17 10:13:27 -0600 | [diff] [blame] | 292 |  | 
| Shawn Willden | 0329a82 | 2017-12-04 13:55:14 -0700 | [diff] [blame] | 293 | return (token_.userId == entry.token_.userId && | 
|  | 294 | token_.authenticatorType == entry.token_.authenticatorType && | 
|  | 295 | token_.authenticatorId == entry.token_.authenticatorId && is_newer_than(&entry)); | 
| Shawn Willden | 489dfe1 | 2015-03-17 10:13:27 -0600 | [diff] [blame] | 296 | } | 
|  | 297 |  | 
| Shawn Willden | 0329a82 | 2017-12-04 13:55:14 -0700 | [diff] [blame] | 298 | }  // namespace keystore |