• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <gtest/gtest.h>
18 
19 #include <endian.h>
20 #include <keymaster/logger.h>
21 
22 #include "../auth_token_table.h"
23 
24 using std::vector;
25 
26 namespace keystore {
27 namespace test {
28 
29 class StdoutLogger : public ::keymaster::Logger {
30   public:
StdoutLogger()31     StdoutLogger() { set_instance(this); }
32 
log_msg(LogLevel level,const char * fmt,va_list args) const33     int log_msg(LogLevel level, const char* fmt, va_list args) const {
34         int output_len = 0;
35         switch (level) {
36         case DEBUG_LVL:
37             output_len = printf("DEBUG: ");
38             break;
39         case INFO_LVL:
40             output_len = printf("INFO: ");
41             break;
42         case WARNING_LVL:
43             output_len = printf("WARNING: ");
44             break;
45         case ERROR_LVL:
46             output_len = printf("ERROR: ");
47             break;
48         case SEVERE_LVL:
49             output_len = printf("SEVERE: ");
50             break;
51         }
52 
53         output_len += vprintf(fmt, args);
54         output_len += printf("\n");
55         return output_len;
56     }
57 };
58 
59 StdoutLogger logger;
60 
TEST(AuthTokenTableTest,Create)61 TEST(AuthTokenTableTest, Create) {
62     AuthTokenTable table;
63 }
64 
make_token(uint64_t rsid,uint64_t ssid=0,uint64_t challenge=0,uint64_t timestamp=0)65 static HardwareAuthToken make_token(uint64_t rsid, uint64_t ssid = 0, uint64_t challenge = 0,
66                                     uint64_t timestamp = 0) {
67     HardwareAuthToken token;
68     token.userId = rsid;
69     token.authenticatorId = ssid;
70     token.authenticatorType = HardwareAuthenticatorType::PASSWORD;
71     token.challenge = challenge;
72     token.timestamp = timestamp;
73     return token;
74 }
75 
make_set(uint64_t rsid,uint32_t timeout=10000)76 static AuthorizationSet make_set(uint64_t rsid, uint32_t timeout = 10000) {
77     AuthorizationSetBuilder builder;
78     builder.Authorization(TAG_USER_AUTH_TYPE, HardwareAuthenticatorType::PASSWORD)
79         .Authorization(TAG_USER_SECURE_ID, rsid);
80     // Use timeout == 0 to indicate tags that require auth per operation.
81     if (timeout != 0) builder.Authorization(TAG_AUTH_TIMEOUT, timeout);
82     return builder;
83 }
84 
85 // Tests obviously run so fast that a real-time clock with a one-second granularity rarely changes
86 // output during a test run.  This test clock "ticks" one second every time it's called.
monotonic_clock()87 static time_t monotonic_clock() {
88     static time_t time = 0;
89     return time++;
90 }
91 
TEST(AuthTokenTableTest,SimpleAddAndFindTokens)92 TEST(AuthTokenTableTest, SimpleAddAndFindTokens) {
93     AuthTokenTable table;
94 
95     table.AddAuthenticationToken(make_token(1, 2));
96     table.AddAuthenticationToken(make_token(3, 4));
97     EXPECT_EQ(2U, table.size());
98 
99     const HardwareAuthToken* found;
100 
101     ASSERT_EQ(AuthTokenTable::OK,
102               table.FindAuthorization(make_set(1), KeyPurpose::SIGN, 0, &found));
103     EXPECT_EQ(1U, found->userId);
104     EXPECT_EQ(2U, found->authenticatorId);
105 
106     ASSERT_EQ(AuthTokenTable::OK,
107               table.FindAuthorization(make_set(2), KeyPurpose::SIGN, 0, &found));
108     EXPECT_EQ(1U, found->userId);
109     EXPECT_EQ(2U, found->authenticatorId);
110 
111     ASSERT_EQ(AuthTokenTable::OK,
112               table.FindAuthorization(make_set(3), KeyPurpose::SIGN, 0, &found));
113     EXPECT_EQ(3U, found->userId);
114     EXPECT_EQ(4U, found->authenticatorId);
115 
116     ASSERT_EQ(AuthTokenTable::OK,
117               table.FindAuthorization(make_set(4), KeyPurpose::SIGN, 0, &found));
118     EXPECT_EQ(3U, found->userId);
119     EXPECT_EQ(4U, found->authenticatorId);
120 
121     ASSERT_EQ(AuthTokenTable::AUTH_TOKEN_NOT_FOUND,
122               table.FindAuthorization(make_set(5), KeyPurpose::SIGN, 0, &found));
123 }
124 
TEST(AuthTokenTableTest,FlushTable)125 TEST(AuthTokenTableTest, FlushTable) {
126     AuthTokenTable table(3, monotonic_clock);
127 
128     table.AddAuthenticationToken(make_token(1));
129     table.AddAuthenticationToken(make_token(2));
130     table.AddAuthenticationToken(make_token(3));
131 
132     const HardwareAuthToken* found;
133 
134     // All three should be in the table.
135     EXPECT_EQ(3U, table.size());
136     EXPECT_EQ(AuthTokenTable::OK,
137               table.FindAuthorization(make_set(1), KeyPurpose::SIGN, 0, &found));
138     EXPECT_EQ(AuthTokenTable::OK,
139               table.FindAuthorization(make_set(2), KeyPurpose::SIGN, 0, &found));
140     EXPECT_EQ(AuthTokenTable::OK,
141               table.FindAuthorization(make_set(3), KeyPurpose::SIGN, 0, &found));
142 
143     table.Clear();
144     EXPECT_EQ(0U, table.size());
145 }
146 
TEST(AuthTokenTableTest,TableOverflow)147 TEST(AuthTokenTableTest, TableOverflow) {
148     AuthTokenTable table(3, monotonic_clock);
149 
150     table.AddAuthenticationToken(make_token(1));
151     table.AddAuthenticationToken(make_token(2));
152     table.AddAuthenticationToken(make_token(3));
153 
154     const HardwareAuthToken* found;
155 
156     // All three should be in the table.
157     EXPECT_EQ(3U, table.size());
158     EXPECT_EQ(AuthTokenTable::OK,
159               table.FindAuthorization(make_set(1), KeyPurpose::SIGN, 0, &found));
160     EXPECT_EQ(AuthTokenTable::OK,
161               table.FindAuthorization(make_set(2), KeyPurpose::SIGN, 0, &found));
162     EXPECT_EQ(AuthTokenTable::OK,
163               table.FindAuthorization(make_set(3), KeyPurpose::SIGN, 0, &found));
164 
165     table.AddAuthenticationToken(make_token(4));
166 
167     // Oldest should be gone.
168     EXPECT_EQ(3U, table.size());
169     EXPECT_EQ(AuthTokenTable::AUTH_TOKEN_NOT_FOUND,
170               table.FindAuthorization(make_set(1), KeyPurpose::SIGN, 0, &found));
171 
172     // Others should be there, including the new one (4).  Search for it first, then the others, so
173     // 4 becomes the least recently used.
174     EXPECT_EQ(AuthTokenTable::OK,
175               table.FindAuthorization(make_set(4), KeyPurpose::SIGN, 0, &found));
176     EXPECT_EQ(AuthTokenTable::OK,
177               table.FindAuthorization(make_set(2), KeyPurpose::SIGN, 0, &found));
178     EXPECT_EQ(AuthTokenTable::OK,
179               table.FindAuthorization(make_set(3), KeyPurpose::SIGN, 0, &found));
180 
181     table.AddAuthenticationToken(make_token(5));
182 
183     // 5 should have replaced 4.
184     EXPECT_EQ(3U, table.size());
185     EXPECT_EQ(AuthTokenTable::AUTH_TOKEN_NOT_FOUND,
186               table.FindAuthorization(make_set(4), KeyPurpose::SIGN, 0, &found));
187     EXPECT_EQ(AuthTokenTable::OK,
188               table.FindAuthorization(make_set(2), KeyPurpose::SIGN, 0, &found));
189     EXPECT_EQ(AuthTokenTable::OK,
190               table.FindAuthorization(make_set(5), KeyPurpose::SIGN, 0, &found));
191     EXPECT_EQ(AuthTokenTable::OK,
192               table.FindAuthorization(make_set(3), KeyPurpose::SIGN, 0, &found));
193 
194     table.AddAuthenticationToken(make_token(6));
195     table.AddAuthenticationToken(make_token(7));
196 
197     // 2 and 5 should be gone
198     EXPECT_EQ(3U, table.size());
199     EXPECT_EQ(AuthTokenTable::AUTH_TOKEN_NOT_FOUND,
200               table.FindAuthorization(make_set(2), KeyPurpose::SIGN, 0, &found));
201     EXPECT_EQ(AuthTokenTable::AUTH_TOKEN_NOT_FOUND,
202               table.FindAuthorization(make_set(5), KeyPurpose::SIGN, 0, &found));
203     EXPECT_EQ(AuthTokenTable::OK,
204               table.FindAuthorization(make_set(6), KeyPurpose::SIGN, 0, &found));
205     EXPECT_EQ(AuthTokenTable::OK,
206               table.FindAuthorization(make_set(7), KeyPurpose::SIGN, 0, &found));
207     EXPECT_EQ(AuthTokenTable::OK,
208               table.FindAuthorization(make_set(3), KeyPurpose::SIGN, 0, &found));
209 
210     table.AddAuthenticationToken(make_token(8));
211     table.AddAuthenticationToken(make_token(9));
212     table.AddAuthenticationToken(make_token(10));
213 
214     // Only the three most recent should be there.
215     EXPECT_EQ(3U, table.size());
216     EXPECT_EQ(AuthTokenTable::AUTH_TOKEN_NOT_FOUND,
217               table.FindAuthorization(make_set(1), KeyPurpose::SIGN, 0, &found));
218     EXPECT_EQ(AuthTokenTable::AUTH_TOKEN_NOT_FOUND,
219               table.FindAuthorization(make_set(2), KeyPurpose::SIGN, 0, &found));
220     EXPECT_EQ(AuthTokenTable::AUTH_TOKEN_NOT_FOUND,
221               table.FindAuthorization(make_set(3), KeyPurpose::SIGN, 0, &found));
222     EXPECT_EQ(AuthTokenTable::AUTH_TOKEN_NOT_FOUND,
223               table.FindAuthorization(make_set(4), KeyPurpose::SIGN, 0, &found));
224     EXPECT_EQ(AuthTokenTable::AUTH_TOKEN_NOT_FOUND,
225               table.FindAuthorization(make_set(5), KeyPurpose::SIGN, 0, &found));
226     EXPECT_EQ(AuthTokenTable::AUTH_TOKEN_NOT_FOUND,
227               table.FindAuthorization(make_set(6), KeyPurpose::SIGN, 0, &found));
228     EXPECT_EQ(AuthTokenTable::AUTH_TOKEN_NOT_FOUND,
229               table.FindAuthorization(make_set(7), KeyPurpose::SIGN, 0, &found));
230     EXPECT_EQ(AuthTokenTable::OK,
231               table.FindAuthorization(make_set(8), KeyPurpose::SIGN, 0, &found));
232     EXPECT_EQ(AuthTokenTable::OK,
233               table.FindAuthorization(make_set(9), KeyPurpose::SIGN, 0, &found));
234     EXPECT_EQ(AuthTokenTable::OK,
235               table.FindAuthorization(make_set(10), KeyPurpose::SIGN, 0, &found));
236 }
237 
TEST(AuthTokenTableTest,AuthenticationNotRequired)238 TEST(AuthTokenTableTest, AuthenticationNotRequired) {
239     AuthTokenTable table;
240     const HardwareAuthToken* found;
241 
242     EXPECT_EQ(AuthTokenTable::AUTH_NOT_REQUIRED,
243               table.FindAuthorization(AuthorizationSetBuilder().Authorization(TAG_NO_AUTH_REQUIRED),
244                                       KeyPurpose::SIGN, 0 /* no challenge */, &found));
245 }
246 
TEST(AuthTokenTableTest,OperationHandleNotFound)247 TEST(AuthTokenTableTest, OperationHandleNotFound) {
248     AuthTokenTable table;
249     const HardwareAuthToken* found;
250 
251     table.AddAuthenticationToken(make_token(1, 0, 1, 5));
252     EXPECT_EQ(AuthTokenTable::AUTH_TOKEN_NOT_FOUND,
253               table.FindAuthorization(make_set(1, 0 /* no timeout */), KeyPurpose::SIGN,
254                                       2 /* non-matching challenge */, &found));
255     EXPECT_EQ(AuthTokenTable::OK,
256               table.FindAuthorization(make_set(1, 0 /* no timeout */), KeyPurpose::SIGN,
257                                       1 /* matching challenge */, &found));
258     table.MarkCompleted(1);
259     EXPECT_EQ(AuthTokenTable::AUTH_TOKEN_NOT_FOUND,
260               table.FindAuthorization(make_set(1, 0 /* no timeout */), KeyPurpose::SIGN,
261                                       1 /* used challenge */, &found));
262 }
263 
TEST(AuthTokenTableTest,OperationHandleRequired)264 TEST(AuthTokenTableTest, OperationHandleRequired) {
265     AuthTokenTable table;
266     const HardwareAuthToken* found;
267 
268     table.AddAuthenticationToken(make_token(1));
269     EXPECT_EQ(AuthTokenTable::OP_HANDLE_REQUIRED,
270               table.FindAuthorization(make_set(1, 0 /* no timeout */), KeyPurpose::SIGN,
271                                       0 /* no op handle */, &found));
272 }
273 
TEST(AuthTokenTableTest,AuthSidChanged)274 TEST(AuthTokenTableTest, AuthSidChanged) {
275     AuthTokenTable table;
276     const HardwareAuthToken* found;
277 
278     table.AddAuthenticationToken(make_token(1, 3, /* op handle */ 1));
279     EXPECT_EQ(AuthTokenTable::AUTH_TOKEN_WRONG_SID,
280               table.FindAuthorization(make_set(2, 0 /* no timeout */), KeyPurpose::SIGN,
281                                       1 /* op handle */, &found));
282 }
283 
TEST(AuthTokenTableTest,TokenExpired)284 TEST(AuthTokenTableTest, TokenExpired) {
285     AuthTokenTable table(5, monotonic_clock);
286     const HardwareAuthToken* found;
287 
288     auto key_info = make_set(1, 5 /* five second timeout */);
289 
290     // monotonic_clock "ticks" one second each time it's called, which is once per request, so the
291     // sixth request should fail, since key_info says the key is good for five seconds.
292     //
293     // Note that this tests the decision of the AuthTokenTable to reject a request it knows is
294     // expired.  An additional check of the secure timestamp (in the token) will be made by
295     // keymaster when the found token is passed to it.
296     table.AddAuthenticationToken(make_token(1, 0));
297     EXPECT_EQ(AuthTokenTable::OK,
298               table.FindAuthorization(key_info, KeyPurpose::SIGN, 0 /* no op handle */, &found));
299     EXPECT_EQ(AuthTokenTable::OK,
300               table.FindAuthorization(key_info, KeyPurpose::SIGN, 0 /* no op handle */, &found));
301     EXPECT_EQ(AuthTokenTable::OK,
302               table.FindAuthorization(key_info, KeyPurpose::SIGN, 0 /* no op handle */, &found));
303     EXPECT_EQ(AuthTokenTable::OK,
304               table.FindAuthorization(key_info, KeyPurpose::SIGN, 0 /* no op handle */, &found));
305     EXPECT_EQ(AuthTokenTable::OK,
306               table.FindAuthorization(key_info, KeyPurpose::SIGN, 0 /* no op handle */, &found));
307     EXPECT_EQ(AuthTokenTable::AUTH_TOKEN_EXPIRED,
308               table.FindAuthorization(key_info, KeyPurpose::SIGN, 0 /* no op handle */, &found));
309 }
310 
TEST(AuthTokenTableTest,MarkNonexistentEntryCompleted)311 TEST(AuthTokenTableTest, MarkNonexistentEntryCompleted) {
312     AuthTokenTable table;
313     // Marking a nonexistent entry completed is ignored.  This test is mainly for code coverage.
314     table.MarkCompleted(1);
315 }
316 
TEST(AuthTokenTableTest,SupersededEntries)317 TEST(AuthTokenTableTest, SupersededEntries) {
318     AuthTokenTable table;
319     const HardwareAuthToken* found;
320 
321     // Add two identical tokens, without challenges.  The second should supersede the first, based
322     // on timestamp (fourth arg to make_token).
323     table.AddAuthenticationToken(make_token(1, 0, 0, 0));
324     table.AddAuthenticationToken(make_token(1, 0, 0, 1));
325     EXPECT_EQ(1U, table.size());
326     EXPECT_EQ(AuthTokenTable::OK,
327               table.FindAuthorization(make_set(1), KeyPurpose::SIGN, 0, &found));
328     EXPECT_EQ(1U, found->timestamp);
329 
330     // Add a third token, this with a different RSID.  It should not be superseded.
331     table.AddAuthenticationToken(make_token(2, 0, 0, 2));
332     EXPECT_EQ(2U, table.size());
333 
334     // Add two more, superseding each of the two in the table.
335     table.AddAuthenticationToken(make_token(1, 0, 0, 3));
336     table.AddAuthenticationToken(make_token(2, 0, 0, 4));
337     EXPECT_EQ(2U, table.size());
338     EXPECT_EQ(AuthTokenTable::OK,
339               table.FindAuthorization(make_set(1), KeyPurpose::SIGN, 0, &found));
340     EXPECT_EQ(3U, found->timestamp);
341     EXPECT_EQ(AuthTokenTable::OK,
342               table.FindAuthorization(make_set(2), KeyPurpose::SIGN, 0, &found));
343     EXPECT_EQ(4U, found->timestamp);
344 
345     // Add another, this one with a challenge value.  It should supersede the old one since it is
346     // newer, and matches other than the challenge.
347     table.AddAuthenticationToken(make_token(1, 0, 1, 5));
348     EXPECT_EQ(2U, table.size());
349 
350     // And another, also with a challenge.  Because of the challenge values, the one just added
351     // cannot be superseded.
352     table.AddAuthenticationToken(make_token(1, 0, 2, 6));
353     EXPECT_EQ(3U, table.size());
354 
355     // Should be able to find each of them, by specifying their challenge, with a key that is not
356     // timed (timed keys don't care about challenges).
357     EXPECT_EQ(AuthTokenTable::OK,
358               table.FindAuthorization(make_set(1, 0 /* no timeout*/), KeyPurpose::SIGN,
359                                       1 /* challenge */, &found));
360     EXPECT_EQ(5U, found->timestamp);
361     EXPECT_EQ(AuthTokenTable::OK,
362               table.FindAuthorization(make_set(1, 0 /* no timeout */), KeyPurpose::SIGN,
363                                       2 /* challenge */, &found));
364     EXPECT_EQ(6U, found->timestamp);
365 
366     // Add another, without a challenge, and the same timestamp as the last one.  This new one
367     // actually could be considered already-superseded, but the table doesn't handle that case,
368     // since it seems unlikely to occur in practice.
369     table.AddAuthenticationToken(make_token(1, 0, 0, 6));
370     EXPECT_EQ(4U, table.size());
371     EXPECT_EQ(AuthTokenTable::OK,
372               table.FindAuthorization(make_set(1), KeyPurpose::SIGN, 0 /* challenge */, &found));
373     EXPECT_EQ(6U, found->timestamp);
374 
375     // Add another without a challenge but an increased timestamp. This should supersede the
376     // previous challenge-free entry.
377     table.AddAuthenticationToken(make_token(1, 0, 0, 7));
378     EXPECT_EQ(4U, table.size());
379     EXPECT_EQ(AuthTokenTable::OK,
380               table.FindAuthorization(make_set(1, 0 /* no timeout */), KeyPurpose::SIGN,
381                                       2 /* challenge */, &found));
382     EXPECT_EQ(6U, found->timestamp);
383     EXPECT_EQ(AuthTokenTable::OK,
384               table.FindAuthorization(make_set(1), KeyPurpose::SIGN, 0 /* challenge */, &found));
385     EXPECT_EQ(7U, found->timestamp);
386 
387     // Mark the entry with challenge 2 as complete.  Since there's a newer challenge-free entry, the
388     // challenge entry will be superseded.
389     table.MarkCompleted(2);
390     EXPECT_EQ(3U, table.size());
391     EXPECT_EQ(AuthTokenTable::AUTH_TOKEN_NOT_FOUND,
392               table.FindAuthorization(make_set(1, 0 /* no timeout */), KeyPurpose::SIGN,
393                                       2 /* challenge */, &found));
394     EXPECT_EQ(AuthTokenTable::OK,
395               table.FindAuthorization(make_set(1), KeyPurpose::SIGN, 0 /* challenge */, &found));
396     EXPECT_EQ(7U, found->timestamp);
397 
398     // Add another SID 1 entry with a challenge.  It supersedes the previous SID 1 entry with
399     // no challenge (timestamp 7), but not the one with challenge 1 (timestamp 5).
400     table.AddAuthenticationToken(make_token(1, 0, 3, 8));
401     EXPECT_EQ(3U, table.size());
402 
403     EXPECT_EQ(AuthTokenTable::OK,
404               table.FindAuthorization(make_set(1, 0 /* no timeout */), KeyPurpose::SIGN,
405                                       1 /* challenge */, &found));
406     EXPECT_EQ(5U, found->timestamp);
407 
408     EXPECT_EQ(AuthTokenTable::OK,
409               table.FindAuthorization(make_set(1, 0 /* no timeout */), KeyPurpose::SIGN,
410                                       3 /* challenge */, &found));
411     EXPECT_EQ(8U, found->timestamp);
412 
413     // SID 2 entry is still there.
414     EXPECT_EQ(AuthTokenTable::OK,
415               table.FindAuthorization(make_set(2), KeyPurpose::SIGN, 0 /* challenge */, &found));
416     EXPECT_EQ(4U, found->timestamp);
417 
418     // Mark the entry with challenge 3 as complete.  Since the older challenge 1 entry is
419     // incomplete, nothing is superseded.
420     table.MarkCompleted(3);
421     EXPECT_EQ(3U, table.size());
422 
423     EXPECT_EQ(AuthTokenTable::OK,
424               table.FindAuthorization(make_set(1, 0 /* no timeout */), KeyPurpose::SIGN,
425                                       1 /* challenge */, &found));
426     EXPECT_EQ(5U, found->timestamp);
427 
428     EXPECT_EQ(AuthTokenTable::OK,
429               table.FindAuthorization(make_set(1), KeyPurpose::SIGN, 0 /* challenge */, &found));
430     EXPECT_EQ(8U, found->timestamp);
431 
432     // Mark the entry with challenge 1 as complete.  Since there's a newer one (with challenge 3,
433     // completed), the challenge 1 entry is superseded and removed.
434     table.MarkCompleted(1);
435     EXPECT_EQ(2U, table.size());
436     EXPECT_EQ(AuthTokenTable::AUTH_TOKEN_NOT_FOUND,
437               table.FindAuthorization(make_set(1, 0 /* no timeout */), KeyPurpose::SIGN,
438                                       1 /* challenge */, &found));
439     EXPECT_EQ(AuthTokenTable::OK,
440               table.FindAuthorization(make_set(1), KeyPurpose::SIGN, 0 /* challenge */, &found));
441     EXPECT_EQ(8U, found->timestamp);
442 }
443 
444 }  // namespace test
445 }  // namespace keystore
446