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.

Bug: 219962281
Test: atest VtsHalRemotelyProvisionedComponentTargetTest


(cherry picked from https://android-review.googlesource.com/q/commit:81880284af9f8d6f82d8fbd4b82c70307fcb3762)
Merged-In: I9e7769861529b8095d77ce8fee3c78222579d38c
Change-Id: I9e7769861529b8095d77ce8fee3c78222579d38c
This commit is contained in:
Max Bires
2022-01-30 17:28:13 -08:00
committed by Subrahmanya Manikanta Venkateswarlu Bhamidipati Kameswara Sri
parent 12b2c93b10
commit b326f33fbc

View File

@@ -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<bytevec> 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<bytevec> verifyAndParseCoseSign1Cwt(const cppbor::Array* coseSign1,
const bytevec& signingCoseKey, const bytevec& aad) {
if (!coseSign1 || coseSign1->size() != kCoseSign1EntryCount) {
@@ -126,18 +143,16 @@ ErrMsgOr<bytevec> 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<bytevec> verifyAndParseCoseSign1Cwt(const cppbor::Array* coseSign1,
return "Signature verification failed";
}
return serializedKey->asBstr()->value();
return serializedKey.moveValue();
}
ErrMsgOr<std::vector<BccEntryData>> validateBcc(const cppbor::Array* bcc) {
@@ -156,8 +171,11 @@ ErrMsgOr<std::vector<BccEntryData>> validateBcc(const cppbor::Array* bcc) {
std::vector<BccEntryData> 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<std::vector<BccEntryData>> 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;