From 81880284af9f8d6f82d8fbd4b82c70307fcb3762 Mon Sep 17 00:00:00 2001 From: Max Bires Date: Sun, 30 Jan 2022 17:28:13 -0800 Subject: [PATCH] Include the CWT validation in VTS testing. This change modifies the VTS tests to validate the structure of the CBOR Web Token (CWT) which contains the public key as one of several fields in the map. It only validates the subset of fields expected to be seen in the VTS tests. The ones related to the actual BCC will not be present in test mode due to the security guarantees of the interface. Additionally, the test will also now check that the first key in the BCC array matches the public key that should be self-signed in the certificate located in the second index of the BCC array. Test: atest VtsHalRemotelyProvisionedComponentTargetTest Change-Id: I9e7769861529b8095d77ce8fee3c78222579d38c --- .../keymint/support/remote_prov_utils.cpp | 43 +++++++++++++++---- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/security/keymint/support/remote_prov_utils.cpp b/security/keymint/support/remote_prov_utils.cpp index 0cbee51044..35cb891dac 100644 --- a/security/keymint/support/remote_prov_utils.cpp +++ b/security/keymint/support/remote_prov_utils.cpp @@ -26,6 +26,11 @@ namespace aidl::android::hardware::security::keymint::remote_prov { +constexpr uint32_t kBccPayloadIssuer = 1; +constexpr uint32_t kBccPayloadSubject = 2; +constexpr int32_t kBccPayloadSubjPubKey = -4670552; +constexpr int32_t kBccPayloadKeyUsage = -4670553; + bytevec kTestMacKey(32 /* count */, 0 /* byte value */); bytevec randomBytes(size_t numBytes) { @@ -98,6 +103,18 @@ bytevec getProdEekChain() { return prodEek; } +ErrMsgOr validatePayloadAndFetchPubKey(const cppbor::Map* payload) { + const auto& issuer = payload->get(kBccPayloadIssuer); + if (!issuer || !issuer->asTstr()) return "Issuer is not present or not a tstr."; + const auto& subject = payload->get(kBccPayloadSubject); + if (!subject || !subject->asTstr()) return "Subject is not present or not a tstr."; + const auto& keyUsage = payload->get(kBccPayloadKeyUsage); + if (!keyUsage || !keyUsage->asBstr()) return "Key usage is not present or not a bstr."; + const auto& serializedKey = payload->get(kBccPayloadSubjPubKey); + if (!serializedKey || !serializedKey->asBstr()) return "Key is not present or not a bstr."; + return serializedKey->asBstr()->value(); +} + ErrMsgOr verifyAndParseCoseSign1Cwt(const cppbor::Array* coseSign1, const bytevec& signingCoseKey, const bytevec& aad) { if (!coseSign1 || coseSign1->size() != kCoseSign1EntryCount) { @@ -126,18 +143,16 @@ ErrMsgOr verifyAndParseCoseSign1Cwt(const cppbor::Array* coseSign1, return "Unsupported signature algorithm"; } - // TODO(jbires): Handle CWTs as the CoseSign1 payload in a less hacky way. Since the CWT payload - // is extremely remote provisioning specific, probably just make a separate - // function there. auto [parsedPayload, __, payloadErrMsg] = cppbor::parse(payload); if (!parsedPayload) return payloadErrMsg + " when parsing key"; if (!parsedPayload->asMap()) return "CWT must be a map"; - auto serializedKey = parsedPayload->asMap()->get(-4670552)->clone(); - if (!serializedKey || !serializedKey->asBstr()) return "Could not find key entry"; + auto serializedKey = validatePayloadAndFetchPubKey(parsedPayload->asMap()); + if (!serializedKey) { + return "CWT validation failed: " + serializedKey.moveMessage(); + } bool selfSigned = signingCoseKey.empty(); - auto key = - CoseKey::parseEd25519(selfSigned ? serializedKey->asBstr()->value() : signingCoseKey); + auto key = CoseKey::parseEd25519(selfSigned ? *serializedKey : signingCoseKey); if (!key) return "Bad signing key: " + key.moveMessage(); bytevec signatureInput = @@ -148,7 +163,7 @@ ErrMsgOr verifyAndParseCoseSign1Cwt(const cppbor::Array* coseSign1, return "Signature verification failed"; } - return serializedKey->asBstr()->value(); + return serializedKey.moveValue(); } ErrMsgOr> validateBcc(const cppbor::Array* bcc) { @@ -156,8 +171,11 @@ ErrMsgOr> validateBcc(const cppbor::Array* bcc) { std::vector result; + const auto& devicePubKey = bcc->get(0); + if (!devicePubKey->asMap()) return "Invalid device public key at the 1st entry in the BCC"; + bytevec prevKey; - // TODO(jbires): Actually process the pubKey at the start of the new bcc entry + for (size_t i = 1; i < bcc->size(); ++i) { const cppbor::Array* entry = bcc->get(i)->asArray(); if (!entry || entry->size() != kCoseSign1EntryCount) { @@ -177,6 +195,13 @@ ErrMsgOr> validateBcc(const cppbor::Array* bcc) { // This entry's public key is the signing key for the next entry. prevKey = payload.moveValue(); + if (i == 1) { + auto [parsedRootKey, _, errMsg] = cppbor::parse(prevKey); + if (!parsedRootKey || !parsedRootKey->asMap()) return "Invalid payload entry in BCC."; + if (*parsedRootKey != *devicePubKey) { + return "Device public key doesn't match BCC root."; + } + } } return result;