From f48a2a7bbfb7b6e251917f2728b03f2c2db48713 Mon Sep 17 00:00:00 2001 From: Tri Vo Date: Thu, 29 Sep 2022 16:14:53 -0700 Subject: [PATCH 1/2] Bump Identity and KM AIDL API versions Imported interfaces are versioned, i.e. bumping an interface version necessiates bumping the version of importing interfaces. Keystore and Identity import KM. We are uprevving KM, so all three need to be bumped at the same time. Test: m Change-Id: I46b253e72f2f245bd628ed2ae1f2f4e0572827e7 --- compatibility_matrices/compatibility_matrix.current.xml | 6 +++--- confirmationui/aidl/Android.bp | 2 +- gatekeeper/aidl/Android.bp | 2 +- identity/aidl/Android.bp | 8 ++++---- security/keymint/aidl/Android.bp | 8 ++++---- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/compatibility_matrices/compatibility_matrix.current.xml b/compatibility_matrices/compatibility_matrix.current.xml index 5dcea2d8a2..9bea1f12b5 100644 --- a/compatibility_matrices/compatibility_matrix.current.xml +++ b/compatibility_matrices/compatibility_matrix.current.xml @@ -352,7 +352,7 @@ android.hardware.identity - 1-4 + 1-5 IIdentityCredentialStore default @@ -399,7 +399,7 @@ android.hardware.security.keymint - 1-2 + 1-3 IKeyMintDevice default @@ -408,7 +408,7 @@ android.hardware.security.keymint - 1-2 + 1-3 IRemotelyProvisionedComponent default diff --git a/confirmationui/aidl/Android.bp b/confirmationui/aidl/Android.bp index 4ba4acd0d0..5395101ab2 100644 --- a/confirmationui/aidl/Android.bp +++ b/confirmationui/aidl/Android.bp @@ -20,7 +20,7 @@ aidl_interface { name: "android.hardware.confirmationui", vendor_available: true, imports: [ - "android.hardware.security.keymint-V2", + "android.hardware.security.keymint-V3", ], srcs: ["android/hardware/confirmationui/*.aidl"], stability: "vintf", diff --git a/gatekeeper/aidl/Android.bp b/gatekeeper/aidl/Android.bp index 6b1bc7e816..770e8ab694 100644 --- a/gatekeeper/aidl/Android.bp +++ b/gatekeeper/aidl/Android.bp @@ -11,7 +11,7 @@ aidl_interface { name: "android.hardware.gatekeeper", vendor_available: true, imports: [ - "android.hardware.security.keymint-V2", + "android.hardware.security.keymint-V3", ], srcs: ["android/hardware/gatekeeper/*.aidl"], stability: "vintf", diff --git a/identity/aidl/Android.bp b/identity/aidl/Android.bp index 57451ed53e..a33d43c43f 100644 --- a/identity/aidl/Android.bp +++ b/identity/aidl/Android.bp @@ -14,7 +14,7 @@ aidl_interface { "android/hardware/identity/*.aidl", ], imports: [ - "android.hardware.keymaster", + "android.hardware.keymaster-V3", "android.hardware.security.keymint", ], stability: "vintf", @@ -66,20 +66,20 @@ aidl_interface { cc_defaults { name: "identity_use_latest_hal_aidl_ndk_static", static_libs: [ - "android.hardware.identity-V4-ndk", + "android.hardware.identity-V5-ndk", ], } cc_defaults { name: "identity_use_latest_hal_aidl_ndk_shared", shared_libs: [ - "android.hardware.identity-V4-ndk", + "android.hardware.identity-V5-ndk", ], } cc_defaults { name: "identity_use_latest_hal_aidl_cpp_static", static_libs: [ - "android.hardware.identity-V4-cpp", + "android.hardware.identity-V5-cpp", ], } diff --git a/security/keymint/aidl/Android.bp b/security/keymint/aidl/Android.bp index 96d44a7935..6efff2f0da 100644 --- a/security/keymint/aidl/Android.bp +++ b/security/keymint/aidl/Android.bp @@ -52,21 +52,21 @@ aidl_interface { cc_defaults { name: "keymint_use_latest_hal_aidl_ndk_static", static_libs: [ - "android.hardware.security.keymint-V2-ndk", + "android.hardware.security.keymint-V3-ndk", ], } cc_defaults { name: "keymint_use_latest_hal_aidl_ndk_shared", shared_libs: [ - "android.hardware.security.keymint-V2-ndk", + "android.hardware.security.keymint-V3-ndk", ], } cc_defaults { name: "keymint_use_latest_hal_aidl_cpp_static", static_libs: [ - "android.hardware.security.keymint-V2-cpp", + "android.hardware.security.keymint-V3-cpp", ], } @@ -76,6 +76,6 @@ cc_defaults { rust_defaults { name: "keymint_use_latest_hal_aidl_rust", rustlibs: [ - "android.hardware.security.keymint-V2-rust", + "android.hardware.security.keymint-V3-rust", ], } From 0d6204e2b7a6f15367df41e06d20ab231c6498cc Mon Sep 17 00:00:00 2001 From: Tri Vo Date: Thu, 29 Sep 2022 16:15:34 -0700 Subject: [PATCH 2/2] Update VtsHalRemotelyProvisionedComponentTargetTest to v3 Bug: 235265072 Test: atest VtsHalRemotelyProvisionedComponentTargetTest Change-Id: I01e387a0784c3548a4661a73d7bd3d5bec9fb42e --- .../VtsRemotelyProvisionedComponentTests.cpp | 163 +++++++++- .../include/remote_prov/remote_prov_utils.h | 15 + .../keymint/support/remote_prov_utils.cpp | 296 +++++++++++++++++- 3 files changed, 453 insertions(+), 21 deletions(-) diff --git a/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp b/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp index 9b21e4e98b..4f361bb464 100644 --- a/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp +++ b/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp @@ -46,6 +46,7 @@ using ::std::vector; namespace { constexpr int32_t VERSION_WITH_UNIQUE_ID_SUPPORT = 2; +constexpr int32_t VERSION_WITHOUT_TEST_MODE = 3; #define INSTANTIATE_REM_PROV_AIDL_TEST(name) \ GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(name); \ @@ -180,6 +181,15 @@ class VtsRemotelyProvisionedComponentTests : public testing::TestWithParam* payload_value) { + if (rpcHardwareInfo.versionNumber >= VERSION_WITHOUT_TEST_MODE) { + check_maced_pubkey(macedPubKey, false, payload_value); + } else { + check_maced_pubkey(macedPubKey, testMode, payload_value); + } + } + protected: std::shared_ptr provisionable_; RpcHardwareInfo rpcHardwareInfo; @@ -256,7 +266,7 @@ TEST_P(GenerateKeyTests, generateEcdsaP256Key_prodMode) { auto status = provisionable_->generateEcdsaP256KeyPair(testMode, &macedPubKey, &privateKeyBlob); ASSERT_TRUE(status.isOk()); vector coseKeyData; - check_maced_pubkey(macedPubKey, testMode, &coseKeyData); + checkMacedPubkeyVersioned(macedPubKey, testMode, &coseKeyData); } /** @@ -279,7 +289,7 @@ TEST_P(GenerateKeyTests, generateAndUseEcdsaP256Key_prodMode) { auto status = provisionable_->generateEcdsaP256KeyPair(testMode, &macedPubKey, &privateKeyBlob); ASSERT_TRUE(status.isOk()); vector coseKeyData; - check_maced_pubkey(macedPubKey, testMode, &coseKeyData); + checkMacedPubkeyVersioned(macedPubKey, testMode, &coseKeyData); AttestationKey attestKey; attestKey.keyBlob = std::move(privateKeyBlob); @@ -334,13 +344,13 @@ TEST_P(GenerateKeyTests, generateEcdsaP256Key_testMode) { bool testMode = true; auto status = provisionable_->generateEcdsaP256KeyPair(testMode, &macedPubKey, &privateKeyBlob); ASSERT_TRUE(status.isOk()); - - check_maced_pubkey(macedPubKey, testMode, nullptr); + checkMacedPubkeyVersioned(macedPubKey, testMode, nullptr); } -class CertificateRequestTest : public VtsRemotelyProvisionedComponentTests { +class CertificateRequestTestBase : public VtsRemotelyProvisionedComponentTests { protected: - CertificateRequestTest() : eekId_(string_to_bytevec("eekid")), challenge_(randomBytes(64)) {} + CertificateRequestTestBase() + : eekId_(string_to_bytevec("eekid")), challenge_(randomBytes(64)) {} void generateTestEekChain(size_t eekLength) { auto chain = generateEekChain(rpcHardwareInfo.supportedEekCurve, eekLength, eekId_); @@ -359,7 +369,7 @@ class CertificateRequestTest : public VtsRemotelyProvisionedComponentTests { ASSERT_TRUE(status.isOk()) << status.getMessage(); vector payload_value; - check_maced_pubkey(key, testMode, &payload_value); + checkMacedPubkeyVersioned(key, testMode, &payload_value); cborKeysToSign_.add(cppbor::EncodedItem(payload_value)); } } @@ -372,6 +382,18 @@ class CertificateRequestTest : public VtsRemotelyProvisionedComponentTests { cppbor::Array cborKeysToSign_; }; +class CertificateRequestTest : public CertificateRequestTestBase { + protected: + void SetUp() override { + CertificateRequestTestBase::SetUp(); + + if (rpcHardwareInfo.versionNumber >= VERSION_WITHOUT_TEST_MODE) { + GTEST_SKIP() << "This test case only applies to RKP v1 and v2. " + << "RKP version discovered: " << rpcHardwareInfo.versionNumber; + } + } +}; + /** * Generate an empty certificate request in test mode, and decrypt and verify the structure and * content. @@ -638,4 +660,131 @@ TEST_P(CertificateRequestTest, NonEmptyRequest_testKeyInProdCert) { INSTANTIATE_REM_PROV_AIDL_TEST(CertificateRequestTest); +class CertificateRequestV2Test : public CertificateRequestTestBase { + void SetUp() override { + CertificateRequestTestBase::SetUp(); + + if (rpcHardwareInfo.versionNumber < VERSION_WITHOUT_TEST_MODE) { + GTEST_SKIP() << "This test case only applies to RKP v3 and above. " + << "RKP version discovered: " << rpcHardwareInfo.versionNumber; + } + } +}; + +/** + * Generate an empty certificate request, and decrypt and verify the structure and content. + */ +TEST_P(CertificateRequestV2Test, EmptyRequest) { + bytevec csr; + + auto status = + provisionable_->generateCertificateRequestV2({} /* keysToSign */, challenge_, &csr); + ASSERT_TRUE(status.isOk()) << status.getMessage(); + + auto result = verifyProductionCsr(cppbor::Array(), csr, provisionable_.get(), challenge_); + ASSERT_TRUE(result) << result.message(); +} + +/** + * Generate a non-empty certificate request. Decrypt, parse and validate the contents. + */ +TEST_P(CertificateRequestV2Test, NonEmptyRequest) { + generateKeys(false /* testMode */, 1 /* numKeys */); + + bytevec csr; + + auto status = provisionable_->generateCertificateRequestV2(keysToSign_, challenge_, &csr); + ASSERT_TRUE(status.isOk()) << status.getMessage(); + + auto result = verifyProductionCsr(cborKeysToSign_, csr, provisionable_.get(), challenge_); + ASSERT_TRUE(result) << result.message(); +} + +/** + * Generate a non-empty certificate request. Make sure contents are reproducible. + */ +TEST_P(CertificateRequestV2Test, NonEmptyRequestReproducible) { + generateKeys(false /* testMode */, 1 /* numKeys */); + + bytevec csr; + + auto status = provisionable_->generateCertificateRequestV2(keysToSign_, challenge_, &csr); + ASSERT_TRUE(status.isOk()) << status.getMessage(); + + auto firstBcc = verifyProductionCsr(cborKeysToSign_, csr, provisionable_.get(), challenge_); + ASSERT_TRUE(firstBcc) << firstBcc.message(); + + status = provisionable_->generateCertificateRequestV2(keysToSign_, challenge_, &csr); + ASSERT_TRUE(status.isOk()) << status.getMessage(); + + auto secondBcc = verifyProductionCsr(cborKeysToSign_, csr, provisionable_.get(), challenge_); + ASSERT_TRUE(secondBcc) << secondBcc.message(); + + ASSERT_EQ(firstBcc->size(), secondBcc->size()); + for (auto i = 0; i < firstBcc->size(); i++) { + ASSERT_EQ(firstBcc->at(i).pubKey, secondBcc->at(i).pubKey); + } +} + +/** + * Generate a non-empty certificate request with multiple keys. + */ +TEST_P(CertificateRequestV2Test, NonEmptyRequestMultipleKeys) { + // TODO(b/254137722): define a minimum number of keys that must be supported. + generateKeys(false /* testMode */, 5 /* numKeys */); + + bytevec csr; + + auto status = provisionable_->generateCertificateRequestV2(keysToSign_, challenge_, &csr); + ASSERT_TRUE(status.isOk()) << status.getMessage(); + + auto result = verifyProductionCsr(cborKeysToSign_, csr, provisionable_.get(), challenge_); + ASSERT_TRUE(result) << result.message(); +} + +/** + * Generate a non-empty certificate request, but with the MAC corrupted on the keypair. + */ +TEST_P(CertificateRequestV2Test, NonEmptyRequestCorruptMac) { + generateKeys(false /* testMode */, 1 /* numKeys */); + auto result = corrupt_maced_key(keysToSign_[0]); + ASSERT_TRUE(result) << result.moveMessage(); + MacedPublicKey keyWithCorruptMac = result.moveValue(); + + bytevec csr; + auto status = + provisionable_->generateCertificateRequestV2({keyWithCorruptMac}, challenge_, &csr); + ASSERT_FALSE(status.isOk()) << status.getMessage(); + EXPECT_EQ(status.getServiceSpecificError(), BnRemotelyProvisionedComponent::STATUS_INVALID_MAC); +} + +/** + * Generate a non-empty certificate request in prod mode, with test keys. Test mode must be + * ignored, i.e. test must pass. + */ +TEST_P(CertificateRequestV2Test, NonEmptyRequest_testKeyInProdCert) { + generateKeys(true /* testMode */, 1 /* numKeys */); + + bytevec csr; + auto status = provisionable_->generateCertificateRequestV2(keysToSign_, challenge_, &csr); + ASSERT_TRUE(status.isOk()) << status.getMessage(); +} + +/** + * Call generateCertificateRequest(). Make sure it's removed. + */ +TEST_P(CertificateRequestV2Test, CertificateRequestV1Removed) { + generateTestEekChain(2); + bytevec keysToSignMac; + DeviceInfo deviceInfo; + ProtectedData protectedData; + auto status = provisionable_->generateCertificateRequest( + true /* testMode */, {} /* keysToSign */, testEekChain_.chain, challenge_, &deviceInfo, + &protectedData, &keysToSignMac); + ASSERT_FALSE(status.isOk()) << status.getMessage(); + EXPECT_EQ(status.getServiceSpecificError(), BnRemotelyProvisionedComponent::STATUS_REMOVED); +} + +INSTANTIATE_REM_PROV_AIDL_TEST(CertificateRequestV2Test); + } // namespace aidl::android::hardware::security::keymint::test diff --git a/security/keymint/support/include/remote_prov/remote_prov_utils.h b/security/keymint/support/include/remote_prov/remote_prov_utils.h index 9ea20ace0e..6871e1b272 100644 --- a/security/keymint/support/include/remote_prov/remote_prov_utils.h +++ b/security/keymint/support/include/remote_prov/remote_prov_utils.h @@ -177,4 +177,19 @@ ErrMsgOr> verifyProductionProtectedData( const EekChain& eekChain, const std::vector& eekId, int32_t supportedEekCurve, IRemotelyProvisionedComponent* provisionable, const std::vector& challenge); +/** + * Verify the CSR as if the device is still early in the factory process and may not + * have all device identifiers provisioned yet. + */ +ErrMsgOr> verifyFactoryCsr(const cppbor::Array& keysToSign, + const std::vector& csr, + IRemotelyProvisionedComponent* provisionable, + const std::vector& challenge); +/** + * Verify the CSR as if the device is a final production sample. + */ +ErrMsgOr> verifyProductionCsr( + const cppbor::Array& keysToSign, const std::vector& csr, + IRemotelyProvisionedComponent* provisionable, const std::vector& challenge); + } // namespace aidl::android::hardware::security::keymint::remote_prov diff --git a/security/keymint/support/remote_prov_utils.cpp b/security/keymint/support/remote_prov_utils.cpp index 06514961da..f7ab3ac6fd 100644 --- a/security/keymint/support/remote_prov_utils.cpp +++ b/security/keymint/support/remote_prov_utils.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include namespace aidl::android::hardware::security::keymint::remote_prov { @@ -45,6 +46,8 @@ constexpr int kP256AffinePointSize = 32; using EC_KEY_Ptr = bssl::UniquePtr; using EVP_PKEY_Ptr = bssl::UniquePtr; using EVP_PKEY_CTX_Ptr = bssl::UniquePtr; +using X509_Ptr = bssl::UniquePtr; +using CRYPTO_BUFFER_Ptr = bssl::UniquePtr; ErrMsgOr ecKeyGetPrivateKey(const EC_KEY* ecKey) { // Extract private key. @@ -527,22 +530,27 @@ ErrMsgOr> parseAndValidateDeviceInfo( if (parsed->clone()->asMap()->canonicalize().encode() != deviceInfoBytes) { return "DeviceInfo ordering is non-canonical."; } - const std::unique_ptr& version = parsed->get("version"); - if (!version) { - return "Device info is missing version"; - } - if (!version->asUint()) { - return "version must be an unsigned integer"; - } + RpcHardwareInfo info; provisionable->getHardwareInfo(&info); - if (version->asUint()->value() != info.versionNumber) { - return "DeviceInfo version (" + std::to_string(version->asUint()->value()) + - ") does not match the remotely provisioned component version (" + - std::to_string(info.versionNumber) + ")."; + if (info.versionNumber < 3) { + const std::unique_ptr& version = parsed->get("version"); + if (!version) { + return "Device info is missing version"; + } + if (!version->asUint()) { + return "version must be an unsigned integer"; + } + if (version->asUint()->value() != info.versionNumber) { + return "DeviceInfo version (" + std::to_string(version->asUint()->value()) + + ") does not match the remotely provisioned component version (" + + std::to_string(info.versionNumber) + ")."; + } } + std::string error; - switch (version->asUint()->value()) { + switch (info.versionNumber) { + case 3: case 2: for (const auto& entry : kAttestationIdEntrySet) { error += checkMapEntry(isFactory && !entry.alwaysValidate, *parsed, cppbor::TSTR, @@ -579,7 +587,7 @@ ErrMsgOr> parseAndValidateDeviceInfo( kValidAttIdStates); break; default: - return "Unrecognized version: " + std::to_string(version->asUint()->value()); + return "Unrecognized version: " + std::to_string(info.versionNumber); } if (!error.empty()) { @@ -734,4 +742,264 @@ ErrMsgOr> verifyProductionProtectedData( /*isFactory=*/false); } -} // namespace aidl::android::hardware::security::keymint::remote_prov \ No newline at end of file +ErrMsgOr parseX509Cert(const std::vector& cert) { + CRYPTO_BUFFER_Ptr certBuf(CRYPTO_BUFFER_new(cert.data(), cert.size(), nullptr)); + if (!certBuf.get()) { + return "Failed to create crypto buffer."; + } + X509_Ptr result(X509_parse_from_buffer(certBuf.get())); + if (!result.get()) { + return "Failed to parse certificate."; + } + return result; +} + +std::string getX509IssuerName(const X509_Ptr& cert) { + char* name = X509_NAME_oneline(X509_get_issuer_name(cert.get()), nullptr, 0); + std::string result(name); + OPENSSL_free(name); + return result; +} + +std::string getX509SubjectName(const X509_Ptr& cert) { + char* name = X509_NAME_oneline(X509_get_subject_name(cert.get()), nullptr, 0); + std::string result(name); + OPENSSL_free(name); + return result; +} + +// Validates the certificate chain and returns the leaf public key. +ErrMsgOr validateCertChain(const cppbor::Array& chain) { + uint8_t rawPubKey[64]; + size_t rawPubKeySize = sizeof(rawPubKey); + for (size_t i = 0; i < chain.size(); ++i) { + // Root must be self-signed. + size_t signingCertIndex = (i > 1) ? i - 1 : i; + auto& keyCertItem = chain[i]; + auto& signingCertItem = chain[signingCertIndex]; + if (!keyCertItem || !keyCertItem->asBstr()) { + return "Key certificate must be a Bstr."; + } + if (!signingCertItem || !signingCertItem->asBstr()) { + return "Signing certificate must be a Bstr."; + } + + auto keyCert = parseX509Cert(keyCertItem->asBstr()->value()); + if (!keyCert) { + return keyCert.message(); + } + auto signingCert = parseX509Cert(keyCertItem->asBstr()->value()); + if (!signingCert) { + return signingCert.message(); + } + + EVP_PKEY_Ptr pubKey(X509_get_pubkey(keyCert->get())); + if (!pubKey.get()) { + return "Failed to get public key."; + } + EVP_PKEY_Ptr signingPubKey(X509_get_pubkey(signingCert->get())); + if (!signingPubKey.get()) { + return "Failed to get signing public key."; + } + + if (!X509_verify(keyCert->get(), signingPubKey.get())) { + return "Verification of certificate " + std::to_string(i) + + " faile. OpenSSL error string: " + ERR_error_string(ERR_get_error(), NULL); + } + + auto certIssuer = getX509IssuerName(*keyCert); + auto signerSubj = getX509SubjectName(*signingCert); + if (certIssuer != signerSubj) { + return "Certificate " + std::to_string(i) + " has wrong issuer. Signer subject is " + + signerSubj + " Issuer subject is " + certIssuer; + } + + rawPubKeySize = sizeof(rawPubKey); + if (!EVP_PKEY_get_raw_public_key(pubKey.get(), rawPubKey, &rawPubKeySize)) { + return "Failed to get raw public key."; + } + } + + return bytevec(rawPubKey, rawPubKey + rawPubKeySize); +} + +std::string validateUdsCerts(const cppbor::Map& udsCerts, const bytevec& udsPub) { + for (const auto& [signerName, udsCertChain] : udsCerts) { + if (!signerName || !signerName->asTstr()) { + return "Signer Name must be a Tstr."; + } + if (!udsCertChain || !udsCertChain->asArray()) { + return "UDS certificate chain must be an Array."; + } + if (udsCertChain->asArray()->size() < 2) { + return "UDS certificate chain must have at least two entries: root and leaf."; + } + + auto leafPubKey = validateCertChain(*udsCertChain->asArray()); + if (!leafPubKey) { + return leafPubKey.message(); + } + if (*leafPubKey != udsPub) { + return "Leaf public key in UDS certificat chain doesn't match UDS public key."; + } + } + return ""; +} + +ErrMsgOr parseAndValidateCsrPayload(const cppbor::Array& keysToSign, + const std::vector& csrPayload, + IRemotelyProvisionedComponent* provisionable, + const std::vector& challenge, + bool isFactory) { + auto [parsedCsrPayload, _, errMsg] = cppbor::parse(csrPayload); + if (!parsedCsrPayload) { + return errMsg; + } + if (!parsedCsrPayload->asArray()) { + return "CSR payload is not a CBOR array."; + } + if (parsedCsrPayload->asArray()->size() != 5U) { + return "CSR payload must contain version, certificate type, device info, challenge, keys. " + "However, the parsed CSR payload has " + + std::to_string(parsedCsrPayload->asArray()->size()) + " entries."; + } + + auto& signedVersion = parsedCsrPayload->asArray()->get(0); + auto& signedCertificateType = parsedCsrPayload->asArray()->get(1); + auto& signedDeviceInfo = parsedCsrPayload->asArray()->get(2); + auto& signedChallenge = parsedCsrPayload->asArray()->get(3); + auto& signedKeys = parsedCsrPayload->asArray()->get(4); + + if (!signedVersion || !signedVersion->asUint() || signedVersion->asUint()->value() != 1U) { + return "CSR payload version must be an unsigned integer and must be equal to 1."; + } + if (!signedCertificateType || !signedCertificateType->asTstr()) { + // Certificate type is allowed to be extendend by vendor, i.e. we can't + // enforce its value. + return "Certificate type must be a Tstr."; + } + if (!signedDeviceInfo || !signedDeviceInfo->asMap()) { + return "Device info must be an Map."; + } + if (!signedChallenge || !signedChallenge->asBstr()) { + return "Challenge must be a Bstr."; + } + if (!signedKeys || !signedKeys->asArray()) { + return "Keys must be an Array."; + } + + auto result = parseAndValidateDeviceInfo(signedDeviceInfo->asMap()->encode(), provisionable, + isFactory); + if (!result) { + return result.message(); + } + + if (challenge.size() < 32 || challenge.size() > 64) { + return "Challenge size must be between 32 and 64 bytes inclusive. " + "However, challenge is " + + std::to_string(challenge.size()) + " bytes long."; + } + + auto challengeBstr = cppbor::Bstr(challenge); + if (*signedChallenge->asBstr() != challengeBstr) { + return "Signed challenge does not match." + "\n Actual: " + + cppbor::prettyPrint(signedChallenge->asBstr(), 64 /* maxBStrSize */) + + "\nExpected: " + cppbor::prettyPrint(&challengeBstr, 64 /* maxBStrSize */); + } + + if (signedKeys->asArray()->encode() != keysToSign.encode()) { + return "Signed keys do not match."; + } + + return std::move(*parsedCsrPayload->asArray()); +} + +ErrMsgOr> verifyCsr(const cppbor::Array& keysToSign, + const std::vector& csr, + IRemotelyProvisionedComponent* provisionable, + const std::vector& challenge, + bool isFactory) { + auto [parsedCsr, _, csrErrMsg] = cppbor::parse(csr); + if (!parsedCsr) { + return csrErrMsg; + } + if (!parsedCsr->asArray()) { + return "CSR is not a CBOR array."; + } + if (parsedCsr->asArray()->size() != 4U) { + return "CSR must contain version, UDS certificates, DICE chain, and signed data. " + "However, the parsed CSR has " + + std::to_string(parsedCsr->asArray()->size()) + " entries."; + } + + auto& version = parsedCsr->asArray()->get(0); + auto& udsCerts = parsedCsr->asArray()->get(1); + auto& diceCertChain = parsedCsr->asArray()->get(2); + auto& signedData = parsedCsr->asArray()->get(3); + + if (!version || !version->asUint() || version->asUint()->value() != 3U) { + return "Version must be an unsigned integer and must be equal to 3."; + } + if (!udsCerts || !udsCerts->asMap()) { + return "UdsCerts must be an Map."; + } + if (!diceCertChain || !diceCertChain->asArray()) { + return "DiceCertChain must be an Array."; + } + if (!signedData || !signedData->asArray()) { + return "SignedData must be an Array."; + } + + RpcHardwareInfo info; + provisionable->getHardwareInfo(&info); + if (version->asUint()->value() != info.versionNumber) { + return "CSR version (" + std::to_string(version->asUint()->value()) + + ") does not match the remotely provisioned component version (" + + std::to_string(info.versionNumber) + ")."; + } + + // DICE chain is [ pubkey, + DiceChainEntry ]. Its format is the same as BCC from RKP v1-2. + auto diceContents = validateBcc(diceCertChain->asArray()); + if (!diceContents) { + return diceContents.message() + "\n" + prettyPrint(diceCertChain.get()); + } + if (diceContents->size() == 0U) { + return "The DICE chain is empty. It must contain at least one entry."; + } + + auto& udsPub = diceContents->back().pubKey; + + auto error = validateUdsCerts(*udsCerts->asMap(), udsPub); + if (!error.empty()) { + return error; + } + + auto csrPayload = verifyAndParseCoseSign1(signedData->asArray(), udsPub, {} /* aad */); + if (!csrPayload) { + return csrPayload.message(); + } + + auto parsedCsrPayload = parseAndValidateCsrPayload(keysToSign, *csrPayload, provisionable, + challenge, isFactory); + if (!parsedCsrPayload) { + return parsedCsrPayload.message(); + } + + return *diceContents; +} + +ErrMsgOr> verifyFactoryCsr(const cppbor::Array& keysToSign, + const std::vector& csr, + IRemotelyProvisionedComponent* provisionable, + const std::vector& challenge) { + return verifyCsr(keysToSign, csr, provisionable, challenge, /*isFactory=*/true); +} + +ErrMsgOr> verifyProductionCsr( + const cppbor::Array& keysToSign, const std::vector& csr, + IRemotelyProvisionedComponent* provisionable, const std::vector& challenge) { + return verifyCsr(keysToSign, csr, provisionable, challenge, /*isFactory=*/false); +} + +} // namespace aidl::android::hardware::security::keymint::remote_prov