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