diff options
Diffstat (limited to 'security/keymint/aidl/vts/functional/VerificationTokenTest.cpp')
-rw-r--r-- | security/keymint/aidl/vts/functional/VerificationTokenTest.cpp | 168 |
1 files changed, 168 insertions, 0 deletions
diff --git a/security/keymint/aidl/vts/functional/VerificationTokenTest.cpp b/security/keymint/aidl/vts/functional/VerificationTokenTest.cpp new file mode 100644 index 0000000000..0b1eccddfd --- /dev/null +++ b/security/keymint/aidl/vts/functional/VerificationTokenTest.cpp @@ -0,0 +1,168 @@ +/* + * 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. + */ + +#include "KeyMintAidlTestBase.h" + +namespace aidl::android::hardware::security::keymint::test { + +class VerificationTokenTest : public KeyMintAidlTestBase { + protected: + struct VerifyAuthorizationResult { + ErrorCode error; + VerificationToken token; + }; + + VerifyAuthorizationResult verifyAuthorization(uint64_t operationHandle, + const HardwareAuthToken& authToken) { + VerifyAuthorizationResult result; + + Status err; + err = keyMint().verifyAuthorization(operationHandle, // + authToken, // + &result.token); + + result.error = GetReturnErrorCode(err); + 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; + } +}; + +/* + * VerificationTokens exist to facilitate cross-KeyMint verification of requirements. As + * such, the precise capabilities required will vary depending on the specific vendor + * implementations. Essentially, VerificationTokens are a "hook" to enable vendor + * implementations to communicate, so the precise usage is defined by those vendors. The only + * thing we really can test is that tokens can be created by TEE keyMints, and that the + * timestamps increase as expected. + */ +TEST_P(VerificationTokenTest, TestCreation) { + auto result1 = verifyAuthorization(1 /* operation handle */, HardwareAuthToken()); + auto result1_time = getTime(); + + if (SecLevel() == SecurityLevel::STRONGBOX) { + // StrongBox should not implement verifyAuthorization. + EXPECT_EQ(ErrorCode::UNIMPLEMENTED, result1.error); + return; + } + + ASSERT_EQ(ErrorCode::OK, result1.error); + EXPECT_EQ(1U, result1.token.challenge); + EXPECT_EQ(SecLevel(), result1.token.securityLevel); + EXPECT_GT(result1.token.timestamp.milliSeconds, 0U); + + constexpr uint32_t time_to_sleep = 200; + sleep_ms(time_to_sleep); + + auto result2 = verifyAuthorization(2 /* operation handle */, HardwareAuthToken()); + + auto result2_time = getTime(); + ASSERT_EQ(ErrorCode::OK, result2.error); + EXPECT_EQ(2U, result2.token.challenge); + EXPECT_EQ(SecLevel(), result2.token.securityLevel); + + 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 + 20) + << "The verifyAuthorization call took " << (host_time_delta - time_to_sleep) + << " ms? That's awful!"; + + auto km_time_delta = + result2.token.timestamp.milliSeconds - result1.token.timestamp.milliSeconds; + + // If not too much else is going on on the system, the time delta should be quite close. Allow + // 2 ms of slop just to avoid test flakiness. + // + // TODO(swillden): see if we can output values so they can be gathered across many runs and + // report if times aren't nearly always <1ms apart. + EXPECT_LE(host_time_delta, km_time_delta + 2); + EXPECT_LE(km_time_delta, host_time_delta + 2); + 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 verifyAuthorization with the exact same set of parameters. + */ +TEST_P(VerificationTokenTest, MacChangesOnChangingTimestamp) { + auto result1 = verifyAuthorization(0 /* operation handle */, HardwareAuthToken()); + auto result1_time = getTime(); + + if (SecLevel() == SecurityLevel::STRONGBOX) { + // StrongBox should not implement verifyAuthorization. + EXPECT_EQ(ErrorCode::UNIMPLEMENTED, result1.error); + return; + } + + EXPECT_EQ(ErrorCode::OK, result1.error); + EXPECT_EQ(0U, result1.token.challenge); + EXPECT_EQ(SecLevel(), result1.token.securityLevel); + EXPECT_GT(result1.token.timestamp.milliSeconds, 0U); + + constexpr uint32_t time_to_sleep = 200; + sleep_ms(time_to_sleep); + + auto result2 = verifyAuthorization(0 /* operation handle */, HardwareAuthToken()); + // ASSERT_TRUE(result2.callSuccessful); + auto result2_time = getTime(); + EXPECT_EQ(ErrorCode::OK, result2.error); + EXPECT_EQ(0U, result2.token.challenge); + EXPECT_EQ(SecLevel(), result2.token.securityLevel); + + 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 + 20) + << "The verifyAuthorization call took " << (host_time_delta - time_to_sleep) + << " ms? That's awful!"; + + auto km_time_delta = + result2.token.timestamp.milliSeconds - result1.token.timestamp.milliSeconds; + + EXPECT_LE(host_time_delta, km_time_delta + 2); + EXPECT_LE(km_time_delta, host_time_delta + 2); + 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_KEYMINT_AIDL_TEST(VerificationTokenTest); + +} // namespace aidl::android::hardware::security::keymint::test |