Source release 19.4.0

This commit is contained in:
Vicky Min
2024-11-27 00:07:23 +00:00
parent 11c108a8da
commit 22759672a8
72 changed files with 5321 additions and 2622 deletions

View File

@@ -7,15 +7,10 @@
#ifndef WVOEC_UTIL_BCC_VALIDATOR_H_
#define WVOEC_UTIL_BCC_VALIDATOR_H_
#include <inttypes.h>
#include <sstream>
#include <string>
#include <vector>
#include "cbor_validator.h"
#include "cppbor.h"
#include "wv_class_utils.h"
#include "prov4_validation_helper.h"
namespace wvoec {
namespace util {
@@ -34,11 +29,99 @@ enum BccCurve {
kBccP384 = 3
};
// 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
};
// BCC definition:
// https://source.corp.google.com/h/googleplex-android/platform/superproject/main/+/main:hardware/interfaces/security/rkp/aidl/android/hardware/security/keymint/generateCertificateRequestV2.cddl
// See PubKeyEd25519/PubKeyECDSA256/PubKeyECDSA384 in BCC definition.
struct BccPublicKeyInfo {
BccSignatureAlgorithm signature_algorithm;
BccCurve curve;
std::pair<FieldStatus, int> key_type;
std::pair<FieldStatus, BccSignatureAlgorithm> signature_algorithm;
std::pair<FieldStatus, BccCurve> curve;
// Raw EC key bytes extracted from BCC
std::vector<uint8_t> key_bytes;
std::pair<FieldStatus, std::vector<uint8_t>> key_bytes;
std::string ToString() const;
CborMessageStatus Validate(
std::vector<std::pair<CborMessageStatus, std::string>>& msgs) const;
};
// protected : bstr .cbor { 1 : AlgorithmEdDSA / AlgorithmES256 / AlgorithmES384
// }
struct BccEntryProtected {
std::pair<FieldStatus, int64_t> algorithm;
std::string ToString() const;
CborMessageStatus Validate(
std::vector<std::pair<CborMessageStatus, std::string>>& msgs) const;
};
// See ConfigurationDescriptor in BCC definition.
struct ConfigurationDescriptor {
std::pair<FieldStatus, std::string> component_name;
std::pair<FieldStatus, std::string> component_version;
std::pair<FieldStatus, std::string> resettable; // null string
std::pair<FieldStatus, uint64_t> security_version;
std::pair<FieldStatus, std::string> vm_marker; // null string
std::string ToString() const;
// Validate ConfigurationDescriptor and set |is_widevine_entry| to true if the
// component_name is "widevine". Caller ensures that |is_widevine_entry| is
// not null.
CborMessageStatus Validate(
std::vector<std::pair<CborMessageStatus, std::string>>& msgs,
bool* is_widevine_entry) const;
};
// See DiceChainEntryPayload in BCC definition.
struct BccEntryPayload {
std::pair<FieldStatus, std::string> issuer;
std::pair<FieldStatus, std::string> subject;
std::pair<FieldStatus, std::string> profile_name;
std::pair<FieldStatus, BccPublicKeyInfo> subject_public_key;
std::pair<FieldStatus, std::vector<uint8_t>> key_usage;
std::pair<FieldStatus, std::vector<uint8_t>> code_hash;
std::pair<FieldStatus, std::vector<uint8_t>> code_descriptor;
std::pair<FieldStatus, std::vector<uint8_t>> config_hash;
std::pair<FieldStatus, ConfigurationDescriptor> config_descriptor;
std::pair<FieldStatus, std::vector<uint8_t>> authority_hash;
std::pair<FieldStatus, std::vector<uint8_t>> authority_descriptor;
std::pair<FieldStatus, std::vector<uint8_t>> mode;
std::string ToString() const;
// Validate BccEntryPayload and set |is_widevine_entry| to true if the payload
// contains a Widevine certificate. Caller ensures that |is_widevine_entry| is
// not null.
CborMessageStatus Validate(
std::vector<std::pair<CborMessageStatus, std::string>>& msgs,
bool is_degenerated, bool* is_widevine_entry) const;
};
// See DiceChainEntry in BCC definition.
struct BccEntry {
std::pair<FieldStatus, BccEntryProtected> protected_data;
std::pair<FieldStatus, std::string> unprotected;
std::pair<FieldStatus, BccEntryPayload> payload;
std::pair<FieldStatus, std::vector<uint8_t>> signature;
std::string ToString() const;
// Validate BccEntryPayload and set |is_widevine_entry| to true if the BCC
// entry contains a Widevine certificate. Caller ensures that
// |is_widevine_entry| is not null.
CborMessageStatus Validate(
std::vector<std::pair<CborMessageStatus, std::string>>& msgs,
bool is_degenerated, bool* is_widevine_entry) const;
};
struct Bcc {
BccPublicKeyInfo dk_pub;
std::vector<BccEntry> entries;
std::string ToString() const;
CborMessageStatus Validate(
std::vector<std::pair<CborMessageStatus, std::string>>& msgs,
bool is_degenerated) const;
};
// BccValidator processes a Provisioning 4.0 device root of trust. It extracts
@@ -52,25 +135,34 @@ class BccValidator : public CborValidator {
virtual ~BccValidator() override = default;
WVCDM_DISALLOW_COPY_AND_MOVE(BccValidator);
// 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.
// Verifies the Cbor struct of a client generated root of trust.
virtual CborMessageStatus Validate() override;
// Outputs BCC in YAML.
// Outputs formatted BCC.
virtual std::string GetFormattedMessage() const override;
private:
// Processes CoseKey PubKeyEd25519 / PubKeyECDSA256, prints into |fmt_msgs|,
// and extracts the PubKey to *|public_key_info|.
// Processes CoseKey PubKeyEd25519 / PubKeyECDSA256 / PubKeyECDSA384, which
// contains subject public key, and extracts the PubKey to *|public_key_info|.
// Caller ensures that all pointers are not null.
CborMessageStatus ProcessSubjectPublicKeyInfo(
const cppbor::Map& public_key_info_map,
std::vector<std::string>& fmt_msgs, BccPublicKeyInfo* public_key_info);
// Processes DiceChainEntryPayload, which contains subject public key, prints
// into |fmt_msgs|, and extracts the PubKey to *|public_key_info|.
CborMessageStatus ProcessDiceChainEntryPayload(
const std::vector<uint8_t>& payload, std::vector<std::string>& fmt_msgs,
BccPublicKeyInfo* public_key_info);
const cppbor::Map* public_key_map, BccPublicKeyInfo* public_key_info);
// Processes protected field in Bcc entry and extracts it *|protected_data|.
// Caller ensures that all pointers are not null.
CborMessageStatus ProcessBccEntryProtected(const cppbor::Map* protected_map,
BccEntryProtected* protected_data);
// Processes DiceChainEntryPayload and extracts the payload to *|payload|.
// Caller ensures that all pointers are not null.
CborMessageStatus ProcessDiceChainEntryPayload(const cppbor::Map* payload_map,
BccEntryPayload* payload);
// Processes ConfigurationDescriptor in DiceChainEntryPayload and extracts the
// ConfigurationDescriptor to *|cd|. Caller ensures that all pointers are not
// null.
CborMessageStatus ProcessConfigurationDescriptor(
const cppbor::Map* config_descriptor_map, ConfigurationDescriptor* cd);
// Verifies the raw EC signature |signature| with the public key
// |signing_key|. |signature| extracted from BCC is not ASN.1 DER encoded.
bool VerifySignature(const BccPublicKeyInfo& signing_key,

View File

@@ -7,12 +7,6 @@
#ifndef WVOEC_UTIL_CBOR_VALIDATOR_H_
#define WVOEC_UTIL_CBOR_VALIDATOR_H_
#include <inttypes.h>
#include <string>
#include <utility>
#include <vector>
#include "cppbor.h"
#include "cppbor_parse.h"
#include "wv_class_utils.h"
@@ -28,8 +22,9 @@ enum CborMessageStatus {
kCborParseError = 2,
kCborValidateOk = 3,
kCborValidateWarning = 4,
kCborValidateError = 5,
kCborValidateFatal = 6
kCborValidateError = 5, // e.g. unexpected value, signature error, etc.
kCborValidateFatal =
6, // e.g. unexpected data type, key size, or missing required field
};
std::string CppborMajorTypeToString(cppbor::MajorType type);
@@ -61,10 +56,6 @@ class CborValidator {
// 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();
@@ -77,6 +68,8 @@ class CborValidator {
static std::string CheckMapEntry(const cppbor::Map& map,
cppbor::MajorType major_type,
const std::string& entry_name);
// Formats the parsed CBOR |input| and adds identation for readability.
static std::string FormatString(const std::string& input);
CborMessageStatus message_status_ = kCborUninitialized;
private:

View File

@@ -15,10 +15,47 @@
#include "cbor_validator.h"
#include "cppbor.h"
#include "prov4_validation_helper.h"
#include "wv_class_utils.h"
namespace wvoec {
namespace util {
struct DeviceInfo {
// Version 2 and 3 fields
std::pair<FieldStatus, std::string> brand;
std::pair<FieldStatus, std::string> manufacturer;
std::pair<FieldStatus, std::string> product;
std::pair<FieldStatus, std::string> model;
std::pair<FieldStatus, std::string> device;
std::pair<FieldStatus, std::string>
vb_state; // "green" / "yellow" / "orange"
std::pair<FieldStatus, std::string>
bootloader_state; // "locked" / "unlocked"
std::pair<FieldStatus, std::vector<uint8_t>> vbmeta_digest;
std::pair<FieldStatus, std::string> os_version;
std::pair<FieldStatus, std::string> system_patch_level; // YYYYMM
std::pair<FieldStatus, std::string> boot_patch_level; // YYYYMMDD
std::pair<FieldStatus, std::string> vendor_patch_level; // YYYYMMDD
std::pair<FieldStatus, std::string> security_level; // "tee" / "strongbox"
std::pair<FieldStatus, std::string> fused; // 1 / 0
// Version 1 fields
std::pair<FieldStatus, std::string> board;
std::pair<FieldStatus, std::string> version;
std::pair<FieldStatus, std::string> att_id_state;
std::string ToString() const;
CborMessageStatus Validate(
std::vector<std::pair<CborMessageStatus, std::string>>& msgs, bool is_gms,
int version_number) const;
CborMessageStatus ValidateV3Fields(
bool is_tee_device_info,
std::vector<std::pair<CborMessageStatus, std::string>>& msgs) const;
CborMessageStatus ValidateV2Fields(
bool is_tee_device_info,
std::vector<std::pair<CborMessageStatus, std::string>>& msgs) const;
CborMessageStatus ValidateV1Fields(
std::vector<std::pair<CborMessageStatus, std::string>>& msgs) const;
};
// DeviceInfoValidator parses and validates a Cbor struct of DeviceInfo used by
// Provisioning 4.0. DeviceInfo definition:
// https://source.corp.google.com/h/googleplex-android/platform/superproject/main/+/main:hardware/interfaces/security/rkp/aidl/android/hardware/security/keymint/DeviceInfoV3.cddl
@@ -27,8 +64,8 @@ class DeviceInfoValidator : public CborValidator {
DeviceInfoValidator() = delete;
WVCDM_DISALLOW_COPY_AND_MOVE(DeviceInfoValidator);
explicit DeviceInfoValidator(int version_number)
: version_number_(version_number) {}
explicit DeviceInfoValidator(int version_number = 3, bool is_gms = false)
: version_number_(version_number), is_gms_(is_gms) {}
virtual ~DeviceInfoValidator() override = default;
@@ -41,15 +78,15 @@ class DeviceInfoValidator : public CborValidator {
virtual std::string GetFormattedMessage() const override;
private:
// Checks whether a device info entry with |entry_name| and |major_type|
// exists in |device_info| map.
void CheckDeviceInfoMapEntry(const cppbor::Map& device_info,
cppbor::MajorType major_type,
const std::string& entry_name);
// Builds a struct of DeviceInfo from input CBOR map |device_info_map|.
CborMessageStatus BuildDeviceInfo(DeviceInfo& device_info,
const cppbor::Map* device_info_map);
// Used to generate formatted message.
std::stringstream msg_ss_;
// Device info version. Validations are done based on the version number.
int version_number_;
// Whether the device is a GMS device.
bool is_gms_;
// Saved Cbor-encoded device info.
std::vector<uint8_t> device_info_bytes_;
}; // class DeviceInfoValidator

View File

@@ -0,0 +1,96 @@
// Copyright 2024 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License
// Agreement.
//
#ifndef WVOEC_UTIL_PROV4_VALIDATION_HELPER_H_
#define WVOEC_UTIL_PROV4_VALIDATION_HELPER_H_
#include <sstream>
#include "cbor_validator.h"
#include "string_conversions.h"
namespace wvoec {
namespace util {
enum FieldStatus {
kAbsent = 0, // field key doesn't exist
kEmpty = 1, // field value is empty, e.g. empty string, map, array, etc.
kPresent = 2 // present and non-empty
};
std::string StatusToString(FieldStatus status);
// Apply a new status to current status if it is more severe.
void ApplyStatus(CborMessageStatus& status, CborMessageStatus new_status);
// Validates that the given field name is present, and prints error messages
// if not.
template <typename T>
CborMessageStatus ValidateRequiredField(
const std::string& name, const std::string& component,
const std::pair<FieldStatus, T>& p,
std::vector<std::pair<CborMessageStatus, std::string>>& msgs) {
if (p.first != kPresent) {
msgs.push_back(std::make_pair(
kCborValidateError, component + ": missing required field " + name));
return kCborValidateError;
}
return kCborValidateOk;
}
// Validates that the given field name is present, and prints warning messages
// if not.
template <typename T>
CborMessageStatus ValidateImportantField(
const std::string& name, const std::string& component,
const std::pair<FieldStatus, T>& p,
std::vector<std::pair<CborMessageStatus, std::string>>& msgs) {
if (p.first != kPresent) {
msgs.push_back(std::make_pair(
kCborValidateWarning, component + ": missing important field " + name));
return kCborValidateWarning;
}
return kCborValidateOk;
}
// Print a field value with a built-in type in component with |name| to
// stringstream.
template <typename T>
void PrintField(std::stringstream& ss, const std::string& name,
const std::pair<FieldStatus, T>& p) {
ss << " " << name << ":";
if (p.first != kPresent) {
ss << StatusToString(p.first) << ",";
} else {
ss << p.second << ",";
}
}
// Print a field encoded as a CBOR bstr in component with |name| to
// stringstream.
template <typename T>
void PrintBstrField(std::stringstream& ss, const std::string& name,
const std::pair<FieldStatus, T>& p) {
ss << " " << name << ":";
if (p.first != kPresent) {
ss << StatusToString(p.first) << ",";
} else {
ss << wvutil::b2a_hex(p.second) << ",";
}
}
// Print a field encoded as CBOR structure in component with |name| to
// stringstream.
template <typename T>
void PrintCborField(std::stringstream& ss, const std::string& name,
const std::pair<FieldStatus, T>& p) {
ss << " " << name << ":";
if (p.first != kPresent) {
ss << StatusToString(p.first) << ",";
} else {
ss << p.second.ToString() << ",";
}
}
} // namespace util
} // namespace wvoec
#endif // WVOEC_UTIL_PROV4_VALIDATION_HELPER_H_

View File

@@ -8,10 +8,12 @@
#define WVOEC_UTIL_SIGNED_CSR_PAYLOAD_VALIDATOR_H_
#include <sstream>
#include <string>
#include "bcc_validator.h"
#include "cbor_validator.h"
#include "cppbor.h"
#include "device_info_validator.h"
#include "prov4_validation_helper.h"
#include "wv_class_utils.h"
namespace wvoec {
@@ -20,6 +22,67 @@ namespace util {
// SignedData<CsrPayload>. The definition of SignedData<T> and CsrPayload can be
// found at:
// https://source.corp.google.com/h/googleplex-android/platform/superproject/main/+/main:hardware/interfaces/security/rkp/aidl/android/hardware/security/keymint/generateCertificateRequestV2.cddl
struct CertificateType {
std::pair<FieldStatus, std::string> type;
std::string ToString() const;
CborMessageStatus Validate(
std::vector<std::pair<CborMessageStatus, std::string>>& msgs) const;
};
// CsrPayload = [ ; CBOR Array defining the payload for Csr
// version: 3, ; The CsrPayload CDDL Schema version.
// CertificateType, ; The type of certificate being requested.
// DeviceInfo, ; Defined in the relevant DeviceInfoV*.cddl file.
// KeysToSign, ; Provided by the method parameters
// ]
struct CsrPayload {
std::pair<FieldStatus, std::string> version;
std::pair<FieldStatus, CertificateType> certificate_type;
std::pair<FieldStatus, DeviceInfo> device_info;
std::vector<BccPublicKeyInfo> keys_to_sign; // always empty
std::string ToString() const;
CborMessageStatus Validate(
std::vector<std::pair<CborMessageStatus, std::string>>& msgs) const;
};
struct SignedDataProtected {
std::pair<FieldStatus, int64_t> algorithm;
std::string ToString() const;
CborMessageStatus Validate(
std::vector<std::pair<CborMessageStatus, std::string>>& msgs) const;
};
// SignedData<[
// challenge: bstr .size (0..64), ; Provided by the method parameters
// bstr .cbor T,
// ]>,
struct DataToBeSigned {
std::pair<FieldStatus, std::vector<uint8_t>> challenge;
std::pair<FieldStatus, CsrPayload> csr_payload;
std::string ToString() const;
CborMessageStatus Validate(
std::vector<std::pair<CborMessageStatus, std::string>>& msgs) const;
};
// clang-format off
// SignedData<Data> = [
// protected: bstr .cbor { 1 : AlgorithmEdDSA / AlgorithmES256 / AlgorithmES384 },
// unprotected: {},
// payload: bstr .cbor Data / nil,
// signature: bstr ; PureEd25519(CDI_Leaf_Priv, SignedDataSigStruct<Data>) /
// ; ECDSA(CDI_Leaf_Priv, SignedDataSigStruct<Data>)
// ]
// clang-format on
struct SignedCsrPayload {
std::pair<FieldStatus, SignedDataProtected> protected_data;
std::pair<FieldStatus, std::string> unprotected;
std::pair<FieldStatus, DataToBeSigned> payload;
std::pair<FieldStatus, std::vector<uint8_t>> signature;
std::string ToString() const;
CborMessageStatus Validate(
std::vector<std::pair<CborMessageStatus, std::string>>& msgs) const;
};
class SignedCsrPayloadValidator : public CborValidator {
public:
explicit SignedCsrPayloadValidator() {}
@@ -32,9 +95,20 @@ class SignedCsrPayloadValidator : public CborValidator {
virtual std::string GetFormattedMessage() const override;
private:
CborMessageStatus ValidateProtectedParams(
const cppbor::Bstr* protected_params);
CborMessageStatus ValidateDataToBeSigned(const cppbor::Bstr* data);
// Processes protected field in signed csr payload and extracts it to
// *|protected_data|.
// Caller ensures that all pointers are not null.
CborMessageStatus ProcessSignedDataProtected(
const cppbor::Map* protected_map, SignedDataProtected* protected_data);
// Processes the data to be signed and extracts it to *|payload_to_be_signed|.
// Caller ensures that all pointers are not null.
CborMessageStatus ProcessDataToBeSigned(
const cppbor::Array* payload_to_be_signed_array,
DataToBeSigned* payload_to_be_signed);
// Processes csr payload field and extracts it to *|csr_payload|.
// Caller ensures that all pointers are not null.
CborMessageStatus ProcessCsrPayload(const cppbor::Array* csr_payload_array,
CsrPayload* csr_payload);
// Used to generate formatted message.
std::stringstream msg_ss_;
}; // class SignedCsrPayloadValidator

View File

@@ -29,6 +29,7 @@
'<(oemcrypto_dir)/util/src/oemcrypto_key_deriver.cpp',
'<(oemcrypto_dir)/util/src/oemcrypto_oem_cert.cpp',
'<(oemcrypto_dir)/util/src/oemcrypto_rsa_key.cpp',
'<(oemcrypto_dir)/util/src/prov4_validation_helper.cpp',
'<(oemcrypto_dir)/util/src/signed_csr_payload_validator.cpp',
'<(oemcrypto_dir)/util/src/wvcrc.cpp',
],

File diff suppressed because it is too large Load Diff

View File

@@ -6,12 +6,7 @@
//
#include "cbor_validator.h"
#include <stddef.h>
#include <iomanip>
#include <iostream>
#include <string>
#include <vector>
#include <sstream>
namespace wvoec {
namespace util {
@@ -135,5 +130,37 @@ std::string CborValidator::CheckMapEntry(const cppbor::Map& map,
}
return "";
}
std::string CborValidator::FormatString(const std::string& input) {
std::stringstream ss;
int indent = 0;
for (size_t i = 0; i < input.length(); ++i) {
char current = input[i];
if (current == '[' || current == '{') {
if (i > 0 && input[i - 1] != ':') {
ss << std::string(indent, ' ') << current << std::endl;
} else {
ss << std::string(1, ' ') << current << std::endl;
}
indent += 4;
} else if (current == ']' || current == '}') {
indent -= 4;
ss << std::string(indent, ' ') << current;
} else if (current == ',') {
ss << ',' << std::endl;
} else {
// Handle key-value pairs
if (current != ' ') { // skip any spaces
ss << std::string(indent, ' ') << current;
while (i + 1 < input.length() && input[i + 1] != ',' &&
input[i + 1] != '{' && input[i + 1] != '[' &&
input[i + 1] != ']' && input[i + 1] != '}') {
ss << input[++i];
}
}
}
}
return ss.str();
}
} // namespace util
} // namespace wvoec

View File

@@ -8,14 +8,15 @@
#include <set>
#include "prov4_validation_helper.h"
#include "string_conversions.h"
namespace wvoec {
namespace util {
namespace {
// Number of required device info properties returned from TEE for DeviceInfo
// version v3.
constexpr uint32_t kNumTeeDeviceInfoEntriesV3 = 14;
const std::string kComponent = "DeviceInfo";
// Device info properties returned from TEE for DeviceInfo version v3.
const std::vector<std::string> kDeviceInfoKeysV3 = {"brand",
"manufacturer",
"product",
@@ -31,19 +32,441 @@ const std::vector<std::string> kDeviceInfoKeysV3 = {"brand",
"security_level",
"fused"};
struct AttestationIdEntry {
const char* id;
bool alwaysValidate;
};
bool isValidYYYYMM(const std::string& date) {
// Check if the string has exactly 6 characters
if (date.size() != 6) {
return false;
}
// Check if all characters are digits
for (char c : date) {
if (!std::isdigit(c)) {
return false;
}
}
// Extract year and month as integers
int year = std::stoi(date.substr(0, 4)); // YYYY
int month = std::stoi(date.substr(4, 2)); // MM
// Check if year is within a reasonable range (e.g., 1000 to 9999)
if (year < 1000 || year > 9999) {
return false;
}
// Check if month is valid (01-12)
if (month < 1 || month > 12) {
return false;
}
return true;
}
// Attestation Id and whether it is required.
constexpr AttestationIdEntry kAttestationIdEntrySet[] = {{"brand", false},
{"manufacturer", true},
{"product", true},
{"model", true},
{"device", false}};
bool isValidYYYYMMDD(const std::string& date) {
// Check if the string has exactly 8 characters
if (date.size() != 8) {
return false;
}
// Check if all characters are digits
for (char c : date) {
if (!std::isdigit(c)) {
return false;
}
}
// Extract year, month, and day as integers
int year = std::stoi(date.substr(0, 4)); // YYYY
int month = std::stoi(date.substr(4, 2)); // MM
int day = std::stoi(date.substr(6, 2)); // DD
if (!isValidYYYYMM(date.substr(0, 6))) return false;
// Check if year is within a reasonable range (e.g., 1000 to 9999)
if (year < 1000 || year > 9999) {
return false;
}
// Check if month is valid (01-12)
if (month < 1 || month > 12) {
return false;
}
// Check if day is valid based on the month
if (day < 1 || day > 31) {
return false;
}
// Additional check for days in specific months
if ((month == 4 || month == 6 || month == 9 || month == 11) && day > 30) {
return false; // April, June, September, November have 30 days
}
if (month == 2) { // February
// Simple leap year check
bool isLeapYear = (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
if (day > 29 || (day == 29 && !isLeapYear)) {
return false; // February can't have more than 29 days, and only 29 in
// leap years
}
}
return true;
}
} // namespace
std::string DeviceInfo::ToString() const {
std::stringstream ss;
ss << "{";
PrintField(ss, "brand", brand);
PrintField(ss, "manufacturer", manufacturer);
PrintField(ss, "product", product);
PrintField(ss, "model", model);
PrintField(ss, "device", device);
PrintField(ss, "vb_state", vb_state);
PrintField(ss, "bootloader_state", bootloader_state);
PrintBstrField(ss, "vbmeta_digest", vbmeta_digest);
PrintField(ss, "os_version", os_version);
PrintField(ss, "system_patch_level", system_patch_level);
PrintField(ss, "boot_patch_level", boot_patch_level);
PrintField(ss, "vendor_patch_level", vendor_patch_level);
PrintField(ss, "security_level", security_level);
PrintField(ss, "fused", fused);
PrintField(ss, "board", board);
PrintField(ss, "version", version);
PrintField(ss, "att_id_state", att_id_state);
ss << " }";
return ss.str();
}
CborMessageStatus DeviceInfoValidator::BuildDeviceInfo(
DeviceInfo& device_info, const cppbor::Map* device_info_map) {
std::set<std::string> previous_keys;
for (auto const& entry : *device_info_map) {
if (!entry.first->asTstr()) {
AddValidationMessage(
kCborValidateError,
"Unexpected entry key type. Expected TSTR, but got " +
CppborMajorTypeToString(entry.first->type()));
continue;
}
const std::string& key = entry.first->asTstr()->value();
if (previous_keys.find(key) != previous_keys.end()) {
AddValidationMessage(kCborValidateError,
"Duplicate device info entry: " + key);
continue;
}
previous_keys.insert(key);
if (key == "brand") {
if (entry.second->asTstr() == nullptr) {
device_info.brand.first = kEmpty;
} else {
device_info.brand.second = entry.second->asTstr()->value();
device_info.brand.first =
device_info.brand.second.empty() ? kEmpty : kPresent;
}
} else if (key == "manufacturer") {
if (entry.second->asTstr() == nullptr) {
device_info.manufacturer.first = kEmpty;
} else {
device_info.manufacturer.second = entry.second->asTstr()->value();
device_info.manufacturer.first =
device_info.manufacturer.second.empty() ? kEmpty : kPresent;
}
} else if (key == "product") {
if (entry.second->asTstr() == nullptr) {
device_info.product.first = kEmpty;
} else {
device_info.product.second = entry.second->asTstr()->value();
device_info.product.first =
device_info.product.second.empty() ? kEmpty : kPresent;
}
} else if (key == "model") {
if (entry.second->asTstr() == nullptr) {
device_info.model.first = kEmpty;
} else {
device_info.model.second = entry.second->asTstr()->value();
device_info.model.first =
device_info.model.second.empty() ? kEmpty : kPresent;
}
} else if (key == "device") {
if (entry.second->asTstr() == nullptr) {
device_info.device.first = kEmpty;
} else {
device_info.device.second = entry.second->asTstr()->value();
device_info.device.first =
device_info.device.second.empty() ? kEmpty : kPresent;
}
} else if (key == "vb_state") {
if (entry.second->asTstr() == nullptr) {
device_info.vb_state.first = kEmpty;
} else {
device_info.vb_state.second = entry.second->asTstr()->value();
device_info.vb_state.first =
device_info.vb_state.second.empty() ? kEmpty : kPresent;
}
} else if (key == "bootloader_state") {
if (entry.second->asTstr() == nullptr) {
device_info.bootloader_state.first = kEmpty;
} else {
device_info.bootloader_state.second = entry.second->asTstr()->value();
device_info.bootloader_state.first =
device_info.bootloader_state.second.empty() ? kEmpty : kPresent;
}
} else if (key == "vbmeta_digest") {
if (entry.second->asBstr() == nullptr) {
device_info.vbmeta_digest.first = kEmpty;
} else {
device_info.vbmeta_digest.second = entry.second->asBstr()->value();
device_info.vbmeta_digest.first =
device_info.vbmeta_digest.second.empty() ? kEmpty : kPresent;
}
} else if (key == "os_version") {
if (entry.second->asTstr() == nullptr) {
device_info.os_version.first = kEmpty;
} else {
device_info.os_version.second = entry.second->asTstr()->value();
device_info.os_version.first =
device_info.os_version.second.empty() ? kEmpty : kPresent;
}
} else if (key == "system_patch_level") {
if (entry.second->asUint() == nullptr) {
device_info.system_patch_level.first = kEmpty;
} else {
device_info.system_patch_level.second =
std::to_string(entry.second->asUint()->value());
device_info.system_patch_level.first =
device_info.system_patch_level.second.empty() ? kEmpty : kPresent;
}
} else if (key == "boot_patch_level") {
if (entry.second->asUint() == nullptr) {
device_info.boot_patch_level.first = kEmpty;
} else {
device_info.boot_patch_level.second =
std::to_string(entry.second->asUint()->value());
device_info.boot_patch_level.first =
device_info.boot_patch_level.second.empty() ? kEmpty : kPresent;
}
} else if (key == "vendor_patch_level") {
if (entry.second->asUint() == nullptr) {
device_info.vendor_patch_level.first = kEmpty;
} else {
device_info.vendor_patch_level.second =
std::to_string(entry.second->asUint()->value());
device_info.vendor_patch_level.first =
device_info.vendor_patch_level.second.empty() ? kEmpty : kPresent;
}
} else if (key == "security_level") {
if (entry.second->asTstr() == nullptr) {
device_info.security_level.first = kEmpty;
} else {
device_info.security_level.second = entry.second->asTstr()->value();
device_info.security_level.first =
device_info.security_level.second.empty() ? kEmpty : kPresent;
}
} else if (key == "fused") {
if (entry.second->asUint() == nullptr) {
device_info.fused.first = kEmpty;
} else {
device_info.fused.second =
std::to_string(entry.second->asUint()->value());
device_info.fused.first =
device_info.fused.second.empty() ? kEmpty : kPresent;
}
} else if (key == "board") {
if (entry.second->asTstr() == nullptr) {
device_info.board.first = kEmpty;
} else {
device_info.board.second = entry.second->asTstr()->value();
device_info.board.first =
device_info.board.second.empty() ? kEmpty : kPresent;
}
} else if (key == "version") {
if (entry.second->asUint() == nullptr) {
device_info.version.first = kEmpty;
} else {
device_info.version.second =
std::to_string(entry.second->asUint()->value());
device_info.version.first =
device_info.version.second.empty() ? kEmpty : kPresent;
}
} else if (key == "att_id_state") {
if (entry.second->asTstr() == nullptr) {
device_info.att_id_state.first = kEmpty;
} else {
device_info.att_id_state.second = entry.second->asTstr()->value();
device_info.att_id_state.first =
device_info.att_id_state.second.empty() ? kEmpty : kPresent;
}
}
}
return kCborValidateOk;
}
CborMessageStatus DeviceInfo::ValidateV1Fields(
std::vector<std::pair<CborMessageStatus, std::string>>& msgs) const {
CborMessageStatus status = kCborParseOk;
CborMessageStatus cur_status =
ValidateRequiredField("security_level", kComponent, security_level, msgs);
ApplyStatus(status, cur_status);
cur_status =
ValidateRequiredField("att_id_state", kComponent, att_id_state, msgs);
ApplyStatus(status, cur_status);
return status;
}
CborMessageStatus DeviceInfo::ValidateV2Fields(
bool is_tee_device_info,
std::vector<std::pair<CborMessageStatus, std::string>>& msgs) const {
CborMessageStatus status = kCborParseOk;
CborMessageStatus cur_status;
// TEE IRPC instances require all entries to be present in device info.
// Non-TEE instances may omit `os_version`.
if (is_tee_device_info) {
cur_status =
ValidateRequiredField("os_version", kComponent, os_version, msgs);
ApplyStatus(status, cur_status);
}
cur_status = ValidateRequiredField("brand", kComponent, brand, msgs);
ApplyStatus(status, cur_status);
cur_status = ValidateRequiredField("product", kComponent, product, msgs);
ApplyStatus(status, cur_status);
cur_status = ValidateRequiredField("device", kComponent, device, msgs);
ApplyStatus(status, cur_status);
cur_status = ValidateRequiredField("vb_state", kComponent, vb_state, msgs);
ApplyStatus(status, cur_status);
if (cur_status == kCborValidateOk) {
const std::string& value = vb_state.second;
if (value != "green" && value != "yellow" && value != "orange") {
msgs.push_back(std::make_pair(
kCborValidateError,
kComponent + ": unexpected value for vb_state (" + value + ")"));
ApplyStatus(status, kCborValidateError);
}
}
cur_status = ValidateRequiredField("bootloader_state", kComponent,
bootloader_state, msgs);
ApplyStatus(status, cur_status);
if (cur_status == kCborValidateOk) {
const std::string& value = bootloader_state.second;
if (value != "locked" && value != "unlocked") {
msgs.push_back(std::make_pair(
kCborValidateError, kComponent +
": unexpected value for bootloader_state (" +
value + ")"));
ApplyStatus(status, kCborValidateError);
}
}
cur_status =
ValidateRequiredField("vbmeta_digest", kComponent, vbmeta_digest, msgs);
ApplyStatus(status, cur_status);
cur_status =
ValidateRequiredField("security_level", kComponent, security_level, msgs);
ApplyStatus(status, cur_status);
if (cur_status == kCborValidateOk) {
const std::string& value = security_level.second;
if (value != "tee" && value != "strongbox") {
msgs.push_back(std::make_pair(
kCborValidateError, kComponent +
": unexpected value for security_level (" +
value + ")"));
ApplyStatus(status, kCborValidateError);
}
}
cur_status = ValidateRequiredField("fused", kComponent, fused, msgs);
ApplyStatus(status, cur_status);
if (cur_status == kCborValidateOk) {
const std::string& value = fused.second;
if (value != "1" && value != "0") {
msgs.push_back(std::make_pair(
kCborValidateError,
kComponent + ": unexpected value for fused (" + value + ")"));
ApplyStatus(status, kCborValidateError);
}
}
return status;
}
CborMessageStatus DeviceInfo::ValidateV3Fields(
bool is_tee_device_info,
std::vector<std::pair<CborMessageStatus, std::string>>& msgs) const {
CborMessageStatus status = kCborParseOk;
CborMessageStatus cur_status;
// Checks for the required fields that only apply to v3: system_patch_level,
// boot_patch_level, vendor_patch_level
cur_status = ValidateRequiredField("system_patch_level", kComponent,
system_patch_level, msgs);
ApplyStatus(status, cur_status);
if (cur_status == kCborValidateOk) {
const std::string& value = system_patch_level.second;
if (!isValidYYYYMM(value)) {
msgs.push_back(std::make_pair(
kCborValidateError, kComponent +
": invalid value for system_patch_level (" +
value + "), should be YYYYMM"));
ApplyStatus(status, kCborValidateError);
}
}
cur_status = ValidateRequiredField("boot_patch_level", kComponent,
boot_patch_level, msgs);
ApplyStatus(status, cur_status);
if (cur_status == kCborValidateOk) {
const std::string& value = boot_patch_level.second;
if (!isValidYYYYMMDD(value)) {
msgs.push_back(std::make_pair(
kCborValidateError, kComponent +
": invalid value for boot_patch_level (" +
value + "), should be YYYYMMDD"));
ApplyStatus(status, kCborValidateError);
}
}
cur_status = ValidateRequiredField("vendor_patch_level", kComponent,
vendor_patch_level, msgs);
ApplyStatus(status, cur_status);
if (cur_status == kCborValidateOk) {
const std::string& value = vendor_patch_level.second;
if (!isValidYYYYMMDD(value)) {
msgs.push_back(std::make_pair(
kCborValidateError, kComponent +
": invalid value for vendor_patch_level (" +
value + "), should be YYYYMMDD"));
ApplyStatus(status, kCborValidateError);
}
}
cur_status = ValidateV2Fields(is_tee_device_info, msgs);
ApplyStatus(status, cur_status);
return status;
}
CborMessageStatus DeviceInfo::Validate(
std::vector<std::pair<CborMessageStatus, std::string>>& msgs, bool is_gms,
int version_number) const {
CborMessageStatus status = kCborParseOk;
CborMessageStatus cur_status;
// AOSP and CE devices.
if (!is_gms) {
cur_status =
ValidateImportantField("manufacturer", kComponent, manufacturer, msgs);
ApplyStatus(status, cur_status);
cur_status = ValidateImportantField("model", kComponent, model, msgs);
ApplyStatus(status, cur_status);
if (fused.first != kPresent) {
msgs.push_back(std::make_pair(
kCborValidateWarning,
"DeviceInfo: missing field fused. Treat this as an error if it is "
"from a device that uses a de-generated BCC, in which case fused is "
"a required field."));
ApplyStatus(status, kCborValidateWarning);
}
return status;
}
// GMS device requires more fields.
bool is_tee_device_info =
(security_level.first == kPresent && security_level.second == "tee");
if (version_number == 3) {
cur_status = ValidateV3Fields(is_tee_device_info, msgs);
ApplyStatus(status, cur_status);
} else if (version_number == 2) {
cur_status = ValidateV2Fields(is_tee_device_info, msgs);
ApplyStatus(status, cur_status);
} else if (version_number == 1) {
cur_status = ValidateV1Fields(msgs);
ApplyStatus(status, cur_status);
} else {
msgs.push_back({kCborValidateFatal, "Unrecognized device info version: " +
std::to_string(version_number)});
return kCborValidateFatal;
}
return status;
}
CborMessageStatus DeviceInfoValidator::Parse(
const std::vector<uint8_t>& device_info) {
message_status_ = CborValidator::Parse(device_info);
@@ -71,157 +494,33 @@ CborMessageStatus DeviceInfoValidator::Validate() {
AddValidationMessage(kCborValidateError,
"Device info ordering is non-canonical.");
}
const cppbor::Item* security_level =
GetMapEntry(*device_info_map, "security_level");
const bool is_tee_device_info = security_level && security_level->asTstr() &&
security_level->asTstr()->value() == "tee";
std::set<std::string> previous_keys;
switch (version_number_) {
case 3:
if (is_tee_device_info &&
device_info_map->size() != kNumTeeDeviceInfoEntriesV3) {
AddValidationMessage(
kCborValidateError,
"Incorrect number of TEE device info entries. Expected " +
std::to_string(kNumTeeDeviceInfoEntriesV3) + " but got " +
std::to_string(device_info_map->size()));
}
// TEE IRPC instances require all entries to be present in device info.
// Non-TEE instances may omit `os_version`.
if (!is_tee_device_info &&
(device_info_map->size() != kNumTeeDeviceInfoEntriesV3 &&
device_info_map->size() != kNumTeeDeviceInfoEntriesV3 - 1)) {
AddValidationMessage(
kCborValidateError,
"Incorrect number of non-TEE device info entries. Expected " +
std::to_string(kNumTeeDeviceInfoEntriesV3 - 1) + " but got " +
std::to_string(device_info_map->size()));
}
for (auto const& entry : *device_info_map) {
if (!entry.first->asTstr()) {
AddValidationMessage(
kCborValidateError,
"Unexpected entry key type. Expected TSTR, but got " +
CppborMajorTypeToString(entry.first->type()));
continue;
}
const std::string& key = entry.first->asTstr()->value();
if (previous_keys.find(key) != previous_keys.end()) {
AddValidationMessage(kCborValidateError,
"Duplicate device info entry: " + key);
}
previous_keys.insert(key);
if (std::find(kDeviceInfoKeysV3.begin(), kDeviceInfoKeysV3.end(),
key) == kDeviceInfoKeysV3.end()) {
AddValidationMessage(kCborValidateError,
"Unrecognized device info entry: " + key);
}
}
// Checks for the required fields that only apply to v3.
CheckDeviceInfoMapEntry(*device_info_map, cppbor::UINT,
"system_patch_level");
CheckDeviceInfoMapEntry(*device_info_map, cppbor::UINT,
"boot_patch_level");
CheckDeviceInfoMapEntry(*device_info_map, cppbor::UINT,
"vendor_patch_level");
// Fall through
CORE_UTIL_FALLTHROUGH;
case 2:
for (const auto& entry : kAttestationIdEntrySet) {
if (entry.alwaysValidate) {
CheckDeviceInfoMapEntry(*device_info_map, cppbor::TSTR, entry.id);
}
}
CheckDeviceInfoMapEntry(*device_info_map, cppbor::TSTR, "vb_state");
CheckDeviceInfoMapEntry(*device_info_map, cppbor::TSTR,
"bootloader_state");
CheckDeviceInfoMapEntry(*device_info_map, cppbor::BSTR, "vbmeta_digest");
CheckDeviceInfoMapEntry(*device_info_map, cppbor::UINT,
"system_patch_level");
CheckDeviceInfoMapEntry(*device_info_map, cppbor::UINT,
"boot_patch_level");
CheckDeviceInfoMapEntry(*device_info_map, cppbor::UINT,
"vendor_patch_level");
CheckDeviceInfoMapEntry(*device_info_map, cppbor::UINT, "fused");
CheckDeviceInfoMapEntry(*device_info_map, cppbor::TSTR, "security_level");
if (is_tee_device_info) {
CheckDeviceInfoMapEntry(*device_info_map, cppbor::TSTR, "os_version");
}
break;
case 1:
CheckDeviceInfoMapEntry(*device_info_map, cppbor::TSTR, "security_level");
CheckDeviceInfoMapEntry(*device_info_map, cppbor::TSTR, "att_id_state");
break;
default:
AddValidationMessage(
kCborValidateFatal,
"Unrecognized version: " + std::to_string(version_number_));
return message_status_;
}
DeviceInfo device_info;
CborMessageStatus cur_status = BuildDeviceInfo(device_info, device_info_map);
if (cur_status == kCborValidateFatal) return cur_status;
// Print the parse device info.
msg_ss_ << device_info.ToString() << "\n";
std::vector<std::pair<CborMessageStatus, std::string>> msgs;
cur_status = device_info.Validate(msgs, is_gms_, version_number_);
ApplyStatus(message_status_, cur_status);
for (const auto& msg : msgs) {
AddValidationMessage(msg.first, msg.second);
}
if (message_status_ == kCborParseOk) message_status_ = kCborValidateOk;
return message_status_;
}
void DeviceInfoValidator::CheckDeviceInfoMapEntry(
const cppbor::Map& device_info, cppbor::MajorType major_type,
const std::string& entry_name) {
const std::string error = CheckMapEntry(device_info, major_type, entry_name);
if (!error.empty()) {
AddValidationMessage(kCborValidateError, error);
}
}
std::string DeviceInfoValidator::GetFormattedMessage() const {
if (message_status_ == kCborUninitialized ||
message_status_ == kCborParseError ||
message_status_ == kCborValidateFatal) {
message_status_ == kCborParseError) {
return std::string();
}
const cppbor::Item* parsed_item = std::get<0>(parse_result()).get();
if (parsed_item == nullptr) {
return "<null>";
}
// Writes YAML-formatted output to |msg_ss_|.
std::stringstream msg_ss;
msg_ss << "---\n";
msg_ss << "DEVICE INFO MAP:\n";
for (auto const& entry : *(parsed_item->asMap())) {
auto const& entry_value = entry.second;
// Device info map only allows TSTR key type.
if (!entry.first->asTstr()) continue;
const std::string& name = entry.first->asTstr()->value();
msg_ss << " " << name << ": ";
switch (entry_value->type()) {
case cppbor::TSTR: {
const std::string val = entry_value->asTstr()->value().empty()
? "<null>"
: entry_value->asTstr()->value();
msg_ss << val << "\n";
break;
}
case cppbor::UINT:
msg_ss << std::to_string(entry_value->asUint()->value()) << "\n";
break;
case cppbor::NINT:
msg_ss << std::to_string(entry_value->asNint()->value()) << "\n";
break;
case cppbor::BSTR: {
const std::vector<uint8_t>& bytes = entry_value->asBstr()->value();
const std::string val =
bytes.empty() ? "<null>" : wvutil::b2a_hex(bytes);
msg_ss << val << "\n";
break;
}
default:
msg_ss << "Unsupported type ("
<< CppborMajorTypeToString(entry_value->type()) << ")\n";
break;
}
}
msg_ss << "...\n";
return msg_ss.str();
return FormatString(msg_ss_.str());
}
} // namespace util
} // namespace wvoec

View File

@@ -0,0 +1,25 @@
// Copyright 2024 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License
// Agreement.
//
#include "prov4_validation_helper.h"
namespace wvoec {
namespace util {
std::string StatusToString(FieldStatus status) {
if (status == FieldStatus::kAbsent) {
return "<absent>";
}
if (status == FieldStatus::kEmpty) {
return "<empty>";
}
return "present";
}
void ApplyStatus(CborMessageStatus& status, CborMessageStatus new_status) {
if (new_status > status) {
status = new_status;
}
}
} // namespace util
} // namespace wvoec

View File

@@ -13,24 +13,164 @@
namespace wvoec {
namespace util {
namespace {
enum CoseKeyAlgorithm : int {
AES_GCM_256 = 3,
HMAC_256 = 5,
ES256 = -7, // ECDSA with SHA-256
EDDSA = -8,
ECDH_ES_HKDF_256 = -25,
ES384 = -35, // ECDSA with SHA-384
};
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";
}
}
const std::string kCsrPayloadVersion = "3";
constexpr size_t kMaxChallengeSize = 64;
} // namespace
std::string CertificateType::ToString() const {
std::stringstream ss;
ss << "{";
ss << " type:";
if (type.first != kPresent) {
ss << StatusToString(type.first) << ",";
} else {
ss << type.second << ",";
}
ss << " }";
return ss.str();
}
CborMessageStatus CertificateType::Validate(
std::vector<std::pair<CborMessageStatus, std::string>>& msgs) const {
CborMessageStatus status = kCborParseOk;
const std::string component = "CertificateType";
CborMessageStatus cur_status =
ValidateRequiredField("type", component, type, msgs);
ApplyStatus(status, cur_status);
if (cur_status == kCborValidateOk) {
if (type.second != "widevine" && type.second != "keymint" &&
type.second != "rkp-vm") {
msgs.push_back(std::make_pair(
kCborValidateError,
component + ": Invalid certificate type " + type.second));
ApplyStatus(status, kCborValidateError);
}
}
return status;
}
std::string SignedDataProtected::ToString() const {
std::stringstream ss;
ss << "{";
ss << " algorithm:";
if (algorithm.first != kPresent) {
ss << StatusToString(algorithm.first) << ",";
} else {
if (algorithm.second == DEVICE_KEY_ALGORITHM_EDDSA) {
ss << "EdDSA" << ",";
} else if (algorithm.second == DEVICE_KEY_ALGORITHM_ES256) {
ss << "ECDSA_SHA256" << ",";
} else if (algorithm.second == DEVICE_KEY_ALGORITHM_ES384) {
ss << "ECDSA_SHA384" << ",";
} else {
ss << "unknown(" << algorithm.second << ")" << ",";
}
}
ss << " }";
return ss.str();
}
CborMessageStatus SignedDataProtected::Validate(
std::vector<std::pair<CborMessageStatus, std::string>>& msgs) const {
CborMessageStatus status = kCborParseOk;
const std::string component = "SignedDataProtected";
CborMessageStatus cur_status =
ValidateRequiredField("algorithm", component, algorithm, msgs);
ApplyStatus(status, cur_status);
if (cur_status == kCborValidateOk) {
if (algorithm.second != DEVICE_KEY_ALGORITHM_EDDSA &&
algorithm.second != DEVICE_KEY_ALGORITHM_ES256 &&
algorithm.second != DEVICE_KEY_ALGORITHM_ES384) {
msgs.push_back(std::make_pair(kCborValidateError,
component + ": Invalid algorithm " +
std::to_string(algorithm.second)));
ApplyStatus(status, kCborValidateError);
}
}
return status;
}
// CsrPayload = [ ; CBOR Array defining the payload for CSR.
// version: 3, ; The CsrPayload CDDL Schema version.
// CertificateType: "widevine" ; The type of certificate being requested.
// DeviceInfo, ; Defined in Android DeviceInfo.aidl
// KeysToSign: [] ; Empty list
// ]
std::string CsrPayload::ToString() const {
std::stringstream ss;
ss << "[";
PrintField(ss, "version", version);
PrintCborField(ss, "certificate_type", certificate_type);
PrintCborField(ss, "device_info", device_info);
ss << " keys_to_sign:[";
for (auto& key : keys_to_sign) {
PrintCborField(ss, "keys_to_sign", std::make_pair(kAbsent, key));
}
ss << " ],";
ss << " ]";
return ss.str();
}
CborMessageStatus CsrPayload::Validate(
std::vector<std::pair<CborMessageStatus, std::string>>& msgs) const {
CborMessageStatus status = kCborParseOk;
const std::string component = "CsrPayload";
CborMessageStatus cur_status =
ValidateRequiredField("version", component, version, msgs);
ApplyStatus(status, cur_status);
if (cur_status == kCborValidateOk && version.second != kCsrPayloadVersion) {
msgs.push_back(std::make_pair(
kCborValidateError, component + ": Invalid version " + version.second +
". Expected " + kCsrPayloadVersion));
ApplyStatus(status, kCborValidateError);
}
cur_status =
ValidateRequiredField("device_info", component, device_info, msgs);
ApplyStatus(status, cur_status);
// Validation of device_info is done in DeviceInfoValidator separately for
// now.
return status;
}
// DataToBeSigned = [
// challenge: bstr .size (0..64),
// bstr .cbor CsrPayload,
// ]
std::string DataToBeSigned::ToString() const {
std::stringstream ss;
ss << "[";
PrintBstrField(ss, "challenge", challenge);
PrintCborField(ss, "csr_payload", csr_payload);
ss << " ]";
return ss.str();
}
CborMessageStatus DataToBeSigned::Validate(
std::vector<std::pair<CborMessageStatus, std::string>>& msgs) const {
CborMessageStatus status = kCborParseOk;
const std::string component = "DataToBeSigned";
CborMessageStatus cur_status =
ValidateRequiredField("challenge", component, challenge, msgs);
ApplyStatus(status, cur_status);
if (cur_status == kCborValidateOk &&
challenge.second.size() > kMaxChallengeSize) {
msgs.push_back(std::make_pair(kCborValidateError,
"Unexpected challenge size: " +
std::to_string(challenge.second.size()) +
". Expected no more than " +
std::to_string(kMaxChallengeSize)));
ApplyStatus(status, kCborValidateError);
}
cur_status =
ValidateRequiredField("csr_payload", component, csr_payload, msgs);
ApplyStatus(status, cur_status);
if (cur_status == kCborValidateOk) {
cur_status = csr_payload.second.Validate(msgs);
ApplyStatus(status, cur_status);
}
return status;
}
// clang-format off
// Signed CSR payload CBOR data to be verified:
// SignedData<CsrPayload> = [
@@ -41,134 +181,154 @@ void AddMessages(std::stringstream& ss,
// signature: bstr ; PureEd25519(priv_key, Sig_structure<DataToBeSigned>) /
// ; ECDSA(priv_key, Sig_structure<DataToBeSigned>)
// ]
//
// DataToBeSigned = [
// challenge: bstr .size (0..64),
// bstr .cbor CsrPayload,
// ]
//
// CsrPayload = [ ; CBOR Array defining the payload for CSR.
// version: 3, ; The CsrPayload CDDL Schema version.
// CertificateType: "widevine" ; The type of certificate being requested.
// DeviceInfo, ; Defined in Android DeviceInfo.aidl
// KeysToSign: [] ; Empty list
// ]
//
// Sig_structure<DataToBeSigned> = [
// context: "Signature1",
// protected: bstr .cbor { 1 : AlgorithmEdDSA / AlgorithmES256 / AlgorithmES384 },
// external_aad: bstr .size 0,
// payload: bstr .cbor DataToBeSigned / nil,
// ]
// clang-format on
std::string SignedCsrPayload::ToString() const {
std::stringstream ss;
ss << "SignedCSRpayload = [";
PrintCborField(ss, "protected", protected_data);
PrintField(ss, "unprotected", unprotected);
PrintCborField(ss, "payload", payload);
PrintBstrField(ss, "signature", signature);
ss << " ]";
return ss.str();
}
CborMessageStatus SignedCsrPayload::Validate(
std::vector<std::pair<CborMessageStatus, std::string>>& msgs) const {
CborMessageStatus status = kCborParseOk;
const std::string component = "SignedCsrPayload";
CborMessageStatus cur_status =
ValidateRequiredField("protected", component, protected_data, msgs);
ApplyStatus(status, cur_status);
if (cur_status == kCborValidateOk) {
cur_status = protected_data.second.Validate(msgs);
ApplyStatus(status, cur_status);
}
cur_status = ValidateRequiredField("payload", component, payload, msgs);
ApplyStatus(status, cur_status);
if (cur_status == kCborValidateOk) {
cur_status = payload.second.Validate(msgs);
ApplyStatus(status, cur_status);
}
cur_status = ValidateRequiredField("signature", component, signature, msgs);
ApplyStatus(status, cur_status);
// Skip CoseSign1 signature verification since the validator doesn't have
// verifying keys.
return status;
}
// Caller ensures the pointer is not NULL.
CborMessageStatus SignedCsrPayloadValidator::ValidateProtectedParams(
const cppbor::Bstr* protected_params) {
auto parse_result = cppbor::parse(protected_params);
std::unique_ptr<cppbor::Item> parsed_protected_params =
std::move(std::get<0>(parse_result));
const std::string error_message = std::move(std::get<2>(parse_result));
if (!parsed_protected_params || !error_message.empty()) {
AddValidationMessage(kCborValidateFatal,
"Unable to parse protectedParams: " + error_message);
return message_status_;
}
// |parsed_protected_params| is a CBOR map of 1 entry
const cppbor::Map* protected_params_map = parsed_protected_params->asMap();
if (!protected_params_map) {
AddValidationMessage(
kCborValidateFatal,
"ProtectedParams must be a CBOR map. Actual type: " +
CppborMajorTypeToString(parsed_protected_params->type()));
return message_status_;
}
if (protected_params_map->size() == 0) {
AddValidationMessage(kCborValidateFatal, "ProtectedParams is empty.");
return message_status_;
}
if (protected_params_map->size() > 1) {
CborMessageStatus SignedCsrPayloadValidator::ProcessSignedDataProtected(
const cppbor::Map* protected_map, SignedDataProtected* protected_data) {
if (protected_map->size() > 1) {
AddValidationMessage(kCborValidateWarning,
"ProtectedParams expects 1 entry, but got " +
std::to_string(protected_params_map->size()));
"Protected data map expects 1 entry, but got " +
std::to_string(protected_map->size()));
}
// TODO(b/314141962): Replace this with the map lookup function in cppbor
// library
bool algo_found = false;
for (auto const& entry : *protected_params_map) {
if (!entry.first->asInt()) {
AddValidationMessage(kCborValidateWarning,
"Unsupported key type: " +
CppborMajorTypeToString(entry.first->type()));
} else if (entry.first->asInt()->value() != 1) {
AddValidationMessage(kCborValidateWarning,
"Unsupported key value: " +
std::to_string(entry.first->asInt()->value()));
} else {
auto& algorithm = entry.second;
if (!algorithm) {
AddValidationMessage(kCborValidateFatal, "Algorithm value is missing.");
return message_status_;
}
if (!algorithm->asInt()) {
AddValidationMessage(kCborValidateFatal,
"Unsupported signature algorithm value type: " +
CppborMajorTypeToString(algorithm->type()));
return message_status_;
}
std::string kv = "1: ";
const int64_t algo = algorithm->asInt()->value();
if (algo == EDDSA)
kv += "EDDSA";
else if (algo == ES256)
kv += "ES256";
else if (algo == ES384)
kv += "ES384";
else {
kv += std::to_string(algo);
AddValidationMessage(
kCborValidateFatal,
"Unsupported signature algorithm value: " + std::to_string(algo));
return message_status_;
}
AddMessages(msg_ss_, {std::move(kv)}, 1);
algo_found = true;
for (size_t index = 0; index < protected_map->size(); ++index) {
std::pair<const std::unique_ptr<cppbor::Item>&,
const std::unique_ptr<cppbor::Item>&>
entry = (*protected_map)[index];
if (entry.first->type() != cppbor::NINT &&
entry.first->type() != cppbor::UINT) {
AddValidationMessage(
kCborValidateWarning,
"Invalid key type in protected data map in signed CSR payload: " +
CppborMajorTypeToString(entry.first->type()));
continue;
}
const int64_t map_key = entry.first->asInt()->value();
if (map_key == 1) {
if (entry.second->type() != cppbor::NINT &&
entry.second->type() != cppbor::UINT) {
AddValidationMessage(kCborValidateError,
"Invalid value type in protected data map for "
"key 1: " +
CppborMajorTypeToString(entry.second->type()));
continue;
}
protected_data->algorithm.first = kPresent;
protected_data->algorithm.second = entry.second->asInt()->value();
} else {
AddValidationMessage(kCborValidateWarning,
"Unsupported key value in protected data map: " +
std::to_string(map_key));
}
}
if (!algo_found) {
AddValidationMessage(kCborValidateFatal,
"ProtectedParams has no signature algorithm.");
}
return message_status_;
}
// Caller ensures the pointer is not NULL.
CborMessageStatus SignedCsrPayloadValidator::ValidateDataToBeSigned(
const cppbor::Bstr* data) {
if (data->value().empty()) {
AddValidationMessage(kCborValidateFatal, "Payload to be signed is empty.");
return message_status_;
CborMessageStatus SignedCsrPayloadValidator::ProcessCsrPayload(
const cppbor::Array* csr_payload_array, CsrPayload* csr_payload) {
if (csr_payload_array->size() < 4U) {
AddValidationMessage(kCborValidateFatal,
"CSR payload must contain 4 elements. Actual size: " +
std::to_string(csr_payload_array->size()));
return kCborValidateFatal;
}
auto parse_result = cppbor::parse(data);
std::unique_ptr<cppbor::Item> parsed_payload_to_be_signed =
std::move(std::get<0>(parse_result));
std::string error_message = std::move(std::get<2>(parse_result));
if (!parsed_payload_to_be_signed || !error_message.empty()) {
// |csr_payload_array| is an array of 4 elements.
const cppbor::Uint* version = csr_payload_array->get(0)->asUint();
if (version != nullptr) {
csr_payload->version.first = kPresent;
csr_payload->version.second = std::to_string(version->value());
} else {
AddValidationMessage(
kCborValidateFatal,
"Unable to parse the payload to be signed: " + error_message);
return message_status_;
kCborValidateError,
"CSR payload version must be an unsigned integer. Actual type: " +
CppborMajorTypeToString(csr_payload_array->get(0)->type()));
}
// Verify that |parsed_payload_to_be_signed| is a valid array.
const cppbor::Array* payload_to_be_signed_array =
parsed_payload_to_be_signed->asArray();
if (!payload_to_be_signed_array) {
const cppbor::Tstr* certificate_type = csr_payload_array->get(1)->asTstr();
if (certificate_type != nullptr) {
if (certificate_type->value().empty()) {
csr_payload->certificate_type.first = kEmpty;
} else {
csr_payload->certificate_type.first = kPresent;
CertificateType& certificate_type_ref =
csr_payload->certificate_type.second;
certificate_type_ref.type.second = certificate_type->value();
certificate_type_ref.type.first =
certificate_type_ref.type.second.empty() ? kEmpty : kPresent;
}
} else {
AddValidationMessage(
kCborValidateFatal,
"Payload to be signed must be a CBOR array. Actual type: " +
CppborMajorTypeToString(parsed_payload_to_be_signed->type()));
return message_status_;
kCborValidateError,
"Certificate type must be Tstr. Actual type: " +
CppborMajorTypeToString(csr_payload_array->get(1)->type()));
}
const cppbor::Map* device_info_map = csr_payload_array->get(2)->asMap();
if (device_info_map != nullptr) {
if (device_info_map->size() == 0) {
csr_payload->device_info.first = kEmpty;
} else {
// Dummy device info as a placeholder for now.
// TODO: parse the device info.
DeviceInfo device_info;
csr_payload->device_info.first = kPresent;
csr_payload->device_info.second = std::move(device_info);
}
} else {
AddValidationMessage(
kCborValidateError,
"Device info must be a CBOR map. Actual type: " +
CppborMajorTypeToString(csr_payload_array->get(2)->type()));
}
const cppbor::Array* keys = csr_payload_array->get(3)->asArray();
if (!keys) {
AddValidationMessage(
kCborValidateError,
"Keys must be a CBOR array. Actual type: " +
CppborMajorTypeToString(csr_payload_array->get(3)->type()));
}
return message_status_;
}
// Caller ensures the pointer is not NULL.
CborMessageStatus SignedCsrPayloadValidator::ProcessDataToBeSigned(
const cppbor::Array* payload_to_be_signed_array,
DataToBeSigned* payload_to_be_signed) {
if (payload_to_be_signed_array->size() != 2U) {
AddValidationMessage(
kCborValidateFatal,
@@ -177,39 +337,27 @@ CborMessageStatus SignedCsrPayloadValidator::ValidateDataToBeSigned(
std::to_string(payload_to_be_signed_array->size()));
return message_status_;
}
// Element 0: challenge.
std::string msg = "- Challenge: ";
const cppbor::Bstr* challenge = payload_to_be_signed_array->get(0)->asBstr();
if (!challenge) {
AddValidationMessage(kCborValidateFatal,
"Challenge must be Bstr. Actual type: " +
CppborMajorTypeToString(
payload_to_be_signed_array->get(0)->type()));
return message_status_;
if (challenge != nullptr) {
if (!challenge->value().empty()) {
payload_to_be_signed->challenge.first = kPresent;
payload_to_be_signed->challenge.second = challenge->value();
} else {
payload_to_be_signed->challenge.first = kEmpty;
}
}
if (challenge->value().size() > 64) {
AddValidationMessage(
kCborValidateError,
"Challenge size must be between 0 and 64 bytes inclusive. "
"However, challenge is " +
std::to_string(challenge->value().size()) + " bytes long.");
}
msg += wvutil::b2a_hex(challenge->value());
AddMessages(msg_ss_, {std::move(msg)}, 1);
// Element 1: CsrPayload.
AddMessages(msg_ss_, {"- CsrPayload:"}, 1);
const cppbor::Bstr* csr_payload =
payload_to_be_signed_array->get(1)->asBstr();
if (!csr_payload) {
AddValidationMessage(kCborValidateFatal, "CSR payload is missing.");
return message_status_;
}
parse_result = cppbor::parse(csr_payload);
auto parse_result = cppbor::parse(csr_payload);
std::unique_ptr<cppbor::Item> parsed_csr_payload =
std::move(std::get<0>(parse_result));
error_message = std::move(std::get<2>(parse_result));
auto error_message = std::move(std::get<2>(parse_result));
if (!parsed_csr_payload) {
AddValidationMessage(kCborValidateFatal,
"Unable to parse CSR payload: " + error_message);
@@ -222,63 +370,15 @@ CborMessageStatus SignedCsrPayloadValidator::ValidateDataToBeSigned(
CppborMajorTypeToString(parsed_csr_payload->type()));
return message_status_;
}
if (parsed_csr_payload->asArray()->size() != 4U) {
AddValidationMessage(
kCborValidateFatal,
"CSR payload must contain 4 elements. Actual size: " +
std::to_string(parsed_csr_payload->asArray()->size()));
return message_status_;
if (parsed_csr_payload->asArray()->size() == 0) {
payload_to_be_signed->csr_payload.first = kEmpty;
} else {
payload_to_be_signed->csr_payload.first = kPresent;
CborMessageStatus status =
ProcessCsrPayload(parsed_csr_payload->asArray(),
&payload_to_be_signed->csr_payload.second);
ApplyStatus(message_status_, status);
}
// |parsed_csr_payload| is an array of 4 elements.
const cppbor::Uint* version = parsed_csr_payload->asArray()->get(0)->asUint();
if (!version) {
AddValidationMessage(kCborValidateFatal,
"CSR payload version must be an unsigned integer.");
return message_status_;
}
AddMessages(msg_ss_, {"- version: " + std::to_string(version->value())}, 2);
if (version->value() != 3U) {
AddValidationMessage(kCborValidateError,
"CSR payload version must be must be equal to 3.");
}
const cppbor::Tstr* certificate_type =
parsed_csr_payload->asArray()->get(1)->asTstr();
if (!certificate_type) {
// Certificate type is allowed to be extended by vendor, i.e. we can't
// enforce its value.
AddValidationMessage(
kCborValidateFatal,
"Certificate type must be Tstr. Actual type: " +
CppborMajorTypeToString(
parsed_csr_payload->asArray()->get(1)->type()));
return message_status_;
}
AddMessages(msg_ss_, {"- certificate_type: " + certificate_type->value()}, 2);
const cppbor::Map* device_info =
parsed_csr_payload->asArray()->get(2)->asMap();
if (!device_info) {
AddValidationMessage(
kCborValidateFatal,
"Device info must be a CBOR map. Actual type: " +
CppborMajorTypeToString(
parsed_csr_payload->asArray()->get(2)->type()));
return message_status_;
}
AddMessages(msg_ss_, {"- device_info: " + cppbor::prettyPrint(device_info)},
2);
const cppbor::Array* keys = parsed_csr_payload->asArray()->get(3)->asArray();
if (!keys) {
AddValidationMessage(
kCborValidateFatal,
"Keys must be a CBOR array. Actual type: " +
CppborMajorTypeToString(
parsed_csr_payload->asArray()->get(3)->type()));
return message_status_;
}
AddMessages(msg_ss_, {"- keys_to_sign: " + cppbor::prettyPrint(keys)}, 2);
return message_status_;
}
@@ -288,16 +388,15 @@ CborMessageStatus SignedCsrPayloadValidator::Validate() {
std::get<0>(parse_result()).get();
if (!parsed_signed_csr_payload) {
AddValidationMessage(kCborValidateFatal, "Signed CSR payload is empty.");
return message_status_;
return kCborValidateFatal;
}
// Verify that |parsed_signed_csr_payload| is a valid array.
if (!parsed_signed_csr_payload->asArray()) {
AddValidationMessage(
kCborValidateFatal,
"Signed CSR payload must be a CBOR array. Actual type: " +
CppborMajorTypeToString(parsed_signed_csr_payload->type()));
return message_status_;
return kCborValidateFatal;
}
const cppbor::Array* signed_csr_payload_array =
parsed_signed_csr_payload->asArray();
@@ -305,43 +404,78 @@ CborMessageStatus SignedCsrPayloadValidator::Validate() {
AddValidationMessage(kCborValidateFatal,
"Invalid CoseSign1 size. Actual: " +
std::to_string(signed_csr_payload_array->size()));
return message_status_;
return kCborValidateFatal;
}
// Writes YAML-formatted output to |msg_ss_| during validation.
msg_ss_.str(std::string());
msg_ss_ << "---"
<< "\n";
// Parse each element of the array.
CborMessageStatus status = kCborValidateOk;
SignedCsrPayload signed_csr_payload;
// Element 0: protected params.
msg_ss_ << "- Protected params:\n";
const cppbor::Bstr* protected_params =
signed_csr_payload_array->get(0)->asBstr();
CborMessageStatus status = ValidateProtectedParams(protected_params);
if (status == kCborValidateFatal) return kCborValidateFatal;
if (!protected_params) {
AddValidationMessage(
kCborValidateFatal,
"Signed csr payload protected data type must be Bstr. Actual type: " +
CppborMajorTypeToString(signed_csr_payload_array->get(0)->type()));
return kCborValidateFatal;
}
// Parse and verify the protected data.
const std::vector<uint8_t>& encoded_protected_data =
signed_csr_payload_array->get(0)->asBstr()->value();
if (encoded_protected_data.empty()) {
AddValidationMessage(kCborValidateFatal,
"Empty signed csr payload protected data.");
return kCborValidateFatal;
}
auto parse_protected_data_result = cppbor::parse(encoded_protected_data);
std::unique_ptr<cppbor::Item> protected_data_item =
std::move(std::get<0>(parse_protected_data_result));
std::string error_message =
std::move(std::get<2>(parse_protected_data_result));
if (protected_data_item == nullptr || !error_message.empty()) {
AddValidationMessage(
kCborValidateFatal,
"Unable to parse signed csr payload protected: " + error_message);
return kCborValidateFatal;
}
if (protected_data_item->type() != cppbor::MAP) {
AddValidationMessage(
kCborValidateFatal,
"Unexpected signed csr payload protected type: " +
CppborMajorTypeToString(protected_data_item->type()));
return kCborValidateFatal;
}
const cppbor::Map* protected_map = protected_data_item->asMap();
if (protected_map == nullptr) {
AddValidationMessage(kCborValidateFatal,
"Signed csr payload protected map is null.");
return kCborValidateFatal;
}
if (protected_map->size() == 0) {
signed_csr_payload.protected_data.first = kEmpty;
} else {
signed_csr_payload.protected_data.first = kPresent;
status = ProcessSignedDataProtected(
protected_map, &signed_csr_payload.protected_data.second);
if (status == kCborValidateFatal) return status;
}
// Element 1: unprotected params map.
std::string msg = "- Unprotected params: ";
const cppbor::Map* unprotected_params =
signed_csr_payload_array->get(1)->asMap();
if (!unprotected_params) {
AddValidationMessage(
kCborValidateFatal,
kCborValidateError,
"UnprotectedParams must be a CBOR map. Actual type: " +
CppborMajorTypeToString(signed_csr_payload_array->get(1)->type()));
return message_status_;
}
if (unprotected_params->size() == 0) {
msg += "<null>";
} else {
msg += "non-null map";
AddValidationMessage(kCborValidateWarning,
"UnprotectedParams is expected to be empty.");
// Unprotected data is supposed to be empty and can be ignored.
signed_csr_payload.unprotected.first = kEmpty;
signed_csr_payload.unprotected.second = "{}";
}
msg_ss_ << msg << "\n";
// Element 2: payload (DataToBeSigned).
msg_ss_ << "- Payload:\n";
const cppbor::Bstr* payload_to_be_signed =
signed_csr_payload_array->get(2)->asBstr();
if (!payload_to_be_signed) {
@@ -351,29 +485,58 @@ CborMessageStatus SignedCsrPayloadValidator::Validate() {
CppborMajorTypeToString(signed_csr_payload_array->get(2)->type()));
return message_status_;
}
status = ValidateDataToBeSigned(payload_to_be_signed);
if (status == kCborValidateFatal) return kCborValidateFatal;
auto parse_payload_to_be_signed_result = cppbor::parse(payload_to_be_signed);
std::unique_ptr<cppbor::Item> parsed_payload_to_be_signed_item =
std::move(std::get<0>(parse_payload_to_be_signed_result));
error_message = std::move(std::get<2>(parse_payload_to_be_signed_result));
if (!parsed_payload_to_be_signed_item || !error_message.empty()) {
AddValidationMessage(
kCborValidateFatal,
"Unable to parse the payload to be signed: " + error_message);
return kCborValidateFatal;
}
// Verify that |parsed_payload_to_be_signed_item| is a valid array.
const cppbor::Array* payload_to_be_signed_array =
parsed_payload_to_be_signed_item->asArray();
if (!payload_to_be_signed_array) {
AddValidationMessage(
kCborValidateFatal,
"Payload to be signed must be a CBOR array. Actual type: " +
CppborMajorTypeToString(parsed_payload_to_be_signed_item->type()));
return kCborValidateFatal;
}
if (payload_to_be_signed_array->size() == 0) {
signed_csr_payload.payload.first = kEmpty;
} else {
signed_csr_payload.payload.first = kPresent;
status = ProcessDataToBeSigned(payload_to_be_signed_array,
&signed_csr_payload.payload.second);
if (status == kCborValidateFatal) return kCborValidateFatal;
}
// Element 3: signature.
std::string sig_msg = "- Signature: ";
const cppbor::Bstr* signature = signed_csr_payload_array->get(3)->asBstr();
if (!signature) {
AddValidationMessage(
kCborValidateFatal,
"CoseSign1 signature must be Bstr. Actual type: " +
CppborMajorTypeToString(signed_csr_payload_array->get(3)->type()));
return message_status_;
return kCborValidateFatal;
}
// Skip CoseSign1 signature verification since the validator doesn't have
// verifying keys.
if (signature->value().empty()) {
sig_msg += "<null>";
AddValidationMessage(kCborValidateError, "CoseSign1 signature is missing.");
} else {
sig_msg += wvutil::b2a_hex(signature->value());
signed_csr_payload.signature.second = signature->value();
signed_csr_payload.signature.first =
signed_csr_payload.signature.second.empty() ? kEmpty : kPresent;
// Print the parsed signed csr payload.
msg_ss_ << signed_csr_payload.ToString() << "\n";
// Start validation.
std::vector<std::pair<CborMessageStatus, std::string>> msgs;
status = signed_csr_payload.Validate(msgs);
ApplyStatus(message_status_, status);
for (const auto& msg : msgs) {
AddValidationMessage(msg.first, msg.second);
}
msg_ss_ << sig_msg << "\n";
msg_ss_ << "...\n";
if (message_status_ == kCborParseOk) message_status_ = kCborValidateOk;
return message_status_;
}
@@ -387,7 +550,7 @@ std::string SignedCsrPayloadValidator::GetFormattedMessage() const {
if (parsed_item == nullptr) {
return "<null>";
}
return msg_ss_.str();
return FormatString(msg_ss_.str());
}
} // namespace util
} // namespace wvoec

View File

@@ -8,6 +8,7 @@
#include <gtest/gtest.h>
#include "bcc_validator.h"
#include "log.h"
using ::testing::AllOf;
using ::testing::Ge;
@@ -17,60 +18,227 @@ using ::testing::Le;
namespace wvoec {
namespace util {
namespace {
// Self-signed phase 1 BCC generated by OPK reference implementation.
// Phase 2 DICE BCC generated by OPK reference implementation.
const std::vector<uint8_t> kBcc = {
0x83, 0xa5, 0x01, 0x01, 0x03, 0x27, 0x04, 0x81, 0x02, 0x20, 0x06, 0x21,
0x58, 0x20, 0x02, 0xab, 0xbe, 0x80, 0x02, 0x41, 0xf1, 0x0b, 0x40, 0xff,
0xd5, 0xf0, 0xd0, 0x26, 0x65, 0x70, 0xdc, 0x5a, 0x97, 0xa6, 0x80, 0xee,
0x15, 0x95, 0xec, 0x26, 0x36, 0x70, 0x77, 0xe5, 0xfb, 0x10, 0x84, 0x43,
0xa1, 0x01, 0x27, 0xa0, 0x58, 0xf8, 0xaa, 0x01, 0x68, 0x69, 0x73, 0x73,
0x75, 0x65, 0x72, 0x20, 0x30, 0x02, 0x67, 0x65, 0x6e, 0x74, 0x72, 0x79,
0x20, 0x31, 0x3a, 0x00, 0x47, 0x44, 0x59, 0x6a, 0x61, 0x6e, 0x64, 0x72,
0x6f, 0x69, 0x64, 0x2e, 0x31, 0x35, 0x3a, 0x00, 0x47, 0x44, 0x57, 0x58,
0x2d, 0xa5, 0x01, 0x01, 0x03, 0x27, 0x04, 0x81, 0x02, 0x20, 0x06, 0x21,
0x58, 0x20, 0x3c, 0xfe, 0x6e, 0x28, 0x8b, 0x53, 0x31, 0x64, 0xb2, 0x16,
0x26, 0x4c, 0x5f, 0x8e, 0x76, 0xfd, 0xb9, 0x25, 0x8e, 0x8c, 0xf5, 0x80,
0xfe, 0xa5, 0x74, 0xbb, 0x0c, 0x5a, 0xef, 0xe3, 0x21, 0xce, 0x3a, 0x00,
0x47, 0x44, 0x58, 0x41, 0x20, 0x3a, 0x00, 0x47, 0x44, 0x50, 0x58, 0x20,
0xb3, 0x16, 0x96, 0xda, 0x0e, 0x6c, 0x46, 0x26, 0x1e, 0x52, 0x9b, 0x43,
0x9b, 0x68, 0xd0, 0x32, 0x5e, 0x31, 0x39, 0xf9, 0x69, 0x28, 0xec, 0x03,
0xeb, 0x06, 0x4c, 0xf3, 0x60, 0xf1, 0xd3, 0x0e, 0x3a, 0x00, 0x47, 0x44,
0x52, 0x58, 0x20, 0x38, 0x0c, 0x43, 0xc2, 0xc5, 0x36, 0x5a, 0x91, 0xb4,
0x2d, 0x85, 0x22, 0xb4, 0xb9, 0x6e, 0x43, 0xd2, 0xd0, 0x3e, 0x2f, 0xc4,
0x1f, 0xb7, 0xb3, 0x4c, 0x6b, 0xfd, 0x90, 0xc7, 0x80, 0x42, 0x84, 0x3a,
0x00, 0x47, 0x44, 0x53, 0x57, 0xa2, 0x3a, 0x00, 0x01, 0x11, 0x71, 0x68,
0x57, 0x69, 0x64, 0x65, 0x76, 0x69, 0x6e, 0x65, 0x3a, 0x00, 0x01, 0x11,
0x72, 0x62, 0x31, 0x39, 0x3a, 0x00, 0x47, 0x44, 0x54, 0x58, 0x20, 0x2d,
0xd1, 0xac, 0x94, 0xbe, 0x4b, 0x89, 0x98, 0xba, 0xec, 0xb6, 0x01, 0xaf,
0xaf, 0xda, 0x01, 0x8f, 0xeb, 0x03, 0xd2, 0x8f, 0x42, 0x12, 0x55, 0xe4,
0x4b, 0xe2, 0xdf, 0x47, 0x68, 0x27, 0x6a, 0x3a, 0x00, 0x47, 0x44, 0x56,
0x41, 0x01, 0x58, 0x40, 0xd7, 0xe8, 0x9b, 0x9b, 0x8f, 0x3f, 0xe7, 0x66,
0x24, 0x87, 0x48, 0xf4, 0xbd, 0x47, 0x3b, 0x85, 0xfb, 0x2c, 0x0e, 0xed,
0x7d, 0xf4, 0xa6, 0xb5, 0x30, 0x77, 0xea, 0x92, 0x63, 0xc0, 0x1e, 0x8b,
0x40, 0xec, 0x71, 0x66, 0xf8, 0xe2, 0x92, 0x07, 0x67, 0x08, 0x25, 0x70,
0x4a, 0x06, 0x41, 0x72, 0x21, 0xdf, 0xd4, 0xa7, 0x1a, 0xeb, 0xe9, 0x5f,
0x11, 0x30, 0x5b, 0xaa, 0x09, 0xe3, 0x24, 0x00, 0x84, 0x43, 0xa1, 0x01,
0x27, 0xa0, 0x58, 0xf7, 0xaa, 0x01, 0x67, 0x65, 0x6e, 0x74, 0x72, 0x79,
0x20, 0x31, 0x02, 0x67, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x20, 0x32, 0x3a,
0x00, 0x47, 0x44, 0x59, 0x6a, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64,
0x2e, 0x31, 0x35, 0x3a, 0x00, 0x47, 0x44, 0x57, 0x58, 0x2d, 0xa5, 0x01,
0x01, 0x03, 0x27, 0x04, 0x81, 0x02, 0x20, 0x06, 0x21, 0x58, 0x20, 0xe6,
0xc1, 0x67, 0xa8, 0x6d, 0x92, 0x16, 0xb8, 0x9c, 0xd1, 0xc4, 0xd4, 0xd3,
0xf3, 0xc1, 0x22, 0x95, 0x80, 0x49, 0xd0, 0xb3, 0x50, 0x9f, 0x1d, 0xec,
0x6f, 0xa8, 0x6c, 0xd2, 0xbd, 0x1a, 0x68, 0x3a, 0x00, 0x47, 0x44, 0x58,
0x41, 0x20, 0x3a, 0x00, 0x47, 0x44, 0x50, 0x58, 0x20, 0x8d, 0x76, 0x93,
0x9d, 0x2f, 0x0e, 0x5f, 0xbd, 0x36, 0x58, 0x51, 0x53, 0xc4, 0xa0, 0xaf,
0xdb, 0x91, 0x41, 0x6a, 0x62, 0xf0, 0x03, 0x6a, 0x77, 0x8b, 0x2e, 0x5f,
0xa7, 0x29, 0x94, 0x69, 0x9d, 0x3a, 0x00, 0x47, 0x44, 0x52, 0x58, 0x20,
0xc0, 0x0b, 0xa4, 0x78, 0xac, 0xa9, 0xb6, 0x96, 0x64, 0xaa, 0x55, 0xf6,
0x52, 0xe2, 0xa6, 0x7b, 0xd7, 0x6f, 0xd5, 0xe5, 0xc1, 0xbd, 0xef, 0x97,
0xf3, 0x0e, 0xea, 0xbc, 0x4b, 0x9e, 0xb7, 0xec, 0x3a, 0x00, 0x47, 0x44,
0x53, 0x57, 0xa2, 0x3a, 0x00, 0x01, 0x11, 0x71, 0x68, 0x57, 0x69, 0x64,
0x65, 0x76, 0x69, 0x6e, 0x65, 0x3a, 0x00, 0x01, 0x11, 0x72, 0x62, 0x31,
0x39, 0x3a, 0x00, 0x47, 0x44, 0x54, 0x58, 0x20, 0x35, 0x65, 0xd2, 0xaf,
0x10, 0xf0, 0x67, 0x3e, 0x58, 0xf9, 0xfc, 0xc0, 0x6d, 0x46, 0xd2, 0x61,
0x34, 0xa6, 0xf1, 0xd5, 0x0d, 0x7b, 0x6e, 0x4f, 0xd7, 0x13, 0xcb, 0x1d,
0xd6, 0xef, 0x2a, 0x23, 0x3a, 0x00, 0x47, 0x44, 0x56, 0x41, 0x01, 0x58,
0x40, 0xa6, 0x67, 0x96, 0xf6, 0x31, 0x68, 0x45, 0x49, 0x7c, 0x38, 0x3f,
0xde, 0x91, 0x02, 0xe3, 0x2a, 0x91, 0xc8, 0x0b, 0x3c, 0xdf, 0x2b, 0x18,
0xcf, 0x9f, 0x06, 0xc0, 0xbe, 0x58, 0xbe, 0x12, 0x2c, 0xaa, 0x32, 0xa1,
0x34, 0x0d, 0xf2, 0x8b, 0xa5, 0x87, 0x17, 0x66, 0x61, 0xd9, 0xdc, 0x08,
0x52, 0x86, 0x51, 0x6f, 0x63, 0xfc, 0xaf, 0x7e, 0xc7, 0xeb, 0xa6, 0x73,
0x19, 0xb1, 0x1a, 0xc9, 0x06};
// Phase 1 self-signed BCC generated by OPK reference implementation.
const std::vector<uint8_t> kDegeneratedBcc = {
0x82, 0xa5, 0x01, 0x01, 0x03, 0x27, 0x04, 0x81, 0x02, 0x20, 0x06, 0x21,
0x58, 0x20, 0x02, 0xab, 0xbe, 0x80, 0x02, 0x41, 0xf1, 0x0b, 0x40, 0xff,
0xd5, 0xf0, 0xd0, 0x26, 0x65, 0x70, 0xdc, 0x5a, 0x97, 0xa6, 0x80, 0xee,
0x15, 0x95, 0xec, 0x26, 0x36, 0x70, 0x77, 0xe5, 0xfb, 0x10, 0x84, 0x43,
0xa1, 0x01, 0x27, 0xa0, 0x58, 0x40, 0xa4, 0x01, 0x60, 0x02, 0x60, 0x3a,
0x00, 0x47, 0x44, 0x57, 0x58, 0x2d, 0xa5, 0x01, 0x01, 0x03, 0x27, 0x04,
0x81, 0x02, 0x20, 0x06, 0x21, 0x58, 0x20, 0x02, 0xab, 0xbe, 0x80, 0x02,
0x41, 0xf1, 0x0b, 0x40, 0xff, 0xd5, 0xf0, 0xd0, 0x26, 0x65, 0x70, 0xdc,
0x5a, 0x97, 0xa6, 0x80, 0xee, 0x15, 0x95, 0xec, 0x26, 0x36, 0x70, 0x77,
0xe5, 0xfb, 0x10, 0x3a, 0x00, 0x47, 0x44, 0x58, 0x41, 0x20, 0x58, 0x40,
0x73, 0x02, 0x36, 0xaa, 0x6d, 0x52, 0x50, 0x67, 0x43, 0xc4, 0x0b, 0xf8,
0x3f, 0x35, 0x2a, 0xd8, 0x44, 0x09, 0xf4, 0x1d, 0xca, 0x91, 0x12, 0x27,
0x01, 0xdf, 0x73, 0xb7, 0x9b, 0x31, 0x28, 0x8e, 0xae, 0x9b, 0xc6, 0x7a,
0xdc, 0x07, 0xab, 0x69, 0xd2, 0x85, 0x9a, 0x15, 0x8b, 0xe3, 0x5b, 0xf2,
0x94, 0x95, 0xee, 0x49, 0x74, 0xc5, 0x85, 0x62, 0x3d, 0x46, 0x4c, 0xeb,
0x11, 0x89, 0x68, 0x02};
0xa1, 0x01, 0x27, 0xa0, 0x58, 0xf8, 0xaa, 0x01, 0x68, 0x69, 0x73, 0x73,
0x75, 0x65, 0x72, 0x20, 0x30, 0x02, 0x67, 0x65, 0x6e, 0x74, 0x72, 0x79,
0x20, 0x31, 0x3a, 0x00, 0x47, 0x44, 0x59, 0x6a, 0x61, 0x6e, 0x64, 0x72,
0x6f, 0x69, 0x64, 0x2e, 0x31, 0x35, 0x3a, 0x00, 0x47, 0x44, 0x57, 0x58,
0x2d, 0xa5, 0x01, 0x01, 0x03, 0x27, 0x04, 0x81, 0x02, 0x20, 0x06, 0x21,
0x58, 0x20, 0x02, 0xab, 0xbe, 0x80, 0x02, 0x41, 0xf1, 0x0b, 0x40, 0xff,
0xd5, 0xf0, 0xd0, 0x26, 0x65, 0x70, 0xdc, 0x5a, 0x97, 0xa6, 0x80, 0xee,
0x15, 0x95, 0xec, 0x26, 0x36, 0x70, 0x77, 0xe5, 0xfb, 0x10, 0x3a, 0x00,
0x47, 0x44, 0x58, 0x41, 0x20, 0x3a, 0x00, 0x47, 0x44, 0x50, 0x58, 0x20,
0xb3, 0x16, 0x96, 0xda, 0x0e, 0x6c, 0x46, 0x26, 0x1e, 0x52, 0x9b, 0x43,
0x9b, 0x68, 0xd0, 0x32, 0x5e, 0x31, 0x39, 0xf9, 0x69, 0x28, 0xec, 0x03,
0xeb, 0x06, 0x4c, 0xf3, 0x60, 0xf1, 0xd3, 0x0e, 0x3a, 0x00, 0x47, 0x44,
0x52, 0x58, 0x20, 0x38, 0x0c, 0x43, 0xc2, 0xc5, 0x36, 0x5a, 0x91, 0xb4,
0x2d, 0x85, 0x22, 0xb4, 0xb9, 0x6e, 0x43, 0xd2, 0xd0, 0x3e, 0x2f, 0xc4,
0x1f, 0xb7, 0xb3, 0x4c, 0x6b, 0xfd, 0x90, 0xc7, 0x80, 0x42, 0x84, 0x3a,
0x00, 0x47, 0x44, 0x53, 0x57, 0xa2, 0x3a, 0x00, 0x01, 0x11, 0x71, 0x68,
0x57, 0x69, 0x64, 0x65, 0x76, 0x69, 0x6e, 0x65, 0x3a, 0x00, 0x01, 0x11,
0x72, 0x62, 0x31, 0x39, 0x3a, 0x00, 0x47, 0x44, 0x54, 0x58, 0x20, 0x2d,
0xd1, 0xac, 0x94, 0xbe, 0x4b, 0x89, 0x98, 0xba, 0xec, 0xb6, 0x01, 0xaf,
0xaf, 0xda, 0x01, 0x8f, 0xeb, 0x03, 0xd2, 0x8f, 0x42, 0x12, 0x55, 0xe4,
0x4b, 0xe2, 0xdf, 0x47, 0x68, 0x27, 0x6a, 0x3a, 0x00, 0x47, 0x44, 0x56,
0x41, 0x01, 0x58, 0x40, 0x3a, 0xda, 0xff, 0x7f, 0x11, 0xc4, 0xd8, 0x62,
0x15, 0x06, 0x03, 0x1a, 0x9b, 0x4d, 0x23, 0xf1, 0x97, 0x33, 0x94, 0x67,
0xfa, 0xef, 0x29, 0xe4, 0x18, 0x34, 0x38, 0xad, 0x1d, 0x0d, 0x91, 0x17,
0x3a, 0xe3, 0x6b, 0xd0, 0x50, 0xc9, 0x86, 0x8c, 0x9b, 0x62, 0x6b, 0xac,
0x8e, 0xa4, 0xa7, 0x4b, 0x94, 0x6b, 0xc7, 0xce, 0xf4, 0xe4, 0xe5, 0x12,
0x1d, 0xf6, 0x19, 0xf9, 0x4d, 0x5b, 0x9a, 0x00};
// Key bytes all zero
const std::vector<uint8_t> kBccWrongEntryKey = {
0x82, 0xa5, 0x01, 0x01, 0x03, 0x27, 0x04, 0x81, 0x02, 0x20, 0x06, 0x21,
0x58, 0x20, 0x02, 0xab, 0xbe, 0x80, 0x02, 0x41, 0xf1, 0x0b, 0x40, 0xff,
0xd5, 0xf0, 0xd0, 0x26, 0x65, 0x70, 0xdc, 0x5a, 0x97, 0xa6, 0x80, 0xee,
0x15, 0x95, 0xec, 0x26, 0x36, 0x70, 0x77, 0xe5, 0xfb, 0x10, 0x84, 0x43,
0xa1, 0x01, 0x27, 0xa0, 0x58, 0x40, 0xa4, 0x01, 0x60, 0x02, 0x60, 0x3a,
0x00, 0x47, 0x44, 0x57, 0x58, 0x2d, 0xa5, 0x01, 0x01, 0x03, 0x27, 0x04,
0x81, 0x02, 0x20, 0x06, 0x21, 0x58, 0x20, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
0xaa, 0xaa, 0xaa, 0x3a, 0x00, 0x47, 0x44, 0x58, 0x41, 0x20, 0x58, 0x40,
0x89, 0x1d, 0xff, 0xb3, 0x3b, 0xe2, 0xdc, 0xc6, 0xbc, 0xbd, 0xc7, 0xcd,
0x3f, 0x9c, 0x43, 0xf6, 0xdd, 0xea, 0x58, 0x53, 0x45, 0x8f, 0x87, 0x17,
0x0a, 0xe4, 0x06, 0xf2, 0xbe, 0x14, 0x69, 0x13, 0x3d, 0x1d, 0xd0, 0x52,
0x8f, 0x56, 0x4b, 0x0f, 0xad, 0x2e, 0xf0, 0xbf, 0xbb, 0xd1, 0x35, 0x9c,
0x5a, 0xe8, 0x67, 0xbe, 0xec, 0xff, 0x9d, 0xfe, 0xac, 0x8d, 0x47, 0x4e,
0x6d, 0xd1, 0xd3, 0x02};
0x83, 0xa5, 0x01, 0x01, 0x03, 0x27, 0x04, 0x81, 0x02, 0x20, 0x06, 0x21,
0x58, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0x43,
0xa1, 0x01, 0x27, 0xa0, 0x58, 0xf8, 0xaa, 0x01, 0x68, 0x69, 0x73, 0x73,
0x75, 0x65, 0x72, 0x20, 0x30, 0x02, 0x67, 0x65, 0x6e, 0x74, 0x72, 0x79,
0x20, 0x31, 0x3a, 0x00, 0x47, 0x44, 0x59, 0x6a, 0x61, 0x6e, 0x64, 0x72,
0x6f, 0x69, 0x64, 0x2e, 0x31, 0x35, 0x3a, 0x00, 0x47, 0x44, 0x57, 0x58,
0x2d, 0xa5, 0x01, 0x01, 0x03, 0x27, 0x04, 0x81, 0x02, 0x20, 0x06, 0x21,
0x58, 0x20, 0x3c, 0xfe, 0x6e, 0x28, 0x8b, 0x53, 0x31, 0x64, 0xb2, 0x16,
0x26, 0x4c, 0x5f, 0x8e, 0x76, 0xfd, 0xb9, 0x25, 0x8e, 0x8c, 0xf5, 0x80,
0xfe, 0xa5, 0x74, 0xbb, 0x0c, 0x5a, 0xef, 0xe3, 0x21, 0xce, 0x3a, 0x00,
0x47, 0x44, 0x58, 0x41, 0x20, 0x3a, 0x00, 0x47, 0x44, 0x50, 0x58, 0x20,
0xb3, 0x16, 0x96, 0xda, 0x0e, 0x6c, 0x46, 0x26, 0x1e, 0x52, 0x9b, 0x43,
0x9b, 0x68, 0xd0, 0x32, 0x5e, 0x31, 0x39, 0xf9, 0x69, 0x28, 0xec, 0x03,
0xeb, 0x06, 0x4c, 0xf3, 0x60, 0xf1, 0xd3, 0x0e, 0x3a, 0x00, 0x47, 0x44,
0x52, 0x58, 0x20, 0x38, 0x0c, 0x43, 0xc2, 0xc5, 0x36, 0x5a, 0x91, 0xb4,
0x2d, 0x85, 0x22, 0xb4, 0xb9, 0x6e, 0x43, 0xd2, 0xd0, 0x3e, 0x2f, 0xc4,
0x1f, 0xb7, 0xb3, 0x4c, 0x6b, 0xfd, 0x90, 0xc7, 0x80, 0x42, 0x84, 0x3a,
0x00, 0x47, 0x44, 0x53, 0x57, 0xa2, 0x3a, 0x00, 0x01, 0x11, 0x71, 0x68,
0x57, 0x69, 0x64, 0x65, 0x76, 0x69, 0x6e, 0x65, 0x3a, 0x00, 0x01, 0x11,
0x72, 0x62, 0x31, 0x39, 0x3a, 0x00, 0x47, 0x44, 0x54, 0x58, 0x20, 0x2d,
0xd1, 0xac, 0x94, 0xbe, 0x4b, 0x89, 0x98, 0xba, 0xec, 0xb6, 0x01, 0xaf,
0xaf, 0xda, 0x01, 0x8f, 0xeb, 0x03, 0xd2, 0x8f, 0x42, 0x12, 0x55, 0xe4,
0x4b, 0xe2, 0xdf, 0x47, 0x68, 0x27, 0x6a, 0x3a, 0x00, 0x47, 0x44, 0x56,
0x41, 0x01, 0x58, 0x40, 0xd7, 0xe8, 0x9b, 0x9b, 0x8f, 0x3f, 0xe7, 0x66,
0x24, 0x87, 0x48, 0xf4, 0xbd, 0x47, 0x3b, 0x85, 0xfb, 0x2c, 0x0e, 0xed,
0x7d, 0xf4, 0xa6, 0xb5, 0x30, 0x77, 0xea, 0x92, 0x63, 0xc0, 0x1e, 0x8b,
0x40, 0xec, 0x71, 0x66, 0xf8, 0xe2, 0x92, 0x07, 0x67, 0x08, 0x25, 0x70,
0x4a, 0x06, 0x41, 0x72, 0x21, 0xdf, 0xd4, 0xa7, 0x1a, 0xeb, 0xe9, 0x5f,
0x11, 0x30, 0x5b, 0xaa, 0x09, 0xe3, 0x24, 0x00, 0x84, 0x43, 0xa1, 0x01,
0x27, 0xa0, 0x58, 0xf7, 0xaa, 0x01, 0x67, 0x65, 0x6e, 0x74, 0x72, 0x79,
0x20, 0x31, 0x02, 0x67, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x20, 0x32, 0x3a,
0x00, 0x47, 0x44, 0x59, 0x6a, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64,
0x2e, 0x31, 0x35, 0x3a, 0x00, 0x47, 0x44, 0x57, 0x58, 0x2d, 0xa5, 0x01,
0x01, 0x03, 0x27, 0x04, 0x81, 0x02, 0x20, 0x06, 0x21, 0x58, 0x20, 0xe6,
0xc1, 0x67, 0xa8, 0x6d, 0x92, 0x16, 0xb8, 0x9c, 0xd1, 0xc4, 0xd4, 0xd3,
0xf3, 0xc1, 0x22, 0x95, 0x80, 0x49, 0xd0, 0xb3, 0x50, 0x9f, 0x1d, 0xec,
0x6f, 0xa8, 0x6c, 0xd2, 0xbd, 0x1a, 0x68, 0x3a, 0x00, 0x47, 0x44, 0x58,
0x41, 0x20, 0x3a, 0x00, 0x47, 0x44, 0x50, 0x58, 0x20, 0x8d, 0x76, 0x93,
0x9d, 0x2f, 0x0e, 0x5f, 0xbd, 0x36, 0x58, 0x51, 0x53, 0xc4, 0xa0, 0xaf,
0xdb, 0x91, 0x41, 0x6a, 0x62, 0xf0, 0x03, 0x6a, 0x77, 0x8b, 0x2e, 0x5f,
0xa7, 0x29, 0x94, 0x69, 0x9d, 0x3a, 0x00, 0x47, 0x44, 0x52, 0x58, 0x20,
0xc0, 0x0b, 0xa4, 0x78, 0xac, 0xa9, 0xb6, 0x96, 0x64, 0xaa, 0x55, 0xf6,
0x52, 0xe2, 0xa6, 0x7b, 0xd7, 0x6f, 0xd5, 0xe5, 0xc1, 0xbd, 0xef, 0x97,
0xf3, 0x0e, 0xea, 0xbc, 0x4b, 0x9e, 0xb7, 0xec, 0x3a, 0x00, 0x47, 0x44,
0x53, 0x57, 0xa2, 0x3a, 0x00, 0x01, 0x11, 0x71, 0x68, 0x57, 0x69, 0x64,
0x65, 0x76, 0x69, 0x6e, 0x65, 0x3a, 0x00, 0x01, 0x11, 0x72, 0x62, 0x31,
0x39, 0x3a, 0x00, 0x47, 0x44, 0x54, 0x58, 0x20, 0x35, 0x65, 0xd2, 0xaf,
0x10, 0xf0, 0x67, 0x3e, 0x58, 0xf9, 0xfc, 0xc0, 0x6d, 0x46, 0xd2, 0x61,
0x34, 0xa6, 0xf1, 0xd5, 0x0d, 0x7b, 0x6e, 0x4f, 0xd7, 0x13, 0xcb, 0x1d,
0xd6, 0xef, 0x2a, 0x23, 0x3a, 0x00, 0x47, 0x44, 0x56, 0x41, 0x01, 0x58,
0x40, 0xa6, 0x67, 0x96, 0xf6, 0x31, 0x68, 0x45, 0x49, 0x7c, 0x38, 0x3f,
0xde, 0x91, 0x02, 0xe3, 0x2a, 0x91, 0xc8, 0x0b, 0x3c, 0xdf, 0x2b, 0x18,
0xcf, 0x9f, 0x06, 0xc0, 0xbe, 0x58, 0xbe, 0x12, 0x2c, 0xaa, 0x32, 0xa1,
0x34, 0x0d, 0xf2, 0x8b, 0xa5, 0x87, 0x17, 0x66, 0x61, 0xd9, 0xdc, 0x08,
0x52, 0x86, 0x51, 0x6f, 0x63, 0xfc, 0xaf, 0x7e, 0xc7, 0xeb, 0xa6, 0x73,
0x19, 0xb1, 0x1a, 0xc9, 0x06};
const std::vector<uint8_t> kBccMissingIssuer = {
0x82, 0xa5, 0x01, 0x01, 0x03, 0x27, 0x04, 0x81, 0x02, 0x20, 0x06, 0x21,
0x83, 0xa5, 0x01, 0x01, 0x03, 0x27, 0x04, 0x81, 0x02, 0x20, 0x06, 0x21,
0x58, 0x20, 0x02, 0xab, 0xbe, 0x80, 0x02, 0x41, 0xf1, 0x0b, 0x40, 0xff,
0xd5, 0xf0, 0xd0, 0x26, 0x65, 0x70, 0xdc, 0x5a, 0x97, 0xa6, 0x80, 0xee,
0x15, 0x95, 0xec, 0x26, 0x36, 0x70, 0x77, 0xe5, 0xfb, 0x10, 0x84, 0x43,
0xa1, 0x01, 0x27, 0xa0, 0x58, 0x3e, 0xa3, 0x02, 0x60, 0x3a, 0x00, 0x47,
0x44, 0x57, 0x58, 0x2d, 0xa5, 0x01, 0x01, 0x03, 0x27, 0x04, 0x81, 0x02,
0x20, 0x06, 0x21, 0x58, 0x20, 0x02, 0xab, 0xbe, 0x80, 0x02, 0x41, 0xf1,
0x0b, 0x40, 0xff, 0xd5, 0xf0, 0xd0, 0x26, 0x65, 0x70, 0xdc, 0x5a, 0x97,
0xa6, 0x80, 0xee, 0x15, 0x95, 0xec, 0x26, 0x36, 0x70, 0x77, 0xe5, 0xfb,
0x10, 0x3a, 0x00, 0x47, 0x44, 0x58, 0x41, 0x20, 0x58, 0x40, 0xf9, 0x46,
0x36, 0xbd, 0x95, 0x75, 0xc2, 0x3d, 0xf9, 0xa2, 0xbe, 0x60, 0x8e, 0xbf,
0x64, 0x89, 0xdf, 0xb9, 0x9c, 0x3c, 0x17, 0x36, 0x23, 0x9a, 0x68, 0x1a,
0x34, 0x36, 0x51, 0x89, 0x59, 0xf2, 0x54, 0x62, 0xd3, 0x8f, 0xeb, 0x9b,
0x75, 0x3e, 0xe9, 0xfc, 0xe3, 0xc2, 0x8f, 0x84, 0xb1, 0x71, 0xcd, 0x29,
0x12, 0x65, 0xeb, 0xab, 0x28, 0x4b, 0xe2, 0x3e, 0x1b, 0xd8, 0x17, 0xdb,
0x97, 0x0f};
0xa1, 0x01, 0x27, 0xa0, 0x58, 0xf8, 0xaa, 0x01, 0x68, 0x69, 0x73, 0x73,
0x75, 0x65, 0x72, 0x20, 0x30, 0x02, 0x67, 0x65, 0x6e, 0x74, 0x72, 0x79,
0x20, 0x31, 0x3a, 0x00, 0x47, 0x44, 0x59, 0x6a, 0x61, 0x6e, 0x64, 0x72,
0x6f, 0x69, 0x64, 0x2e, 0x31, 0x35, 0x3a, 0x00, 0x47, 0x44, 0x57, 0x58,
0x2d, 0xa5, 0x01, 0x01, 0x03, 0x27, 0x04, 0x81, 0x02, 0x20, 0x06, 0x21,
0x58, 0x20, 0x3c, 0xfe, 0x6e, 0x28, 0x8b, 0x53, 0x31, 0x64, 0xb2, 0x16,
0x26, 0x4c, 0x5f, 0x8e, 0x76, 0xfd, 0xb9, 0x25, 0x8e, 0x8c, 0xf5, 0x80,
0xfe, 0xa5, 0x74, 0xbb, 0x0c, 0x5a, 0xef, 0xe3, 0x21, 0xce, 0x3a, 0x00,
0x47, 0x44, 0x58, 0x41, 0x20, 0x3a, 0x00, 0x47, 0x44, 0x50, 0x58, 0x20,
0xb3, 0x16, 0x96, 0xda, 0x0e, 0x6c, 0x46, 0x26, 0x1e, 0x52, 0x9b, 0x43,
0x9b, 0x68, 0xd0, 0x32, 0x5e, 0x31, 0x39, 0xf9, 0x69, 0x28, 0xec, 0x03,
0xeb, 0x06, 0x4c, 0xf3, 0x60, 0xf1, 0xd3, 0x0e, 0x3a, 0x00, 0x47, 0x44,
0x52, 0x58, 0x20, 0x38, 0x0c, 0x43, 0xc2, 0xc5, 0x36, 0x5a, 0x91, 0xb4,
0x2d, 0x85, 0x22, 0xb4, 0xb9, 0x6e, 0x43, 0xd2, 0xd0, 0x3e, 0x2f, 0xc4,
0x1f, 0xb7, 0xb3, 0x4c, 0x6b, 0xfd, 0x90, 0xc7, 0x80, 0x42, 0x84, 0x3a,
0x00, 0x47, 0x44, 0x53, 0x57, 0xa2, 0x3a, 0x00, 0x01, 0x11, 0x71, 0x68,
0x57, 0x69, 0x64, 0x65, 0x76, 0x69, 0x6e, 0x65, 0x3a, 0x00, 0x01, 0x11,
0x72, 0x62, 0x31, 0x39, 0x3a, 0x00, 0x47, 0x44, 0x54, 0x58, 0x20, 0x2d,
0xd1, 0xac, 0x94, 0xbe, 0x4b, 0x89, 0x98, 0xba, 0xec, 0xb6, 0x01, 0xaf,
0xaf, 0xda, 0x01, 0x8f, 0xeb, 0x03, 0xd2, 0x8f, 0x42, 0x12, 0x55, 0xe4,
0x4b, 0xe2, 0xdf, 0x47, 0x68, 0x27, 0x6a, 0x3a, 0x00, 0x47, 0x44, 0x56,
0x41, 0x01, 0x58, 0x40, 0xd7, 0xe8, 0x9b, 0x9b, 0x8f, 0x3f, 0xe7, 0x66,
0x24, 0x87, 0x48, 0xf4, 0xbd, 0x47, 0x3b, 0x85, 0xfb, 0x2c, 0x0e, 0xed,
0x7d, 0xf4, 0xa6, 0xb5, 0x30, 0x77, 0xea, 0x92, 0x63, 0xc0, 0x1e, 0x8b,
0x40, 0xec, 0x71, 0x66, 0xf8, 0xe2, 0x92, 0x07, 0x67, 0x08, 0x25, 0x70,
0x4a, 0x06, 0x41, 0x72, 0x21, 0xdf, 0xd4, 0xa7, 0x1a, 0xeb, 0xe9, 0x5f,
0x11, 0x30, 0x5b, 0xaa, 0x09, 0xe3, 0x24, 0x00, 0x84, 0x43, 0xa1, 0x01,
0x27, 0xa0, 0x58, 0xf0, 0xaa, 0x01, 0x60, 0x02, 0x67, 0x65, 0x6e, 0x74,
0x72, 0x79, 0x20, 0x32, 0x3a, 0x00, 0x47, 0x44, 0x59, 0x6a, 0x61, 0x6e,
0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x31, 0x35, 0x3a, 0x00, 0x47, 0x44,
0x57, 0x58, 0x2d, 0xa5, 0x01, 0x01, 0x03, 0x27, 0x04, 0x81, 0x02, 0x20,
0x06, 0x21, 0x58, 0x20, 0xe6, 0xc1, 0x67, 0xa8, 0x6d, 0x92, 0x16, 0xb8,
0x9c, 0xd1, 0xc4, 0xd4, 0xd3, 0xf3, 0xc1, 0x22, 0x95, 0x80, 0x49, 0xd0,
0xb3, 0x50, 0x9f, 0x1d, 0xec, 0x6f, 0xa8, 0x6c, 0xd2, 0xbd, 0x1a, 0x68,
0x3a, 0x00, 0x47, 0x44, 0x58, 0x41, 0x20, 0x3a, 0x00, 0x47, 0x44, 0x50,
0x58, 0x20, 0x8d, 0x76, 0x93, 0x9d, 0x2f, 0x0e, 0x5f, 0xbd, 0x36, 0x58,
0x51, 0x53, 0xc4, 0xa0, 0xaf, 0xdb, 0x91, 0x41, 0x6a, 0x62, 0xf0, 0x03,
0x6a, 0x77, 0x8b, 0x2e, 0x5f, 0xa7, 0x29, 0x94, 0x69, 0x9d, 0x3a, 0x00,
0x47, 0x44, 0x52, 0x58, 0x20, 0xc0, 0x0b, 0xa4, 0x78, 0xac, 0xa9, 0xb6,
0x96, 0x64, 0xaa, 0x55, 0xf6, 0x52, 0xe2, 0xa6, 0x7b, 0xd7, 0x6f, 0xd5,
0xe5, 0xc1, 0xbd, 0xef, 0x97, 0xf3, 0x0e, 0xea, 0xbc, 0x4b, 0x9e, 0xb7,
0xec, 0x3a, 0x00, 0x47, 0x44, 0x53, 0x57, 0xa2, 0x3a, 0x00, 0x01, 0x11,
0x71, 0x68, 0x57, 0x69, 0x64, 0x65, 0x76, 0x69, 0x6e, 0x65, 0x3a, 0x00,
0x01, 0x11, 0x72, 0x62, 0x31, 0x39, 0x3a, 0x00, 0x47, 0x44, 0x54, 0x58,
0x20, 0x35, 0x65, 0xd2, 0xaf, 0x10, 0xf0, 0x67, 0x3e, 0x58, 0xf9, 0xfc,
0xc0, 0x6d, 0x46, 0xd2, 0x61, 0x34, 0xa6, 0xf1, 0xd5, 0x0d, 0x7b, 0x6e,
0x4f, 0xd7, 0x13, 0xcb, 0x1d, 0xd6, 0xef, 0x2a, 0x23, 0x3a, 0x00, 0x47,
0x44, 0x56, 0x41, 0x01, 0x58, 0x40, 0x5a, 0x77, 0x3b, 0x51, 0x81, 0xad,
0x48, 0x54, 0x39, 0x86, 0x89, 0xd9, 0x0c, 0x87, 0x0a, 0x1b, 0x8c, 0x17,
0xd8, 0x14, 0xad, 0xfd, 0x64, 0x46, 0x71, 0x97, 0xa2, 0xd5, 0x15, 0x52,
0x1f, 0x87, 0xee, 0x48, 0x55, 0x4c, 0xba, 0x01, 0xcb, 0x18, 0xa4, 0x26,
0x89, 0xb7, 0x6e, 0x91, 0xc8, 0x21, 0x68, 0x1e, 0xad, 0x4a, 0x95, 0x33,
0xe7, 0xaa, 0x68, 0xe5, 0x06, 0x4d, 0xfa, 0xcf, 0x73, 0x03};
} // namespace
static void DumpValidatorOutput(const BccValidator& validator) {
const std::string out = validator.GetFormattedMessage();
LOGI("%s", out.c_str());
for (auto& msg : validator.GetValidateMessages()) {
LOGE("Error code %d: %s", msg.first, msg.second.c_str());
}
}
TEST(OEMCryptoBccValidatorTest, BccParseError) {
const std::vector<uint8_t> bcc_bad(kBcc.begin(), kBcc.end() - 1);
BccValidator validator;
@@ -89,8 +257,25 @@ TEST(OEMCryptoBccValidatorTest, Bcc) {
result = validator.Validate();
EXPECT_THAT(result, AllOf(Ge(kCborValidateOk), Le(kCborValidateWarning)));
const std::string out = validator.GetFormattedMessage();
EXPECT_THAT(out, HasSubstr("key encoding format: DEVICE_KEY_BYTE_STRING"));
EXPECT_THAT(out, HasSubstr("key algorithm type: EDDSA"));
EXPECT_THAT(out, HasSubstr("key_type:byte_string"));
EXPECT_THAT(out, HasSubstr("signature_algorithm:EdDSA"));
if (result >= kCborValidateWarning) {
DumpValidatorOutput(validator);
}
}
TEST(OEMCryptoBccValidatorTest, DegeneratedBcc) {
BccValidator validator;
CborMessageStatus result = validator.Parse(kDegeneratedBcc);
EXPECT_EQ(kCborParseOk, result);
result = validator.Validate();
EXPECT_THAT(result, AllOf(Ge(kCborValidateOk), Le(kCborValidateWarning)));
const std::string out = validator.GetFormattedMessage();
EXPECT_THAT(out, HasSubstr("key_type:byte_string"));
EXPECT_THAT(out, HasSubstr("signature_algorithm:EdDSA"));
if (result >= kCborValidateWarning) {
DumpValidatorOutput(validator);
}
}
TEST(OEMCryptoBccValidatorTest, BccWrongEntryKey) {
@@ -101,12 +286,17 @@ TEST(OEMCryptoBccValidatorTest, BccWrongEntryKey) {
EXPECT_EQ(result, kCborValidateError);
// Non-fatal validation error should be able to return formatted output.
const std::string out = validator.GetFormattedMessage();
EXPECT_THAT(out, HasSubstr("key encoding format: DEVICE_KEY_BYTE_STRING"));
EXPECT_THAT(out, HasSubstr("key algorithm type: EDDSA"));
EXPECT_THAT(out, HasSubstr("key_type:byte_string"));
EXPECT_THAT(out, HasSubstr("key_bytes:000000"));
const std::vector<std::pair<CborMessageStatus, std::string>> msgs =
validator.GetValidateMessages();
EXPECT_EQ(1u, msgs.size());
// Expect more than 1 validation errors caused by wrong entry key including
// invalid key, signature error, etc
EXPECT_GE(msgs.size(), 1u);
EXPECT_EQ(kCborValidateError, msgs[0].first);
if (result >= kCborValidateWarning) {
DumpValidatorOutput(validator);
}
}
TEST(OEMCryptoBccValidatorTest, BccParseThreeTimes) {
@@ -135,13 +325,16 @@ TEST(OEMCryptoBccValidatorTest, BccMissingIssuer) {
result = validator.Validate();
EXPECT_EQ(result, kCborValidateError);
const std::string out = validator.GetFormattedMessage();
EXPECT_THAT(out, HasSubstr("key encoding format: DEVICE_KEY_BYTE_STRING"));
EXPECT_THAT(out, HasSubstr("key algorithm type: EDDSA"));
EXPECT_THAT(out, HasSubstr("key_type:byte_string"));
EXPECT_THAT(out, HasSubstr("signature_algorithm:EdDSA"));
const std::vector<std::pair<CborMessageStatus, std::string>> msgs =
validator.GetValidateMessages();
EXPECT_EQ(1u, msgs.size());
EXPECT_EQ(kCborValidateError, msgs[0].first);
EXPECT_THAT(msgs[0].second, HasSubstr("Missing Issuer"));
EXPECT_THAT(msgs[0].second, HasSubstr("missing required field issuer"));
if (result >= kCborValidateWarning) {
DumpValidatorOutput(validator);
}
}
} // namespace util
} // namespace wvoec

View File

@@ -8,6 +8,7 @@
#include <gtest/gtest.h>
#include "device_info_validator.h"
#include "log.h"
using ::testing::AllOf;
using ::testing::Ge;
@@ -30,7 +31,7 @@ cppbor::Map BuildDeviceInfoMap(int version) {
.add("model", "model")
.add("vb_state", "green")
.add("bootloader_state", "unlocked")
.add("vbmeta_digest", cppbor::Bstr(std::vector<uint8_t>()))
.add("vbmeta_digest", cppbor::Bstr(std::vector<uint8_t>(32, 0xCC)))
.add("os_version", "os_version")
.add("system_patch_level", 202312)
.add("boot_patch_level", 20231201)
@@ -61,6 +62,14 @@ std::vector<uint8_t> BuildDeviceInfo(int version) {
}
} // namespace
static void DumpValidatorOutput(const DeviceInfoValidator& validator) {
const std::string out = validator.GetFormattedMessage();
LOGI("%s", out.c_str());
for (auto& msg : validator.GetValidateMessages()) {
LOGE("Error code %d: %s", msg.first, msg.second.c_str());
}
}
TEST(OEMCryptoDeviceInfoValidatorTest, DeviceInfoParseError) {
const std::vector<uint8_t> device_info = BuildDeviceInfo(kDeviceVersion3);
const std::vector<uint8_t> device_info_bad(device_info.begin(),
@@ -87,6 +96,9 @@ TEST(OEMCryptoDeviceInfoValidatorTest, DeviceInfoNotMap) {
EXPECT_EQ(1u, msgs.size());
EXPECT_EQ(kCborValidateFatal, msgs[0].first);
EXPECT_THAT(msgs[0].second, HasSubstr("Device info is not a CBOR map"));
if (result >= kCborValidateWarning) {
DumpValidatorOutput(validator);
}
}
TEST(OEMCryptoDeviceInfoValidatorTest,
@@ -95,8 +107,7 @@ TEST(OEMCryptoDeviceInfoValidatorTest,
cppbor::Map()
.add("brand", "brand")
.add("manufacturer", "manufacturer")
.add(123, 456) // Non-Tstr key type
.add("system_patch_level", "not a uint") // Non-uint value type
.add(123, 456) // Non-Tstr key type
.canonicalize()
.encode();
DeviceInfoValidator validator(kDeviceVersion3);
@@ -112,25 +123,22 @@ TEST(OEMCryptoDeviceInfoValidatorTest,
return p.second.find("Unexpected entry key type") != std::string::npos;
});
EXPECT_EQ(true, unexpected_key_type_found);
const bool unexpected_value_type_found = std::any_of(
msgs.begin(), msgs.end(),
[](const std::pair<CborMessageStatus, std::string>& p) {
return p.second.find("system_patch_level has the wrong type") !=
std::string::npos;
});
EXPECT_EQ(true, unexpected_value_type_found);
const bool missing_model_found = std::any_of(
msgs.begin(), msgs.end(),
[](const std::pair<CborMessageStatus, std::string>& p) {
return p.second.find("model is missing") != std::string::npos;
});
const bool missing_model_found =
std::any_of(msgs.begin(), msgs.end(),
[](const std::pair<CborMessageStatus, std::string>& p) {
return p.second.find("missing important field model") !=
std::string::npos;
});
EXPECT_EQ(true, missing_model_found);
if (result >= kCborValidateWarning) {
DumpValidatorOutput(validator);
}
}
TEST(OEMCryptoDeviceInfoValidatorTest, DeviceInfoV3NonCanonical) {
const cppbor::Map map = BuildDeviceInfoMap(kDeviceVersion3);
const std::vector<uint8_t> device_info = map.encode();
DeviceInfoValidator validator(kDeviceVersion3);
DeviceInfoValidator validator(kDeviceVersion3, true /* is_gms */);
CborMessageStatus result = validator.Parse(device_info);
EXPECT_EQ(kCborParseOk, result);
result = validator.Validate();
@@ -141,43 +149,97 @@ TEST(OEMCryptoDeviceInfoValidatorTest, DeviceInfoV3NonCanonical) {
EXPECT_EQ(kCborValidateError, msgs[0].first);
EXPECT_THAT(msgs[0].second,
HasSubstr("Device info ordering is non-canonical"));
if (result >= kCborValidateWarning) {
DumpValidatorOutput(validator);
}
}
TEST(OEMCryptoDeviceInfoValidatorTest, DeviceInfoV3) {
const std::vector<uint8_t> device_info = BuildDeviceInfo(kDeviceVersion3);
DeviceInfoValidator validator(kDeviceVersion3);
DeviceInfoValidator validator(kDeviceVersion3, true /* is_gms */);
CborMessageStatus result = validator.Parse(device_info);
EXPECT_EQ(kCborParseOk, result);
result = validator.Validate();
EXPECT_THAT(result, AllOf(Ge(kCborValidateOk), Le(kCborValidateWarning)));
const std::string out = validator.GetFormattedMessage();
EXPECT_THAT(out, HasSubstr("manufacturer: manufacturer"));
EXPECT_THAT(out, HasSubstr("model: model"));
EXPECT_THAT(out, HasSubstr("fused: 0"));
EXPECT_THAT(out, HasSubstr("manufacturer:manufacturer"));
EXPECT_THAT(out, HasSubstr("model:model"));
EXPECT_THAT(out, HasSubstr("fused:0"));
if (result >= kCborValidateWarning) {
DumpValidatorOutput(validator);
}
}
TEST(OEMCryptoDeviceInfoValidatorTest, DeviceInfoV3InvalidFields) {
cppbor::Map device_info_map =
cppbor::Map()
.add("brand", "brand")
.add("manufacturer", "manufacturer")
.add("product", "product")
.add("model", "model")
.add("vb_state", "invalid_green") // invalid value
.add("bootloader_state", "invalid_unlocked") // invalid value
.add("vbmeta_digest", cppbor::Bstr(std::vector<uint8_t>(32, 0xCC)))
.add("os_version", "os_version")
.add("system_patch_level", 100) // invalid value, expects "YYYYMM"
.add("boot_patch_level",
12345678) // invalid value, expectes "YYYYMMDD"
.add("vendor_patch_level",
"20231201") // invalid value, expects YYYYMMDD in int
.add("security_level", "tee")
.add("device", "device")
.add("fused", 9); // invalid value, expects 0 or 1
auto device_info = device_info_map.canonicalize().encode();
DeviceInfoValidator validator(kDeviceVersion3, true /* is_gms */);
CborMessageStatus result = validator.Parse(device_info);
EXPECT_EQ(kCborParseOk, result);
result = validator.Validate();
EXPECT_THAT(result, Ge(kCborValidateError));
const std::vector<std::pair<CborMessageStatus, std::string>> msgs =
validator.GetValidateMessages();
std::string out = "";
for (auto& msg : msgs) {
out += (msg.second + "\n");
}
EXPECT_THAT(out, HasSubstr("invalid value for system_patch_level"));
EXPECT_THAT(out, HasSubstr("invalid value for boot_patch_level"));
EXPECT_THAT(out, HasSubstr("missing required field vendor_patch_level"));
EXPECT_THAT(out, HasSubstr("unexpected value for vb_state"));
EXPECT_THAT(out, HasSubstr("unexpected value for bootloader_state"));
EXPECT_THAT(out, HasSubstr("unexpected value for fused"));
if (result >= kCborValidateWarning) {
DumpValidatorOutput(validator);
}
}
TEST(OEMCryptoDeviceInfoValidatorTest, DeviceInfoV2) {
const std::vector<uint8_t> device_info = BuildDeviceInfo(kDeviceVersion2);
DeviceInfoValidator validator(kDeviceVersion2);
DeviceInfoValidator validator(kDeviceVersion2, true /* is_gms */);
CborMessageStatus result = validator.Parse(device_info);
EXPECT_EQ(kCborParseOk, result);
result = validator.Validate();
EXPECT_THAT(result, AllOf(Ge(kCborValidateOk), Le(kCborValidateWarning)));
const std::string out = validator.GetFormattedMessage();
EXPECT_THAT(out, HasSubstr("manufacturer: manufacturer"));
EXPECT_THAT(out, HasSubstr("model: model"));
EXPECT_THAT(out, HasSubstr("fused: 0"));
EXPECT_THAT(out, HasSubstr("version: 2"));
EXPECT_THAT(out, HasSubstr("manufacturer:manufacturer"));
EXPECT_THAT(out, HasSubstr("model:model"));
EXPECT_THAT(out, HasSubstr("fused:0"));
EXPECT_THAT(out, HasSubstr("version:2"));
if (result >= kCborValidateWarning) {
DumpValidatorOutput(validator);
}
}
TEST(OEMCryptoDeviceInfoValidatorTest, DeviceInfoV1MissingField) {
const std::vector<uint8_t> device_info = cppbor::Map()
.add("brand", "brand")
.add("security_level", "tee")
.add("version", 1)
.canonicalize()
.encode();
DeviceInfoValidator validator(kDeviceVersion1);
const std::vector<uint8_t> device_info =
cppbor::Map()
.add("manufacturer", "manufacturer")
.add("model", "model")
.add("brand", "brand")
.add("security_level", "tee")
.add("version", 1)
.canonicalize()
.encode();
DeviceInfoValidator validator(kDeviceVersion1, true /* is_gms */);
CborMessageStatus result = validator.Parse(device_info);
EXPECT_EQ(kCborParseOk, result);
result = validator.Validate();
@@ -186,19 +248,50 @@ TEST(OEMCryptoDeviceInfoValidatorTest, DeviceInfoV1MissingField) {
validator.GetValidateMessages();
EXPECT_EQ(1u, msgs.size());
EXPECT_EQ(kCborValidateError, msgs[0].first);
EXPECT_THAT(msgs[0].second, HasSubstr("att_id_state is missing"));
EXPECT_THAT(msgs[0].second, HasSubstr("missing required field att_id_state"));
if (result >= kCborValidateWarning) {
DumpValidatorOutput(validator);
}
}
TEST(OEMCryptoDeviceInfoValidatorTest, DeviceInfoV1) {
DeviceInfoValidator validator(kDeviceVersion1);
DeviceInfoValidator validator(kDeviceVersion1, true /* is_gms */);
const std::vector<uint8_t> device_info = BuildDeviceInfo(kDeviceVersion1);
CborMessageStatus result = validator.Parse(device_info);
EXPECT_EQ(kCborParseOk, result);
result = validator.Validate();
EXPECT_THAT(result, AllOf(Ge(kCborValidateOk), Le(kCborValidateWarning)));
const std::string out = validator.GetFormattedMessage();
EXPECT_THAT(out, HasSubstr("board: board"));
EXPECT_THAT(out, HasSubstr("version: 1"));
EXPECT_THAT(out, HasSubstr("board:board"));
EXPECT_THAT(out, HasSubstr("version:1"));
if (result >= kCborValidateWarning) {
DumpValidatorOutput(validator);
}
}
TEST(OEMCryptoDeviceInfoValidatorTest, DeviceInfoMissingFused) {
const std::vector<uint8_t> device_info_bad =
cppbor::Map()
.add("model", "model")
.add("manufacturer", "manufacturer")
.canonicalize()
.encode();
DeviceInfoValidator validator(kDeviceVersion1, false /* is_gms */);
CborMessageStatus result = validator.Parse(device_info_bad);
EXPECT_EQ(kCborParseOk, result);
result = validator.Validate();
EXPECT_EQ(kCborValidateWarning, result);
const std::vector<std::pair<CborMessageStatus, std::string>> msgs =
validator.GetValidateMessages();
const bool missing_fused_found = std::any_of(
msgs.begin(), msgs.end(),
[](const std::pair<CborMessageStatus, std::string>& p) {
return p.second.find("missing field fused") != std::string::npos;
});
EXPECT_EQ(true, missing_fused_found);
if (result >= kCborValidateWarning) {
DumpValidatorOutput(validator);
}
}
} // namespace util
} // namespace wvoec

View File

@@ -7,6 +7,7 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "log.h"
#include "signed_csr_payload_validator.h"
using ::testing::AllOf;
@@ -86,6 +87,14 @@ std::vector<uint8_t> GetDefaultSignedCsrPayload() {
.add(cppbor::Bstr(GetDefaultSignature()))
.encode();
}
static void DumpValidatorOutput(const SignedCsrPayloadValidator& validator) {
const std::string out = validator.GetFormattedMessage();
LOGI("%s", out.c_str());
for (auto& msg : validator.GetValidateMessages()) {
LOGE("Error code %d: %s", msg.first, msg.second.c_str());
}
}
} // namespace
TEST(OEMCryptoSignedCsrPayloadValidatorTest, SignedCsrPayloadParseError) {
@@ -119,7 +128,8 @@ TEST(OEMCryptoSignedCsrPayloadValidatorTest, ProtectedDataNotMap) {
validator.GetValidateMessages();
EXPECT_EQ(1u, msgs.size());
EXPECT_EQ(kCborValidateFatal, msgs[0].first);
EXPECT_THAT(msgs[0].second, HasSubstr("ProtectedParams must be a CBOR map"));
EXPECT_THAT(msgs[0].second,
HasSubstr("Unexpected signed csr payload protected type: ARRAY"));
}
TEST(OEMCryptoSignedCsrPayloadValidatorTest, ProtectedDataMapMissingKey) {
@@ -135,12 +145,13 @@ TEST(OEMCryptoSignedCsrPayloadValidatorTest, ProtectedDataMapMissingKey) {
CborMessageStatus result = validator.Parse(signed_csr_payload);
EXPECT_EQ(kCborParseOk, result);
result = validator.Validate();
EXPECT_THAT(result, kCborValidateFatal);
EXPECT_THAT(result, kCborValidateError);
const std::vector<std::pair<CborMessageStatus, std::string>> msgs =
validator.GetValidateMessages();
EXPECT_EQ(1u, msgs.size());
EXPECT_EQ(kCborValidateFatal, msgs[0].first);
EXPECT_THAT(msgs[0].second, HasSubstr("ProtectedParams is empty"));
EXPECT_EQ(kCborValidateError, msgs[0].first);
EXPECT_THAT(msgs[0].second, HasSubstr("missing required field protected"));
DumpValidatorOutput(validator);
}
TEST(OEMCryptoSignedCsrPayloadValidatorTest, ProtectedDataMapKeyWarnings) {
@@ -163,7 +174,7 @@ TEST(OEMCryptoSignedCsrPayloadValidatorTest, ProtectedDataMapKeyWarnings) {
const bool unexpected_key_type_found = std::any_of(
msgs.begin(), msgs.end(),
[](const std::pair<CborMessageStatus, std::string>& p) {
return p.second.find("Unsupported key type") != std::string::npos;
return p.second.find("Invalid key type") != std::string::npos;
});
EXPECT_EQ(true, unexpected_key_type_found);
const bool unexpected_key_value_found = std::any_of(
@@ -172,13 +183,14 @@ TEST(OEMCryptoSignedCsrPayloadValidatorTest, ProtectedDataMapKeyWarnings) {
return p.second.find("Unsupported key value") != std::string::npos;
});
EXPECT_EQ(true, unexpected_key_value_found);
const bool unexpected_entry_found =
std::any_of(msgs.begin(), msgs.end(),
[](const std::pair<CborMessageStatus, std::string>& p) {
return p.second.find("ProtectedParams expects 1 entry") !=
std::string::npos;
});
const bool unexpected_entry_found = std::any_of(
msgs.begin(), msgs.end(),
[](const std::pair<CborMessageStatus, std::string>& p) {
return p.second.find("Protected data map expects 1 entry") !=
std::string::npos;
});
EXPECT_EQ(true, unexpected_entry_found);
DumpValidatorOutput(validator);
}
TEST(OEMCryptoSignedCsrPayloadValidatorTest, InvalidChallengeAndPayload) {
@@ -212,16 +224,13 @@ TEST(OEMCryptoSignedCsrPayloadValidatorTest, InvalidChallengeAndPayload) {
const bool challenge_error_found = std::any_of(
msgs.begin(), msgs.end(),
[](const std::pair<CborMessageStatus, std::string>& p) {
return p.second.find("Challenge size must be between 0 and 64 bytes") !=
std::string::npos;
return p.second.find("Unexpected challenge size") != std::string::npos;
});
EXPECT_EQ(true, challenge_error_found);
const bool csr_payload_error_found = std::any_of(
msgs.begin(), msgs.end(),
[](const std::pair<CborMessageStatus, std::string>& p) {
return p.second.find(
"CSR payload version must be must be equal to 3") !=
std::string::npos;
return p.second.find("Invalid version") != std::string::npos;
});
EXPECT_EQ(true, csr_payload_error_found);
}
@@ -250,11 +259,11 @@ TEST(OEMCryptoSignedCsrPayloadValidatorTest, KeysToSignEmptyList) {
CborMessageStatus result = validator.Parse(signed_csr_payload);
EXPECT_EQ(kCborParseOk, result);
result = validator.Validate();
EXPECT_THAT(result, kCborValidateFatal);
EXPECT_THAT(result, kCborValidateError);
const std::vector<std::pair<CborMessageStatus, std::string>> msgs =
validator.GetValidateMessages();
EXPECT_EQ(1u, msgs.size());
EXPECT_EQ(kCborValidateFatal, msgs[0].first);
EXPECT_EQ(kCborValidateError, msgs[0].first);
EXPECT_THAT(msgs[0].second, HasSubstr("Keys must be a CBOR array"));
}
@@ -275,7 +284,7 @@ TEST(OEMCryptoSignedCsrPayloadValidatorTest, SignatureMissing) {
validator.GetValidateMessages();
EXPECT_EQ(1u, msgs.size());
EXPECT_EQ(kCborValidateError, msgs[0].first);
EXPECT_THAT(msgs[0].second, HasSubstr("CoseSign1 signature is missing"));
EXPECT_THAT(msgs[0].second, HasSubstr("missing required field signature"));
}
TEST(OEMCryptoSignedCsrPayloadValidatorTest, ValidateOk) {
@@ -285,10 +294,9 @@ TEST(OEMCryptoSignedCsrPayloadValidatorTest, ValidateOk) {
result = validator.Validate();
EXPECT_THAT(result, AllOf(Ge(kCborValidateOk), Le(kCborValidateWarning)));
const std::string out = validator.GetFormattedMessage();
EXPECT_THAT(out, HasSubstr("1: ES256"));
EXPECT_THAT(out, HasSubstr("version: 3"));
EXPECT_THAT(out, HasSubstr("certificate_type: widevine"));
EXPECT_THAT(out, HasSubstr("keys_to_sign: []"));
EXPECT_THAT(out, HasSubstr("algorithm:ECDSA_SHA256"));
EXPECT_THAT(out, HasSubstr("version:3"));
EXPECT_THAT(out, HasSubstr("type:widevine"));
}
} // namespace util
} // namespace wvoec