diff --git a/gatekeeper/aidl/vts/functional/Android.bp b/gatekeeper/aidl/vts/functional/Android.bp new file mode 100644 index 0000000000..2fa80de358 --- /dev/null +++ b/gatekeeper/aidl/vts/functional/Android.bp @@ -0,0 +1,39 @@ +// +// Copyright (C) 2022 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. +// + +package { + // See: http://go/android-license-faq + default_applicable_licenses: ["hardware_interfaces_license"], +} + +cc_test { + name: "VtsHalGatekeeperTargetTest", + defaults: [ + "VtsHalTargetTestDefaults", + "use_libaidlvintf_gtest_helper_static", + "keymint_use_latest_hal_aidl_ndk_shared", + ], + srcs: ["VtsHalGatekeeperTargetTest.cpp"], + shared_libs: [ + "libbinder_ndk", + "libbase", + ], + static_libs: ["android.hardware.gatekeeper-V1-ndk"], + test_suites: [ + "general-tests", + "vts", + ], +} diff --git a/gatekeeper/aidl/vts/functional/VtsHalGatekeeperTargetTest.cpp b/gatekeeper/aidl/vts/functional/VtsHalGatekeeperTargetTest.cpp new file mode 100644 index 0000000000..c89243b913 --- /dev/null +++ b/gatekeeper/aidl/vts/functional/VtsHalGatekeeperTargetTest.cpp @@ -0,0 +1,413 @@ +/* + * Copyright (C) 2022 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_aidl_hal_test" + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using aidl::android::hardware::gatekeeper::GatekeeperEnrollResponse; +using aidl::android::hardware::gatekeeper::GatekeeperVerifyResponse; +using aidl::android::hardware::gatekeeper::IGatekeeper; +using aidl::android::hardware::security::keymint::HardwareAuthToken; +using Status = ::ndk::ScopedAStatus; + +struct GatekeeperRequest { + uint32_t uid; + uint64_t challenge; + std::vector curPwdHandle; + std::vector curPwd; + std::vector newPwd; + GatekeeperRequest() : uid(0), challenge(0) {} +}; + +// ASSERT_* macros generate return "void" internally +// we have to use EXPECT_* if we return anything but "void" +static void verifyAuthToken(GatekeeperVerifyResponse& rsp) { + uint32_t auth_type = static_cast(rsp.hardwareAuthToken.authenticatorType); + uint64_t auth_tstamp = static_cast(rsp.hardwareAuthToken.timestamp.milliSeconds); + + EXPECT_EQ(HW_AUTH_PASSWORD, auth_type); + EXPECT_NE(UINT64_C(~0), auth_tstamp); + ALOGI("Authenticator ID: %016" PRIX64, rsp.hardwareAuthToken.authenticatorId); + EXPECT_NE(UINT32_C(0), rsp.hardwareAuthToken.userId); +} + +// The main test class for Gatekeeper AIDL HAL. +class GatekeeperAidlTest : public ::testing::TestWithParam { + protected: + void setUid(uint32_t uid) { uid_ = uid; } + + Status doEnroll(GatekeeperRequest& req, GatekeeperEnrollResponse& rsp) { + Status ret; + while (true) { + ret = gatekeeper_->enroll(uid_, req.curPwdHandle, req.curPwd, req.newPwd, &rsp); + if (ret.isOk()) break; + if (getReturnStatusCode(ret) != IGatekeeper::ERROR_RETRY_TIMEOUT) break; + ALOGI("%s: got retry code; retrying in 1 sec", __func__); + sleep(1); + } + return ret; + } + + Status doVerify(GatekeeperRequest& req, GatekeeperVerifyResponse& rsp) { + Status ret; + while (true) { + ret = gatekeeper_->verify(uid_, req.challenge, req.curPwdHandle, req.newPwd, &rsp); + if (ret.isOk()) break; + if (getReturnStatusCode(ret) != IGatekeeper::ERROR_RETRY_TIMEOUT) break; + ALOGI("%s: got retry code; retrying in 1 sec", __func__); + sleep(1); + } + return ret; + } + + Status doDeleteUser() { return gatekeeper_->deleteUser(uid_); } + + Status doDeleteAllUsers() { return gatekeeper_->deleteAllUsers(); } + + void generatePassword(std::vector& password, uint8_t seed) { + password.resize(16); + memset(password.data(), seed, password.size()); + } + + void checkEnroll(GatekeeperEnrollResponse& rsp, Status& ret, bool expectSuccess) { + if (expectSuccess) { + EXPECT_TRUE(ret.isOk()); + EXPECT_EQ(IGatekeeper::STATUS_OK, rsp.statusCode); + EXPECT_NE(nullptr, rsp.data.data()); + EXPECT_GT(rsp.data.size(), UINT32_C(0)); + EXPECT_NE(UINT32_C(0), rsp.secureUserId); + } else { + EXPECT_EQ(IGatekeeper::ERROR_GENERAL_FAILURE, getReturnStatusCode(ret)); + EXPECT_EQ(UINT32_C(0), rsp.data.size()); + } + } + + void checkVerify(GatekeeperVerifyResponse& rsp, Status& ret, uint64_t challenge, + bool expectSuccess) { + if (expectSuccess) { + EXPECT_TRUE(ret.isOk()); + EXPECT_GE(rsp.statusCode, IGatekeeper::STATUS_OK); + EXPECT_LE(rsp.statusCode, IGatekeeper::STATUS_REENROLL); + + verifyAuthToken(rsp); + EXPECT_EQ(challenge, rsp.hardwareAuthToken.challenge); + } else { + EXPECT_EQ(IGatekeeper::ERROR_GENERAL_FAILURE, getReturnStatusCode(ret)); + } + } + + void enrollNewPassword(std::vector& password, GatekeeperEnrollResponse& rsp, + bool expectSuccess) { + GatekeeperRequest req; + req.newPwd = password; + Status ret = doEnroll(req, rsp); + checkEnroll(rsp, ret, expectSuccess); + } + + void verifyPassword(std::vector& password, std::vector& passwordHandle, + uint64_t challenge, GatekeeperVerifyResponse& 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; + Status ret = doVerify(verifyReq, verifyRsp); + checkVerify(verifyRsp, ret, challenge, expectSuccess); + } + + int32_t getReturnStatusCode(const Status& result) { + if (!result.isOk()) { + if (result.getExceptionCode() == EX_SERVICE_SPECIFIC) { + return result.getServiceSpecificError(); + } + return IGatekeeper::ERROR_GENERAL_FAILURE; + } + return IGatekeeper::STATUS_OK; + } + + protected: + std::shared_ptr gatekeeper_; + uint32_t uid_; + + public: + GatekeeperAidlTest() : uid_(0) {} + virtual void SetUp() override { + gatekeeper_ = IGatekeeper::fromBinder( + ndk::SpAIBinder(AServiceManager_waitForService(GetParam().c_str()))); + ASSERT_NE(nullptr, gatekeeper_.get()); + doDeleteAllUsers(); + } + + virtual void TearDown() override { doDeleteAllUsers(); } +}; + +/** + * Ensure we can enroll new password + */ +TEST_P(GatekeeperAidlTest, EnrollSuccess) { + std::vector password; + GatekeeperEnrollResponse 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(GatekeeperAidlTest, EnrollNoPassword) { + std::vector password; + GatekeeperEnrollResponse rsp; + ALOGI("Testing Enroll (expected failure)"); + enrollNewPassword(password, rsp, false); + ALOGI("Testing Enroll done"); +} + +/** + * Ensure we can successfully verify previously enrolled password + */ +TEST_P(GatekeeperAidlTest, VerifySuccess) { + GatekeeperEnrollResponse enrollRsp; + GatekeeperVerifyResponse verifyRsp; + std::vector password; + + ALOGI("Testing Enroll+Verify (expected success)"); + generatePassword(password, 0); + enrollNewPassword(password, enrollRsp, true); + verifyPassword(password, enrollRsp.data, 1, verifyRsp, true); + + ALOGI("Testing unenrolled password doesn't verify"); + verifyRsp = {0, 0, {}}; + generatePassword(password, 1); + verifyPassword(password, enrollRsp.data, 1, verifyRsp, false); + 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(GatekeeperAidlTest, TrustedReenroll) { + GatekeeperEnrollResponse enrollRsp; + GatekeeperRequest reenrollReq; + GatekeeperEnrollResponse reenrollRsp; + GatekeeperVerifyResponse verifyRsp; + GatekeeperVerifyResponse reenrollVerifyRsp; + std::vector password; + std::vector 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 = newPassword; + reenrollReq.curPwd = password; + reenrollReq.curPwdHandle = enrollRsp.data; + + Status ret = doEnroll(reenrollReq, reenrollRsp); + checkEnroll(reenrollRsp, ret, true); + verifyPassword(newPassword, reenrollRsp.data, 0, reenrollVerifyRsp, true); + ALOGI("Trusted ReEnroll+Verify done"); + + verifyAuthToken(verifyRsp); + verifyAuthToken(reenrollVerifyRsp); + EXPECT_EQ(verifyRsp.hardwareAuthToken.userId, reenrollVerifyRsp.hardwareAuthToken.userId); + 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(GatekeeperAidlTest, UntrustedReenroll) { + GatekeeperEnrollResponse enrollRsp; + GatekeeperEnrollResponse reenrollRsp; + GatekeeperVerifyResponse verifyRsp; + GatekeeperVerifyResponse reenrollVerifyRsp; + std::vector password; + std::vector 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"); + + verifyAuthToken(verifyRsp); + verifyAuthToken(reenrollVerifyRsp); + EXPECT_NE(verifyRsp.hardwareAuthToken.userId, reenrollVerifyRsp.hardwareAuthToken.userId); + ALOGI("Testing Untrusted Reenroll done"); +} + +/** + * Ensure we don't get successful verify with invalid data + */ +TEST_P(GatekeeperAidlTest, VerifyNoData) { + std::vector password; + std::vector passwordHandle; + GatekeeperVerifyResponse verifyRsp; + + ALOGI("Testing Verify (expected failure)"); + verifyPassword(password, passwordHandle, 0, verifyRsp, false); + ALOGI("Testing Verify done"); +} + +/** + * Ensure we can not verify password after we enrolled it and then deleted user + */ +TEST_P(GatekeeperAidlTest, DeleteUserTest) { + std::vector password; + GatekeeperEnrollResponse enrollRsp; + GatekeeperVerifyResponse verifyRsp; + 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"); + auto result = doDeleteUser(); + EXPECT_TRUE(result.isOk() || + (getReturnStatusCode(result) == IGatekeeper::ERROR_NOT_IMPLEMENTED)); + ALOGI("DeleteUser done"); + if (result.isOk()) { + verifyRsp = {0, 0, {}}; + verifyPassword(password, enrollRsp.data, 0, verifyRsp, false); + ALOGI("Verify after Delete done (must fail)"); + } + ALOGI("Testing deleteUser done: rsp=%" PRIi32, getReturnStatusCode(result)); +} + +/** + * Ensure we can not delete a user that does not exist + */ +TEST_P(GatekeeperAidlTest, DeleteInvalidUserTest) { + std::vector password; + GatekeeperEnrollResponse enrollRsp; + GatekeeperVerifyResponse verifyRsp; + 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 + Status result1 = doDeleteUser(); + EXPECT_TRUE(result1.isOk() || + (getReturnStatusCode(result1) == IGatekeeper::ERROR_NOT_IMPLEMENTED)); + + // Delete the user again + Status result2 = doDeleteUser(); + int32_t retCode2 = getReturnStatusCode(result2); + EXPECT_TRUE((retCode2 == IGatekeeper::ERROR_NOT_IMPLEMENTED) || + (retCode2 == IGatekeeper::ERROR_GENERAL_FAILURE)); + ALOGI("DeleteUser done"); + ALOGI("Testing deleteUser done: rsp=%" PRIi32, retCode2); +} + +/** + * Ensure we can not verify passwords after we enrolled them and then deleted + * all users + */ +TEST_P(GatekeeperAidlTest, DeleteAllUsersTest) { + struct UserData { + uint32_t userId; + std::vector password; + GatekeeperEnrollResponse enrollRsp; + GatekeeperVerifyResponse verifyRsp; + UserData(int id) { userId = id; } + } users[3]{10001, 10002, 10003}; + 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"); + + Status result = doDeleteAllUsers(); + EXPECT_TRUE(result.isOk() || + (getReturnStatusCode(result) == IGatekeeper::ERROR_NOT_IMPLEMENTED)); + ALOGI("All users deleted"); + + if (result.isOk()) { + // 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); + users[i].verifyRsp = {0, 0, {}}; + verifyPassword(users[i].password, users[i].enrollRsp.data, 0, users[i].verifyRsp, + false); + } + ALOGI("Multiple users verified after delete (all must fail)"); + } + + ALOGI("Testing deleteAllUsers done: rsp=%" PRIi32, getReturnStatusCode(result)); +} + +GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(GatekeeperAidlTest); +INSTANTIATE_TEST_SUITE_P( + PerInstance, GatekeeperAidlTest, + testing::ValuesIn(android::getAidlHalInstanceNames(IGatekeeper::descriptor)), + android::PrintInstanceNameToString); + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + ABinderProcess_setThreadPoolMaxThreadCount(1); + ABinderProcess_startThreadPool(); + return RUN_ALL_TESTS(); +}