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