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 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: 35 StdoutLogger() { set_instance(this); } 36 37 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 65 TEST(AuthTokenTableTest, Create) { 66 AuthTokenTable table; 67 } 68 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 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. 93 static time_t monotonic_clock() { 94 static time_t time = 0; 95 return time++; 96 } 97 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 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 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 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 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 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 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 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 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 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