Files
hardware_interfaces/gatekeeper/aidl/vts/functional/VtsHalGatekeeperTargetTest.cpp
Eric Biggers 8bf0780fd9 Test that the password isn't truncated (again)
Test that Gatekeeper doesn't truncate passwords, either due to them
containing NUL bytes or being long.

This is https://r.android.com/2151558 ported to the AIDL test.  Even
though the AIDL test wasn't added until after my change, it was forked
from an earlier version of the HIDL test that didn't have my change.

Bug: 238919794
Test: atest VtsHalGatekeeperTargetTest # on Cuttlefish
Change-Id: I6fec951e67a35d5275a67244fbef07d1435c9f4f
2023-08-05 02:47:59 +00:00

455 lines
16 KiB
C++

/*
* 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 that passwords containing a NUL byte aren't truncated
*/
TEST_P(GatekeeperAidlTest, PasswordIsBinaryData) {
GatekeeperEnrollResponse enrollRsp;
GatekeeperVerifyResponse verifyRsp;
std::vector<uint8_t> rightPassword = {'A', 'B', 'C', '\0', 'D', 'E', 'F'};
std::vector<uint8_t> wrongPassword = {'A', 'B', 'C', '\0', '\0', '\0', '\0'};
ALOGI("Testing Enroll+Verify of password with embedded NUL (expected success)");
enrollNewPassword(rightPassword, enrollRsp, true);
verifyPassword(rightPassword, enrollRsp.data, 1, verifyRsp, true);
ALOGI("Testing Verify of wrong password (expected failure)");
verifyPassword(wrongPassword, enrollRsp.data, 1, verifyRsp, false);
ALOGI("PasswordIsBinaryData test done");
}
/**
* Ensure that long passwords aren't truncated
*/
TEST_P(GatekeeperAidlTest, LongPassword) {
GatekeeperEnrollResponse enrollRsp;
GatekeeperVerifyResponse verifyRsp;
std::vector<uint8_t> password;
password.resize(64); // maximum length used by Android
memset(password.data(), 'A', password.size());
ALOGI("Testing Enroll+Verify of long password (expected success)");
enrollNewPassword(password, enrollRsp, true);
verifyPassword(password, enrollRsp.data, 1, verifyRsp, true);
ALOGI("Testing Verify of wrong password (expected failure)");
password[password.size() - 1] ^= 1;
verifyPassword(password, enrollRsp.data, 1, verifyRsp, false);
ALOGI("LongPassword test 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();
}