diff options
Diffstat (limited to 'security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp')
-rw-r--r-- | security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp | 221 |
1 files changed, 199 insertions, 22 deletions
diff --git a/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp b/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp index 38f3586862..7184613cfa 100644 --- a/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp +++ b/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp @@ -20,6 +20,7 @@ #include <aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.h> #include <aidl/android/hardware/security/keymint/SecurityLevel.h> #include <android/binder_manager.h> +#include <binder/IServiceManager.h> #include <cppbor_parse.h> #include <gmock/gmock.h> #include <keymaster/cppcose/cppcose.h> @@ -29,6 +30,7 @@ #include <openssl/ec_key.h> #include <openssl/x509.h> #include <remote_prov/remote_prov_utils.h> +#include <set> #include <vector> #include "KeyMintAidlTestBase.h" @@ -40,6 +42,8 @@ using ::std::vector; namespace { +constexpr int32_t VERSION_WITH_UNIQUE_ID_SUPPORT = 2; + #define INSTANTIATE_REM_PROV_AIDL_TEST(name) \ GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(name); \ INSTANTIATE_TEST_SUITE_P( \ @@ -47,11 +51,28 @@ namespace { testing::ValuesIn(VtsRemotelyProvisionedComponentTests::build_params()), \ ::android::PrintInstanceNameToString) +using ::android::sp; using bytevec = std::vector<uint8_t>; using testing::MatchesRegex; using namespace remote_prov; using namespace keymaster; +std::set<std::string> getAllowedVbStates() { + return {"green", "yellow", "orange"}; +} + +std::set<std::string> getAllowedBootloaderStates() { + return {"locked", "unlocked"}; +} + +std::set<std::string> getAllowedSecurityLevels() { + return {"tee", "strongbox"}; +} + +std::set<std::string> getAllowedAttIdStates() { + return {"locked", "open"}; +} + bytevec string_to_bytevec(const char* s) { const uint8_t* p = reinterpret_cast<const uint8_t*>(s); return bytevec(p, p + strlen(s)); @@ -164,6 +185,7 @@ class VtsRemotelyProvisionedComponentTests : public testing::TestWithParam<std:: provisionable_ = IRemotelyProvisionedComponent::fromBinder(binder); } ASSERT_NE(provisionable_, nullptr); + ASSERT_TRUE(provisionable_->getHardwareInfo(&rpcHardwareInfo).isOk()); } static vector<string> build_params() { @@ -173,8 +195,70 @@ class VtsRemotelyProvisionedComponentTests : public testing::TestWithParam<std:: protected: std::shared_ptr<IRemotelyProvisionedComponent> provisionable_; + RpcHardwareInfo rpcHardwareInfo; }; +/** + * Verify that every implementation reports a different unique id. + */ +TEST(NonParameterizedTests, eachRpcHasAUniqueId) { + std::set<std::string> uniqueIds; + for (auto hal : ::android::getAidlHalInstanceNames(IRemotelyProvisionedComponent::descriptor)) { + ASSERT_TRUE(AServiceManager_isDeclared(hal.c_str())); + ::ndk::SpAIBinder binder(AServiceManager_waitForService(hal.c_str())); + std::shared_ptr<IRemotelyProvisionedComponent> rpc = + IRemotelyProvisionedComponent::fromBinder(binder); + ASSERT_NE(rpc, nullptr); + + RpcHardwareInfo hwInfo; + ASSERT_TRUE(rpc->getHardwareInfo(&hwInfo).isOk()); + + int32_t version; + ASSERT_TRUE(rpc->getInterfaceVersion(&version).isOk()); + if (version >= VERSION_WITH_UNIQUE_ID_SUPPORT) { + ASSERT_TRUE(hwInfo.uniqueId); + auto [_, wasInserted] = uniqueIds.insert(*hwInfo.uniqueId); + EXPECT_TRUE(wasInserted); + } else { + ASSERT_FALSE(hwInfo.uniqueId); + } + } +} + +using GetHardwareInfoTests = VtsRemotelyProvisionedComponentTests; + +INSTANTIATE_REM_PROV_AIDL_TEST(GetHardwareInfoTests); + +/** + * Verify that a valid curve is reported by the implementation. + */ +TEST_P(GetHardwareInfoTests, supportsValidCurve) { + RpcHardwareInfo hwInfo; + ASSERT_TRUE(provisionable_->getHardwareInfo(&hwInfo).isOk()); + + const std::set<int> validCurves = {RpcHardwareInfo::CURVE_P256, RpcHardwareInfo::CURVE_25519}; + ASSERT_EQ(validCurves.count(hwInfo.supportedEekCurve), 1) + << "Invalid curve: " << hwInfo.supportedEekCurve; +} + +/** + * Verify that the unique id is within the length limits as described in RpcHardwareInfo.aidl. + */ +TEST_P(GetHardwareInfoTests, uniqueId) { + int32_t version; + ASSERT_TRUE(provisionable_->getInterfaceVersion(&version).isOk()); + + if (version < VERSION_WITH_UNIQUE_ID_SUPPORT) { + return; + } + + RpcHardwareInfo hwInfo; + ASSERT_TRUE(provisionable_->getHardwareInfo(&hwInfo).isOk()); + ASSERT_TRUE(hwInfo.uniqueId); + EXPECT_GE(hwInfo.uniqueId->size(), 1); + EXPECT_LE(hwInfo.uniqueId->size(), 32); +} + using GenerateKeyTests = VtsRemotelyProvisionedComponentTests; INSTANTIATE_REM_PROV_AIDL_TEST(GenerateKeyTests); @@ -222,7 +306,7 @@ TEST_P(GenerateKeyTests, generateAndUseEcdsaP256Key_prodMode) { // Generate an ECDSA key that is attested by the generated P256 keypair. AuthorizationSet keyDesc = AuthorizationSetBuilder() .Authorization(TAG_NO_AUTH_REQUIRED) - .EcdsaSigningKey(256) + .EcdsaSigningKey(EcCurve::P_256) .AttestationChallenge("foo") .AttestationApplicationId("bar") .Digest(Digest::NONE) @@ -236,9 +320,11 @@ TEST_P(GenerateKeyTests, generateAndUseEcdsaP256Key_prodMode) { vector<Certificate> attested_key_cert_chain = std::move(creationResult.certificateChain); EXPECT_EQ(attested_key_cert_chain.size(), 1); + int32_t aidl_version = 0; + ASSERT_TRUE(keyMint->getInterfaceVersion(&aidl_version).isOk()); AuthorizationSet hw_enforced = HwEnforcedAuthorizations(attested_key_characteristics); AuthorizationSet sw_enforced = SwEnforcedAuthorizations(attested_key_characteristics); - EXPECT_TRUE(verify_attestation_record("foo", "bar", sw_enforced, hw_enforced, + EXPECT_TRUE(verify_attestation_record(aidl_version, "foo", "bar", sw_enforced, hw_enforced, info.securityLevel, attested_key_cert_chain[0].encodedCertificate)); @@ -272,13 +358,11 @@ TEST_P(GenerateKeyTests, generateEcdsaP256Key_testMode) { class CertificateRequestTest : public VtsRemotelyProvisionedComponentTests { protected: - CertificateRequestTest() : eekId_(string_to_bytevec("eekid")), challenge_(randomBytes(32)) { - generateTestEekChain(3); - } + CertificateRequestTest() : eekId_(string_to_bytevec("eekid")), challenge_(randomBytes(64)) {} void generateTestEekChain(size_t eekLength) { - auto chain = generateEekChain(eekLength, eekId_); - EXPECT_TRUE(chain) << chain.message(); + auto chain = generateEekChain(rpcHardwareInfo.supportedEekCurve, eekLength, eekId_); + ASSERT_TRUE(chain) << chain.message(); if (chain) testEekChain_ = chain.moveValue(); testEekLength_ = eekLength; } @@ -298,6 +382,17 @@ class CertificateRequestTest : public VtsRemotelyProvisionedComponentTests { } } + ErrMsgOr<bytevec> getSessionKey(ErrMsgOr<std::pair<bytevec, bytevec>>& senderPubkey) { + if (rpcHardwareInfo.supportedEekCurve == RpcHardwareInfo::CURVE_25519 || + rpcHardwareInfo.supportedEekCurve == RpcHardwareInfo::CURVE_NONE) { + return x25519_HKDF_DeriveKey(testEekChain_.last_pubkey, testEekChain_.last_privkey, + senderPubkey->first, false /* senderIsA */); + } else { + return ECDH_HKDF_DeriveKey(testEekChain_.last_pubkey, testEekChain_.last_privkey, + senderPubkey->first, false /* senderIsA */); + } + } + void checkProtectedData(const DeviceInfo& deviceInfo, const cppbor::Array& keysToSign, const bytevec& keysToSignMac, const ProtectedData& protectedData, std::vector<BccEntryData>* bccOutput = nullptr) { @@ -310,9 +405,7 @@ class CertificateRequestTest : public VtsRemotelyProvisionedComponentTests { ASSERT_TRUE(senderPubkey) << senderPubkey.message(); EXPECT_EQ(senderPubkey->second, eekId_); - auto sessionKey = - x25519_HKDF_DeriveKey(testEekChain_.last_pubkey, testEekChain_.last_privkey, - senderPubkey->first, false /* senderIsA */); + auto sessionKey = getSessionKey(senderPubkey); ASSERT_TRUE(sessionKey) << sessionKey.message(); auto protectedDataPayload = @@ -322,7 +415,8 @@ class CertificateRequestTest : public VtsRemotelyProvisionedComponentTests { auto [parsedPayload, __, payloadErrMsg] = cppbor::parse(*protectedDataPayload); ASSERT_TRUE(parsedPayload) << "Failed to parse payload: " << payloadErrMsg; ASSERT_TRUE(parsedPayload->asArray()); - EXPECT_EQ(parsedPayload->asArray()->size(), 2U); + // Strongbox may contain additional certificate chain. + EXPECT_LE(parsedPayload->asArray()->size(), 3U); auto& signedMac = parsedPayload->asArray()->get(0); auto& bcc = parsedPayload->asArray()->get(1); @@ -337,8 +431,9 @@ class CertificateRequestTest : public VtsRemotelyProvisionedComponentTests { auto [deviceInfoMap, __2, deviceInfoErrMsg] = cppbor::parse(deviceInfo.deviceInfo); ASSERT_TRUE(deviceInfoMap) << "Failed to parse deviceInfo: " << deviceInfoErrMsg; ASSERT_TRUE(deviceInfoMap->asMap()); - + checkDeviceInfo(deviceInfoMap->asMap(), deviceInfo.deviceInfo); auto& signingKey = bccContents->back().pubKey; + deviceInfoMap->asMap()->canonicalize(); auto macKey = verifyAndParseCoseSign1(signedMac->asArray(), signingKey, cppbor::Array() // SignedMacAad .add(challenge_) @@ -364,6 +459,80 @@ class CertificateRequestTest : public VtsRemotelyProvisionedComponentTests { } } + void checkType(const cppbor::Map* devInfo, uint8_t majorType, std::string entryName) { + const auto& val = devInfo->get(entryName); + ASSERT_TRUE(val) << entryName << " does not exist"; + ASSERT_EQ(val->type(), majorType) << entryName << " has the wrong type."; + switch (majorType) { + case cppbor::TSTR: + EXPECT_GT(val->asTstr()->value().size(), 0); + break; + case cppbor::BSTR: + EXPECT_GT(val->asBstr()->value().size(), 0); + break; + default: + break; + } + } + + void checkDeviceInfo(const cppbor::Map* deviceInfo, bytevec deviceInfoBytes) { + EXPECT_EQ(deviceInfo->clone()->asMap()->canonicalize().encode(), deviceInfoBytes) + << "DeviceInfo ordering is non-canonical."; + const auto& version = deviceInfo->get("version"); + ASSERT_TRUE(version); + ASSERT_TRUE(version->asUint()); + RpcHardwareInfo info; + provisionable_->getHardwareInfo(&info); + ASSERT_EQ(version->asUint()->value(), info.versionNumber); + std::set<std::string> allowList; + switch (version->asUint()->value()) { + // These fields became mandated in version 2. + case 2: + checkType(deviceInfo, cppbor::TSTR, "brand"); + checkType(deviceInfo, cppbor::TSTR, "manufacturer"); + checkType(deviceInfo, cppbor::TSTR, "product"); + checkType(deviceInfo, cppbor::TSTR, "model"); + checkType(deviceInfo, cppbor::TSTR, "device"); + // TODO: Refactor the KeyMint code that validates these fields and include it here. + checkType(deviceInfo, cppbor::TSTR, "vb_state"); + allowList = getAllowedVbStates(); + EXPECT_NE(allowList.find(deviceInfo->get("vb_state")->asTstr()->value()), + allowList.end()); + checkType(deviceInfo, cppbor::TSTR, "bootloader_state"); + allowList = getAllowedBootloaderStates(); + EXPECT_NE(allowList.find(deviceInfo->get("bootloader_state")->asTstr()->value()), + allowList.end()); + checkType(deviceInfo, cppbor::BSTR, "vbmeta_digest"); + checkType(deviceInfo, cppbor::UINT, "system_patch_level"); + checkType(deviceInfo, cppbor::UINT, "boot_patch_level"); + checkType(deviceInfo, cppbor::UINT, "vendor_patch_level"); + checkType(deviceInfo, cppbor::UINT, "fused"); + EXPECT_LT(deviceInfo->get("fused")->asUint()->value(), 2); // Must be 0 or 1. + checkType(deviceInfo, cppbor::TSTR, "security_level"); + allowList = getAllowedSecurityLevels(); + EXPECT_NE(allowList.find(deviceInfo->get("security_level")->asTstr()->value()), + allowList.end()); + if (deviceInfo->get("security_level")->asTstr()->value() == "tee") { + checkType(deviceInfo, cppbor::TSTR, "os_version"); + } + break; + case 1: + checkType(deviceInfo, cppbor::TSTR, "security_level"); + allowList = getAllowedSecurityLevels(); + EXPECT_NE(allowList.find(deviceInfo->get("security_level")->asTstr()->value()), + allowList.end()); + if (version->asUint()->value() == 1) { + checkType(deviceInfo, cppbor::TSTR, "att_id_state"); + allowList = getAllowedAttIdStates(); + EXPECT_NE(allowList.find(deviceInfo->get("att_id_state")->asTstr()->value()), + allowList.end()); + } + break; + default: + FAIL() << "Unrecognized version: " << version->asUint()->value(); + } + } + bytevec eekId_; size_t testEekLength_; EekChain testEekChain_; @@ -406,6 +575,7 @@ TEST_P(CertificateRequestTest, NewKeyPerCallInTestMode) { bytevec keysToSignMac; DeviceInfo deviceInfo; ProtectedData protectedData; + generateTestEekChain(3); auto status = provisionable_->generateCertificateRequest( testMode, {} /* keysToSign */, testEekChain_.chain, challenge_, &deviceInfo, &protectedData, &keysToSignMac); @@ -445,8 +615,8 @@ TEST_P(CertificateRequestTest, DISABLED_EmptyRequest_prodMode) { DeviceInfo deviceInfo; ProtectedData protectedData; auto status = provisionable_->generateCertificateRequest( - testMode, {} /* keysToSign */, getProdEekChain(), challenge_, &deviceInfo, - &protectedData, &keysToSignMac); + testMode, {} /* keysToSign */, getProdEekChain(rpcHardwareInfo.supportedEekCurve), + challenge_, &deviceInfo, &protectedData, &keysToSignMac); EXPECT_TRUE(status.isOk()); } @@ -486,8 +656,8 @@ TEST_P(CertificateRequestTest, DISABLED_NonEmptyRequest_prodMode) { DeviceInfo deviceInfo; ProtectedData protectedData; auto status = provisionable_->generateCertificateRequest( - testMode, keysToSign_, getProdEekChain(), challenge_, &deviceInfo, &protectedData, - &keysToSignMac); + testMode, keysToSign_, getProdEekChain(rpcHardwareInfo.supportedEekCurve), challenge_, + &deviceInfo, &protectedData, &keysToSignMac); EXPECT_TRUE(status.isOk()); } @@ -497,11 +667,14 @@ TEST_P(CertificateRequestTest, DISABLED_NonEmptyRequest_prodMode) { TEST_P(CertificateRequestTest, NonEmptyRequestCorruptMac_testMode) { bool testMode = true; generateKeys(testMode, 1 /* numKeys */); - MacedPublicKey keyWithCorruptMac = corrupt_maced_key(keysToSign_[0]).moveValue(); + auto result = corrupt_maced_key(keysToSign_[0]); + ASSERT_TRUE(result) << result.moveMessage(); + MacedPublicKey keyWithCorruptMac = result.moveValue(); bytevec keysToSignMac; DeviceInfo deviceInfo; ProtectedData protectedData; + generateTestEekChain(3); auto status = provisionable_->generateCertificateRequest( testMode, {keyWithCorruptMac}, testEekChain_.chain, challenge_, &deviceInfo, &protectedData, &keysToSignMac); @@ -515,14 +688,16 @@ TEST_P(CertificateRequestTest, NonEmptyRequestCorruptMac_testMode) { TEST_P(CertificateRequestTest, NonEmptyRequestCorruptMac_prodMode) { bool testMode = false; generateKeys(testMode, 1 /* numKeys */); - MacedPublicKey keyWithCorruptMac = corrupt_maced_key(keysToSign_[0]).moveValue(); + auto result = corrupt_maced_key(keysToSign_[0]); + ASSERT_TRUE(result) << result.moveMessage(); + MacedPublicKey keyWithCorruptMac = result.moveValue(); bytevec keysToSignMac; DeviceInfo deviceInfo; ProtectedData protectedData; auto status = provisionable_->generateCertificateRequest( - testMode, {keyWithCorruptMac}, getProdEekChain(), challenge_, &deviceInfo, - &protectedData, &keysToSignMac); + testMode, {keyWithCorruptMac}, getProdEekChain(rpcHardwareInfo.supportedEekCurve), + challenge_, &deviceInfo, &protectedData, &keysToSignMac); ASSERT_FALSE(status.isOk()) << status.getMessage(); EXPECT_EQ(status.getServiceSpecificError(), BnRemotelyProvisionedComponent::STATUS_INVALID_MAC); } @@ -535,7 +710,7 @@ TEST_P(CertificateRequestTest, NonEmptyCorruptEekRequest_prodMode) { bool testMode = false; generateKeys(testMode, 4 /* numKeys */); - auto prodEekChain = getProdEekChain(); + auto prodEekChain = getProdEekChain(rpcHardwareInfo.supportedEekCurve); auto [parsedChain, _, parseErr] = cppbor::parse(prodEekChain); ASSERT_NE(parsedChain, nullptr) << parseErr; ASSERT_NE(parsedChain->asArray(), nullptr); @@ -566,7 +741,7 @@ TEST_P(CertificateRequestTest, NonEmptyIncompleteEekRequest_prodMode) { // Build an EEK chain that omits the first self-signed cert. auto truncatedChain = cppbor::Array(); - auto [chain, _, parseErr] = cppbor::parse(getProdEekChain()); + auto [chain, _, parseErr] = cppbor::parse(getProdEekChain(rpcHardwareInfo.supportedEekCurve)); ASSERT_TRUE(chain); auto eekChain = chain->asArray(); ASSERT_NE(eekChain, nullptr); @@ -594,6 +769,7 @@ TEST_P(CertificateRequestTest, NonEmptyRequest_prodKeyInTestCert) { bytevec keysToSignMac; DeviceInfo deviceInfo; ProtectedData protectedData; + generateTestEekChain(3); auto status = provisionable_->generateCertificateRequest( true /* testMode */, keysToSign_, testEekChain_.chain, challenge_, &deviceInfo, &protectedData, &keysToSignMac); @@ -612,6 +788,7 @@ TEST_P(CertificateRequestTest, NonEmptyRequest_testKeyInProdCert) { bytevec keysToSignMac; DeviceInfo deviceInfo; ProtectedData protectedData; + generateTestEekChain(3); auto status = provisionable_->generateCertificateRequest( false /* testMode */, keysToSign_, testEekChain_.chain, challenge_, &deviceInfo, &protectedData, &keysToSignMac); |