blob: 032f7e2475953af585286b36fb6803c2bc0d31fe [file] [log] [blame]
/*
* 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();
}