mirror of
https://github.com/Evolution-X/hardware_interfaces
synced 2026-02-01 11:36:00 +00:00
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
This commit is contained in:
39
gatekeeper/aidl/vts/functional/Android.bp
Normal file
39
gatekeeper/aidl/vts/functional/Android.bp
Normal file
@@ -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",
|
||||
],
|
||||
}
|
||||
413
gatekeeper/aidl/vts/functional/VtsHalGatekeeperTargetTest.cpp
Normal file
413
gatekeeper/aidl/vts/functional/VtsHalGatekeeperTargetTest.cpp
Normal file
@@ -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();
|
||||
}
|
||||
Reference in New Issue
Block a user