Source release 19.4.0
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
96
oemcrypto/util/include/prov4_validation_helper.h
Normal file
96
oemcrypto/util/include/prov4_validation_helper.h
Normal 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_
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
25
oemcrypto/util/src/prov4_validation_helper.cpp
Normal file
25
oemcrypto/util/src/prov4_validation_helper.cpp
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user