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