Loading security/keymint/aidl/vts/functional/Android.bp +4 −0 Original line number Diff line number Diff line Loading @@ -35,9 +35,12 @@ cc_defaults { "libbinder_ndk", "libcrypto", "libbase", "libgatekeeper", "packagemanager_aidl-cpp", ], static_libs: [ "android.hardware.gatekeeper@1.0", "android.hardware.gatekeeper-V1-ndk", "android.hardware.security.rkp-V3-ndk", "android.hardware.security.secureclock-V1-ndk", "libcppbor_external", Loading @@ -59,6 +62,7 @@ cc_test { ], srcs: [ "AttestKeyTest.cpp", "AuthTest.cpp", "DeviceUniqueAttestationTest.cpp", "KeyBlobUpgradeTest.cpp", "KeyMintTest.cpp", Loading security/keymint/aidl/vts/functional/AuthTest.cpp 0 → 100644 +412 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 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 "keymint_1_test" #include <cutils/log.h> #include <iostream> #include <optional> #include "KeyMintAidlTestBase.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/secureclock/ISecureClock.h> #include <android-base/logging.h> #include <android/binder_manager.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 aidl::android::hardware::security::secureclock::ISecureClock; #include <android/hardware/gatekeeper/1.0/IGatekeeper.h> #include <android/hardware/gatekeeper/1.0/types.h> #include <gatekeeper/password_handle.h> // for password_handle_t #include <hardware/hw_auth_token.h> using ::android::sp; using IHidlGatekeeper = ::android::hardware::gatekeeper::V1_0::IGatekeeper; using HidlGatekeeperResponse = ::android::hardware::gatekeeper::V1_0::GatekeeperResponse; using HidlGatekeeperStatusCode = ::android::hardware::gatekeeper::V1_0::GatekeeperStatusCode; namespace aidl::android::hardware::security::keymint::test { class AuthTest : public KeyMintAidlTestBase { public: void SetUp() { KeyMintAidlTestBase::SetUp(); // Find the default Gatekeeper instance. string gk_name = string(IGatekeeper::descriptor) + "/default"; if (AServiceManager_isDeclared(gk_name.c_str())) { // Enroll a user with AIDL Gatekeeper. ::ndk::SpAIBinder binder(AServiceManager_waitForService(gk_name.c_str())); gk_ = IGatekeeper::fromBinder(binder); } else { // Prior to Android U, Gatekeeper was HIDL not AIDL and so may not be present. // Try to enroll user with HIDL Gatekeeper instead. string gk_name = "default"; hidl_gk_ = IHidlGatekeeper::getService(gk_name.c_str()); if (hidl_gk_ == nullptr) { std::cerr << "No HIDL Gatekeeper instance for '" << gk_name << "' found.\n"; return; } std::cerr << "No AIDL Gatekeeper instance for '" << gk_name << "' found, using HIDL.\n"; } // If the device needs timestamps, find the default ISecureClock instance. if (timestamp_token_required_) { string clock_name = string(ISecureClock::descriptor) + "/default"; if (AServiceManager_isDeclared(clock_name.c_str())) { ::ndk::SpAIBinder binder(AServiceManager_waitForService(clock_name.c_str())); clock_ = ISecureClock::fromBinder(binder); } else { std::cerr << "No ISecureClock instance for '" << clock_name << "' found.\n"; } } // Enroll a password for a user. uid_ = 10001; password_ = "correcthorsebatterystaple"; std::optional<GatekeeperEnrollResponse> rsp = doEnroll(password_); ASSERT_TRUE(rsp.has_value()); sid_ = rsp->secureUserId; handle_ = rsp->data; } void TearDown() { if (gk_ == nullptr) return; gk_->deleteUser(uid_); } bool GatekeeperAvailable() { return (gk_ != nullptr) || (hidl_gk_ != nullptr); } std::optional<GatekeeperEnrollResponse> doEnroll(const std::vector<uint8_t>& newPwd, const std::vector<uint8_t>& curHandle = {}, const std::vector<uint8_t>& curPwd = {}) { if (gk_ != nullptr) { while (true) { GatekeeperEnrollResponse rsp; Status status = gk_->enroll(uid_, curHandle, curPwd, newPwd, &rsp); if (!status.isOk() && status.getExceptionCode() == EX_SERVICE_SPECIFIC && status.getServiceSpecificError() == IGatekeeper::ERROR_RETRY_TIMEOUT) { sleep(1); continue; } if (status.isOk()) { return std::move(rsp); } else { GTEST_LOG_(ERROR) << "doEnroll(AIDL) failed: " << status; return std::nullopt; } } } else if (hidl_gk_ != nullptr) { while (true) { HidlGatekeeperResponse rsp; auto status = hidl_gk_->enroll( uid_, curHandle, curPwd, newPwd, [&rsp](const HidlGatekeeperResponse& cbRsp) { rsp = cbRsp; }); if (!status.isOk()) { GTEST_LOG_(ERROR) << "doEnroll(HIDL) failed"; return std::nullopt; } if (rsp.code == HidlGatekeeperStatusCode::ERROR_RETRY_TIMEOUT) { sleep(1); continue; } if (rsp.code != HidlGatekeeperStatusCode::STATUS_OK) { GTEST_LOG_(ERROR) << "doEnroll(HIDL) failed with " << int(rsp.code); return std::nullopt; } // "Parse" the returned data to get at the secure user ID. if (rsp.data.size() != sizeof(::gatekeeper::password_handle_t)) { GTEST_LOG_(ERROR) << "HAL returned password handle of invalid length " << rsp.data.size(); return std::nullopt; } const ::gatekeeper::password_handle_t* handle = reinterpret_cast<const ::gatekeeper::password_handle_t*>(rsp.data.data()); // Translate HIDL response to look like an AIDL response. GatekeeperEnrollResponse aidl_rsp; aidl_rsp.statusCode = IGatekeeper::STATUS_OK; aidl_rsp.data = rsp.data; aidl_rsp.secureUserId = handle->user_id; return aidl_rsp; } } else { return std::nullopt; } } std::optional<GatekeeperEnrollResponse> doEnroll(const string& newPwd, const std::vector<uint8_t>& curHandle = {}, const string& curPwd = {}) { return doEnroll(std::vector<uint8_t>(newPwd.begin(), newPwd.end()), curHandle, std::vector<uint8_t>(curPwd.begin(), curPwd.end())); } std::optional<HardwareAuthToken> doVerify(uint64_t challenge, const std::vector<uint8_t>& handle, const std::vector<uint8_t>& pwd) { if (gk_ != nullptr) { while (true) { GatekeeperVerifyResponse rsp; Status status = gk_->verify(uid_, challenge, handle, pwd, &rsp); if (!status.isOk() && status.getExceptionCode() == EX_SERVICE_SPECIFIC && status.getServiceSpecificError() == IGatekeeper::ERROR_RETRY_TIMEOUT) { sleep(1); continue; } if (status.isOk()) { return rsp.hardwareAuthToken; } else { GTEST_LOG_(ERROR) << "doVerify(AIDL) failed: " << status; return std::nullopt; } } } else if (hidl_gk_ != nullptr) { while (true) { HidlGatekeeperResponse rsp; auto status = hidl_gk_->verify( uid_, challenge, handle, pwd, [&rsp](const HidlGatekeeperResponse& cbRsp) { rsp = cbRsp; }); if (!status.isOk()) { GTEST_LOG_(ERROR) << "doVerify(HIDL) failed"; return std::nullopt; } if (rsp.code == HidlGatekeeperStatusCode::ERROR_RETRY_TIMEOUT) { sleep(1); continue; } if (rsp.code != HidlGatekeeperStatusCode::STATUS_OK) { GTEST_LOG_(ERROR) << "doVerify(HIDL) failed with " << int(rsp.code); return std::nullopt; } // "Parse" the returned data to get auth token contents. if (rsp.data.size() != sizeof(hw_auth_token_t)) { GTEST_LOG_(ERROR) << "Incorrect size of AuthToken payload."; return std::nullopt; } const hw_auth_token_t* hwAuthToken = reinterpret_cast<const hw_auth_token_t*>(rsp.data.data()); HardwareAuthToken authToken; authToken.timestamp.milliSeconds = betoh64(hwAuthToken->timestamp); authToken.challenge = hwAuthToken->challenge; authToken.userId = hwAuthToken->user_id; authToken.authenticatorId = hwAuthToken->authenticator_id; authToken.authenticatorType = static_cast<HardwareAuthenticatorType>( betoh32(hwAuthToken->authenticator_type)); authToken.mac.assign(&hwAuthToken->hmac[0], &hwAuthToken->hmac[32]); return authToken; } } else { return std::nullopt; } } std::optional<HardwareAuthToken> doVerify(uint64_t challenge, const std::vector<uint8_t>& handle, const string& pwd) { return doVerify(challenge, handle, std::vector<uint8_t>(pwd.begin(), pwd.end())); } // Variants of the base class methods but with authentication information included. string ProcessMessage(const vector<uint8_t>& key_blob, KeyPurpose operation, const string& message, const AuthorizationSet& in_params, AuthorizationSet* out_params, const HardwareAuthToken& hat) { AuthorizationSet begin_out_params; ErrorCode result = Begin(operation, key_blob, in_params, out_params, hat); EXPECT_EQ(ErrorCode::OK, result); if (result != ErrorCode::OK) { return ""; } std::optional<secureclock::TimeStampToken> time_token = std::nullopt; if (timestamp_token_required_ && clock_ != nullptr) { // Ask a secure clock instance for a timestamp, including the per-op challenge. secureclock::TimeStampToken token; EXPECT_EQ(ErrorCode::OK, GetReturnErrorCode(clock_->generateTimeStamp(challenge_, &token))); time_token = token; } string output; EXPECT_EQ(ErrorCode::OK, Finish(message, {} /* signature */, &output, hat, time_token)); return output; } string EncryptMessage(const vector<uint8_t>& key_blob, const string& message, const AuthorizationSet& in_params, AuthorizationSet* out_params, const HardwareAuthToken& hat) { SCOPED_TRACE("EncryptMessage"); return ProcessMessage(key_blob, KeyPurpose::ENCRYPT, message, in_params, out_params, hat); } string DecryptMessage(const vector<uint8_t>& key_blob, const string& ciphertext, const AuthorizationSet& params, const HardwareAuthToken& hat) { SCOPED_TRACE("DecryptMessage"); AuthorizationSet out_params; string plaintext = ProcessMessage(key_blob, KeyPurpose::DECRYPT, ciphertext, params, &out_params, hat); EXPECT_TRUE(out_params.empty()); return plaintext; } protected: std::shared_ptr<IGatekeeper> gk_; sp<IHidlGatekeeper> hidl_gk_; std::shared_ptr<ISecureClock> clock_; string password_; uint32_t uid_; long sid_; std::vector<uint8_t> handle_; }; // Test use of a key that requires user-authentication within recent history. TEST_P(AuthTest, TimeoutAuthentication) { if (!GatekeeperAvailable()) { GTEST_SKIP() << "No Gatekeeper available"; } if (timestamp_token_required_ && clock_ == nullptr) { GTEST_SKIP() << "Device requires timestamps and no ISecureClock available"; } // Create an AES key that requires authentication within the last 3 seconds. const uint32_t timeout_secs = 3; auto builder = AuthorizationSetBuilder() .AesEncryptionKey(256) .BlockMode(BlockMode::ECB) .Padding(PaddingMode::PKCS7) .Authorization(TAG_USER_SECURE_ID, sid_) .Authorization(TAG_USER_AUTH_TYPE, HardwareAuthenticatorType::PASSWORD) .Authorization(TAG_AUTH_TIMEOUT, timeout_secs); vector<uint8_t> keyblob; vector<KeyCharacteristics> key_characteristics; vector<Certificate> cert_chain; ASSERT_EQ(ErrorCode::OK, GenerateKey(builder, std::nullopt, &keyblob, &key_characteristics, &cert_chain)); // Attempt to use the AES key without authentication. const string message = "Hello World!"; AuthorizationSet out_params; auto params = AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::PKCS7); EXPECT_EQ(ErrorCode::KEY_USER_NOT_AUTHENTICATED, Begin(KeyPurpose::ENCRYPT, keyblob, params, &out_params)); // Verify to get a HAT, arbitrary challenge. const uint64_t challenge = 42; const std::optional<HardwareAuthToken> hat = doVerify(challenge, handle_, password_); ASSERT_TRUE(hat.has_value()); EXPECT_EQ(hat->userId, sid_); // Adding the auth token makes it possible to use the AES key. const string ciphertext = EncryptMessage(keyblob, message, params, &out_params, hat.value()); const string plaintext = DecryptMessage(keyblob, ciphertext, params, hat.value()); EXPECT_EQ(message, plaintext); // Altering a single bit in the MAC means no auth. HardwareAuthToken dodgy_hat = hat.value(); ASSERT_GT(dodgy_hat.mac.size(), 0); dodgy_hat.mac[0] ^= 0x01; EXPECT_EQ(ErrorCode::KEY_USER_NOT_AUTHENTICATED, Begin(KeyPurpose::ENCRYPT, keyblob, params, &out_params, dodgy_hat)); // Wait for long enough that the hardware auth token expires. sleep(timeout_secs + 1); if (!timestamp_token_required_) { // KeyMint implementation has its own clock, and can immediately detect timeout. EXPECT_EQ(ErrorCode::KEY_USER_NOT_AUTHENTICATED, Begin(KeyPurpose::ENCRYPT, keyblob, params, &out_params, hat)); } else { // KeyMint implementation has no clock, so only detects timeout via timestamp token provided // on update()/finish(). ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, keyblob, params, &out_params, hat)); secureclock::TimeStampToken time_token; EXPECT_EQ(ErrorCode::OK, GetReturnErrorCode(clock_->generateTimeStamp(challenge_, &time_token))); string output; EXPECT_EQ(ErrorCode::KEY_USER_NOT_AUTHENTICATED, Finish(message, {} /* signature */, &output, hat, time_token)); } } // Test use of a key that requires an auth token for each action on the operation, with // a per-operation challenge value included. TEST_P(AuthTest, AuthPerOperation) { if (!GatekeeperAvailable()) { GTEST_SKIP() << "No Gatekeeper available"; } // Create an AES key that requires authentication per-action. auto builder = AuthorizationSetBuilder() .AesEncryptionKey(256) .BlockMode(BlockMode::ECB) .Padding(PaddingMode::PKCS7) .Authorization(TAG_USER_SECURE_ID, sid_) .Authorization(TAG_USER_AUTH_TYPE, HardwareAuthenticatorType::PASSWORD); vector<uint8_t> keyblob; vector<KeyCharacteristics> key_characteristics; vector<Certificate> cert_chain; ASSERT_EQ(ErrorCode::OK, GenerateKey(builder, std::nullopt, &keyblob, &key_characteristics, &cert_chain)); // Attempt to use the AES key without authentication fails after begin. const string message = "Hello World!"; AuthorizationSet out_params; auto params = AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::PKCS7); EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, keyblob, params, &out_params)); string output; EXPECT_EQ(ErrorCode::KEY_USER_NOT_AUTHENTICATED, Finish(message, {} /* signature */, &output)); // Verify to get a HAT, but with an arbitrary challenge. const uint64_t unrelated_challenge = 42; const std::optional<HardwareAuthToken> unrelated_hat = doVerify(unrelated_challenge, handle_, password_); ASSERT_TRUE(unrelated_hat.has_value()); EXPECT_EQ(unrelated_hat->userId, sid_); // Attempt to use the AES key with an unrelated authentication fails after begin. EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, keyblob, params, &out_params, unrelated_hat.value())); EXPECT_EQ(ErrorCode::KEY_USER_NOT_AUTHENTICATED, Finish(message, {} /* signature */, &output, unrelated_hat.value())); // Now get a HAT with the challenge from an in-progress operation. EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, keyblob, params, &out_params)); const std::optional<HardwareAuthToken> hat = doVerify(challenge_, handle_, password_); ASSERT_TRUE(hat.has_value()); EXPECT_EQ(hat->userId, sid_); string ciphertext; EXPECT_EQ(ErrorCode::OK, Finish(message, {} /* signature */, &ciphertext, hat.value())); // Altering a single bit in the MAC means no auth. EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, keyblob, params, &out_params)); std::optional<HardwareAuthToken> dodgy_hat = doVerify(challenge_, handle_, password_); ASSERT_TRUE(dodgy_hat.has_value()); EXPECT_EQ(dodgy_hat->userId, sid_); ASSERT_GT(dodgy_hat->mac.size(), 0); dodgy_hat->mac[0] ^= 0x01; EXPECT_EQ(ErrorCode::KEY_USER_NOT_AUTHENTICATED, Finish(message, {} /* signature */, &ciphertext, hat.value())); } INSTANTIATE_KEYMINT_AIDL_TEST(AuthTest); } // namespace aidl::android::hardware::security::keymint::test security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp +8 −6 Original line number Diff line number Diff line Loading @@ -552,12 +552,13 @@ ErrorCode KeyMintAidlTestBase::Begin(KeyPurpose purpose, const vector<uint8_t>& ErrorCode KeyMintAidlTestBase::Begin(KeyPurpose purpose, const vector<uint8_t>& key_blob, const AuthorizationSet& in_params, AuthorizationSet* out_params) { AuthorizationSet* out_params, std::optional<HardwareAuthToken> hat) { SCOPED_TRACE("Begin"); Status result; BeginResult out; result = keymint_->begin(purpose, key_blob, in_params.vector_data(), std::nullopt, &out); result = keymint_->begin(purpose, key_blob, in_params.vector_data(), hat, &out); if (result.isOk()) { *out_params = out.params; Loading Loading @@ -611,8 +612,9 @@ ErrorCode KeyMintAidlTestBase::Update(const string& input, string* output) { return GetReturnErrorCode(result); } ErrorCode KeyMintAidlTestBase::Finish(const string& input, const string& signature, string* output) { ErrorCode KeyMintAidlTestBase::Finish(const string& input, const string& signature, string* output, std::optional<HardwareAuthToken> hat, std::optional<secureclock::TimeStampToken> time_token) { SCOPED_TRACE("Finish"); Status result; Loading @@ -621,8 +623,8 @@ ErrorCode KeyMintAidlTestBase::Finish(const string& input, const string& signatu vector<uint8_t> oPut; result = op_->finish(vector<uint8_t>(input.begin(), input.end()), vector<uint8_t>(signature.begin(), signature.end()), {} /* authToken */, {} /* timestampToken */, {} /* confirmationToken */, &oPut); vector<uint8_t>(signature.begin(), signature.end()), hat, time_token, {} /* confirmationToken */, &oPut); if (result.isOk()) output->append(oPut.begin(), oPut.end()); Loading security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h +5 −2 Original line number Diff line number Diff line Loading @@ -161,7 +161,8 @@ class KeyMintAidlTestBase : public ::testing::TestWithParam<string> { const AuthorizationSet& in_params, AuthorizationSet* out_params, std::shared_ptr<IKeyMintOperation>& op); ErrorCode Begin(KeyPurpose purpose, const vector<uint8_t>& key_blob, const AuthorizationSet& in_params, AuthorizationSet* out_params); const AuthorizationSet& in_params, AuthorizationSet* out_params, std::optional<HardwareAuthToken> hat = std::nullopt); ErrorCode Begin(KeyPurpose purpose, const AuthorizationSet& in_params, AuthorizationSet* out_params); ErrorCode Begin(KeyPurpose purpose, const AuthorizationSet& in_params); Loading @@ -169,7 +170,9 @@ class KeyMintAidlTestBase : public ::testing::TestWithParam<string> { ErrorCode UpdateAad(const string& input); ErrorCode Update(const string& input, string* output); ErrorCode Finish(const string& message, const string& signature, string* output); ErrorCode Finish(const string& message, const string& signature, string* output, std::optional<HardwareAuthToken> hat = std::nullopt, std::optional<secureclock::TimeStampToken> time_token = std::nullopt); ErrorCode Finish(const string& message, string* output) { return Finish(message, {} /* signature */, output); } Loading Loading
security/keymint/aidl/vts/functional/Android.bp +4 −0 Original line number Diff line number Diff line Loading @@ -35,9 +35,12 @@ cc_defaults { "libbinder_ndk", "libcrypto", "libbase", "libgatekeeper", "packagemanager_aidl-cpp", ], static_libs: [ "android.hardware.gatekeeper@1.0", "android.hardware.gatekeeper-V1-ndk", "android.hardware.security.rkp-V3-ndk", "android.hardware.security.secureclock-V1-ndk", "libcppbor_external", Loading @@ -59,6 +62,7 @@ cc_test { ], srcs: [ "AttestKeyTest.cpp", "AuthTest.cpp", "DeviceUniqueAttestationTest.cpp", "KeyBlobUpgradeTest.cpp", "KeyMintTest.cpp", Loading
security/keymint/aidl/vts/functional/AuthTest.cpp 0 → 100644 +412 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 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 "keymint_1_test" #include <cutils/log.h> #include <iostream> #include <optional> #include "KeyMintAidlTestBase.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/secureclock/ISecureClock.h> #include <android-base/logging.h> #include <android/binder_manager.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 aidl::android::hardware::security::secureclock::ISecureClock; #include <android/hardware/gatekeeper/1.0/IGatekeeper.h> #include <android/hardware/gatekeeper/1.0/types.h> #include <gatekeeper/password_handle.h> // for password_handle_t #include <hardware/hw_auth_token.h> using ::android::sp; using IHidlGatekeeper = ::android::hardware::gatekeeper::V1_0::IGatekeeper; using HidlGatekeeperResponse = ::android::hardware::gatekeeper::V1_0::GatekeeperResponse; using HidlGatekeeperStatusCode = ::android::hardware::gatekeeper::V1_0::GatekeeperStatusCode; namespace aidl::android::hardware::security::keymint::test { class AuthTest : public KeyMintAidlTestBase { public: void SetUp() { KeyMintAidlTestBase::SetUp(); // Find the default Gatekeeper instance. string gk_name = string(IGatekeeper::descriptor) + "/default"; if (AServiceManager_isDeclared(gk_name.c_str())) { // Enroll a user with AIDL Gatekeeper. ::ndk::SpAIBinder binder(AServiceManager_waitForService(gk_name.c_str())); gk_ = IGatekeeper::fromBinder(binder); } else { // Prior to Android U, Gatekeeper was HIDL not AIDL and so may not be present. // Try to enroll user with HIDL Gatekeeper instead. string gk_name = "default"; hidl_gk_ = IHidlGatekeeper::getService(gk_name.c_str()); if (hidl_gk_ == nullptr) { std::cerr << "No HIDL Gatekeeper instance for '" << gk_name << "' found.\n"; return; } std::cerr << "No AIDL Gatekeeper instance for '" << gk_name << "' found, using HIDL.\n"; } // If the device needs timestamps, find the default ISecureClock instance. if (timestamp_token_required_) { string clock_name = string(ISecureClock::descriptor) + "/default"; if (AServiceManager_isDeclared(clock_name.c_str())) { ::ndk::SpAIBinder binder(AServiceManager_waitForService(clock_name.c_str())); clock_ = ISecureClock::fromBinder(binder); } else { std::cerr << "No ISecureClock instance for '" << clock_name << "' found.\n"; } } // Enroll a password for a user. uid_ = 10001; password_ = "correcthorsebatterystaple"; std::optional<GatekeeperEnrollResponse> rsp = doEnroll(password_); ASSERT_TRUE(rsp.has_value()); sid_ = rsp->secureUserId; handle_ = rsp->data; } void TearDown() { if (gk_ == nullptr) return; gk_->deleteUser(uid_); } bool GatekeeperAvailable() { return (gk_ != nullptr) || (hidl_gk_ != nullptr); } std::optional<GatekeeperEnrollResponse> doEnroll(const std::vector<uint8_t>& newPwd, const std::vector<uint8_t>& curHandle = {}, const std::vector<uint8_t>& curPwd = {}) { if (gk_ != nullptr) { while (true) { GatekeeperEnrollResponse rsp; Status status = gk_->enroll(uid_, curHandle, curPwd, newPwd, &rsp); if (!status.isOk() && status.getExceptionCode() == EX_SERVICE_SPECIFIC && status.getServiceSpecificError() == IGatekeeper::ERROR_RETRY_TIMEOUT) { sleep(1); continue; } if (status.isOk()) { return std::move(rsp); } else { GTEST_LOG_(ERROR) << "doEnroll(AIDL) failed: " << status; return std::nullopt; } } } else if (hidl_gk_ != nullptr) { while (true) { HidlGatekeeperResponse rsp; auto status = hidl_gk_->enroll( uid_, curHandle, curPwd, newPwd, [&rsp](const HidlGatekeeperResponse& cbRsp) { rsp = cbRsp; }); if (!status.isOk()) { GTEST_LOG_(ERROR) << "doEnroll(HIDL) failed"; return std::nullopt; } if (rsp.code == HidlGatekeeperStatusCode::ERROR_RETRY_TIMEOUT) { sleep(1); continue; } if (rsp.code != HidlGatekeeperStatusCode::STATUS_OK) { GTEST_LOG_(ERROR) << "doEnroll(HIDL) failed with " << int(rsp.code); return std::nullopt; } // "Parse" the returned data to get at the secure user ID. if (rsp.data.size() != sizeof(::gatekeeper::password_handle_t)) { GTEST_LOG_(ERROR) << "HAL returned password handle of invalid length " << rsp.data.size(); return std::nullopt; } const ::gatekeeper::password_handle_t* handle = reinterpret_cast<const ::gatekeeper::password_handle_t*>(rsp.data.data()); // Translate HIDL response to look like an AIDL response. GatekeeperEnrollResponse aidl_rsp; aidl_rsp.statusCode = IGatekeeper::STATUS_OK; aidl_rsp.data = rsp.data; aidl_rsp.secureUserId = handle->user_id; return aidl_rsp; } } else { return std::nullopt; } } std::optional<GatekeeperEnrollResponse> doEnroll(const string& newPwd, const std::vector<uint8_t>& curHandle = {}, const string& curPwd = {}) { return doEnroll(std::vector<uint8_t>(newPwd.begin(), newPwd.end()), curHandle, std::vector<uint8_t>(curPwd.begin(), curPwd.end())); } std::optional<HardwareAuthToken> doVerify(uint64_t challenge, const std::vector<uint8_t>& handle, const std::vector<uint8_t>& pwd) { if (gk_ != nullptr) { while (true) { GatekeeperVerifyResponse rsp; Status status = gk_->verify(uid_, challenge, handle, pwd, &rsp); if (!status.isOk() && status.getExceptionCode() == EX_SERVICE_SPECIFIC && status.getServiceSpecificError() == IGatekeeper::ERROR_RETRY_TIMEOUT) { sleep(1); continue; } if (status.isOk()) { return rsp.hardwareAuthToken; } else { GTEST_LOG_(ERROR) << "doVerify(AIDL) failed: " << status; return std::nullopt; } } } else if (hidl_gk_ != nullptr) { while (true) { HidlGatekeeperResponse rsp; auto status = hidl_gk_->verify( uid_, challenge, handle, pwd, [&rsp](const HidlGatekeeperResponse& cbRsp) { rsp = cbRsp; }); if (!status.isOk()) { GTEST_LOG_(ERROR) << "doVerify(HIDL) failed"; return std::nullopt; } if (rsp.code == HidlGatekeeperStatusCode::ERROR_RETRY_TIMEOUT) { sleep(1); continue; } if (rsp.code != HidlGatekeeperStatusCode::STATUS_OK) { GTEST_LOG_(ERROR) << "doVerify(HIDL) failed with " << int(rsp.code); return std::nullopt; } // "Parse" the returned data to get auth token contents. if (rsp.data.size() != sizeof(hw_auth_token_t)) { GTEST_LOG_(ERROR) << "Incorrect size of AuthToken payload."; return std::nullopt; } const hw_auth_token_t* hwAuthToken = reinterpret_cast<const hw_auth_token_t*>(rsp.data.data()); HardwareAuthToken authToken; authToken.timestamp.milliSeconds = betoh64(hwAuthToken->timestamp); authToken.challenge = hwAuthToken->challenge; authToken.userId = hwAuthToken->user_id; authToken.authenticatorId = hwAuthToken->authenticator_id; authToken.authenticatorType = static_cast<HardwareAuthenticatorType>( betoh32(hwAuthToken->authenticator_type)); authToken.mac.assign(&hwAuthToken->hmac[0], &hwAuthToken->hmac[32]); return authToken; } } else { return std::nullopt; } } std::optional<HardwareAuthToken> doVerify(uint64_t challenge, const std::vector<uint8_t>& handle, const string& pwd) { return doVerify(challenge, handle, std::vector<uint8_t>(pwd.begin(), pwd.end())); } // Variants of the base class methods but with authentication information included. string ProcessMessage(const vector<uint8_t>& key_blob, KeyPurpose operation, const string& message, const AuthorizationSet& in_params, AuthorizationSet* out_params, const HardwareAuthToken& hat) { AuthorizationSet begin_out_params; ErrorCode result = Begin(operation, key_blob, in_params, out_params, hat); EXPECT_EQ(ErrorCode::OK, result); if (result != ErrorCode::OK) { return ""; } std::optional<secureclock::TimeStampToken> time_token = std::nullopt; if (timestamp_token_required_ && clock_ != nullptr) { // Ask a secure clock instance for a timestamp, including the per-op challenge. secureclock::TimeStampToken token; EXPECT_EQ(ErrorCode::OK, GetReturnErrorCode(clock_->generateTimeStamp(challenge_, &token))); time_token = token; } string output; EXPECT_EQ(ErrorCode::OK, Finish(message, {} /* signature */, &output, hat, time_token)); return output; } string EncryptMessage(const vector<uint8_t>& key_blob, const string& message, const AuthorizationSet& in_params, AuthorizationSet* out_params, const HardwareAuthToken& hat) { SCOPED_TRACE("EncryptMessage"); return ProcessMessage(key_blob, KeyPurpose::ENCRYPT, message, in_params, out_params, hat); } string DecryptMessage(const vector<uint8_t>& key_blob, const string& ciphertext, const AuthorizationSet& params, const HardwareAuthToken& hat) { SCOPED_TRACE("DecryptMessage"); AuthorizationSet out_params; string plaintext = ProcessMessage(key_blob, KeyPurpose::DECRYPT, ciphertext, params, &out_params, hat); EXPECT_TRUE(out_params.empty()); return plaintext; } protected: std::shared_ptr<IGatekeeper> gk_; sp<IHidlGatekeeper> hidl_gk_; std::shared_ptr<ISecureClock> clock_; string password_; uint32_t uid_; long sid_; std::vector<uint8_t> handle_; }; // Test use of a key that requires user-authentication within recent history. TEST_P(AuthTest, TimeoutAuthentication) { if (!GatekeeperAvailable()) { GTEST_SKIP() << "No Gatekeeper available"; } if (timestamp_token_required_ && clock_ == nullptr) { GTEST_SKIP() << "Device requires timestamps and no ISecureClock available"; } // Create an AES key that requires authentication within the last 3 seconds. const uint32_t timeout_secs = 3; auto builder = AuthorizationSetBuilder() .AesEncryptionKey(256) .BlockMode(BlockMode::ECB) .Padding(PaddingMode::PKCS7) .Authorization(TAG_USER_SECURE_ID, sid_) .Authorization(TAG_USER_AUTH_TYPE, HardwareAuthenticatorType::PASSWORD) .Authorization(TAG_AUTH_TIMEOUT, timeout_secs); vector<uint8_t> keyblob; vector<KeyCharacteristics> key_characteristics; vector<Certificate> cert_chain; ASSERT_EQ(ErrorCode::OK, GenerateKey(builder, std::nullopt, &keyblob, &key_characteristics, &cert_chain)); // Attempt to use the AES key without authentication. const string message = "Hello World!"; AuthorizationSet out_params; auto params = AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::PKCS7); EXPECT_EQ(ErrorCode::KEY_USER_NOT_AUTHENTICATED, Begin(KeyPurpose::ENCRYPT, keyblob, params, &out_params)); // Verify to get a HAT, arbitrary challenge. const uint64_t challenge = 42; const std::optional<HardwareAuthToken> hat = doVerify(challenge, handle_, password_); ASSERT_TRUE(hat.has_value()); EXPECT_EQ(hat->userId, sid_); // Adding the auth token makes it possible to use the AES key. const string ciphertext = EncryptMessage(keyblob, message, params, &out_params, hat.value()); const string plaintext = DecryptMessage(keyblob, ciphertext, params, hat.value()); EXPECT_EQ(message, plaintext); // Altering a single bit in the MAC means no auth. HardwareAuthToken dodgy_hat = hat.value(); ASSERT_GT(dodgy_hat.mac.size(), 0); dodgy_hat.mac[0] ^= 0x01; EXPECT_EQ(ErrorCode::KEY_USER_NOT_AUTHENTICATED, Begin(KeyPurpose::ENCRYPT, keyblob, params, &out_params, dodgy_hat)); // Wait for long enough that the hardware auth token expires. sleep(timeout_secs + 1); if (!timestamp_token_required_) { // KeyMint implementation has its own clock, and can immediately detect timeout. EXPECT_EQ(ErrorCode::KEY_USER_NOT_AUTHENTICATED, Begin(KeyPurpose::ENCRYPT, keyblob, params, &out_params, hat)); } else { // KeyMint implementation has no clock, so only detects timeout via timestamp token provided // on update()/finish(). ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, keyblob, params, &out_params, hat)); secureclock::TimeStampToken time_token; EXPECT_EQ(ErrorCode::OK, GetReturnErrorCode(clock_->generateTimeStamp(challenge_, &time_token))); string output; EXPECT_EQ(ErrorCode::KEY_USER_NOT_AUTHENTICATED, Finish(message, {} /* signature */, &output, hat, time_token)); } } // Test use of a key that requires an auth token for each action on the operation, with // a per-operation challenge value included. TEST_P(AuthTest, AuthPerOperation) { if (!GatekeeperAvailable()) { GTEST_SKIP() << "No Gatekeeper available"; } // Create an AES key that requires authentication per-action. auto builder = AuthorizationSetBuilder() .AesEncryptionKey(256) .BlockMode(BlockMode::ECB) .Padding(PaddingMode::PKCS7) .Authorization(TAG_USER_SECURE_ID, sid_) .Authorization(TAG_USER_AUTH_TYPE, HardwareAuthenticatorType::PASSWORD); vector<uint8_t> keyblob; vector<KeyCharacteristics> key_characteristics; vector<Certificate> cert_chain; ASSERT_EQ(ErrorCode::OK, GenerateKey(builder, std::nullopt, &keyblob, &key_characteristics, &cert_chain)); // Attempt to use the AES key without authentication fails after begin. const string message = "Hello World!"; AuthorizationSet out_params; auto params = AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::PKCS7); EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, keyblob, params, &out_params)); string output; EXPECT_EQ(ErrorCode::KEY_USER_NOT_AUTHENTICATED, Finish(message, {} /* signature */, &output)); // Verify to get a HAT, but with an arbitrary challenge. const uint64_t unrelated_challenge = 42; const std::optional<HardwareAuthToken> unrelated_hat = doVerify(unrelated_challenge, handle_, password_); ASSERT_TRUE(unrelated_hat.has_value()); EXPECT_EQ(unrelated_hat->userId, sid_); // Attempt to use the AES key with an unrelated authentication fails after begin. EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, keyblob, params, &out_params, unrelated_hat.value())); EXPECT_EQ(ErrorCode::KEY_USER_NOT_AUTHENTICATED, Finish(message, {} /* signature */, &output, unrelated_hat.value())); // Now get a HAT with the challenge from an in-progress operation. EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, keyblob, params, &out_params)); const std::optional<HardwareAuthToken> hat = doVerify(challenge_, handle_, password_); ASSERT_TRUE(hat.has_value()); EXPECT_EQ(hat->userId, sid_); string ciphertext; EXPECT_EQ(ErrorCode::OK, Finish(message, {} /* signature */, &ciphertext, hat.value())); // Altering a single bit in the MAC means no auth. EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, keyblob, params, &out_params)); std::optional<HardwareAuthToken> dodgy_hat = doVerify(challenge_, handle_, password_); ASSERT_TRUE(dodgy_hat.has_value()); EXPECT_EQ(dodgy_hat->userId, sid_); ASSERT_GT(dodgy_hat->mac.size(), 0); dodgy_hat->mac[0] ^= 0x01; EXPECT_EQ(ErrorCode::KEY_USER_NOT_AUTHENTICATED, Finish(message, {} /* signature */, &ciphertext, hat.value())); } INSTANTIATE_KEYMINT_AIDL_TEST(AuthTest); } // namespace aidl::android::hardware::security::keymint::test
security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp +8 −6 Original line number Diff line number Diff line Loading @@ -552,12 +552,13 @@ ErrorCode KeyMintAidlTestBase::Begin(KeyPurpose purpose, const vector<uint8_t>& ErrorCode KeyMintAidlTestBase::Begin(KeyPurpose purpose, const vector<uint8_t>& key_blob, const AuthorizationSet& in_params, AuthorizationSet* out_params) { AuthorizationSet* out_params, std::optional<HardwareAuthToken> hat) { SCOPED_TRACE("Begin"); Status result; BeginResult out; result = keymint_->begin(purpose, key_blob, in_params.vector_data(), std::nullopt, &out); result = keymint_->begin(purpose, key_blob, in_params.vector_data(), hat, &out); if (result.isOk()) { *out_params = out.params; Loading Loading @@ -611,8 +612,9 @@ ErrorCode KeyMintAidlTestBase::Update(const string& input, string* output) { return GetReturnErrorCode(result); } ErrorCode KeyMintAidlTestBase::Finish(const string& input, const string& signature, string* output) { ErrorCode KeyMintAidlTestBase::Finish(const string& input, const string& signature, string* output, std::optional<HardwareAuthToken> hat, std::optional<secureclock::TimeStampToken> time_token) { SCOPED_TRACE("Finish"); Status result; Loading @@ -621,8 +623,8 @@ ErrorCode KeyMintAidlTestBase::Finish(const string& input, const string& signatu vector<uint8_t> oPut; result = op_->finish(vector<uint8_t>(input.begin(), input.end()), vector<uint8_t>(signature.begin(), signature.end()), {} /* authToken */, {} /* timestampToken */, {} /* confirmationToken */, &oPut); vector<uint8_t>(signature.begin(), signature.end()), hat, time_token, {} /* confirmationToken */, &oPut); if (result.isOk()) output->append(oPut.begin(), oPut.end()); Loading
security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h +5 −2 Original line number Diff line number Diff line Loading @@ -161,7 +161,8 @@ class KeyMintAidlTestBase : public ::testing::TestWithParam<string> { const AuthorizationSet& in_params, AuthorizationSet* out_params, std::shared_ptr<IKeyMintOperation>& op); ErrorCode Begin(KeyPurpose purpose, const vector<uint8_t>& key_blob, const AuthorizationSet& in_params, AuthorizationSet* out_params); const AuthorizationSet& in_params, AuthorizationSet* out_params, std::optional<HardwareAuthToken> hat = std::nullopt); ErrorCode Begin(KeyPurpose purpose, const AuthorizationSet& in_params, AuthorizationSet* out_params); ErrorCode Begin(KeyPurpose purpose, const AuthorizationSet& in_params); Loading @@ -169,7 +170,9 @@ class KeyMintAidlTestBase : public ::testing::TestWithParam<string> { ErrorCode UpdateAad(const string& input); ErrorCode Update(const string& input, string* output); ErrorCode Finish(const string& message, const string& signature, string* output); ErrorCode Finish(const string& message, const string& signature, string* output, std::optional<HardwareAuthToken> hat = std::nullopt, std::optional<secureclock::TimeStampToken> time_token = std::nullopt); ErrorCode Finish(const string& message, string* output) { return Finish(message, {} /* signature */, output); } Loading