Home | History | Annotate | Download | only in tests
      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