From dbb0bea70193d8f1176b2a374e68ba076e3c4549 Mon Sep 17 00:00:00 2001 From: Cong Lin Date: Fri, 22 Sep 2023 11:58:52 -0700 Subject: [PATCH] Add Bcc validator to oemcrypto util and unit tests A Bcc validator that can parse and validate BCC. This is to support better prov40 unit tests regarding OEMCrypto_GetBootCertificateChain() later. Test: opk_ta_p40 Bug: 300304834 Bug: 307968622 Change-Id: I3cfdad9f1891c6abc83051af1d80a20e0adeb58b --- .../oemcrypto/util/include/bcc_validator.h | 54 ++ .../oemcrypto/util/include/cbor_validator.h | 80 +++ .../oemcrypto/util/src/bcc_validator.cpp | 467 ++++++++++++++++++ .../oemcrypto/util/src/cbor_validator.cpp | 113 +++++ 4 files changed, 714 insertions(+) create mode 100644 libwvdrmengine/oemcrypto/util/include/bcc_validator.h create mode 100644 libwvdrmengine/oemcrypto/util/include/cbor_validator.h create mode 100644 libwvdrmengine/oemcrypto/util/src/bcc_validator.cpp create mode 100644 libwvdrmengine/oemcrypto/util/src/cbor_validator.cpp diff --git a/libwvdrmengine/oemcrypto/util/include/bcc_validator.h b/libwvdrmengine/oemcrypto/util/include/bcc_validator.h new file mode 100644 index 00000000..9ed9cfac --- /dev/null +++ b/libwvdrmengine/oemcrypto/util/include/bcc_validator.h @@ -0,0 +1,54 @@ +// Copyright 2023 Google LLC. All Rights Reserved. This file and proprietary +// source code may only be used and distributed under the Widevine License +// Agreement. +// +// Reference implementation utilities of OEMCrypto APIs +// +#ifndef WVOEC_UTIL_BCC_VALIDATOR_H_ +#define WVOEC_UTIL_BCC_VALIDATOR_H_ + +#include +#include +#include + +#include "cbor_validator.h" +#include "cppbor.h" + +namespace wvoec { +namespace util { +// BccValidator processes a Provisioning 4.0 device root of trust. It extracts +// and validates relevant pieces of information of BCC. +// Relevant documents: +// Android definition: go/remote-provisioning-hal#bcc. +// Google Dice Profile: go/dice-profile +class BccValidator : public CborValidator { + public: + explicit BccValidator() {} + virtual ~BccValidator() override = default; + BccValidator(const BccValidator&) = delete; + BccValidator& operator=(const BccValidator&) = delete; + // Verifies the Cbor struct of a client generated root of trust. This message + // is part of an attestation model conforming to the Google Open Dice Profile. + // This message is received from a client device to attest it is a valid + // Widevine device. + virtual CborMessageStatus Validate() override; + // Outputs BCC in YAML. + virtual std::string GetFormattedMessage() const override; + + private: + // Processes CoseKey PubKeyEd25519 / PubKeyECDSA256, prints into |fmt_msgs|, + // and extracts the PubKey string to *|public_key_bytes|. + CborMessageStatus ProcessSubjectPublicKeyInfo( + const cppbor::Map& public_key_info_map, + std::vector& fmt_msgs, std::string* public_key_bytes); + // Processes DiceChainEntryPayload, which contains subject public key, prints + // into |fmt_msgs|, and extracts the PubKey string to *|public_key_bytes|. + CborMessageStatus ProcessDiceChainEntryPayload( + const std::vector& payload, std::vector& fmt_msgs, + std::string* public_key_bytes); + // Used to generate formatted message. + std::stringstream msg_ss_; +}; +} // namespace util +} // namespace wvoec +#endif // WVOEC_UTIL_BCC_VALIDATOR_H_ diff --git a/libwvdrmengine/oemcrypto/util/include/cbor_validator.h b/libwvdrmengine/oemcrypto/util/include/cbor_validator.h new file mode 100644 index 00000000..0f09c2b0 --- /dev/null +++ b/libwvdrmengine/oemcrypto/util/include/cbor_validator.h @@ -0,0 +1,80 @@ +// Copyright 2023 Google LLC. All Rights Reserved. This file and proprietary +// source code may only be used and distributed under the Widevine License +// Agreement. +// +// Reference implementation utilities of OEMCrypto APIs +// +#ifndef WVOEC_UTIL_CBOR_VALIDATOR_H_ +#define WVOEC_UTIL_CBOR_VALIDATOR_H_ + +#include +#include + +#include "cppbor.h" +#include "cppbor_parse.h" + +namespace wvoec { +namespace util { +// CborMessageStatus values are ranked in level of severity. +// kCborUninitialized being the lowest severity, and +// kCborValidateFatal being the highest. +enum CborMessageStatus { + kCborUninitialized = 0, + kCborParseOk = 1, + kCborParseError = 2, + kCborValidateOk = 3, + kCborValidateWarning = 4, + kCborValidateError = 5, + kCborValidateFatal = 6 +}; + +std::string CppborMajorTypeToString(cppbor::MajorType type); +std::string CborMessageStatusToString(CborMessageStatus status); + +class CborValidator { + public: + explicit CborValidator() {} + virtual ~CborValidator() = default; + CborValidator(const CborValidator&) = delete; + CborValidator& operator=(const CborValidator&) = delete; + + // Decodes |cbor| and sets |message_status_|. + virtual CborMessageStatus Parse(const std::vector& cbor); + const cppbor::ParseResult* GetParseResult() const; + // Returns pretty-printed CBOR for |parse_result_|. Returns empty string if + // |parse_result_| is not valid. + std::string GetRawMessage() const; + + // Verifies the fields in |parse_result_| to have expected types and values. + // Requires that Parse() is called first and |parse_result_| contains a valid + // CBOR message. + virtual CborMessageStatus Validate(); + // Returns all validation messages from Validate(). + const std::vector>& + GetValidateMessages() const { + return validate_messages_; + } + // Prints |parse_result_| in readable format. Requires that Parse() is called + // first and |parse_result_| contains a valid CBOR message. + virtual std::string GetFormattedMessage() const; + const cppbor::ParseResult& parse_result() const { return parse_result_; } + const std::vector>& + validate_messages() { + return validate_messages_; + } + + protected: + void Reset(); + // Writes validation output |msg| to |validate_messages_|, and updates + // |message_status_| if the |status| is more severe than the current value. + void AddValidationMessage(CborMessageStatus status, const std::string& msg); + CborMessageStatus message_status_ = kCborUninitialized; + + private: + // Internal status of parsing and validating. + cppbor::ParseResult parse_result_ = {}; + std::vector> validate_messages_; +}; +} // namespace util +} // namespace wvoec +#endif // WVOEC_UTIL_CBOR_VALIDATOR_H_ diff --git a/libwvdrmengine/oemcrypto/util/src/bcc_validator.cpp b/libwvdrmengine/oemcrypto/util/src/bcc_validator.cpp new file mode 100644 index 00000000..fc3b439d --- /dev/null +++ b/libwvdrmengine/oemcrypto/util/src/bcc_validator.cpp @@ -0,0 +1,467 @@ +// Copyright 2023 Google LLC. All Rights Reserved. This file and proprietary +// source code may only be used and distributed under the Widevine License +// Agreement. +// +// Reference implementation utilities of OEMCrypto APIs +// +#include "bcc_validator.h" + +#include + +#include +#include +#include +#include +#include + +#include "string_conversions.h" + +namespace wvoec { +namespace util { +namespace { +// The BCC is encoded using RFC 8949- Concise Binary Object Representation +// (CBOR). + +// The full definition of the following enums can be found here: +// go/remote-provisioning-hal#bcc. + +// The device key is encoded in a cbor map. The key values are a mix of +// positive and negative integer values. +enum { + MAP_KEY_DEVICE_KEY_TYPE = 1, + MAP_KEY_DEVICE_KEY_ALGORITHM = 3, + MAP_KEY_DEVICE_KEY_OPS = 4, + MAP_KEY_DEVICE_KEY_CURVE = -1, + MAP_KEY_DEVICE_KEY_BYTES_0 = -2, + MAP_KEY_DEVICE_KEY_BYTES_1 = -3, +}; + +// The device key may be encoded in the BCC as either X,Y elliptic curve +// coordinates, or as raw bytes. The value is identified using +// MAP_KEY_DEVICE_KEY_TYPE. +enum { + DEVICE_KEY_ENCODING_UNKNOWN = 0, + DEVICE_KEY_BYTE_STRING = 1, + DEVICE_KEY_OCTET_PAIR = 2, +}; + +// Android/Widevine Dice Attestation allows two signing models. This is +// identified using MAP_KEY_DEVICE_KEY_ALGORITHM. +enum { + DEVICE_KEY_ALGORITHM_ES256 = -7, // EC key with SHA-256 + DEVICE_KEY_ALGORITHM_EDDSA = -8, // Pure ED25519. + DEVICE_KEY_ALGORITHM_ES384 = -35, // EC key with SHA-384 +}; + +// The curve used to generate the device public key is identified using the +// MAP_KEY_DEVICE_KEY_CURVE. +enum { + DEVICE_KEY_CURVE_P256 = 1, + DEVICE_KEY_CURVE_P384 = 2, + DEVICE_KEY_CURVE_ED25519 = 6, +}; + +// Sized to hold a component of a P256 public key uncompressed point compatible +// with X9.62. The key is formatted in an Z/X/Y format in which Z == 0x04 and X +// and Y are the public key coordinates. X and Y are each 32 bytes. +constexpr int kP256KeyComponentSize = 256 / 8; +// Sized to hold a P384 public key uncompressed point compatible with X9.62. +// The key is formatted in an Z/X/Y format in which Z == 0x04 and X and Y are +// the public key coordinates. X and Y are each 48 bytes. +constexpr int kP384KeyComponentSize = 384 / 8; +constexpr int kMarshaledP384KeySize = kP384KeyComponentSize * 2 + 1; +constexpr int kMaxMarshaledECKeySize = kMarshaledP384KeySize; +constexpr char kMarshaledECKeyZValue = 0x04; +constexpr int kED25519KeyDataItemSize = 32; +// The Issuer field key in BccEntryPayload. +constexpr int64_t kIssuer = 1; +// The Subject field key in BccEntryPayload. +constexpr int64_t kSubject = 2; +// The SubjectPublicKey field key in BccEntryPayload. +constexpr int64_t kSubjectPublicKey = -4670552; + +struct IssuerSubject { + std::string issuer; + std::string subject; + bool IsValid() const { return !issuer.empty() && !subject.empty(); } + void PrintTo(std::vector& fmt_msgs) const { + fmt_msgs.push_back("Issuer: "); + fmt_msgs.back().append(issuer.empty() ? "" : issuer); + fmt_msgs.push_back("Subject: "); + fmt_msgs.back().append(subject.empty() ? "" : subject); + } +}; + +IssuerSubject GetIssuerSubjectFromBccEntryPayload( + const cppbor::Map* bcc_entry_payload) { + IssuerSubject ret; + for (size_t i = 0; i < bcc_entry_payload->size(); ++i) { + const auto& entry = (*bcc_entry_payload)[i]; + if (entry.first == nullptr || entry.first->asInt() == nullptr || + entry.second == nullptr || entry.second->asTstr() == nullptr) { + continue; + } + const auto& value = entry.second->asTstr()->value(); + if (entry.first->asInt()->value() == kIssuer) { + ret.issuer = value.empty() ? "" : value; + } else if (entry.first->asInt()->value() == kSubject) { + ret.subject = value.empty() ? "" : value; + } + } + return ret; +} + +const cppbor::Bstr* GetSubjectPublicKeyFromBccEntryPayload( + const cppbor::Map* bcc_entry_payload) { + for (size_t i = 0; i < bcc_entry_payload->size(); ++i) { + const auto& entry = (*bcc_entry_payload)[i]; + if (entry.first == nullptr || entry.first->asInt() == nullptr || + entry.second == nullptr) { + continue; + } + if (entry.first->asInt()->value() == kSubjectPublicKey) { + return entry.second->asBstr(); + } + } + return nullptr; +} + +void AddMessages(std::stringstream& ss, + const std::vector& fmt_msgs, int indent) { + const std::string spaces = std::string(indent * 2, ' '); + for (auto& msg : fmt_msgs) { + ss << spaces << msg << "\n"; + } +} +} // namespace + +CborMessageStatus BccValidator::Validate() { + if (message_status_ != kCborParseOk) return message_status_; + const cppbor::Item* parsed_bcc = std::get<0>(parse_result()).get(); + if (parsed_bcc == nullptr) { + AddValidationMessage(kCborValidateFatal, "BCC is empty."); + return message_status_; + } + if (parsed_bcc->asArray() == nullptr) { + AddValidationMessage(kCborValidateFatal, + "BCC is not a CBOR array. Actual type: " + + CppborMajorTypeToString(parsed_bcc->type())); + return message_status_; + } + const cppbor::Array* bcc_array = parsed_bcc->asArray(); + if (bcc_array->size() < 2) { + AddValidationMessage(kCborValidateFatal, + "BCC should contain at least two elements. Actual: " + + std::to_string(bcc_array->size())); + return message_status_; + } + + // Writes YAML-formatted output to |msg_ss_| during validation. + msg_ss_.str(std::string()); + msg_ss_ << "---" + << "\n"; + msg_ss_ << "DEVICE PUBLIC KEY:\n"; + + // The first element in the array contains the root device public key + // definition. + const cppbor::Map* device_public_key_info = (*bcc_array)[0]->asMap(); + if (device_public_key_info == nullptr) { + AddValidationMessage( + kCborValidateFatal, + "Device public key info is not a CBOR map. Actual type: " + + CppborMajorTypeToString((*bcc_array)[0]->type())); + return message_status_; + } + std::vector key_value_texts; + std::string device_public_key_bytes; + CborMessageStatus status = ProcessSubjectPublicKeyInfo( + *device_public_key_info, key_value_texts, &device_public_key_bytes); + AddMessages(msg_ss_, key_value_texts, 1); + if (status == kCborValidateFatal) return status; + + msg_ss_ << "BCC ENTRY:\n"; + // Parse each certificate in the chain. The structure of thr entries are + // COSE_Sign1 (untagged). + for (size_t i = 1; i < bcc_array->size(); ++i) { + msg_ss_ << "- CDI PUBLIC KEY INDEX: " << i << "\n"; + const cppbor::Array* bcc_entry = (*bcc_array)[i]->asArray(); + if (bcc_entry == nullptr) { + AddValidationMessage(kCborValidateFatal, + "BCC entry is empty at index " + std::to_string(i)); + return message_status_; + } + if (bcc_entry->size() != 4) { + AddValidationMessage(kCborValidateFatal, + "BCC entry should contain 4 items. Actual: " + + std::to_string(bcc_entry->size())); + return message_status_; + } + // Skip CoseSign1 signature verification here, only extract pub keys + if ((*bcc_entry)[0]->type() != cppbor::BSTR || + (*bcc_entry)[1]->type() != cppbor::MAP || + (*bcc_entry)[2]->type() != cppbor::BSTR || + (*bcc_entry)[3]->type() != cppbor::BSTR) { + AddValidationMessage(kCborValidateFatal, "Invalid BCC entry type."); + return message_status_; + } + + const std::vector& key_payload = + (*bcc_entry)[2]->asBstr()->value(); + key_value_texts.clear(); + std::string entry_public_key_bytes; + status = ProcessDiceChainEntryPayload(key_payload, key_value_texts, + &entry_public_key_bytes); + AddMessages(msg_ss_, key_value_texts, 1); + if (status == kCborValidateFatal) return status; + // If the size of the BCC array (including device pub key) is 2, then it + // must be a de-generated BCC, which means the second element in the array + // is a self-signed entry. The entry's public key should be identical to the + // device's public key. + if (bcc_array->size() == 2 && i == 1) { + // self-signed BCC entry + if (entry_public_key_bytes != device_public_key_bytes) { + AddValidationMessage(kCborValidateError, + "The public key of a self-signed entry should be " + "identical to its device public key."); + } + } + } + msg_ss_ << "...\n"; + if (message_status_ == kCborParseOk) message_status_ = kCborValidateOk; + return message_status_; +} + +CborMessageStatus BccValidator::ProcessSubjectPublicKeyInfo( + const cppbor::Map& public_key_info_map, std::vector& fmt_msgs, + std::string* public_key_bytes) { + int key_encoding_format = DEVICE_KEY_ENCODING_UNKNOWN; + std::string device_key_bytes_0; + std::string device_key_bytes_1; + std::unordered_set key_set; + for (size_t index = 0; index < public_key_info_map.size(); ++index) { + std::pair&, + const std::unique_ptr&> + entry = public_key_info_map[index]; + if (entry.first->type() != cppbor::NINT && + entry.first->type() != cppbor::UINT) { + AddValidationMessage(kCborValidateFatal, + "Invalid key type in public key info map: " + + CppborMajorTypeToString(entry.first->type())); + return kCborValidateFatal; + } + const int64_t map_key = entry.first->asInt()->value(); + switch (map_key) { + case MAP_KEY_DEVICE_KEY_TYPE: { + if (entry.second->type() != cppbor::UINT) { + AddValidationMessage( + kCborValidateFatal, + "Invalid value type in public key info map for " + "key MAP_KEY_DEVICE_KEY_TYPE: " + + CppborMajorTypeToString(entry.second->type())); + return kCborValidateFatal; + } + std::string kv = "key encoding format: "; + const int64_t value = entry.second->asUint()->value(); + if (value == DEVICE_KEY_OCTET_PAIR) { + key_encoding_format = DEVICE_KEY_OCTET_PAIR; + kv += "DEVICE_KEY_OCTET_PAIR"; + } else if (value == DEVICE_KEY_BYTE_STRING) { + key_encoding_format = DEVICE_KEY_BYTE_STRING; + kv += "DEVICE_KEY_BYTE_STRING"; + } else { + AddValidationMessage(kCborValidateError, + "Invalid value in public key info map for key " + "MAP_KEY_DEVICE_KEY_TYPE: " + + std::to_string(value)); + } + fmt_msgs.push_back(kv); + } break; + case MAP_KEY_DEVICE_KEY_ALGORITHM: { + if (entry.second->type() != cppbor::NINT) { + AddValidationMessage( + kCborValidateFatal, + "Invalid value type in public key info map for " + "key MAP_KEY_DEVICE_KEY_ALGORITHM: " + + CppborMajorTypeToString(entry.second->type())); + return kCborValidateFatal; + } + std::string kv = "key algorithm type: "; + const int64_t value = entry.second->asNint()->value(); + if (value == DEVICE_KEY_ALGORITHM_ES256) { + kv += "ECDSA_SHA256"; + } else if (value == DEVICE_KEY_ALGORITHM_ES384) { + kv += "ECDSA_SHA384"; + } else if (value == DEVICE_KEY_ALGORITHM_EDDSA) { + kv += "EDDSA"; + } else { + AddValidationMessage(kCborValidateError, + "Invalid value in public key info map for key " + "MAP_KEY_DEVICE_KEY_ALGORITHM: " + + std::to_string(value)); + } + fmt_msgs.push_back(kv); + } break; + case MAP_KEY_DEVICE_KEY_OPS: + // The OPS is an array. Ignored for now. + break; + case MAP_KEY_DEVICE_KEY_CURVE: { + if (entry.second->type() != cppbor::UINT) { + AddValidationMessage( + kCborValidateFatal, + "Invalid value type in public key info map for " + "key MAP_KEY_DEVICE_KEY_CURVE: " + + CppborMajorTypeToString(entry.second->type())); + return kCborValidateFatal; + } + std::string kv = "curve: "; + const int64_t value = entry.second->asUint()->value(); + if (value == DEVICE_KEY_CURVE_P256) { + kv += "P256"; + } else if (value == DEVICE_KEY_CURVE_P384) { + kv += "P384"; + } else if (value == DEVICE_KEY_CURVE_ED25519) { + kv += "ED25519"; + } else { + AddValidationMessage(kCborValidateError, + "Invalid value in public key info map for key " + "MAP_KEY_DEVICE_KEY_CURVE: " + + std::to_string(value)); + } + fmt_msgs.push_back(kv); + } break; + case MAP_KEY_DEVICE_KEY_BYTES_0: + case MAP_KEY_DEVICE_KEY_BYTES_1: + // BCC encodes keys as either two X, Y octet strings or a single + // octet string. The format used depends on the key type. + if (entry.second->type() != cppbor::BSTR) { + AddValidationMessage( + kCborValidateFatal, + "Invalid value type in public key info map for " + "key MAP_KEY_DEVICE_KEY_BYTES_0/1: " + + CppborMajorTypeToString(entry.second->type())); + return kCborValidateFatal; + } + const std::vector& key_bytes = entry.second->asBstr()->value(); + // Key byte length depends upon the key type. + if (key_bytes.size() != kED25519KeyDataItemSize && + key_bytes.size() != kP256KeyComponentSize && + key_bytes.size() != kP384KeyComponentSize) { + AddValidationMessage(kCborValidateFatal, + "Malformed public key data size of: " + + std::to_string(key_bytes.size())); + return kCborValidateFatal; + } + std::string& key_bytes_str = map_key == MAP_KEY_DEVICE_KEY_BYTES_0 + ? device_key_bytes_0 + : device_key_bytes_1; + key_bytes_str.assign(key_bytes.begin(), key_bytes.end()); + } + key_set.insert(map_key); + } + if (key_set.find(MAP_KEY_DEVICE_KEY_TYPE) == key_set.end()) { + AddValidationMessage(kCborValidateError, + "Missing MAP_KEY_DEVICE_KEY_TYPE."); + } + if (key_set.find(MAP_KEY_DEVICE_KEY_ALGORITHM) == key_set.end()) { + AddValidationMessage(kCborValidateError, + "Missing MAP_KEY_DEVICE_KEY_ALGORITHM."); + } + if (key_set.find(MAP_KEY_DEVICE_KEY_CURVE) == key_set.end()) { + AddValidationMessage(kCborValidateError, + "Missing MAP_KEY_DEVICE_KEY_CURVE."); + } + if (device_key_bytes_0.empty() || + (key_encoding_format == DEVICE_KEY_OCTET_PAIR && + device_key_bytes_1.empty())) { + AddValidationMessage( + kCborValidateFatal, + "Malformed public key definition. Missing device public key bytes."); + return kCborValidateFatal; + } + std::string device_key_bytes; + if (key_encoding_format == DEVICE_KEY_OCTET_PAIR) { + // Key is an ECDSA elliptic key. We need to return the ANSI X9.62 + // marshaled public key. Generate the marshaled key if needed. The + // marshaled key is needed to create an ECPublicKey object. + // std::string* device_key_bytes = public_key_info.mutable_key_bytes(); + device_key_bytes.reserve(kMaxMarshaledECKeySize); + device_key_bytes.resize(1); + device_key_bytes[0] = kMarshaledECKeyZValue; + device_key_bytes.append(device_key_bytes_0); + device_key_bytes.append(device_key_bytes_1); + } else { + device_key_bytes = device_key_bytes_0; + } + fmt_msgs.push_back("public key bytes: " + wvutil::b2a_hex(device_key_bytes)); + *public_key_bytes = device_key_bytes; + return message_status_; +} + +CborMessageStatus BccValidator::ProcessDiceChainEntryPayload( + const std::vector& payload, std::vector& fmt_msgs, + std::string* entry_public_key_bytes) { + if (payload.empty()) { + AddValidationMessage(kCborValidateFatal, "Empty bcc entry payload."); + return kCborValidateFatal; + } + auto parse_result = cppbor::parse(payload); + std::unique_ptr item = std::move(std::get<0>(parse_result)); + std::string error_message = std::move(std::get<2>(parse_result)); + if (item == nullptr || !error_message.empty()) { + AddValidationMessage(kCborValidateFatal, + "Unable to parse bcc entry payload: " + error_message); + return kCborValidateFatal; + } + if (item->type() != cppbor::MAP) { + AddValidationMessage(kCborValidateFatal, + "Unexpected bcc entry payload type: " + + CppborMajorTypeToString(item->type())); + return kCborValidateFatal; + } + const IssuerSubject issuer_subject = + GetIssuerSubjectFromBccEntryPayload(item->asMap()); + if (!issuer_subject.IsValid()) { + AddValidationMessage(kCborValidateError, "Missing Issuer or Subject."); + } + issuer_subject.PrintTo(fmt_msgs); + const cppbor::Bstr* subject_public_key = + GetSubjectPublicKeyFromBccEntryPayload(item->asMap()); + if (subject_public_key == nullptr) { + AddValidationMessage(kCborValidateFatal, + "Bcc entry payload has no subject public key."); + return kCborValidateFatal; + } + + // Now parse the serialized subject public key. + parse_result = cppbor::parse(subject_public_key->value()); + item = std::move(std::get<0>(parse_result)); + error_message = std::move(std::get<2>(parse_result)); + if (item == nullptr || !error_message.empty()) { + AddValidationMessage( + kCborValidateFatal, + "Unable to parse serialized subject public key: " + error_message); + return kCborValidateFatal; + } + const cppbor::Map* subject_public_key_info = item->asMap(); + if (subject_public_key_info == nullptr) { + AddValidationMessage(kCborValidateFatal, + "Invalid subject public key type. Expected Map."); + return kCborValidateFatal; + } + return ProcessSubjectPublicKeyInfo(*subject_public_key_info, fmt_msgs, + entry_public_key_bytes); +} + +std::string BccValidator::GetFormattedMessage() const { + if (message_status_ == kCborUninitialized || + message_status_ == kCborParseError) { + return std::string(); + } + const cppbor::Item* parsed_item = std::get<0>(parse_result()).get(); + if (parsed_item == nullptr) { + return ""; + } + return msg_ss_.str(); +} +} // namespace util +} // namespace wvoec diff --git a/libwvdrmengine/oemcrypto/util/src/cbor_validator.cpp b/libwvdrmengine/oemcrypto/util/src/cbor_validator.cpp new file mode 100644 index 00000000..59b95704 --- /dev/null +++ b/libwvdrmengine/oemcrypto/util/src/cbor_validator.cpp @@ -0,0 +1,113 @@ +// Copyright 2023 Google LLC. All Rights Reserved. This file and proprietary +// source code may only be used and distributed under the Widevine License +// Agreement. +// +// Reference implementation utilities of OEMCrypto APIs +// +#include "cbor_validator.h" + +#include + +#include +#include +#include +#include +#include + +namespace wvoec { +namespace util { +std::string CppborMajorTypeToString(cppbor::MajorType type) { + switch (type) { + case cppbor::UINT: + return "UINT"; + case cppbor::NINT: + return "NINT"; + case cppbor::BSTR: + return "BSTR"; + case cppbor::TSTR: + return "TSTR"; + case cppbor::ARRAY: + return "ARRAY"; + case cppbor::MAP: + return "MAP"; + case cppbor::SEMANTIC: + return "SEMANTIC"; + case cppbor::SIMPLE: + return "SIMPLE"; + } + return "undefined type"; +} + +std::string CborMessageStatusToString(CborMessageStatus status) { + switch (status) { + case kCborUninitialized: + return "Uninitialized"; + case kCborParseOk: + return "ParseOk"; + case kCborParseError: + return "ParseError"; + case kCborValidateOk: + return "ValidateOk"; + case kCborValidateWarning: + return "ValidateWarning"; + case kCborValidateError: + return "ValidateError"; + case kCborValidateFatal: + return "ValidateFatal"; + } + return "undefined status"; +} + +void CborValidator::Reset() { + message_status_ = kCborUninitialized; + parse_result_ = {nullptr, nullptr, ""}; + validate_messages_.clear(); +} + +CborMessageStatus CborValidator::Parse(const std::vector& cbor) { + Reset(); + parse_result_ = cppbor::parse(cbor); + message_status_ = + (std::get<0>(parse_result_) && std::get<2>(parse_result_).empty()) + ? kCborParseOk + : kCborParseError; + return message_status_; +} + +const cppbor::ParseResult* CborValidator::GetParseResult() const { + if (message_status_ == kCborUninitialized) { + return nullptr; + } + return &parse_result_; +} + +std::string CborValidator::GetRawMessage() const { + if (message_status_ == kCborUninitialized || + message_status_ == kCborParseError) { + return std::string(); + } + const cppbor::Item* parsed_item = std::get<0>(parse_result_).get(); + if (parsed_item == nullptr) { + return ""; + } + return cppbor::prettyPrint(parsed_item); +} + +CborMessageStatus CborValidator::Validate() { + if (message_status_ != kCborParseOk) return message_status_; + // No other validations to be done than Parse() being successful. + AddValidationMessage(kCborValidateOk, "No validations are done."); + return message_status_; +} + +std::string CborValidator::GetFormattedMessage() const { + return GetRawMessage(); +} + +void CborValidator::AddValidationMessage(CborMessageStatus status, + const std::string& msg) { + validate_messages_.push_back({status, msg}); + if (status > message_status_) message_status_ = status; +} +} // namespace util +} // namespace wvoec