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
This commit is contained in:
54
libwvdrmengine/oemcrypto/util/include/bcc_validator.h
Normal file
54
libwvdrmengine/oemcrypto/util/include/bcc_validator.h
Normal file
@@ -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 <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#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<std::string>& 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<uint8_t>& payload, std::vector<std::string>& 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_
|
||||
80
libwvdrmengine/oemcrypto/util/include/cbor_validator.h
Normal file
80
libwvdrmengine/oemcrypto/util/include/cbor_validator.h
Normal file
@@ -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 <string>
|
||||
#include <vector>
|
||||
|
||||
#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<uint8_t>& 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<std::pair<CborMessageStatus, std::string>>&
|
||||
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<std::pair<CborMessageStatus, std::string>>&
|
||||
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<std::pair<CborMessageStatus, std::string>> validate_messages_;
|
||||
};
|
||||
} // namespace util
|
||||
} // namespace wvoec
|
||||
#endif // WVOEC_UTIL_CBOR_VALIDATOR_H_
|
||||
467
libwvdrmengine/oemcrypto/util/src/bcc_validator.cpp
Normal file
467
libwvdrmengine/oemcrypto/util/src/bcc_validator.cpp
Normal file
@@ -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 <stddef.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <iomanip>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#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<std::string>& fmt_msgs) const {
|
||||
fmt_msgs.push_back("Issuer: ");
|
||||
fmt_msgs.back().append(issuer.empty() ? "<missing>" : issuer);
|
||||
fmt_msgs.push_back("Subject: ");
|
||||
fmt_msgs.back().append(subject.empty() ? "<missing>" : 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() ? "<empty>" : value;
|
||||
} else if (entry.first->asInt()->value() == kSubject) {
|
||||
ret.subject = value.empty() ? "<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<std::string>& 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<std::string> 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<uint8_t>& 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<std::string>& 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<int64_t> key_set;
|
||||
for (size_t index = 0; index < public_key_info_map.size(); ++index) {
|
||||
std::pair<const std::unique_ptr<cppbor::Item>&,
|
||||
const std::unique_ptr<cppbor::Item>&>
|
||||
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<uint8_t>& 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<uint8_t>& payload, std::vector<std::string>& 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<cppbor::Item> 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 "<null>";
|
||||
}
|
||||
return msg_ss_.str();
|
||||
}
|
||||
} // namespace util
|
||||
} // namespace wvoec
|
||||
113
libwvdrmengine/oemcrypto/util/src/cbor_validator.cpp
Normal file
113
libwvdrmengine/oemcrypto/util/src/cbor_validator.cpp
Normal file
@@ -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 <stddef.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
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<uint8_t>& 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 "<null>";
|
||||
}
|
||||
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
|
||||
Reference in New Issue
Block a user