[vts] Verify RKP VM DICE chain in IRPC VTS

This cl adds verifications to the IRPC VTS to check that:

- RKP VM DICE chains have a continuous presence of RKP VM markers
till the last DICE certificate.
- Non-RKP VM DICE chains do not have such continuous presence of
RKP VM markers.

Test: atest VtsHalRemotelyProvisionedComponentTargetTest
Test: atest libkeymint_remote_prov_support_test
Bug: 314128697
Change-Id: Ib966b4bd584f1f931b7f19b4b58a1a37b5266f5e
This commit is contained in:
Alice Wang
2024-10-08 07:30:28 +00:00
parent 0de4aa3c22
commit f112ec92ee
5 changed files with 92 additions and 62 deletions

View File

@@ -79,9 +79,13 @@ void KeyMintRemoteProv::process() {
while (mFdp.remaining_bytes()) {
auto invokeProvAPI = mFdp.PickValueInArray<const std::function<void()>>({
[&]() { verifyFactoryCsr(cborKeysToSign, csr, gRPC.get(), challenge); },
[&]() { verifyProductionCsr(cborKeysToSign, csr, gRPC.get(), challenge); },
[&]() { isCsrWithProperDiceChain(csr); },
[&]() {
verifyFactoryCsr(cborKeysToSign, csr, gRPC.get(), kServiceName, challenge);
},
[&]() {
verifyProductionCsr(cborKeysToSign, csr, gRPC.get(), kServiceName, challenge);
},
[&]() { isCsrWithProperDiceChain(csr, kServiceName); },
});
invokeProvAPI();
}

View File

@@ -89,6 +89,11 @@ inline constexpr uint8_t kCoseEncodedEcdsa256GeekCert[] = {
*/
bytevec randomBytes(size_t numBytes);
const std::string DEFAULT_INSTANCE_NAME =
"android.hardware.security.keymint.IRemotelyProvisionedComponent/default";
const std::string RKPVM_INSTANCE_NAME =
"android.hardware.security.keymint.IRemotelyProvisionedComponent/avf";
struct EekChain {
bytevec chain;
bytevec last_pubkey;
@@ -160,7 +165,8 @@ ErrMsgOr<std::vector<BccEntryData>> verifyFactoryProtectedData(
const DeviceInfo& deviceInfo, const cppbor::Array& keysToSign,
const std::vector<uint8_t>& keysToSignMac, const ProtectedData& protectedData,
const EekChain& eekChain, const std::vector<uint8_t>& eekId, int32_t supportedEekCurve,
IRemotelyProvisionedComponent* provisionable, const std::vector<uint8_t>& challenge);
IRemotelyProvisionedComponent* provisionable, const std::string& instanceName,
const std::vector<uint8_t>& challenge);
/**
* Verify the protected data as if the device is a final production sample.
*/
@@ -168,8 +174,8 @@ ErrMsgOr<std::vector<BccEntryData>> verifyProductionProtectedData(
const DeviceInfo& deviceInfo, const cppbor::Array& keysToSign,
const std::vector<uint8_t>& keysToSignMac, const ProtectedData& protectedData,
const EekChain& eekChain, const std::vector<uint8_t>& eekId, int32_t supportedEekCurve,
IRemotelyProvisionedComponent* provisionable, const std::vector<uint8_t>& challenge,
bool allowAnyMode = false);
IRemotelyProvisionedComponent* provisionable, const std::string& instanceName,
const std::vector<uint8_t>& challenge, bool allowAnyMode = false);
/**
* Verify the CSR as if the device is still early in the factory process and may not
@@ -177,22 +183,24 @@ ErrMsgOr<std::vector<BccEntryData>> verifyProductionProtectedData(
*/
ErrMsgOr<std::unique_ptr<cppbor::Array>> verifyFactoryCsr(
const cppbor::Array& keysToSign, const std::vector<uint8_t>& csr,
IRemotelyProvisionedComponent* provisionable, const std::vector<uint8_t>& challenge,
bool allowDegenerate = true);
IRemotelyProvisionedComponent* provisionable, const std::string& instanceName,
const std::vector<uint8_t>& challenge, bool allowDegenerate = true);
/**
* Verify the CSR as if the device is a final production sample.
*/
ErrMsgOr<std::unique_ptr<cppbor::Array>> verifyProductionCsr(
const cppbor::Array& keysToSign, const std::vector<uint8_t>& csr,
IRemotelyProvisionedComponent* provisionable, const std::vector<uint8_t>& challenge,
bool allowAnyMode = false);
IRemotelyProvisionedComponent* provisionable, const std::string& instanceName,
const std::vector<uint8_t>& challenge, bool allowAnyMode = false);
/** Checks whether the CSR has a proper DICE chain. */
ErrMsgOr<bool> isCsrWithProperDiceChain(const std::vector<uint8_t>& csr);
ErrMsgOr<bool> isCsrWithProperDiceChain(const std::vector<uint8_t>& csr,
const std::string& instanceName);
/** Verify the DICE chain. */
ErrMsgOr<std::vector<BccEntryData>> validateBcc(const cppbor::Array* bcc,
hwtrust::DiceChain::Kind kind, bool allowAnyMode,
bool allowDegenerate);
bool allowDegenerate,
const std::string& instanceName);
} // namespace aidl::android::hardware::security::keymint::remote_prov

View File

@@ -52,6 +52,14 @@ using EVP_PKEY_CTX_Ptr = bssl::UniquePtr<EVP_PKEY_CTX>;
using X509_Ptr = bssl::UniquePtr<X509>;
using CRYPTO_BUFFER_Ptr = bssl::UniquePtr<CRYPTO_BUFFER>;
std::string device_suffix(const std::string& name) {
size_t pos = name.find('/');
if (pos == std::string::npos) {
return name;
}
return name.substr(pos + 1);
}
ErrMsgOr<bytevec> ecKeyGetPrivateKey(const EC_KEY* ecKey) {
// Extract private key.
const BIGNUM* bignum = EC_KEY_get0_private_key(ecKey);
@@ -325,7 +333,8 @@ bytevec getProdEekChain(int32_t supportedEekCurve) {
ErrMsgOr<std::vector<BccEntryData>> validateBcc(const cppbor::Array* bcc,
hwtrust::DiceChain::Kind kind, bool allowAnyMode,
bool allowDegenerate) {
bool allowDegenerate,
const std::string& instanceName) {
auto encodedBcc = bcc->encode();
// Use ro.build.type instead of ro.debuggable because ro.debuggable=1 for VTS testing
@@ -334,7 +343,8 @@ ErrMsgOr<std::vector<BccEntryData>> validateBcc(const cppbor::Array* bcc,
allowAnyMode = true;
}
auto chain = hwtrust::DiceChain::Verify(encodedBcc, kind, allowAnyMode);
auto chain =
hwtrust::DiceChain::Verify(encodedBcc, kind, allowAnyMode, device_suffix(instanceName));
if (!chain.ok()) return chain.error().message();
if (!allowDegenerate && !chain->IsProper()) {
@@ -649,8 +659,8 @@ ErrMsgOr<std::vector<BccEntryData>> verifyProtectedData(
const DeviceInfo& deviceInfo, const cppbor::Array& keysToSign,
const std::vector<uint8_t>& keysToSignMac, const ProtectedData& protectedData,
const EekChain& eekChain, const std::vector<uint8_t>& eekId, int32_t supportedEekCurve,
IRemotelyProvisionedComponent* provisionable, const std::vector<uint8_t>& challenge,
bool isFactory, bool allowAnyMode = false) {
IRemotelyProvisionedComponent* provisionable, const std::string& instanceName,
const std::vector<uint8_t>& challenge, bool isFactory, bool allowAnyMode = false) {
auto [parsedProtectedData, _, protDataErrMsg] = cppbor::parse(protectedData.protectedData);
if (!parsedProtectedData) {
return protDataErrMsg;
@@ -707,7 +717,7 @@ ErrMsgOr<std::vector<BccEntryData>> verifyProtectedData(
// BCC is [ pubkey, + BccEntry]
auto bccContents = validateBcc(bcc->asArray(), hwtrust::DiceChain::Kind::kVsr13, allowAnyMode,
/*allowDegenerate=*/true);
/*allowDegenerate=*/true, instanceName);
if (!bccContents) {
return bccContents.message() + "\n" + prettyPrint(bcc.get());
}
@@ -750,9 +760,10 @@ ErrMsgOr<std::vector<BccEntryData>> verifyFactoryProtectedData(
const DeviceInfo& deviceInfo, const cppbor::Array& keysToSign,
const std::vector<uint8_t>& keysToSignMac, const ProtectedData& protectedData,
const EekChain& eekChain, const std::vector<uint8_t>& eekId, int32_t supportedEekCurve,
IRemotelyProvisionedComponent* provisionable, const std::vector<uint8_t>& challenge) {
IRemotelyProvisionedComponent* provisionable, const std::string& instanceName,
const std::vector<uint8_t>& challenge) {
return verifyProtectedData(deviceInfo, keysToSign, keysToSignMac, protectedData, eekChain,
eekId, supportedEekCurve, provisionable, challenge,
eekId, supportedEekCurve, provisionable, instanceName, challenge,
/*isFactory=*/true);
}
@@ -760,10 +771,10 @@ ErrMsgOr<std::vector<BccEntryData>> verifyProductionProtectedData(
const DeviceInfo& deviceInfo, const cppbor::Array& keysToSign,
const std::vector<uint8_t>& keysToSignMac, const ProtectedData& protectedData,
const EekChain& eekChain, const std::vector<uint8_t>& eekId, int32_t supportedEekCurve,
IRemotelyProvisionedComponent* provisionable, const std::vector<uint8_t>& challenge,
bool allowAnyMode) {
IRemotelyProvisionedComponent* provisionable, const std::string& instanceName,
const std::vector<uint8_t>& challenge, bool allowAnyMode) {
return verifyProtectedData(deviceInfo, keysToSign, keysToSignMac, protectedData, eekChain,
eekId, supportedEekCurve, provisionable, challenge,
eekId, supportedEekCurve, provisionable, instanceName, challenge,
/*isFactory=*/false, allowAnyMode);
}
@@ -1003,6 +1014,7 @@ ErrMsgOr<hwtrust::DiceChain::Kind> getDiceChainKind() {
ErrMsgOr<bytevec> parseAndValidateAuthenticatedRequest(const std::vector<uint8_t>& request,
const std::vector<uint8_t>& challenge,
const std::string& instanceName,
bool allowAnyMode = false,
bool allowDegenerate = true) {
auto [parsedRequest, _, csrErrMsg] = cppbor::parse(request);
@@ -1042,7 +1054,8 @@ ErrMsgOr<bytevec> parseAndValidateAuthenticatedRequest(const std::vector<uint8_t
return diceChainKind.message();
}
auto diceContents = validateBcc(diceCertChain, *diceChainKind, allowAnyMode, allowDegenerate);
auto diceContents =
validateBcc(diceCertChain, *diceChainKind, allowAnyMode, allowDegenerate, instanceName);
if (!diceContents) {
return diceContents.message() + "\n" + prettyPrint(diceCertChain);
}
@@ -1071,6 +1084,7 @@ ErrMsgOr<bytevec> parseAndValidateAuthenticatedRequest(const std::vector<uint8_t
ErrMsgOr<std::unique_ptr<cppbor::Array>> verifyCsr(const cppbor::Array& keysToSign,
const std::vector<uint8_t>& csr,
IRemotelyProvisionedComponent* provisionable,
const std::string& instanceName,
const std::vector<uint8_t>& challenge,
bool isFactory, bool allowAnyMode = false,
bool allowDegenerate = true) {
@@ -1081,8 +1095,8 @@ ErrMsgOr<std::unique_ptr<cppbor::Array>> verifyCsr(const cppbor::Array& keysToSi
") does not match expected version (3).";
}
auto csrPayload =
parseAndValidateAuthenticatedRequest(csr, challenge, allowAnyMode, allowDegenerate);
auto csrPayload = parseAndValidateAuthenticatedRequest(csr, challenge, instanceName,
allowAnyMode, allowDegenerate);
if (!csrPayload) {
return csrPayload.message();
}
@@ -1092,20 +1106,22 @@ ErrMsgOr<std::unique_ptr<cppbor::Array>> verifyCsr(const cppbor::Array& keysToSi
ErrMsgOr<std::unique_ptr<cppbor::Array>> verifyFactoryCsr(
const cppbor::Array& keysToSign, const std::vector<uint8_t>& csr,
IRemotelyProvisionedComponent* provisionable, const std::vector<uint8_t>& challenge,
bool allowDegenerate) {
return verifyCsr(keysToSign, csr, provisionable, challenge, /*isFactory=*/true,
IRemotelyProvisionedComponent* provisionable, const std::string& instanceName,
const std::vector<uint8_t>& challenge, bool allowDegenerate) {
return verifyCsr(keysToSign, csr, provisionable, instanceName, challenge, /*isFactory=*/true,
/*allowAnyMode=*/false, allowDegenerate);
}
ErrMsgOr<std::unique_ptr<cppbor::Array>> verifyProductionCsr(
const cppbor::Array& keysToSign, const std::vector<uint8_t>& csr,
IRemotelyProvisionedComponent* provisionable, const std::vector<uint8_t>& challenge,
bool allowAnyMode) {
return verifyCsr(keysToSign, csr, provisionable, challenge, /*isFactory=*/false, allowAnyMode);
IRemotelyProvisionedComponent* provisionable, const std::string& instanceName,
const std::vector<uint8_t>& challenge, bool allowAnyMode) {
return verifyCsr(keysToSign, csr, provisionable, instanceName, challenge, /*isFactory=*/false,
allowAnyMode);
}
ErrMsgOr<bool> isCsrWithProperDiceChain(const std::vector<uint8_t>& csr) {
ErrMsgOr<bool> isCsrWithProperDiceChain(const std::vector<uint8_t>& csr,
const std::string& instanceName) {
auto [parsedRequest, _, csrErrMsg] = cppbor::parse(csr);
if (!parsedRequest) {
return csrErrMsg;
@@ -1136,8 +1152,8 @@ ErrMsgOr<bool> isCsrWithProperDiceChain(const std::vector<uint8_t>& csr) {
}
auto encodedDiceChain = diceCertChain->encode();
auto chain =
hwtrust::DiceChain::Verify(encodedDiceChain, *diceChainKind, /*allowAnyMode=*/false);
auto chain = hwtrust::DiceChain::Verify(encodedDiceChain, *diceChainKind,
/*allowAnyMode=*/false, device_suffix(instanceName));
if (!chain.ok()) return chain.error().message();
return chain->IsProper();
}

View File

@@ -298,9 +298,11 @@ TEST(RemoteProvUtilsTest, validateBccDegenerate) {
ASSERT_TRUE(bcc) << "Error: " << errMsg;
EXPECT_TRUE(validateBcc(bcc->asArray(), hwtrust::DiceChain::Kind::kVsr16,
/*allowAnyMode=*/false, /*allowDegenerate=*/true));
/*allowAnyMode=*/false, /*allowDegenerate=*/true,
DEFAULT_INSTANCE_NAME));
EXPECT_FALSE(validateBcc(bcc->asArray(), hwtrust::DiceChain::Kind::kVsr16,
/*allowAnyMode=*/false, /*allowDegenerate=*/false));
/*allowAnyMode=*/false, /*allowDegenerate=*/false,
DEFAULT_INSTANCE_NAME));
}
} // namespace
} // namespace aidl::android::hardware::security::keymint::remote_prov

View File

@@ -55,10 +55,7 @@ constexpr int32_t VERSION_WITH_SUPPORTED_NUM_KEYS_IN_CSR = 3;
constexpr uint8_t MIN_CHALLENGE_SIZE = 0;
constexpr uint8_t MAX_CHALLENGE_SIZE = 64;
const string DEFAULT_INSTANCE_NAME =
"android.hardware.security.keymint.IRemotelyProvisionedComponent/default";
const string RKP_VM_INSTANCE_NAME =
"android.hardware.security.keymint.IRemotelyProvisionedComponent/avf";
const string KEYMINT_STRONGBOX_INSTANCE_NAME =
"android.hardware.security.keymint.IKeyMintDevice/strongbox";
@@ -188,7 +185,7 @@ class VtsRemotelyProvisionedComponentTests : public testing::TestWithParam<std::
}
ASSERT_NE(provisionable_, nullptr);
auto status = provisionable_->getHardwareInfo(&rpcHardwareInfo);
isRkpVmInstance_ = GetParam() == RKP_VM_INSTANCE_NAME;
isRkpVmInstance_ = GetParam() == RKPVM_INSTANCE_NAME;
if (isRkpVmInstance_) {
if (status.getExceptionCode() == EX_UNSUPPORTED_OPERATION) {
GTEST_SKIP() << "The RKP VM is not supported on this system.";
@@ -227,7 +224,7 @@ TEST(NonParameterizedTests, eachRpcHasAUniqueId) {
RpcHardwareInfo hwInfo;
auto status = rpc->getHardwareInfo(&hwInfo);
if (hal == RKP_VM_INSTANCE_NAME && status.getExceptionCode() == EX_UNSUPPORTED_OPERATION) {
if (hal == RKPVM_INSTANCE_NAME && status.getExceptionCode() == EX_UNSUPPORTED_OPERATION) {
GTEST_SKIP() << "The RKP VM is not supported on this system.";
}
ASSERT_TRUE(status.isOk());
@@ -268,7 +265,7 @@ TEST(NonParameterizedTests, requireDiceOnDefaultInstanceIfStrongboxPresent) {
auto status = rpc->generateCertificateRequestV2({} /* keysToSign */, challenge, &csr);
EXPECT_TRUE(status.isOk()) << status.getDescription();
auto result = isCsrWithProperDiceChain(csr);
auto result = isCsrWithProperDiceChain(csr, DEFAULT_INSTANCE_NAME);
ASSERT_TRUE(result) << result.message();
ASSERT_TRUE(*result);
}
@@ -494,7 +491,7 @@ TEST_P(CertificateRequestTest, EmptyRequest_testMode) {
auto result = verifyProductionProtectedData(
deviceInfo, cppbor::Array(), keysToSignMac, protectedData, testEekChain_, eekId_,
rpcHardwareInfo.supportedEekCurve, provisionable_.get(), challenge_);
rpcHardwareInfo.supportedEekCurve, provisionable_.get(), GetParam(), challenge_);
ASSERT_TRUE(result) << result.message();
}
}
@@ -517,9 +514,10 @@ TEST_P(CertificateRequestTest, NewKeyPerCallInTestMode) {
&protectedData, &keysToSignMac);
ASSERT_TRUE(status.isOk()) << status.getDescription();
auto firstBcc = verifyProductionProtectedData(
deviceInfo, /*keysToSign=*/cppbor::Array(), keysToSignMac, protectedData, testEekChain_,
eekId_, rpcHardwareInfo.supportedEekCurve, provisionable_.get(), challenge_);
auto firstBcc = verifyProductionProtectedData(deviceInfo, /*keysToSign=*/cppbor::Array(),
keysToSignMac, protectedData, testEekChain_,
eekId_, rpcHardwareInfo.supportedEekCurve,
provisionable_.get(), GetParam(), challenge_);
ASSERT_TRUE(firstBcc) << firstBcc.message();
status = provisionable_->generateCertificateRequest(
@@ -527,9 +525,10 @@ TEST_P(CertificateRequestTest, NewKeyPerCallInTestMode) {
&protectedData, &keysToSignMac);
ASSERT_TRUE(status.isOk()) << status.getDescription();
auto secondBcc = verifyProductionProtectedData(
deviceInfo, /*keysToSign=*/cppbor::Array(), keysToSignMac, protectedData, testEekChain_,
eekId_, rpcHardwareInfo.supportedEekCurve, provisionable_.get(), challenge_);
auto secondBcc = verifyProductionProtectedData(deviceInfo, /*keysToSign=*/cppbor::Array(),
keysToSignMac, protectedData, testEekChain_,
eekId_, rpcHardwareInfo.supportedEekCurve,
provisionable_.get(), GetParam(), challenge_);
ASSERT_TRUE(secondBcc) << secondBcc.message();
// Verify that none of the keys in the first BCC are repeated in the second one.
@@ -579,7 +578,7 @@ TEST_P(CertificateRequestTest, NonEmptyRequest_testMode) {
auto result = verifyProductionProtectedData(
deviceInfo, cborKeysToSign_, keysToSignMac, protectedData, testEekChain_, eekId_,
rpcHardwareInfo.supportedEekCurve, provisionable_.get(), challenge_);
rpcHardwareInfo.supportedEekCurve, provisionable_.get(), GetParam(), challenge_);
ASSERT_TRUE(result) << result.message();
}
}
@@ -767,8 +766,8 @@ TEST_P(CertificateRequestV2Test, EmptyRequest) {
provisionable_->generateCertificateRequestV2({} /* keysToSign */, challenge, &csr);
ASSERT_TRUE(status.isOk()) << status.getDescription();
auto result = verifyProductionCsr(cppbor::Array(), csr, provisionable_.get(), challenge,
isRkpVmInstance_);
auto result = verifyProductionCsr(cppbor::Array(), csr, provisionable_.get(), GetParam(),
challenge, isRkpVmInstance_);
ASSERT_TRUE(result) << result.message();
}
}
@@ -789,8 +788,8 @@ TEST_P(CertificateRequestV2Test, NonEmptyRequest) {
auto status = provisionable_->generateCertificateRequestV2(keysToSign_, challenge, &csr);
ASSERT_TRUE(status.isOk()) << status.getDescription();
auto result = verifyProductionCsr(cborKeysToSign_, csr, provisionable_.get(), challenge,
isRkpVmInstance_);
auto result = verifyProductionCsr(cborKeysToSign_, csr, provisionable_.get(), GetParam(),
challenge, isRkpVmInstance_);
ASSERT_TRUE(result) << result.message();
}
}
@@ -820,15 +819,15 @@ TEST_P(CertificateRequestV2Test, NonEmptyRequestReproducible) {
auto status = provisionable_->generateCertificateRequestV2(keysToSign_, challenge_, &csr);
ASSERT_TRUE(status.isOk()) << status.getDescription();
auto firstCsr = verifyProductionCsr(cborKeysToSign_, csr, provisionable_.get(), challenge_,
isRkpVmInstance_);
auto firstCsr = verifyProductionCsr(cborKeysToSign_, csr, provisionable_.get(), GetParam(),
challenge_, isRkpVmInstance_);
ASSERT_TRUE(firstCsr) << firstCsr.message();
status = provisionable_->generateCertificateRequestV2(keysToSign_, challenge_, &csr);
ASSERT_TRUE(status.isOk()) << status.getDescription();
auto secondCsr = verifyProductionCsr(cborKeysToSign_, csr, provisionable_.get(), challenge_,
isRkpVmInstance_);
auto secondCsr = verifyProductionCsr(cborKeysToSign_, csr, provisionable_.get(), GetParam(),
challenge_, isRkpVmInstance_);
ASSERT_TRUE(secondCsr) << secondCsr.message();
ASSERT_EQ(**firstCsr, **secondCsr);
@@ -846,8 +845,8 @@ TEST_P(CertificateRequestV2Test, NonEmptyRequestMultipleKeys) {
auto status = provisionable_->generateCertificateRequestV2(keysToSign_, challenge_, &csr);
ASSERT_TRUE(status.isOk()) << status.getDescription();
auto result = verifyProductionCsr(cborKeysToSign_, csr, provisionable_.get(), challenge_,
isRkpVmInstance_);
auto result = verifyProductionCsr(cborKeysToSign_, csr, provisionable_.get(), GetParam(),
challenge_, isRkpVmInstance_);
ASSERT_TRUE(result) << result.message();
}
@@ -977,7 +976,8 @@ TEST_P(CertificateRequestV2Test, DeviceInfo) {
provisionable_->generateCertificateRequestV2({} /* keysToSign */, challenge_, &csr);
ASSERT_TRUE(irpcStatus.isOk()) << irpcStatus.getDescription();
auto result = verifyProductionCsr(cppbor::Array(), csr, provisionable_.get(), challenge_);
auto result =
verifyProductionCsr(cppbor::Array(), csr, provisionable_.get(), GetParam(), challenge_);
ASSERT_TRUE(result) << result.message();
std::unique_ptr<cppbor::Array> csrPayload = std::move(*result);