| /* |
| * 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, ×pec)); |
| 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(); |
| } |