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 <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