hidl2aidl: conversion of gatekeeper vts tests to use aidl service.
Added vts tests to test the gatekeeper stable aidl implementation.
Bug: 205760843
Test: run vts -m VtsHalGatekeeperTarget
Change-Id: I8ff66855490ec1bd01069512654bbf240773b2dd
diff --git a/gatekeeper/aidl/vts/functional/Android.bp b/gatekeeper/aidl/vts/functional/Android.bp
new file mode 100644
index 0000000..2fa80de
--- /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 0000000..c89243b
--- /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 <inttypes.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <cmath>
+#include <string>
+#include <vector>
+
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <aidl/android/hardware/gatekeeper/GatekeeperEnrollResponse.h>
+#include <aidl/android/hardware/gatekeeper/GatekeeperVerifyResponse.h>
+#include <aidl/android/hardware/gatekeeper/IGatekeeper.h>
+#include <aidl/android/hardware/security/keymint/HardwareAuthToken.h>
+#include <android-base/endian.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include <hardware/hw_auth_token.h>
+
+#include <log/log.h>
+
+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<uint8_t> curPwdHandle;
+ std::vector<uint8_t> curPwd;
+ std::vector<uint8_t> 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<uint32_t>(rsp.hardwareAuthToken.authenticatorType);
+ uint64_t auth_tstamp = static_cast<uint64_t>(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<std::string> {
+ 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<uint8_t>& 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<uint8_t>& password, GatekeeperEnrollResponse& rsp,
+ bool expectSuccess) {
+ GatekeeperRequest req;
+ req.newPwd = password;
+ Status ret = doEnroll(req, rsp);
+ checkEnroll(rsp, ret, expectSuccess);
+ }
+
+ void verifyPassword(std::vector<uint8_t>& password, std::vector<uint8_t>& 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<IGatekeeper> 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<uint8_t> 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<uint8_t> 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<uint8_t> 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<uint8_t> password;
+ std::vector<uint8_t> 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<uint8_t> password;
+ std::vector<uint8_t> 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<uint8_t> password;
+ std::vector<uint8_t> 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<uint8_t> 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<uint8_t> 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<uint8_t> 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();
+}