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