blob: 9820a47eaf89503c4b36fda59eb171658615e069 [file] [log] [blame]
/*
* Copyright (C) 2017 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.
*/
#include <android-base/logging.h>
#include <android/security/keystore/IKeystoreService.h>
#include <android/system/wifi/keystore/1.0/IKeystore.h>
#include <binder/IServiceManager.h>
#include <binder/ProcessState.h>
#include <cutils/properties.h>
#include <gtest/gtest.h>
#include <hidl/GtestPrinter.h>
#include <hidl/ServiceManagement.h>
#include <keymasterV4_0/authorization_set.h>
#include <keystore/keystore_promises.h>
#include <private/android_filesystem_config.h>
#include <utils/String16.h>
using namespace std;
using namespace ::testing;
using namespace android;
using namespace android::binder;
using namespace android::security::keystore;
using namespace android::security::keymaster;
using android::security::keystore::IKeystoreService;
using android::system::wifi::keystore::V1_0::IKeystore;
int main(int argc, char** argv) {
// Start thread pool for Binder
android::ProcessState::self()->startThreadPool();
InitGoogleTest(&argc, argv);
int status = RUN_ALL_TESTS();
return status;
}
namespace {
enum KeyPurpose {
ENCRYPTION,
SIGNING,
};
// The fixture for testing the Wifi Keystore HAL
class WifiKeystoreHalTest : public TestWithParam<std::string> {
protected:
void SetUp() override {
keystore = IKeystore::getService(GetParam());
ASSERT_TRUE(keystore);
sp<android::IServiceManager> service_manager = android::defaultServiceManager();
sp<android::IBinder> keystore_binder =
service_manager->getService(String16(kKeystoreServiceName));
service = interface_cast<IKeystoreService>(keystore_binder);
ASSERT_TRUE(service);
resetState();
}
void TearDown() override { resetState(); }
bool isDebuggableBuild() {
char value[PROPERTY_VALUE_MAX] = {0};
property_get("ro.system.build.type", value, "");
if (strcmp(value, "userdebug") == 0) {
return true;
}
if (strcmp(value, "eng") == 0) {
return true;
}
return false;
}
/**
* Resets the relevant state of the system between tests
*/
void resetState() {
for (uid_t uid : {UID_SELF, AID_WIFI}) {
deleteKey(kTestKeyName, uid);
}
}
/**
* Delete a key if it exists.
*
* @param keyName: name of the key to delete
* @param uid: the uid to delete the key on behalf of. Use
* UID_SELF to use the process' uid.
*
* @return true iff the key existed and is now deleted, false otherwise.
*/
bool deleteKey(std::string keyName, uid_t uid) {
String16 keyName16(keyName.data(), keyName.size());
int32_t result;
auto binder_result = service->del(keyName16, uid, &result);
if (!binder_result.isOk()) {
cout << "deleteKey: failed binder call" << endl;
return false;
}
keystore::KeyStoreNativeReturnCode wrappedResult(result);
return wrappedResult.isOk();
}
/**
* Generate a key for a specific purpose.
*
* This generates a key which can be used either for signing
* or encryption. The signing key is setup to be used in
* the Wifi Keystore HAL's sign() call. The data
* about the key returning from its generation is discarded.
* If this returns 'true' the key generation has completed
* and the key is ready for use.
*
* @param keyName: name of the key to generate
* @param purpose: the purpose the generated key will support
* @param uid: the uid to generate the key on behalf of. Use
* UID_SELF to use the process' uid.
*
* @return true iff the key was successfully generated and is
* ready for use, false otherwise.
*/
bool generateKey(std::string keyName, KeyPurpose purpose, uid_t uid) {
constexpr uint32_t kAESKeySize = 256;
int32_t aidl_return;
vector<uint8_t> entropy;
keystore::AuthorizationSetBuilder key_parameters;
if (purpose == KeyPurpose::SIGNING) {
key_parameters.EcdsaSigningKey(kAESKeySize);
}
if (purpose == KeyPurpose::ENCRYPTION) {
key_parameters.AesEncryptionKey(kAESKeySize);
}
key_parameters.NoDigestOrPadding()
.Authorization(keystore::keymaster::TAG_BLOCK_MODE, keystore::keymaster::BlockMode::CBC)
.Authorization(keystore::keymaster::TAG_NO_AUTH_REQUIRED);
sp<keystore::KeyCharacteristicsPromise> promise(new keystore::KeyCharacteristicsPromise);
auto future = promise->get_future();
String16 keyName16(keyName.data(), keyName.size());
fflush(stdout);
auto binder_result = service->generateKey(
promise, keyName16, KeymasterArguments(key_parameters.hidl_data()), entropy,
uid, // create key for process' uid
0, // empty flags; pick default key provider
&aidl_return);
if (!binder_result.isOk()) {
cout << "generateKey: Failed binder call" << endl;
return false;
}
keystore::KeyStoreNativeReturnCode rc(aidl_return);
if (!rc.isOk()) {
cout << "generateKey: Failed to generate key" << endl;
return false;
}
auto [km_response, characteristics] = future.get();
return true;
}
/**
* Creates a TYPE_GENERIC key blob. This cannot be used for signing.
*
* @param keyName: name of the key to generate.
* @param uid: the uid to insert the key on behalf of. Use
* UID_SELF to use the process' uid.
*
* @returns true iff the key was successfully created, false otherwise.
*/
bool insert(std::string keyName, uid_t uid) {
int32_t aidl_return;
vector<uint8_t> item;
String16 keyName16(keyName.data(), keyName.size());
auto binder_result = service->insert(keyName16, item,
uid, // Use process' uid
0, // empty flags; pick default key provider
&aidl_return);
if (!binder_result.isOk()) {
cout << "insert: Failed binder call" << endl;
return false;
}
keystore::KeyStoreNativeReturnCode rc(aidl_return);
if (!rc.isOk()) {
cout << "insert: Failed to generate key" << endl;
return false;
}
return true;
}
constexpr static const char kKeystoreServiceName[] = "android.security.keystore";
constexpr static const char kTestKeyName[] = "TestKeyName";
constexpr static const int32_t UID_SELF = -1;
sp<IKeystore> keystore;
sp<IKeystoreService> service;
};
TEST_P(WifiKeystoreHalTest, Sign_nullptr_key_name) {
IKeystore::KeystoreStatusCode statusCode;
auto callback = [&statusCode](IKeystore::KeystoreStatusCode status,
const ::android::hardware::hidl_vec<uint8_t>& /*value*/) {
statusCode = status;
return;
};
::android::hardware::hidl_vec<uint8_t> dataToSign;
dataToSign.resize(100);
keystore->sign(nullptr, dataToSign, callback);
EXPECT_EQ(IKeystore::KeystoreStatusCode::ERROR_UNKNOWN, statusCode);
}
TEST_P(WifiKeystoreHalTest, Sign_empty_key_name) {
IKeystore::KeystoreStatusCode statusCode;
auto callback = [&statusCode](IKeystore::KeystoreStatusCode status,
const ::android::hardware::hidl_vec<uint8_t>& /*value*/) {
statusCode = status;
return;
};
::android::hardware::hidl_vec<uint8_t> dataToSign;
dataToSign.resize(100);
keystore->sign("", dataToSign, callback);
EXPECT_EQ(IKeystore::KeystoreStatusCode::ERROR_UNKNOWN, statusCode);
}
TEST_P(WifiKeystoreHalTest, Sign_empty_data) {
if (!isDebuggableBuild()) {
GTEST_SKIP() << "Device not running a debuggable build, cannot make test keys";
}
bool callbackInvoked = false;
auto callback = [&callbackInvoked](IKeystore::KeystoreStatusCode /*status*/,
const ::android::hardware::hidl_vec<uint8_t>& /*value*/) {
// The result is ignored; this callback is a no-op.
callbackInvoked = true;
return;
};
bool result = generateKey(kTestKeyName, KeyPurpose::SIGNING, AID_WIFI);
EXPECT_EQ(result, true);
// The data to sign is empty. The return code is not important, and the attempt could be
// interpreted as valid or an error case. The goal is to determine that the callback
// was invokved.
::android::hardware::hidl_vec<uint8_t> dataToSign;
keystore->sign(kTestKeyName, dataToSign, callback);
EXPECT_EQ(true, callbackInvoked);
}
TEST_P(WifiKeystoreHalTest, Sign_wrong_key_purpose) {
if (!isDebuggableBuild()) {
GTEST_SKIP() << "Device not running a debuggable build, cannot make test keys";
}
IKeystore::KeystoreStatusCode statusCode;
auto callback = [&statusCode](IKeystore::KeystoreStatusCode status,
const ::android::hardware::hidl_vec<uint8_t>& /*value*/) {
statusCode = status;
return;
};
// Create a key which cannot sign; any signing attempt should fail.
bool result = generateKey(kTestKeyName, KeyPurpose::ENCRYPTION, AID_WIFI);
EXPECT_EQ(result, true);
::android::hardware::hidl_vec<uint8_t> dataToSign;
dataToSign.resize(100);
keystore->sign(kTestKeyName, dataToSign, callback);
EXPECT_EQ(IKeystore::KeystoreStatusCode::ERROR_UNKNOWN, statusCode);
}
TEST_P(WifiKeystoreHalTest, Sign_wrong_key_type) {
if (!isDebuggableBuild()) {
GTEST_SKIP() << "Device not running a debuggable build, cannot make test keys";
}
IKeystore::KeystoreStatusCode statusCode;
auto callback = [&statusCode](IKeystore::KeystoreStatusCode status,
const ::android::hardware::hidl_vec<uint8_t>& /*value*/) {
statusCode = status;
return;
};
::android::hardware::hidl_vec<uint8_t> dataToSign;
// Generate a TYPE_GENERIC key instead of a TYPE_KEYMASTER_10 key.
// This also cannot be used to sign.
bool result = insert(kTestKeyName, AID_WIFI);
EXPECT_EQ(result, true);
keystore->sign(kTestKeyName, dataToSign, callback);
EXPECT_EQ(IKeystore::KeystoreStatusCode::ERROR_UNKNOWN, statusCode);
}
TEST_P(WifiKeystoreHalTest, Sign_success) {
if (!isDebuggableBuild()) {
GTEST_SKIP() << "Device not running a debuggable build, cannot make test keys";
}
IKeystore::KeystoreStatusCode statusCode;
auto callback = [&statusCode](IKeystore::KeystoreStatusCode status,
const ::android::hardware::hidl_vec<uint8_t>& /*value*/) {
statusCode = status;
return;
};
::android::hardware::hidl_vec<uint8_t> dataToSign;
bool result = generateKey(kTestKeyName, KeyPurpose::SIGNING, AID_WIFI);
EXPECT_EQ(result, true);
// With data the signing attempt should succeed
dataToSign.resize(100);
keystore->sign(kTestKeyName, dataToSign, callback);
EXPECT_EQ(IKeystore::KeystoreStatusCode::SUCCESS, statusCode);
result = deleteKey(kTestKeyName, AID_WIFI);
EXPECT_EQ(result, true);
}
TEST_P(WifiKeystoreHalTest, GetBlob_null_key_name) {
IKeystore::KeystoreStatusCode statusCode;
auto callback = [&statusCode](IKeystore::KeystoreStatusCode status,
const ::android::hardware::hidl_vec<uint8_t>& /*value*/) {
statusCode = status;
return;
};
// Attempting to get a blob on a non-existent key should fail.
statusCode = IKeystore::KeystoreStatusCode::SUCCESS;
keystore->getBlob(nullptr, callback);
EXPECT_EQ(IKeystore::KeystoreStatusCode::ERROR_UNKNOWN, statusCode);
}
TEST_P(WifiKeystoreHalTest, GetBlob_empty_key_name) {
IKeystore::KeystoreStatusCode statusCode;
auto callback = [&statusCode](IKeystore::KeystoreStatusCode status,
const ::android::hardware::hidl_vec<uint8_t>& /*value*/) {
statusCode = status;
return;
};
// Attempting to get a blob on a non-existent key should fail.
statusCode = IKeystore::KeystoreStatusCode::SUCCESS;
keystore->getBlob("", callback);
EXPECT_EQ(IKeystore::KeystoreStatusCode::ERROR_UNKNOWN, statusCode);
}
TEST_P(WifiKeystoreHalTest, GetBlob_missing_key) {
IKeystore::KeystoreStatusCode statusCode;
auto callback = [&statusCode](IKeystore::KeystoreStatusCode status,
const ::android::hardware::hidl_vec<uint8_t>& /*value*/) {
statusCode = status;
return;
};
// Attempting to get a blob on a non-existent key should fail.
statusCode = IKeystore::KeystoreStatusCode::SUCCESS;
keystore->getBlob(kTestKeyName, callback);
EXPECT_EQ(IKeystore::KeystoreStatusCode::ERROR_UNKNOWN, statusCode);
}
TEST_P(WifiKeystoreHalTest, GetBlob_wrong_user) {
if (!isDebuggableBuild()) {
GTEST_SKIP() << "Device not running a debuggable build, cannot make test keys";
}
IKeystore::KeystoreStatusCode statusCode;
auto callback = [&statusCode](IKeystore::KeystoreStatusCode status,
const ::android::hardware::hidl_vec<uint8_t>& /*value*/) {
statusCode = status;
return;
};
// The HAL is expecting the key to belong to the wifi user.
// If the key belongs to another user's space it should fail.
bool result = insert(kTestKeyName, UID_SELF);
EXPECT_EQ(result, true);
keystore->getBlob(kTestKeyName, callback);
EXPECT_EQ(IKeystore::KeystoreStatusCode::ERROR_UNKNOWN, statusCode);
}
TEST_P(WifiKeystoreHalTest, GetBlob_success) {
if (!isDebuggableBuild()) {
GTEST_SKIP() << "Device not running a debuggable build, cannot make test keys";
}
IKeystore::KeystoreStatusCode statusCode;
auto callback = [&statusCode](IKeystore::KeystoreStatusCode status,
const ::android::hardware::hidl_vec<uint8_t>& /*value*/) {
statusCode = status;
return;
};
// Accessing the key belonging to the wifi user should succeed.
bool result = insert(kTestKeyName, AID_WIFI);
EXPECT_EQ(result, true);
keystore->getBlob(kTestKeyName, callback);
EXPECT_EQ(IKeystore::KeystoreStatusCode::SUCCESS, statusCode);
result = deleteKey(kTestKeyName, AID_WIFI);
EXPECT_EQ(result, true);
}
TEST_P(WifiKeystoreHalTest, GetPublicKey_nullptr_key_name) {
IKeystore::KeystoreStatusCode statusCode;
auto callback = [&statusCode](IKeystore::KeystoreStatusCode status,
const ::android::hardware::hidl_vec<uint8_t>& /*value*/) {
statusCode = status;
return;
};
// Attempting to export a non-existent key should fail.
statusCode = IKeystore::KeystoreStatusCode::SUCCESS;
keystore->getPublicKey(nullptr, callback);
EXPECT_EQ(IKeystore::KeystoreStatusCode::ERROR_UNKNOWN, statusCode);
}
TEST_P(WifiKeystoreHalTest, GetPublicKey_empty_key_name) {
IKeystore::KeystoreStatusCode statusCode;
auto callback = [&statusCode](IKeystore::KeystoreStatusCode status,
const ::android::hardware::hidl_vec<uint8_t>& /*value*/) {
statusCode = status;
return;
};
// Attempting to export a non-existent key should fail.
statusCode = IKeystore::KeystoreStatusCode::SUCCESS;
keystore->getPublicKey("", callback);
EXPECT_EQ(IKeystore::KeystoreStatusCode::ERROR_UNKNOWN, statusCode);
}
TEST_P(WifiKeystoreHalTest, GetPublicKey_wrong_key_name) {
IKeystore::KeystoreStatusCode statusCode;
auto callback = [&statusCode](IKeystore::KeystoreStatusCode status,
const ::android::hardware::hidl_vec<uint8_t>& /*value*/) {
statusCode = status;
return;
};
// Attempting to export a non-existent key should fail.
statusCode = IKeystore::KeystoreStatusCode::SUCCESS;
keystore->getPublicKey(kTestKeyName, callback);
EXPECT_EQ(IKeystore::KeystoreStatusCode::ERROR_UNKNOWN, statusCode);
}
TEST_P(WifiKeystoreHalTest, GetPublicKey_wrong_user) {
if (!isDebuggableBuild()) {
GTEST_SKIP() << "Device not running a debuggable build, cannot make test keys";
}
IKeystore::KeystoreStatusCode statusCode;
auto callback = [&statusCode](IKeystore::KeystoreStatusCode status,
const ::android::hardware::hidl_vec<uint8_t>& /*value*/) {
statusCode = status;
return;
};
// The HAL is expecting the key to belong to the wifi user.
// If the key belongs to another user's space (e.g. root) it should
// not be accessible and should fail.
bool result = generateKey(kTestKeyName, KeyPurpose::SIGNING, UID_SELF);
EXPECT_EQ(result, true);
keystore->getPublicKey(kTestKeyName, callback);
EXPECT_EQ(IKeystore::KeystoreStatusCode::ERROR_UNKNOWN, statusCode);
result = deleteKey(kTestKeyName, UID_SELF);
EXPECT_EQ(result, true);
}
TEST_P(WifiKeystoreHalTest, GetPublicKey_wrong_key_type) {
if (!isDebuggableBuild()) {
GTEST_SKIP() << "Device not running a debuggable build, cannot make test keys";
}
IKeystore::KeystoreStatusCode statusCode;
auto callback = [&statusCode](IKeystore::KeystoreStatusCode status,
const ::android::hardware::hidl_vec<uint8_t>& /*value*/) {
statusCode = status;
return;
};
// A TYPE_GENERIC key (instead of a TYPE_KEYMASTER_10 key)
// should also fail.
bool result = insert(kTestKeyName, AID_WIFI);
EXPECT_EQ(result, true);
keystore->getPublicKey(kTestKeyName, callback);
EXPECT_EQ(IKeystore::KeystoreStatusCode::ERROR_UNKNOWN, statusCode);
result = deleteKey(kTestKeyName, AID_WIFI);
EXPECT_EQ(result, true);
}
TEST_P(WifiKeystoreHalTest, GetPublicKey_success) {
if (!isDebuggableBuild()) {
GTEST_SKIP() << "Device not running a debuggable build, cannot make test keys";
}
IKeystore::KeystoreStatusCode statusCode;
auto callback = [&statusCode](IKeystore::KeystoreStatusCode status,
const ::android::hardware::hidl_vec<uint8_t>& /*value*/) {
statusCode = status;
return;
};
// Accessing the key belonging to the wifi uid should succeed.
bool result = generateKey(kTestKeyName, KeyPurpose::SIGNING, AID_WIFI);
EXPECT_EQ(result, true);
keystore->getPublicKey(kTestKeyName, callback);
EXPECT_EQ(IKeystore::KeystoreStatusCode::SUCCESS, statusCode);
result = deleteKey(kTestKeyName, AID_WIFI);
EXPECT_EQ(result, true);
}
INSTANTIATE_TEST_SUITE_P(
PerInstance, WifiKeystoreHalTest,
testing::ValuesIn(android::hardware::getAllHalInstanceNames(IKeystore::descriptor)),
android::hardware::PrintInstanceNameToString);
} // namespace