/* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define LOG_TAG "gatekeeper_hidl_hal_test" #include #include #include #include #include #include #include #include #include #include #include #include #include #include using ::android::hardware::hidl_string; using ::android::hardware::hidl_vec; using ::android::hardware::gatekeeper::V1_0::IGatekeeper; using ::android::hardware::gatekeeper::V1_0::GatekeeperResponse; using ::android::hardware::gatekeeper::V1_0::GatekeeperStatusCode; using ::android::hardware::Return; using ::android::hardware::Void; using ::android::sp; struct GatekeeperRequest { uint32_t uid; uint64_t challenge; hidl_vec curPwdHandle; hidl_vec curPwd; hidl_vec newPwd; GatekeeperRequest() : uid(0), challenge(0) {} }; // ASSERT_* macros generate return "void" internally // we have to use EXPECT_* if we return anything but "void" static const hw_auth_token_t *toAuthToken(GatekeeperResponse &rsp) { const hw_auth_token_t *auth_token = reinterpret_cast(rsp.data.data()); const size_t auth_token_size = rsp.data.size(); EXPECT_NE(nullptr, auth_token); EXPECT_EQ(sizeof(hw_auth_token_t), auth_token_size); if (auth_token != nullptr && auth_token_size >= sizeof(*auth_token)) { // these are in network order: translate to host uint32_t auth_type = ntohl(auth_token->authenticator_type); uint64_t auth_tstamp = ntohq(auth_token->timestamp); EXPECT_EQ(HW_AUTH_PASSWORD, auth_type); EXPECT_NE(UINT64_C(~0), auth_tstamp); EXPECT_EQ(HW_AUTH_TOKEN_VERSION, auth_token->version); // EXPECT_NE(UINT64_C(0), auth_token->authenticator_id); ALOGI("Authenticator ID: %016" PRIX64, auth_token->authenticator_id); EXPECT_NE(UINT32_C(0), auth_token->user_id); } return auth_token; } // The main test class for Gatekeeper HIDL HAL. class GatekeeperHidlTest : public ::testing::TestWithParam { protected: void setUid(uint32_t uid) { uid_ = uid; } void doEnroll(GatekeeperRequest &req, GatekeeperResponse &rsp) { while (true) { auto ret = gatekeeper_->enroll( uid_, req.curPwdHandle, req.curPwd, req.newPwd, [&rsp](const GatekeeperResponse &cbRsp) { rsp = cbRsp; }); ASSERT_TRUE(ret.isOk()); if (rsp.code != GatekeeperStatusCode::ERROR_RETRY_TIMEOUT) break; ALOGI("%s: got retry code; retrying in 1 sec", __func__); sleep(1); } } void doVerify(GatekeeperRequest &req, GatekeeperResponse &rsp) { while (true) { auto ret = gatekeeper_->verify( uid_, req.challenge, req.curPwdHandle, req.newPwd, [&rsp](const GatekeeperResponse &cb_rsp) { rsp = cb_rsp; }); ASSERT_TRUE(ret.isOk()); if (rsp.code != GatekeeperStatusCode::ERROR_RETRY_TIMEOUT) break; ALOGI("%s: got retry code; retrying in 1 sec", __func__); sleep(1); } } void doDeleteUser(GatekeeperResponse &rsp) { while (true) { auto ret = gatekeeper_->deleteUser( uid_, [&rsp](const GatekeeperResponse &cb_rsp) { rsp = cb_rsp; }); ASSERT_TRUE(ret.isOk()); if (rsp.code != GatekeeperStatusCode::ERROR_RETRY_TIMEOUT) break; ALOGI("%s: got retry code; retrying in 1 sec", __func__); sleep(1); } } void doDeleteAllUsers(GatekeeperResponse &rsp) { while (true) { auto ret = gatekeeper_->deleteAllUsers( [&rsp](const GatekeeperResponse &cb_rsp) { rsp = cb_rsp; }); ASSERT_TRUE(ret.isOk()); if (rsp.code != GatekeeperStatusCode::ERROR_RETRY_TIMEOUT) break; ALOGI("%s: got retry code; retrying in 1 sec", __func__); sleep(1); } } void generatePassword(hidl_vec &password, uint8_t seed) { password.resize(16); memset(password.data(), seed, password.size()); } void checkEnroll(GatekeeperResponse &rsp, bool expectSuccess) { if (expectSuccess) { EXPECT_EQ(GatekeeperStatusCode::STATUS_OK, rsp.code); EXPECT_NE(nullptr, rsp.data.data()); EXPECT_GT(rsp.data.size(), UINT32_C(0)); } else { EXPECT_EQ(GatekeeperStatusCode::ERROR_GENERAL_FAILURE, rsp.code); EXPECT_EQ(UINT32_C(0), rsp.data.size()); } } void checkVerify(GatekeeperResponse &rsp, uint64_t challenge, bool expectSuccess) { if (expectSuccess) { EXPECT_GE(rsp.code, GatekeeperStatusCode::STATUS_OK); EXPECT_LE(rsp.code, GatekeeperStatusCode::STATUS_REENROLL); const hw_auth_token_t *auth_token = toAuthToken(rsp); ASSERT_NE(nullptr, auth_token); EXPECT_EQ(challenge, auth_token->challenge); } else { EXPECT_EQ(GatekeeperStatusCode::ERROR_GENERAL_FAILURE, rsp.code); EXPECT_EQ(UINT32_C(0), rsp.data.size()); } } void enrollNewPassword(hidl_vec &password, GatekeeperResponse &rsp, bool expectSuccess) { GatekeeperRequest req; req.newPwd.setToExternal(password.data(), password.size()); doEnroll(req, rsp); checkEnroll(rsp, expectSuccess); } void verifyPassword(hidl_vec &password, hidl_vec &passwordHandle, uint64_t challenge, GatekeeperResponse &verifyRsp, bool expectSuccess) { GatekeeperRequest verifyReq; // build verify request for the same password (we want it to succeed) verifyReq.newPwd = password; // use enrolled password handle we've got verifyReq.curPwdHandle = passwordHandle; verifyReq.challenge = challenge; doVerify(verifyReq, verifyRsp); checkVerify(verifyRsp, challenge, expectSuccess); } protected: sp gatekeeper_; uint32_t uid_; public: GatekeeperHidlTest() : uid_(0) {} virtual void SetUp() override { GatekeeperResponse rsp; gatekeeper_ = IGatekeeper::getService(GetParam()); ASSERT_NE(nullptr, gatekeeper_.get()); doDeleteAllUsers(rsp); } virtual void TearDown() override { GatekeeperResponse rsp; doDeleteAllUsers(rsp); } }; /** * Ensure we can enroll new password */ TEST_P(GatekeeperHidlTest, EnrollSuccess) { hidl_vec password; GatekeeperResponse rsp; ALOGI("Testing Enroll (expected success)"); generatePassword(password, 0); enrollNewPassword(password, rsp, true); ALOGI("Testing Enroll done"); } /** * Ensure we can not enroll empty password */ TEST_P(GatekeeperHidlTest, EnrollNoPassword) { hidl_vec password; GatekeeperResponse rsp; ALOGI("Testing Enroll (expected failure)"); enrollNewPassword(password, rsp, false); ALOGI("Testing Enroll done"); } /** * Ensure we can successfully verify previously enrolled password */ TEST_P(GatekeeperHidlTest, VerifySuccess) { GatekeeperResponse enrollRsp; GatekeeperResponse verifyRsp; hidl_vec password; ALOGI("Testing Enroll+Verify (expected success)"); generatePassword(password, 0); enrollNewPassword(password, enrollRsp, true); verifyPassword(password, enrollRsp.data, 1, verifyRsp, true); ALOGI("Testing Enroll+Verify done"); } /** * Ensure we can securely update password (keep the same * secure user_id) if we prove we know old password */ TEST_P(GatekeeperHidlTest, TrustedReenroll) { GatekeeperResponse enrollRsp; GatekeeperRequest reenrollReq; GatekeeperResponse reenrollRsp; GatekeeperResponse verifyRsp; GatekeeperResponse reenrollVerifyRsp; hidl_vec password; hidl_vec newPassword; generatePassword(password, 0); ALOGI("Testing Trusted Reenroll (expected success)"); enrollNewPassword(password, enrollRsp, true); verifyPassword(password, enrollRsp.data, 0, verifyRsp, true); ALOGI("Primary Enroll+Verify done"); generatePassword(newPassword, 1); reenrollReq.newPwd.setToExternal(newPassword.data(), newPassword.size()); reenrollReq.curPwd.setToExternal(password.data(), password.size()); reenrollReq.curPwdHandle.setToExternal(enrollRsp.data.data(), enrollRsp.data.size()); doEnroll(reenrollReq, reenrollRsp); checkEnroll(reenrollRsp, true); verifyPassword(newPassword, reenrollRsp.data, 0, reenrollVerifyRsp, true); ALOGI("Trusted ReEnroll+Verify done"); const hw_auth_token_t *first = toAuthToken(verifyRsp); const hw_auth_token_t *second = toAuthToken(reenrollVerifyRsp); if (first != nullptr && second != nullptr) { EXPECT_EQ(first->user_id, second->user_id); } ALOGI("Testing Trusted Reenroll done"); } /** * Ensure we can update password (and get new * secure user_id) if we don't know old password */ TEST_P(GatekeeperHidlTest, UntrustedReenroll) { GatekeeperResponse enrollRsp; GatekeeperResponse reenrollRsp; GatekeeperResponse verifyRsp; GatekeeperResponse reenrollVerifyRsp; hidl_vec password; hidl_vec newPassword; ALOGI("Testing Untrusted Reenroll (expected success)"); generatePassword(password, 0); enrollNewPassword(password, enrollRsp, true); verifyPassword(password, enrollRsp.data, 0, verifyRsp, true); ALOGI("Primary Enroll+Verify done"); generatePassword(newPassword, 1); enrollNewPassword(newPassword, reenrollRsp, true); verifyPassword(newPassword, reenrollRsp.data, 0, reenrollVerifyRsp, true); ALOGI("Untrusted ReEnroll+Verify done"); const hw_auth_token_t *first = toAuthToken(verifyRsp); const hw_auth_token_t *second = toAuthToken(reenrollVerifyRsp); if (first != nullptr && second != nullptr) { EXPECT_NE(first->user_id, second->user_id); } ALOGI("Testing Untrusted Reenroll done"); } /** * Ensure we dont get successful verify with invalid data */ TEST_P(GatekeeperHidlTest, VerifyNoData) { hidl_vec password; hidl_vec passwordHandle; GatekeeperResponse verifyRsp; ALOGI("Testing Verify (expected failure)"); verifyPassword(password, passwordHandle, 0, verifyRsp, false); EXPECT_EQ(GatekeeperStatusCode::ERROR_GENERAL_FAILURE, verifyRsp.code); ALOGI("Testing Verify done"); } /** * Ensure we can not verify password after we enrolled it and then deleted user */ TEST_P(GatekeeperHidlTest, DeleteUserTest) { hidl_vec password; GatekeeperResponse enrollRsp; GatekeeperResponse verifyRsp; GatekeeperResponse delRsp; ALOGI("Testing deleteUser (expected success)"); setUid(10001); generatePassword(password, 0); enrollNewPassword(password, enrollRsp, true); verifyPassword(password, enrollRsp.data, 0, verifyRsp, true); ALOGI("Enroll+Verify done"); doDeleteUser(delRsp); EXPECT_EQ(UINT32_C(0), delRsp.data.size()); EXPECT_TRUE(delRsp.code == GatekeeperStatusCode::ERROR_NOT_IMPLEMENTED || delRsp.code == GatekeeperStatusCode::STATUS_OK); ALOGI("DeleteUser done"); if (delRsp.code == GatekeeperStatusCode::STATUS_OK) { verifyPassword(password, enrollRsp.data, 0, verifyRsp, false); EXPECT_EQ(GatekeeperStatusCode::ERROR_GENERAL_FAILURE, verifyRsp.code); ALOGI("Verify after Delete done (must fail)"); } ALOGI("Testing deleteUser done: rsp=%" PRIi32, delRsp.code); } /** * Ensure we can not delete a user that does not exist */ TEST_P(GatekeeperHidlTest, DeleteInvalidUserTest) { hidl_vec password; GatekeeperResponse enrollRsp; GatekeeperResponse verifyRsp; GatekeeperResponse delRsp1; GatekeeperResponse delRsp2; ALOGI("Testing deleteUser (expected failure)"); setUid(10002); generatePassword(password, 0); enrollNewPassword(password, enrollRsp, true); verifyPassword(password, enrollRsp.data, 0, verifyRsp, true); ALOGI("Enroll+Verify done"); // Delete the user doDeleteUser(delRsp1); EXPECT_EQ(UINT32_C(0), delRsp1.data.size()); EXPECT_TRUE(delRsp1.code == GatekeeperStatusCode::ERROR_NOT_IMPLEMENTED || delRsp1.code == GatekeeperStatusCode::STATUS_OK); // Delete the user again doDeleteUser(delRsp2); EXPECT_EQ(UINT32_C(0), delRsp2.data.size()); EXPECT_TRUE(delRsp2.code == GatekeeperStatusCode::ERROR_NOT_IMPLEMENTED || delRsp2.code == GatekeeperStatusCode::ERROR_GENERAL_FAILURE); ALOGI("DeleteUser done"); ALOGI("Testing deleteUser done: rsp=%" PRIi32, delRsp2.code); } /** * Ensure we can not verify passwords after we enrolled them and then deleted * all users */ TEST_P(GatekeeperHidlTest, DeleteAllUsersTest) { struct UserData { uint32_t userId; hidl_vec password; GatekeeperResponse enrollRsp; GatekeeperResponse verifyRsp; UserData(int id) { userId = id; } } users[3]{10001, 10002, 10003}; GatekeeperResponse delAllRsp; ALOGI("Testing deleteAllUsers (expected success)"); // enroll multiple users for (size_t i = 0; i < sizeof(users) / sizeof(users[0]); ++i) { setUid(users[i].userId); generatePassword(users[i].password, (i % 255) + 1); enrollNewPassword(users[i].password, users[i].enrollRsp, true); } ALOGI("Multiple users enrolled"); // verify multiple users for (size_t i = 0; i < sizeof(users) / sizeof(users[0]); ++i) { setUid(users[i].userId); verifyPassword(users[i].password, users[i].enrollRsp.data, 0, users[i].verifyRsp, true); } ALOGI("Multiple users verified"); doDeleteAllUsers(delAllRsp); EXPECT_EQ(UINT32_C(0), delAllRsp.data.size()); EXPECT_TRUE(delAllRsp.code == GatekeeperStatusCode::ERROR_NOT_IMPLEMENTED || delAllRsp.code == GatekeeperStatusCode::STATUS_OK); ALOGI("All users deleted"); if (delAllRsp.code == GatekeeperStatusCode::STATUS_OK) { // verify multiple users after they are deleted; all must fail for (size_t i = 0; i < sizeof(users) / sizeof(users[0]); ++i) { setUid(users[i].userId); verifyPassword(users[i].password, users[i].enrollRsp.data, 0, users[i].verifyRsp, false); EXPECT_EQ(GatekeeperStatusCode::ERROR_GENERAL_FAILURE, users[i].verifyRsp.code); } ALOGI("Multiple users verified after delete (all must fail)"); } ALOGI("Testing deleteAllUsers done: rsp=%" PRIi32, delAllRsp.code); } GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(GatekeeperHidlTest); INSTANTIATE_TEST_SUITE_P( PerInstance, GatekeeperHidlTest, testing::ValuesIn(android::hardware::getAllHalInstanceNames(IGatekeeper::descriptor)), android::hardware::PrintInstanceNameToString);