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: 31 StdoutLogger() { set_instance(this); } 32 33 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 61 TEST(AuthTokenTableTest, Create) { 62 AuthTokenTable table; 63 } 64 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 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. 87 static time_t monotonic_clock() { 88 static time_t time = 0; 89 return time++; 90 } 91 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 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 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 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 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 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 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 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 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 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