Merge "The implementation of vts and default implementation to support ISecureClock and ISharedSecret AIDLs. Test: atest VtsAidlSecureClockTargetTest, atest VtsAidlSharedSecretTargetTest Bug: b/175136979, b/175141176"

This commit is contained in:
Treehugger Robot 2021-02-12 02:42:35 +00:00 committed by Gerrit Code Review
commit 9717a37b6a
15 changed files with 707 additions and 18 deletions

View file

@ -2,7 +2,11 @@ cc_binary {
name: "android.hardware.security.keymint-service",
relative_install_path: "hw",
init_rc: ["android.hardware.security.keymint-service.rc"],
vintf_fragments: ["android.hardware.security.keymint-service.xml"],
vintf_fragments: [
"android.hardware.security.keymint-service.xml",
"android.hardware.security.sharedsecret-service.xml",
"android.hardware.security.secureclock-service.xml",
],
vendor: true,
cflags: [
"-Wall",
@ -10,6 +14,8 @@ cc_binary {
],
shared_libs: [
"android.hardware.security.keymint-V1-ndk_platform",
"android.hardware.security.sharedsecret-unstable-ndk_platform",
"android.hardware.security.secureclock-unstable-ndk_platform",
"libbase",
"libbinder_ndk",
"libcppbor",

View file

@ -0,0 +1,6 @@
<manifest version="1.0" type="device">
<hal format="aidl">
<name>android.hardware.security.secureclock</name>
<fqname>ISecureClock/default</fqname>
</hal>
</manifest>

View file

@ -0,0 +1,6 @@
<manifest version="1.0" type="device">
<hal format="aidl">
<name>android.hardware.security.sharedsecret</name>
<fqname>ISharedSecret/default</fqname>
</hal>
</manifest>

View file

@ -21,25 +21,38 @@
#include <android/binder_process.h>
#include <AndroidKeyMintDevice.h>
#include <AndroidSecureClock.h>
#include <AndroidSharedSecret.h>
#include <keymaster/soft_keymaster_logger.h>
using aidl::android::hardware::security::keymint::AndroidKeyMintDevice;
using aidl::android::hardware::security::keymint::SecurityLevel;
using aidl::android::hardware::security::secureclock::AndroidSecureClock;
using aidl::android::hardware::security::sharedsecret::AndroidSharedSecret;
template <typename T, class... Args>
std::shared_ptr<T> addService(Args&&... args) {
std::shared_ptr<T> ser = ndk::SharedRefBase::make<T>(std::forward<Args>(args)...);
auto instanceName = std::string(T::descriptor) + "/default";
LOG(INFO) << "adding keymint service instance: " << instanceName;
binder_status_t status =
AServiceManager_addService(ser->asBinder().get(), instanceName.c_str());
CHECK(status == STATUS_OK);
return ser;
}
int main() {
// Zero threads seems like a useless pool, but below we'll join this thread to it, increasing
// the pool size to 1.
ABinderProcess_setThreadPoolMaxThreadCount(0);
// Add Keymint Service
std::shared_ptr<AndroidKeyMintDevice> keyMint =
ndk::SharedRefBase::make<AndroidKeyMintDevice>(SecurityLevel::SOFTWARE);
keymaster::SoftKeymasterLogger logger;
const auto instanceName = std::string(AndroidKeyMintDevice::descriptor) + "/default";
LOG(INFO) << "instance: " << instanceName;
binder_status_t status =
AServiceManager_addService(keyMint->asBinder().get(), instanceName.c_str());
CHECK(status == STATUS_OK);
addService<AndroidKeyMintDevice>(SecurityLevel::SOFTWARE);
// Add Secure Clock Service
addService<AndroidSecureClock>(keyMint);
// Add Shared Secret Service
addService<AndroidSharedSecret>(keyMint);
ABinderProcess_joinThreadPool();
return EXIT_FAILURE; // should not reach
}

View file

@ -1,4 +1,17 @@
///////////////////////////////////////////////////////////////////////////////
/*
* Copyright (C) 2020 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.
* limitations under the License.
*////////////////////////////////////////////////////////////////////////////////
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
///////////////////////////////////////////////////////////////////////////////
@ -20,5 +33,5 @@ package android.hardware.security.secureclock;
@VintfStability
interface ISecureClock {
android.hardware.security.secureclock.TimeStampToken generateTimeStamp(in long challenge);
const String TIME_STAMP_MAC_LABEL = "Time Verification";
const String TIME_STAMP_MAC_LABEL = "Auth Verification";
}

View file

@ -1,4 +1,18 @@
///////////////////////////////////////////////////////////////////////////////
/*
* Copyright (C) 2020 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.
*////////////////////////////////////////////////////////////////////////////////
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
///////////////////////////////////////////////////////////////////////////////

View file

@ -1,4 +1,18 @@
///////////////////////////////////////////////////////////////////////////////
/*
* Copyright 2020 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.
*////////////////////////////////////////////////////////////////////////////////
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
///////////////////////////////////////////////////////////////////////////////

View file

@ -33,7 +33,7 @@ interface ISecureClock {
* String used as context in the HMAC computation signing the generated time stamp.
* See TimeStampToken.mac for details.
*/
const String TIME_STAMP_MAC_LABEL = "Time Verification";
const String TIME_STAMP_MAC_LABEL = "Auth Verification";
/**
* Generates an authenticated timestamp.

View file

@ -39,18 +39,20 @@ parcelable TimeStampToken {
* 32-byte HMAC-SHA256 of the above values, computed as:
*
* HMAC(H,
* ISecureClock.TIME_STAMP_MAC_LABEL || challenge || timestamp)
* ISecureClock.TIME_STAMP_MAC_LABEL || challenge || timestamp || securityLevel )
*
* where:
*
* ``ISecureClock.TIME_STAMP_MAC_LABEL'' is a sting constant defined in ISecureClock.aidl.
*
* ``H'' is the shared HMAC key (see computeSharedHmac() in ISharedHmacSecret).
* ``H'' is the shared HMAC key (see computeSharedHmac() in ISharedSecret).
*
* ``||'' represents concatenation
*
* The representation of challenge and timestamp is as 64-bit unsigned integers in big-endian
* order. securityLevel is represented as a 32-bit unsigned integer in big-endian order.
* order. SecurityLevel is represented as a 32-bit unsigned integer in big-endian order as
* described in android.hardware.security.keymint.SecurityLevel. It represents the security
* level of the secure clock environment.
*/
byte[] mac;
}

View file

@ -0,0 +1,43 @@
//
// Copyright (C) 2020 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.
//
cc_test {
name: "VtsAidlSecureClockTargetTest",
defaults: [
"VtsHalTargetTestDefaults",
"use_libaidlvintf_gtest_helper_static",
],
cflags: [
"-Wall",
"-Wextra",
],
srcs: [
"SecureClockAidlTest.cpp",
],
shared_libs: [
"libbinder_ndk",
"libcrypto",
"libkeymint",
],
static_libs: [
"android.hardware.security.keymint-unstable-ndk_platform",
"android.hardware.security.secureclock-unstable-ndk_platform",
],
test_suites: [
"general-tests",
"vts",
],
}

View file

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2020 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.
-->
<configuration description="Runs VtsAidlSecureClockTargetTest.">
<option name="test-suite-tag" value="apct" />
<option name="test-suite-tag" value="apct-native" />
<target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
</target_preparer>
<target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
<option name="cleanup" value="true" />
<option name="push"
value="VtsAidlSecureClockTargetTest->/data/local/tmp/VtsAidlSecureClockTargetTest" />
</target_preparer>
<test class="com.android.tradefed.testtype.GTest" >
<option name="native-test-device-path" value="/data/local/tmp" />
<option name="module-name" value="VtsAidlSecureClockTargetTest" />
<option name="native-test-timeout" value="900000"/>
</test>
</configuration>

View file

@ -0,0 +1,193 @@
/*
* Copyright (C) 2020 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 "secureclock_test"
#include <android-base/logging.h>
#include <aidl/Gtest.h>
#include <aidl/Vintf.h>
#include <aidl/android/hardware/security/keymint/ErrorCode.h>
#include <aidl/android/hardware/security/secureclock/ISecureClock.h>
#include <android/binder_manager.h>
#include <binder/ProcessState.h>
#include <gtest/gtest.h>
#include <vector>
namespace aidl::android::hardware::security::secureclock::test {
using Status = ::ndk::ScopedAStatus;
using ::aidl::android::hardware::security::keymint::ErrorCode;
using ::std::shared_ptr;
using ::std::string;
using ::std::vector;
class SecureClockAidlTest : public ::testing::TestWithParam<string> {
public:
struct TimestampTokenResult {
ErrorCode error;
TimeStampToken token;
};
TimestampTokenResult getTimestampToken(int64_t in_challenge) {
TimestampTokenResult result;
result.error =
GetReturnErrorCode(secureClock_->generateTimeStamp(in_challenge, &result.token));
return result;
}
uint64_t getTime() {
struct timespec timespec;
EXPECT_EQ(0, clock_gettime(CLOCK_BOOTTIME, &timespec));
return timespec.tv_sec * 1000 + timespec.tv_nsec / 1000000;
}
int sleep_ms(uint32_t milliseconds) {
struct timespec sleep_time = {static_cast<time_t>(milliseconds / 1000),
static_cast<long>(milliseconds % 1000) * 1000000};
while (sleep_time.tv_sec || sleep_time.tv_nsec) {
if (nanosleep(&sleep_time /* to wait */,
&sleep_time /* remaining (on interrruption) */) == 0) {
sleep_time = {};
} else {
if (errno != EINTR) return errno;
}
}
return 0;
}
ErrorCode GetReturnErrorCode(const Status& result) {
if (result.isOk()) return ErrorCode::OK;
if (result.getExceptionCode() == EX_SERVICE_SPECIFIC) {
return static_cast<ErrorCode>(result.getServiceSpecificError());
}
return ErrorCode::UNKNOWN_ERROR;
}
void InitializeSecureClock(std::shared_ptr<ISecureClock> secureClock) {
ASSERT_NE(secureClock, nullptr);
secureClock_ = secureClock;
}
ISecureClock& secureClock() { return *secureClock_; }
static vector<string> build_params() {
auto params = ::android::getAidlHalInstanceNames(ISecureClock::descriptor);
return params;
}
void SetUp() override {
if (AServiceManager_isDeclared(GetParam().c_str())) {
::ndk::SpAIBinder binder(AServiceManager_waitForService(GetParam().c_str()));
InitializeSecureClock(ISecureClock::fromBinder(binder));
} else {
InitializeSecureClock(nullptr);
}
}
void TearDown() override {}
private:
std::shared_ptr<ISecureClock> secureClock_;
};
/*
* The precise capabilities required to generate TimeStampToken will vary depending on the specific
* vendor implementations. The only thing we really can test is that tokens can be created by
* secureclock services, and that the timestamps increase as expected.
*/
TEST_P(SecureClockAidlTest, TestCreation) {
auto result1 = getTimestampToken(1 /* challenge */);
auto result1_time = getTime();
EXPECT_EQ(ErrorCode::OK, result1.error);
EXPECT_EQ(1U, result1.token.challenge);
EXPECT_GT(result1.token.timestamp.milliSeconds, 0U);
unsigned long time_to_sleep = 200;
sleep_ms(time_to_sleep);
auto result2 = getTimestampToken(2 /* challenge */);
auto result2_time = getTime();
EXPECT_EQ(ErrorCode::OK, result2.error);
EXPECT_EQ(2U, result2.token.challenge);
EXPECT_GT(result2.token.timestamp.milliSeconds, 0U);
auto host_time_delta = result2_time - result1_time;
EXPECT_GE(host_time_delta, time_to_sleep)
<< "We slept for " << time_to_sleep << " ms, the clock must have advanced by that much";
EXPECT_LE(host_time_delta, time_to_sleep + 100)
<< "The getTimestampToken call took " << (host_time_delta - time_to_sleep)
<< " ms? That's awful!";
EXPECT_GE(result2.token.timestamp.milliSeconds, result1.token.timestamp.milliSeconds);
unsigned long km_time_delta =
result2.token.timestamp.milliSeconds - result1.token.timestamp.milliSeconds;
// 20 ms of slop just to avoid test flakiness.
EXPECT_LE(host_time_delta, km_time_delta + 20);
EXPECT_LE(km_time_delta, host_time_delta + 20);
ASSERT_EQ(result1.token.mac.size(), result2.token.mac.size());
ASSERT_NE(0,
memcmp(result1.token.mac.data(), result2.token.mac.data(), result1.token.mac.size()));
}
/*
* Test that the mac changes when the time stamp changes. This is does not guarantee that the time
* stamp is included in the mac but on failure we know that it is not. Other than in the test
* case above we call getTimestampToken with the exact same set of parameters.
*/
TEST_P(SecureClockAidlTest, MacChangesOnChangingTimestamp) {
auto result1 = getTimestampToken(0 /* challenge */);
auto result1_time = getTime();
EXPECT_EQ(ErrorCode::OK, result1.error);
EXPECT_EQ(0U, result1.token.challenge);
EXPECT_GT(result1.token.timestamp.milliSeconds, 0U);
unsigned long time_to_sleep = 200;
sleep_ms(time_to_sleep);
auto result2 = getTimestampToken(1 /* challenge */);
auto result2_time = getTime();
EXPECT_EQ(ErrorCode::OK, result2.error);
EXPECT_EQ(1U, result2.token.challenge);
EXPECT_GT(result2.token.timestamp.milliSeconds, 0U);
auto host_time_delta = result2_time - result1_time;
EXPECT_GE(host_time_delta, time_to_sleep)
<< "We slept for " << time_to_sleep << " ms, the clock must have advanced by that much";
EXPECT_LE(host_time_delta, time_to_sleep + 100)
<< "The getTimestampToken call took " << (host_time_delta - time_to_sleep)
<< " ms? That's awful!";
EXPECT_GE(result2.token.timestamp.milliSeconds, result1.token.timestamp.milliSeconds);
unsigned long km_time_delta =
result2.token.timestamp.milliSeconds - result1.token.timestamp.milliSeconds;
EXPECT_LE(host_time_delta, km_time_delta + 20);
EXPECT_LE(km_time_delta, host_time_delta + 20);
ASSERT_EQ(result1.token.mac.size(), result2.token.mac.size());
ASSERT_NE(0,
memcmp(result1.token.mac.data(), result2.token.mac.data(), result1.token.mac.size()));
}
INSTANTIATE_TEST_SUITE_P(PerInstance, SecureClockAidlTest,
testing::ValuesIn(SecureClockAidlTest::build_params()),
::android::PrintInstanceNameToString);
} // namespace aidl::android::hardware::security::secureclock::test
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

View file

@ -0,0 +1,43 @@
//
// Copyright (C) 2020 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.
//
cc_test {
name: "VtsAidlSharedSecretTargetTest",
defaults: [
"VtsHalTargetTestDefaults",
"use_libaidlvintf_gtest_helper_static",
],
srcs: [
"SharedSecretAidlTest.cpp",
],
cflags: [
"-Wall",
"-Wextra",
],
shared_libs: [
"libbinder_ndk",
"libcrypto",
"libkeymint",
],
static_libs: [
"android.hardware.security.keymint-unstable-ndk_platform",
"android.hardware.security.sharedsecret-unstable-ndk_platform",
],
test_suites: [
"general-tests",
"vts",
],
}

View file

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2020 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.
-->
<configuration description="Runs VtsAidlSharedSecretTargetTest.">
<option name="test-suite-tag" value="apct" />
<option name="test-suite-tag" value="apct-native" />
<target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
</target_preparer>
<target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
<option name="cleanup" value="true" />
<option name="push"
value="VtsAidlSharedSecretTargetTest->/data/local/tmp/VtsAidlSharedSecretTargetTest" />
</target_preparer>
<test class="com.android.tradefed.testtype.GTest" >
<option name="native-test-device-path" value="/data/local/tmp" />
<option name="module-name" value="VtsAidlSharedSecretTargetTest" />
<option name="native-test-timeout" value="900000"/>
</test>
</configuration>

View file

@ -0,0 +1,268 @@
/*
* Copyright (C) 2020 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 "sharedsecret_test"
#include <android-base/logging.h>
#include <aidl/Vintf.h>
#include <aidl/android/hardware/security/keymint/ErrorCode.h>
#include <aidl/android/hardware/security/sharedsecret/ISharedSecret.h>
#include <android/binder_manager.h>
#include <gtest/gtest.h>
#include <vector>
namespace aidl::android::hardware::security::sharedsecret::test {
using ::aidl::android::hardware::security::keymint::ErrorCode;
using ::std::shared_ptr;
using ::std::vector;
using Status = ::ndk::ScopedAStatus;
class SharedSecretAidlTest : public ::testing::Test {
public:
struct GetParamsResult {
ErrorCode error;
SharedSecretParameters params;
auto tie() { return std::tie(error, params); }
};
struct ComputeResult {
ErrorCode error;
vector<uint8_t> sharing_check;
auto tie() { return std::tie(error, sharing_check); }
};
GetParamsResult getSharedSecretParameters(shared_ptr<ISharedSecret>& sharedSecret) {
SharedSecretParameters params;
auto error = GetReturnErrorCode(sharedSecret->getSharedSecretParameters(&params));
EXPECT_EQ(ErrorCode::OK, error);
GetParamsResult result;
result.tie() = std::tie(error, params);
return result;
}
vector<SharedSecretParameters> getAllSharedSecretParameters() {
vector<SharedSecretParameters> paramsVec;
for (auto& sharedSecret : allSharedSecrets_) {
auto result = getSharedSecretParameters(sharedSecret);
EXPECT_EQ(ErrorCode::OK, result.error);
if (result.error == ErrorCode::OK) paramsVec.push_back(std::move(result.params));
}
return paramsVec;
}
ComputeResult computeSharedSecret(shared_ptr<ISharedSecret>& sharedSecret,
const vector<SharedSecretParameters>& params) {
std::vector<uint8_t> sharingCheck;
auto error = GetReturnErrorCode(sharedSecret->computeSharedSecret(params, &sharingCheck));
ComputeResult result;
result.tie() = std::tie(error, sharingCheck);
return result;
}
vector<ComputeResult> computeAllSharedSecrets(const vector<SharedSecretParameters>& params) {
vector<ComputeResult> result;
for (auto& sharedSecret : allSharedSecrets_) {
result.push_back(computeSharedSecret(sharedSecret, params));
}
return result;
}
vector<vector<uint8_t>> copyNonces(const vector<SharedSecretParameters>& paramsVec) {
vector<vector<uint8_t>> nonces;
for (auto& param : paramsVec) {
nonces.push_back(param.nonce);
}
return nonces;
}
void verifyResponses(const vector<uint8_t>& expected, const vector<ComputeResult>& responses) {
for (auto& response : responses) {
EXPECT_EQ(ErrorCode::OK, response.error);
EXPECT_EQ(expected, response.sharing_check) << "Sharing check values should match.";
}
}
ErrorCode GetReturnErrorCode(const Status& result) {
if (result.isOk()) return ErrorCode::OK;
if (result.getExceptionCode() == EX_SERVICE_SPECIFIC) {
return static_cast<ErrorCode>(result.getServiceSpecificError());
}
return ErrorCode::UNKNOWN_ERROR;
}
static shared_ptr<ISharedSecret> getSharedSecretService(const char* name) {
if (AServiceManager_isDeclared(name)) {
::ndk::SpAIBinder binder(AServiceManager_waitForService(name));
return ISharedSecret::fromBinder(binder);
}
return nullptr;
}
const vector<shared_ptr<ISharedSecret>>& allSharedSecrets() { return allSharedSecrets_; }
static void SetUpTestCase() {
if (allSharedSecrets_.empty()) {
auto names = ::android::getAidlHalInstanceNames(ISharedSecret::descriptor);
for (const auto& name : names) {
auto servicePtr = getSharedSecretService(name.c_str());
if (servicePtr != nullptr) allSharedSecrets_.push_back(std::move(servicePtr));
}
}
}
static void TearDownTestCase() {}
void SetUp() override {}
void TearDown() override {}
private:
static vector<shared_ptr<ISharedSecret>> allSharedSecrets_;
};
vector<shared_ptr<ISharedSecret>> SharedSecretAidlTest::allSharedSecrets_;
TEST_F(SharedSecretAidlTest, GetParameters) {
auto sharedSecrets = allSharedSecrets();
for (auto sharedSecret : sharedSecrets) {
auto result1 = getSharedSecretParameters(sharedSecret);
EXPECT_EQ(ErrorCode::OK, result1.error);
auto result2 = getSharedSecretParameters(sharedSecret);
EXPECT_EQ(ErrorCode::OK, result2.error);
ASSERT_EQ(result1.params.seed, result2.params.seed)
<< "A given shared secret service should always return the same seed.";
ASSERT_EQ(result1.params.nonce, result2.params.nonce)
<< "A given shared secret service should always return the same nonce until "
"restart.";
}
}
TEST_F(SharedSecretAidlTest, ComputeSharedSecret) {
auto params = getAllSharedSecretParameters();
ASSERT_EQ(allSharedSecrets().size(), params.size())
<< "One or more shared secret services failed to provide parameters.";
auto nonces = copyNonces(params);
EXPECT_EQ(allSharedSecrets().size(), nonces.size());
std::sort(nonces.begin(), nonces.end());
std::unique(nonces.begin(), nonces.end());
EXPECT_EQ(allSharedSecrets().size(), nonces.size());
auto responses = computeAllSharedSecrets(params);
ASSERT_GT(responses.size(), 0U);
verifyResponses(responses[0].sharing_check, responses);
// Do it a second time. Should get the same answers.
params = getAllSharedSecretParameters();
ASSERT_EQ(allSharedSecrets().size(), params.size())
<< "One or more shared secret services failed to provide parameters.";
responses = computeAllSharedSecrets(params);
ASSERT_GT(responses.size(), 0U);
ASSERT_EQ(32U, responses[0].sharing_check.size());
verifyResponses(responses[0].sharing_check, responses);
}
template <class F>
class final_action {
public:
explicit final_action(F f) : f_(std::move(f)) {}
~final_action() { f_(); }
private:
F f_;
};
template <class F>
inline final_action<F> finally(const F& f) {
return final_action<F>(f);
}
TEST_F(SharedSecretAidlTest, ComputeSharedSecretCorruptNonce) {
auto fixup_hmac = finally([&]() { computeAllSharedSecrets(getAllSharedSecretParameters()); });
auto params = getAllSharedSecretParameters();
ASSERT_EQ(allSharedSecrets().size(), params.size())
<< "One or more shared secret services failed to provide parameters.";
// All should be well in the normal case
auto responses = computeAllSharedSecrets(params);
ASSERT_GT(responses.size(), 0U);
vector<uint8_t> correct_response = responses[0].sharing_check;
verifyResponses(correct_response, responses);
// Pick a random param, a random byte within the param's nonce, and a random bit within
// the byte. Flip that bit.
size_t param_to_tweak = rand() % params.size();
uint8_t byte_to_tweak = rand() % sizeof(params[param_to_tweak].nonce);
uint8_t bit_to_tweak = rand() % 8;
params[param_to_tweak].nonce[byte_to_tweak] ^= (1 << bit_to_tweak);
responses = computeAllSharedSecrets(params);
for (size_t i = 0; i < responses.size(); ++i) {
if (i == param_to_tweak) {
EXPECT_EQ(ErrorCode::INVALID_ARGUMENT, responses[i].error)
<< "Shared secret service that provided tweaked param should fail to compute "
"shared secret";
} else {
EXPECT_EQ(ErrorCode::OK, responses[i].error) << "Others should succeed";
EXPECT_NE(correct_response, responses[i].sharing_check)
<< "Others should calculate a different shared secret, due to the tweaked "
"nonce.";
}
}
}
TEST_F(SharedSecretAidlTest, ComputeSharedSecretCorruptSeed) {
auto fixup_hmac = finally([&]() { computeAllSharedSecrets(getAllSharedSecretParameters()); });
auto params = getAllSharedSecretParameters();
ASSERT_EQ(allSharedSecrets().size(), params.size())
<< "One or more shared secret service failed to provide parameters.";
// All should be well in the normal case
auto responses = computeAllSharedSecrets(params);
ASSERT_GT(responses.size(), 0U);
vector<uint8_t> correct_response = responses[0].sharing_check;
verifyResponses(correct_response, responses);
// Pick a random param and modify the seed. We just increase the seed length by 1. It doesn't
// matter what value is in the additional byte; it changes the seed regardless.
auto param_to_tweak = rand() % params.size();
auto& to_tweak = params[param_to_tweak].seed;
ASSERT_TRUE(to_tweak.size() == 32 || to_tweak.size() == 0);
if (!to_tweak.size()) {
to_tweak.resize(32); // Contents don't matter; a little randomization is nice.
}
to_tweak[0]++;
responses = computeAllSharedSecrets(params);
for (size_t i = 0; i < responses.size(); ++i) {
if (i == param_to_tweak) {
EXPECT_EQ(ErrorCode::INVALID_ARGUMENT, responses[i].error)
<< "Shared secret service that provided tweaked param should fail to compute "
"shared secret";
} else {
EXPECT_EQ(ErrorCode::OK, responses[i].error) << "Others should succeed";
EXPECT_NE(correct_response, responses[i].sharing_check)
<< "Others should calculate a different shared secret, due to the tweaked "
"nonce.";
}
}
}
} // namespace aidl::android::hardware::security::sharedsecret::test
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}