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 | |
| 17 | #include <memory> |
Janis Danisevskis | ff3d7f4 | 2018-10-08 07:15:09 -0700 | [diff] [blame] | 18 | #include <mutex> |
Shawn Willden | 489dfe1 | 2015-03-17 10:13:27 -0600 | [diff] [blame] | 19 | #include <vector> |
| 20 | |
Shawn Willden | 0329a82 | 2017-12-04 13:55:14 -0700 | [diff] [blame] | 21 | #include <keystore/keymaster_types.h> |
Shawn Willden | 489dfe1 | 2015-03-17 10:13:27 -0600 | [diff] [blame] | 22 | |
Shawn Willden | 6507c27 | 2016-01-05 22:51:48 -0700 | [diff] [blame] | 23 | #ifndef KEYSTORE_AUTH_TOKEN_TABLE_H_ |
| 24 | #define KEYSTORE_AUTH_TOKEN_TABLE_H_ |
Shawn Willden | 489dfe1 | 2015-03-17 10:13:27 -0600 | [diff] [blame] | 25 | |
Janis Danisevskis | c7a9fa2 | 2016-10-13 18:43:45 +0100 | [diff] [blame] | 26 | namespace keystore { |
| 27 | |
Shawn Willden | 0329a82 | 2017-12-04 13:55:14 -0700 | [diff] [blame] | 28 | using keymaster::HardwareAuthToken; |
Shawn Willden | 489dfe1 | 2015-03-17 10:13:27 -0600 | [diff] [blame] | 29 | |
| 30 | namespace test { |
| 31 | class AuthTokenTableTest; |
| 32 | } // namespace test |
| 33 | |
| 34 | time_t clock_gettime_raw(); |
| 35 | |
| 36 | /** |
| 37 | * AuthTokenTable manages a set of received authorization tokens and can provide the appropriate |
| 38 | * token for authorizing a key operation. |
| 39 | * |
| 40 | * To keep the table from growing without bound, superseded entries are removed when possible, and |
| 41 | * least recently used entries are automatically pruned when when the table exceeds a size limit, |
| 42 | * which is expected to be relatively small, since the implementation uses a linear search. |
| 43 | */ |
| 44 | class AuthTokenTable { |
| 45 | public: |
Chih-Hung Hsieh | d7791be | 2016-07-12 11:58:02 -0700 | [diff] [blame] | 46 | explicit AuthTokenTable(size_t max_entries = 32, time_t (*clock_function)() = clock_gettime_raw) |
Janis Danisevskis | c7a9fa2 | 2016-10-13 18:43:45 +0100 | [diff] [blame] | 47 | : max_entries_(max_entries), last_off_body_(clock_function()), |
| 48 | clock_function_(clock_function) {} |
Shawn Willden | 489dfe1 | 2015-03-17 10:13:27 -0600 | [diff] [blame] | 49 | |
| 50 | enum Error { |
| 51 | OK, |
| 52 | AUTH_NOT_REQUIRED = -1, |
| 53 | AUTH_TOKEN_EXPIRED = -2, // Found a matching token, but it's too old. |
| 54 | AUTH_TOKEN_WRONG_SID = -3, // Found a token with the right challenge, but wrong SID. This |
| 55 | // most likely indicates that the authenticator was updated |
| 56 | // (e.g. new fingerprint enrolled). |
| 57 | OP_HANDLE_REQUIRED = -4, // The key requires auth per use but op_handle was zero. |
| 58 | AUTH_TOKEN_NOT_FOUND = -5, |
| 59 | }; |
| 60 | |
| 61 | /** |
Shawn Willden | bb22a6c | 2017-12-06 19:35:28 -0700 | [diff] [blame] | 62 | * Add an authorization token to the table. |
Shawn Willden | 489dfe1 | 2015-03-17 10:13:27 -0600 | [diff] [blame] | 63 | */ |
Shawn Willden | 0329a82 | 2017-12-04 13:55:14 -0700 | [diff] [blame] | 64 | void AddAuthenticationToken(HardwareAuthToken&& auth_token); |
Shawn Willden | 489dfe1 | 2015-03-17 10:13:27 -0600 | [diff] [blame] | 65 | |
| 66 | /** |
| 67 | * Find an authorization token that authorizes the operation specified by \p operation_handle on |
| 68 | * a key with the characteristics specified in \p key_info. |
| 69 | * |
| 70 | * This method is O(n * m), where n is the number of KM_TAG_USER_SECURE_ID entries in key_info |
| 71 | * and m is the number of entries in the table. It could be made better, but n and m should |
| 72 | * always be small. |
| 73 | * |
| 74 | * The table retains ownership of the returned object. |
| 75 | */ |
Janis Danisevskis | ff3d7f4 | 2018-10-08 07:15:09 -0700 | [diff] [blame] | 76 | std::tuple<Error, HardwareAuthToken> FindAuthorization(const AuthorizationSet& key_info, |
| 77 | KeyPurpose purpose, uint64_t op_handle); |
Shawn Willden | 489dfe1 | 2015-03-17 10:13:27 -0600 | [diff] [blame] | 78 | |
| 79 | /** |
Shawn Willden | 489dfe1 | 2015-03-17 10:13:27 -0600 | [diff] [blame] | 80 | * Mark operation completed. This allows tokens associated with the specified operation to be |
| 81 | * superseded by new tokens. |
| 82 | */ |
Janis Danisevskis | c7a9fa2 | 2016-10-13 18:43:45 +0100 | [diff] [blame] | 83 | void MarkCompleted(const uint64_t op_handle); |
Shawn Willden | 489dfe1 | 2015-03-17 10:13:27 -0600 | [diff] [blame] | 84 | |
Tucker Sylvestro | 0ab28b7 | 2016-08-05 18:02:47 -0400 | [diff] [blame] | 85 | /** |
| 86 | * Update the last_off_body_ timestamp so that tokens which remain authorized only so long as |
| 87 | * the device stays on body can be revoked. |
| 88 | */ |
| 89 | void onDeviceOffBody(); |
| 90 | |
Chad Brubaker | bbc7648 | 2015-04-16 15:16:44 -0700 | [diff] [blame] | 91 | void Clear(); |
| 92 | |
Janis Danisevskis | ff3d7f4 | 2018-10-08 07:15:09 -0700 | [diff] [blame] | 93 | /** |
| 94 | * This function shall only be used for testing. |
| 95 | * |
| 96 | * BEWARE: Since the auth token table can be accessed |
| 97 | * concurrently, the size may be out dated as soon as it returns. |
| 98 | */ |
| 99 | size_t size() const; |
Shawn Willden | 489dfe1 | 2015-03-17 10:13:27 -0600 | [diff] [blame] | 100 | |
| 101 | private: |
| 102 | friend class AuthTokenTableTest; |
| 103 | |
| 104 | class Entry { |
| 105 | public: |
Shawn Willden | 0329a82 | 2017-12-04 13:55:14 -0700 | [diff] [blame] | 106 | Entry(HardwareAuthToken&& token, time_t current_time); |
Chih-Hung Hsieh | f73b198 | 2018-09-25 12:03:21 -0700 | [diff] [blame] | 107 | Entry(Entry&& entry) noexcept { *this = std::move(entry); } |
Shawn Willden | 489dfe1 | 2015-03-17 10:13:27 -0600 | [diff] [blame] | 108 | |
Chih-Hung Hsieh | f73b198 | 2018-09-25 12:03:21 -0700 | [diff] [blame] | 109 | void operator=(Entry&& rhs) noexcept { |
Shawn Willden | 489dfe1 | 2015-03-17 10:13:27 -0600 | [diff] [blame] | 110 | token_ = std::move(rhs.token_); |
| 111 | time_received_ = rhs.time_received_; |
| 112 | last_use_ = rhs.last_use_; |
| 113 | operation_completed_ = rhs.operation_completed_; |
| 114 | } |
| 115 | |
| 116 | bool operator<(const Entry& rhs) const { return last_use_ < rhs.last_use_; } |
| 117 | |
| 118 | void UpdateLastUse(time_t time); |
| 119 | |
| 120 | bool Supersedes(const Entry& entry) const; |
Janis Danisevskis | c7a9fa2 | 2016-10-13 18:43:45 +0100 | [diff] [blame] | 121 | bool SatisfiesAuth(const std::vector<uint64_t>& sids, HardwareAuthenticatorType auth_type); |
Shawn Willden | 489dfe1 | 2015-03-17 10:13:27 -0600 | [diff] [blame] | 122 | |
Rubin Xu | bfb01d9 | 2017-10-23 17:04:25 +0100 | [diff] [blame] | 123 | bool is_newer_than(const Entry* entry) const { |
Janis Danisevskis | c7a9fa2 | 2016-10-13 18:43:45 +0100 | [diff] [blame] | 124 | if (!entry) return true; |
Shawn Willden | 0329a82 | 2017-12-04 13:55:14 -0700 | [diff] [blame] | 125 | uint64_t ts = token_.timestamp; |
| 126 | uint64_t other_ts = entry->token_.timestamp; |
Rubin Xu | bfb01d9 | 2017-10-23 17:04:25 +0100 | [diff] [blame] | 127 | // Normally comparing timestamp_host_order alone is sufficient, but here is an |
| 128 | // additional hack to compare time_received value for some devices where their auth |
| 129 | // tokens contain fixed timestamp (due to the a stuck secure RTC on them) |
| 130 | return (ts > other_ts) || |
| 131 | ((ts == other_ts) && (time_received_ > entry->time_received_)); |
Shawn Willden | 489dfe1 | 2015-03-17 10:13:27 -0600 | [diff] [blame] | 132 | } |
| 133 | |
| 134 | void mark_completed() { operation_completed_ = true; } |
| 135 | |
Shawn Willden | 0329a82 | 2017-12-04 13:55:14 -0700 | [diff] [blame] | 136 | const HardwareAuthToken& token() const & { return token_; } |
Shawn Willden | 489dfe1 | 2015-03-17 10:13:27 -0600 | [diff] [blame] | 137 | time_t time_received() const { return time_received_; } |
| 138 | bool completed() const { return operation_completed_; } |
Shawn Willden | 489dfe1 | 2015-03-17 10:13:27 -0600 | [diff] [blame] | 139 | |
| 140 | private: |
Shawn Willden | 0329a82 | 2017-12-04 13:55:14 -0700 | [diff] [blame] | 141 | bool SatisfiesAuth(uint64_t sid, HardwareAuthenticatorType auth_type) const { |
| 142 | return (sid == token_.userId || sid == token_.authenticatorId) && |
| 143 | (auth_type & token_.authenticatorType) != 0; |
| 144 | } |
| 145 | |
| 146 | HardwareAuthToken token_; |
Shawn Willden | 489dfe1 | 2015-03-17 10:13:27 -0600 | [diff] [blame] | 147 | time_t time_received_; |
| 148 | time_t last_use_; |
| 149 | bool operation_completed_; |
| 150 | }; |
| 151 | |
Janis Danisevskis | ff3d7f4 | 2018-10-08 07:15:09 -0700 | [diff] [blame] | 152 | std::tuple<Error, HardwareAuthToken> |
| 153 | FindAuthPerOpAuthorization(const std::vector<uint64_t>& sids, |
| 154 | HardwareAuthenticatorType auth_type, uint64_t op_handle); |
| 155 | std::tuple<Error, HardwareAuthToken> FindTimedAuthorization(const std::vector<uint64_t>& sids, |
| 156 | HardwareAuthenticatorType auth_type, |
| 157 | const AuthorizationSet& key_info); |
Shawn Willden | 489dfe1 | 2015-03-17 10:13:27 -0600 | [diff] [blame] | 158 | void ExtractSids(const AuthorizationSet& key_info, std::vector<uint64_t>* sids); |
| 159 | void RemoveEntriesSupersededBy(const Entry& entry); |
| 160 | bool IsSupersededBySomeEntry(const Entry& entry); |
| 161 | |
Janis Danisevskis | ff3d7f4 | 2018-10-08 07:15:09 -0700 | [diff] [blame] | 162 | /** |
| 163 | * Guards the entries_ vector against concurrent modification. All public facing methods |
| 164 | * reading of modifying the vector must grab this mutex. |
| 165 | */ |
| 166 | mutable std::mutex entries_mutex_; |
Shawn Willden | 489dfe1 | 2015-03-17 10:13:27 -0600 | [diff] [blame] | 167 | std::vector<Entry> entries_; |
| 168 | size_t max_entries_; |
Tucker Sylvestro | 0ab28b7 | 2016-08-05 18:02:47 -0400 | [diff] [blame] | 169 | time_t last_off_body_; |
Shawn Willden | 489dfe1 | 2015-03-17 10:13:27 -0600 | [diff] [blame] | 170 | time_t (*clock_function_)(); |
| 171 | }; |
| 172 | |
Shawn Willden | 0329a82 | 2017-12-04 13:55:14 -0700 | [diff] [blame] | 173 | } // namespace keystore |
Shawn Willden | 489dfe1 | 2015-03-17 10:13:27 -0600 | [diff] [blame] | 174 | |
Shawn Willden | 6507c27 | 2016-01-05 22:51:48 -0700 | [diff] [blame] | 175 | #endif // KEYSTORE_AUTH_TOKEN_TABLE_H_ |