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:
Cong Lin
2023-09-22 11:58:52 -07:00
committed by Robert Shih
parent c36826607e
commit dbb0bea701
4 changed files with 714 additions and 0 deletions

View 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_

View 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_

View 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

View 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