The implementation of vts and default implementation to support ISecureClock and ISharedSecret AIDLs.
Test: atest VtsAidlSecureClockTargetTest, atest VtsAidlSharedSecretTargetTest
Bug: b/175136979, b/175141176
Change-Id: I4a0d25981d0172c0e2c8defc61b325eca6d6a029
diff --git a/security/secureclock/aidl/vts/functional/SecureClockAidlTest.cpp b/security/secureclock/aidl/vts/functional/SecureClockAidlTest.cpp
new file mode 100644
index 0000000..9ca1ee8
--- /dev/null
+++ b/security/secureclock/aidl/vts/functional/SecureClockAidlTest.cpp
@@ -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, ×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();
+}
\ No newline at end of file