Add signature verification to BCC validator
Each entry in BCC is signed by its parent. BCC validator should be able to validate the signature along the chain. In OPK reference, EdDSA is used. Also adding functions to support ECDSA in oemcrypto_ecc_key module. Test: opk_ta_p40 Bug: 300310163 Bug: 307968622 Change-Id: Ibed895933eeb71b18c467604588cca449cac1af9
This commit is contained in:
@@ -16,6 +16,28 @@
|
|||||||
|
|
||||||
namespace wvoec {
|
namespace wvoec {
|
||||||
namespace util {
|
namespace util {
|
||||||
|
// Enums and struct to hold EC public key info
|
||||||
|
enum BccSignatureAlgorithm {
|
||||||
|
kBccDefaultSignature = 0,
|
||||||
|
kBccEdDsa = 1,
|
||||||
|
kBccEcdsaSha256 = 2,
|
||||||
|
kBccEcdsaSha384 = 3
|
||||||
|
};
|
||||||
|
|
||||||
|
enum BccCurve {
|
||||||
|
kBccDefaultCurve = 0,
|
||||||
|
kBccEd25519 = 1,
|
||||||
|
kBccP256 = 2,
|
||||||
|
kBccP384 = 3
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BccPublicKeyInfo {
|
||||||
|
BccSignatureAlgorithm signature_algorithm;
|
||||||
|
BccCurve curve;
|
||||||
|
// Raw EC key bytes extracted from BCC
|
||||||
|
std::vector<uint8_t> key_bytes;
|
||||||
|
};
|
||||||
|
|
||||||
// BccValidator processes a Provisioning 4.0 device root of trust. It extracts
|
// BccValidator processes a Provisioning 4.0 device root of trust. It extracts
|
||||||
// and validates relevant pieces of information of BCC.
|
// and validates relevant pieces of information of BCC.
|
||||||
// Relevant documents:
|
// Relevant documents:
|
||||||
@@ -37,15 +59,20 @@ class BccValidator : public CborValidator {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
// Processes CoseKey PubKeyEd25519 / PubKeyECDSA256, prints into |fmt_msgs|,
|
// Processes CoseKey PubKeyEd25519 / PubKeyECDSA256, prints into |fmt_msgs|,
|
||||||
// and extracts the PubKey string to *|public_key_bytes|.
|
// and extracts the PubKey to *|public_key_info|.
|
||||||
CborMessageStatus ProcessSubjectPublicKeyInfo(
|
CborMessageStatus ProcessSubjectPublicKeyInfo(
|
||||||
const cppbor::Map& public_key_info_map,
|
const cppbor::Map& public_key_info_map,
|
||||||
std::vector<std::string>& fmt_msgs, std::string* public_key_bytes);
|
std::vector<std::string>& fmt_msgs, BccPublicKeyInfo* public_key_info);
|
||||||
// Processes DiceChainEntryPayload, which contains subject public key, prints
|
// Processes DiceChainEntryPayload, which contains subject public key, prints
|
||||||
// into |fmt_msgs|, and extracts the PubKey string to *|public_key_bytes|.
|
// into |fmt_msgs|, and extracts the PubKey to *|public_key_info|.
|
||||||
CborMessageStatus ProcessDiceChainEntryPayload(
|
CborMessageStatus ProcessDiceChainEntryPayload(
|
||||||
const std::vector<uint8_t>& payload, std::vector<std::string>& fmt_msgs,
|
const std::vector<uint8_t>& payload, std::vector<std::string>& fmt_msgs,
|
||||||
std::string* public_key_bytes);
|
BccPublicKeyInfo* public_key_info);
|
||||||
|
// 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,
|
||||||
|
const std::vector<uint8_t>& message,
|
||||||
|
const std::vector<uint8_t>& signature);
|
||||||
// Used to generate formatted message.
|
// Used to generate formatted message.
|
||||||
std::stringstream msg_ss_;
|
std::stringstream msg_ss_;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -60,6 +60,13 @@ class EccPublicKey {
|
|||||||
size_t length);
|
size_t length);
|
||||||
static std::unique_ptr<EccPublicKey> Load(const std::string& buffer);
|
static std::unique_ptr<EccPublicKey> Load(const std::string& buffer);
|
||||||
static std::unique_ptr<EccPublicKey> Load(const std::vector<uint8_t>& buffer);
|
static std::unique_ptr<EccPublicKey> Load(const std::vector<uint8_t>& buffer);
|
||||||
|
// Loads EC public key from the |curve| and |buffer|.
|
||||||
|
// The provided |buffer| must contain an EC point serialized from raw X9.62
|
||||||
|
// format. For uncompressed form, it is a 1-byte prefix plus two 32-byte
|
||||||
|
// integers representing X, Y coordinates.
|
||||||
|
static std::unique_ptr<EccPublicKey> LoadKeyPoint(EccCurve curve,
|
||||||
|
const uint8_t* buffer,
|
||||||
|
size_t length);
|
||||||
|
|
||||||
// Loads a serialized ECC private key, but only converting the public key.
|
// Loads a serialized ECC private key, but only converting the public key.
|
||||||
static std::unique_ptr<EccPublicKey> LoadPrivateKeyInfo(const uint8_t* buffer,
|
static std::unique_ptr<EccPublicKey> LoadPrivateKeyInfo(const uint8_t* buffer,
|
||||||
@@ -107,6 +114,15 @@ class EccPublicKey {
|
|||||||
const std::string& signature) const;
|
const std::string& signature) const;
|
||||||
OEMCryptoResult VerifySignature(const std::vector<uint8_t>& message,
|
OEMCryptoResult VerifySignature(const std::vector<uint8_t>& message,
|
||||||
const std::vector<uint8_t>& signature) const;
|
const std::vector<uint8_t>& signature) const;
|
||||||
|
// Verifies the raw |signature| matches the provided |message| by the
|
||||||
|
// private equivalent of this public key.
|
||||||
|
// A raw ECDSA signature consists of a pair of integers (r,s). The |signature|
|
||||||
|
// is a concatenation of two octet strings resulting from the integer-to-octet
|
||||||
|
// encoding of the values of r and s, in the order of (r||s).
|
||||||
|
OEMCryptoResult VerifyRawSignature(const uint8_t* message,
|
||||||
|
size_t message_length,
|
||||||
|
const uint8_t* signature,
|
||||||
|
size_t signature_length) const;
|
||||||
|
|
||||||
~EccPublicKey();
|
~EccPublicKey();
|
||||||
|
|
||||||
@@ -125,6 +141,13 @@ class EccPublicKey {
|
|||||||
bool InitFromPrivateKeyInfo(const uint8_t* buffer, size_t length);
|
bool InitFromPrivateKeyInfo(const uint8_t* buffer, size_t length);
|
||||||
// Initializes the public key object from a private.
|
// Initializes the public key object from a private.
|
||||||
bool InitFromPrivateKey(const EccPrivateKey& private_key);
|
bool InitFromPrivateKey(const EccPrivateKey& private_key);
|
||||||
|
// Initializes the public key object from the provided curve and key point
|
||||||
|
// |buffer|.
|
||||||
|
bool InitFromKeyPoint(EccCurve curve, const uint8_t* buffer, size_t length);
|
||||||
|
// Digests the |message| and verifies signature against the provided signature
|
||||||
|
// point.
|
||||||
|
OEMCryptoResult DigestAndVerify(const uint8_t* message, size_t message_length,
|
||||||
|
const ECDSA_SIG* sig_point) const;
|
||||||
|
|
||||||
// OpenSSL/BoringSSL implementation of an ECC key.
|
// OpenSSL/BoringSSL implementation of an ECC key.
|
||||||
// As a public key, this will only have key point initialized.
|
// As a public key, this will only have key point initialized.
|
||||||
|
|||||||
@@ -14,6 +14,9 @@
|
|||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include <openssl/evp.h>
|
||||||
|
|
||||||
|
#include "oemcrypto_ecc_key.h"
|
||||||
#include "string_conversions.h"
|
#include "string_conversions.h"
|
||||||
|
|
||||||
namespace wvoec {
|
namespace wvoec {
|
||||||
@@ -69,8 +72,8 @@ constexpr int kP256KeyComponentSize = 256 / 8;
|
|||||||
// The key is formatted in an Z/X/Y format in which Z == 0x04 and X and Y are
|
// The key is formatted in an Z/X/Y format in which Z == 0x04 and X and Y are
|
||||||
// the public key coordinates. X and Y are each 48 bytes.
|
// the public key coordinates. X and Y are each 48 bytes.
|
||||||
constexpr int kP384KeyComponentSize = 384 / 8;
|
constexpr int kP384KeyComponentSize = 384 / 8;
|
||||||
|
constexpr int kMarshaledP256KeySize = kP256KeyComponentSize * 2 + 1;
|
||||||
constexpr int kMarshaledP384KeySize = kP384KeyComponentSize * 2 + 1;
|
constexpr int kMarshaledP384KeySize = kP384KeyComponentSize * 2 + 1;
|
||||||
constexpr int kMaxMarshaledECKeySize = kMarshaledP384KeySize;
|
|
||||||
constexpr char kMarshaledECKeyZValue = 0x04;
|
constexpr char kMarshaledECKeyZValue = 0x04;
|
||||||
constexpr int kED25519KeyDataItemSize = 32;
|
constexpr int kED25519KeyDataItemSize = 32;
|
||||||
// The Issuer field key in BccEntryPayload.
|
// The Issuer field key in BccEntryPayload.
|
||||||
@@ -79,6 +82,8 @@ constexpr int64_t kIssuer = 1;
|
|||||||
constexpr int64_t kSubject = 2;
|
constexpr int64_t kSubject = 2;
|
||||||
// The SubjectPublicKey field key in BccEntryPayload.
|
// The SubjectPublicKey field key in BccEntryPayload.
|
||||||
constexpr int64_t kSubjectPublicKey = -4670552;
|
constexpr int64_t kSubjectPublicKey = -4670552;
|
||||||
|
// This signature context is defined by COSE SIGN1.
|
||||||
|
constexpr char kSignatureContextString[] = "Signature1";
|
||||||
|
|
||||||
struct IssuerSubject {
|
struct IssuerSubject {
|
||||||
std::string issuer;
|
std::string issuer;
|
||||||
@@ -135,6 +140,60 @@ void AddMessages(std::stringstream& ss,
|
|||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
bool BccValidator::VerifySignature(const BccPublicKeyInfo& signing_key,
|
||||||
|
const std::vector<uint8_t>& message,
|
||||||
|
const std::vector<uint8_t>& signature) {
|
||||||
|
if (signing_key.signature_algorithm == kBccEdDsa) {
|
||||||
|
constexpr size_t kEd25519SignatureLength = 64;
|
||||||
|
// ED25519 incorporates SHA512 into the signing algorithm.
|
||||||
|
if (signature.size() != kEd25519SignatureLength) {
|
||||||
|
AddValidationMessage(
|
||||||
|
kCborValidateError,
|
||||||
|
"Signature has unexpected size: " + std::to_string(signature.size()));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
EVP_PKEY* pkey = nullptr;
|
||||||
|
if ((pkey = EVP_PKEY_new_raw_public_key(
|
||||||
|
EVP_PKEY_ED25519, nullptr,
|
||||||
|
reinterpret_cast<const uint8_t*>(signing_key.key_bytes.data()),
|
||||||
|
signing_key.key_bytes.size())) == nullptr) {
|
||||||
|
AddValidationMessage(
|
||||||
|
kCborValidateError,
|
||||||
|
"Can not create EVP_PKEY_ED25519 from the public key info.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
EVP_MD_CTX* md_ctx = EVP_MD_CTX_new();
|
||||||
|
const bool res =
|
||||||
|
EVP_DigestVerifyInit(md_ctx, nullptr, nullptr, nullptr, pkey) &&
|
||||||
|
EVP_DigestVerify(md_ctx, signature.data(), signature.size(),
|
||||||
|
message.data(), message.size()) == 1;
|
||||||
|
EVP_MD_CTX_free(md_ctx);
|
||||||
|
EVP_PKEY_free(pkey);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
if (signing_key.signature_algorithm == kBccEcdsaSha256 ||
|
||||||
|
signing_key.signature_algorithm == kBccEcdsaSha384) {
|
||||||
|
const EccCurve curve = (signing_key.signature_algorithm == kBccEcdsaSha256)
|
||||||
|
? EccCurve::kEccSecp256r1
|
||||||
|
: EccCurve::kEccSecp384r1;
|
||||||
|
std::unique_ptr<EccPublicKey> key = EccPublicKey::LoadKeyPoint(
|
||||||
|
curve, reinterpret_cast<const uint8_t*>(signing_key.key_bytes.data()),
|
||||||
|
signing_key.key_bytes.size());
|
||||||
|
if (!key) {
|
||||||
|
AddValidationMessage(kCborValidateError,
|
||||||
|
"Can not create ECPublicKey from raw EC KeyPoint.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const OEMCryptoResult res = key->VerifyRawSignature(
|
||||||
|
message.data(), message.size(), signature.data(), signature.size());
|
||||||
|
return (res == OEMCrypto_SUCCESS);
|
||||||
|
}
|
||||||
|
AddValidationMessage(kCborValidateError,
|
||||||
|
"Unknown signature algorithm: " +
|
||||||
|
std::to_string(signing_key.signature_algorithm));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
CborMessageStatus BccValidator::Validate() {
|
CborMessageStatus BccValidator::Validate() {
|
||||||
if (message_status_ != kCborParseOk) return message_status_;
|
if (message_status_ != kCborParseOk) return message_status_;
|
||||||
const cppbor::Item* parsed_bcc = std::get<0>(parse_result()).get();
|
const cppbor::Item* parsed_bcc = std::get<0>(parse_result()).get();
|
||||||
@@ -164,24 +223,26 @@ CborMessageStatus BccValidator::Validate() {
|
|||||||
|
|
||||||
// The first element in the array contains the root device public key
|
// The first element in the array contains the root device public key
|
||||||
// definition.
|
// definition.
|
||||||
const cppbor::Map* device_public_key_info = (*bcc_array)[0]->asMap();
|
const cppbor::Map* device_public_key_map = (*bcc_array)[0]->asMap();
|
||||||
if (device_public_key_info == nullptr) {
|
if (device_public_key_map == nullptr) {
|
||||||
AddValidationMessage(
|
AddValidationMessage(
|
||||||
kCborValidateFatal,
|
kCborValidateFatal,
|
||||||
"Device public key info is not a CBOR map. Actual type: " +
|
"Device public key info is not a CBOR map. Actual type: " +
|
||||||
CppborMajorTypeToString((*bcc_array)[0]->type()));
|
CppborMajorTypeToString((*bcc_array)[0]->type()));
|
||||||
return message_status_;
|
return message_status_;
|
||||||
}
|
}
|
||||||
std::vector<std::string> key_value_texts;
|
BccPublicKeyInfo root_pub_key;
|
||||||
std::string device_public_key_bytes;
|
std::vector<std::string> key_value_texts; // for pretty print
|
||||||
CborMessageStatus status = ProcessSubjectPublicKeyInfo(
|
CborMessageStatus status = ProcessSubjectPublicKeyInfo(
|
||||||
*device_public_key_info, key_value_texts, &device_public_key_bytes);
|
*device_public_key_map, key_value_texts, &root_pub_key);
|
||||||
AddMessages(msg_ss_, key_value_texts, 1);
|
AddMessages(msg_ss_, key_value_texts, 1);
|
||||||
if (status == kCborValidateFatal) return status;
|
if (status == kCborValidateFatal) return status;
|
||||||
|
|
||||||
|
BccPublicKeyInfo leaf_pub_key = root_pub_key;
|
||||||
msg_ss_ << "BCC ENTRY:\n";
|
msg_ss_ << "BCC ENTRY:\n";
|
||||||
// Parse each certificate in the chain. The structure of thr entries are
|
// Parse and verify each certificate in the chain. The structure of thr
|
||||||
// COSE_Sign1 (untagged).
|
// entries are COSE_Sign1 (untagged). leaf_pub_key is being updated while we
|
||||||
|
// process the chain.
|
||||||
for (size_t i = 1; i < bcc_array->size(); ++i) {
|
for (size_t i = 1; i < bcc_array->size(); ++i) {
|
||||||
msg_ss_ << "- CDI PUBLIC KEY INDEX: " << i << "\n";
|
msg_ss_ << "- CDI PUBLIC KEY INDEX: " << i << "\n";
|
||||||
const cppbor::Array* bcc_entry = (*bcc_array)[i]->asArray();
|
const cppbor::Array* bcc_entry = (*bcc_array)[i]->asArray();
|
||||||
@@ -205,25 +266,48 @@ CborMessageStatus BccValidator::Validate() {
|
|||||||
return message_status_;
|
return message_status_;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::vector<uint8_t>& key_payload =
|
// Signature verification Step 1: construct and encode signature input
|
||||||
(*bcc_entry)[2]->asBstr()->value();
|
const std::vector<uint8_t>& protected_bytes =
|
||||||
|
(*bcc_entry)[0]->asBstr()->value();
|
||||||
|
// Index 1 is unprotected parameters, which is ignored.
|
||||||
|
const std::vector<uint8_t>& payload = (*bcc_entry)[2]->asBstr()->value();
|
||||||
|
const std::vector<uint8_t>& actual_signature =
|
||||||
|
(*bcc_entry)[3]->asBstr()->value();
|
||||||
|
|
||||||
|
const std::vector<uint8_t> signature_input =
|
||||||
|
cppbor::Array()
|
||||||
|
.add(kSignatureContextString)
|
||||||
|
.add(protected_bytes)
|
||||||
|
.add(/* AAD */ std::vector<uint8_t>())
|
||||||
|
.add(payload)
|
||||||
|
.encode();
|
||||||
|
|
||||||
|
// Signature verification Step 2: verify
|
||||||
|
if (!VerifySignature(leaf_pub_key, signature_input, actual_signature)) {
|
||||||
|
AddValidationMessage(
|
||||||
|
kCborValidateError,
|
||||||
|
"Failed to verify the signature for BCC entry index: " +
|
||||||
|
std::to_string(i));
|
||||||
|
}
|
||||||
|
|
||||||
key_value_texts.clear();
|
key_value_texts.clear();
|
||||||
std::string entry_public_key_bytes;
|
BccPublicKeyInfo entry_pub_key;
|
||||||
status = ProcessDiceChainEntryPayload(key_payload, key_value_texts,
|
status =
|
||||||
&entry_public_key_bytes);
|
ProcessDiceChainEntryPayload(payload, key_value_texts, &entry_pub_key);
|
||||||
AddMessages(msg_ss_, key_value_texts, 1);
|
AddMessages(msg_ss_, key_value_texts, 1);
|
||||||
if (status == kCborValidateFatal) return status;
|
if (status == kCborValidateFatal) return status;
|
||||||
// If the size of the BCC array (including device pub key) is 2, then it
|
leaf_pub_key = std::move(entry_pub_key);
|
||||||
// must be a de-generated BCC, which means the second element in the array
|
}
|
||||||
// is a self-signed entry. The entry's public key should be identical to the
|
// If the size of the BCC array (including device pub key) is 2, then it
|
||||||
// device's public key.
|
// must be a de-generated BCC, which means the second element in the array
|
||||||
if (bcc_array->size() == 2 && i == 1) {
|
// is a self-signed entry. The entry's public key should be identical to the
|
||||||
// self-signed BCC entry
|
// device's public key.
|
||||||
if (entry_public_key_bytes != device_public_key_bytes) {
|
if (bcc_array->size() == 2) {
|
||||||
AddValidationMessage(kCborValidateError,
|
// self-signed BCC entry
|
||||||
"The public key of a self-signed entry should be "
|
if (leaf_pub_key.key_bytes != root_pub_key.key_bytes) {
|
||||||
"identical to its device public key.");
|
AddValidationMessage(kCborValidateError,
|
||||||
}
|
"The public key of a self-signed entry should be "
|
||||||
|
"identical to its device public key.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
msg_ss_ << "...\n";
|
msg_ss_ << "...\n";
|
||||||
@@ -233,10 +317,10 @@ CborMessageStatus BccValidator::Validate() {
|
|||||||
|
|
||||||
CborMessageStatus BccValidator::ProcessSubjectPublicKeyInfo(
|
CborMessageStatus BccValidator::ProcessSubjectPublicKeyInfo(
|
||||||
const cppbor::Map& public_key_info_map, std::vector<std::string>& fmt_msgs,
|
const cppbor::Map& public_key_info_map, std::vector<std::string>& fmt_msgs,
|
||||||
std::string* public_key_bytes) {
|
BccPublicKeyInfo* public_key_info) {
|
||||||
int key_encoding_format = DEVICE_KEY_ENCODING_UNKNOWN;
|
int key_encoding_format = DEVICE_KEY_ENCODING_UNKNOWN;
|
||||||
std::string device_key_bytes_0;
|
std::vector<uint8_t> device_key_bytes_0;
|
||||||
std::string device_key_bytes_1;
|
std::vector<uint8_t> device_key_bytes_1;
|
||||||
std::unordered_set<int64_t> key_set;
|
std::unordered_set<int64_t> key_set;
|
||||||
for (size_t index = 0; index < public_key_info_map.size(); ++index) {
|
for (size_t index = 0; index < public_key_info_map.size(); ++index) {
|
||||||
std::pair<const std::unique_ptr<cppbor::Item>&,
|
std::pair<const std::unique_ptr<cppbor::Item>&,
|
||||||
@@ -289,10 +373,13 @@ CborMessageStatus BccValidator::ProcessSubjectPublicKeyInfo(
|
|||||||
const int64_t value = entry.second->asNint()->value();
|
const int64_t value = entry.second->asNint()->value();
|
||||||
if (value == DEVICE_KEY_ALGORITHM_ES256) {
|
if (value == DEVICE_KEY_ALGORITHM_ES256) {
|
||||||
kv += "ECDSA_SHA256";
|
kv += "ECDSA_SHA256";
|
||||||
|
public_key_info->signature_algorithm = kBccEcdsaSha256;
|
||||||
} else if (value == DEVICE_KEY_ALGORITHM_ES384) {
|
} else if (value == DEVICE_KEY_ALGORITHM_ES384) {
|
||||||
kv += "ECDSA_SHA384";
|
kv += "ECDSA_SHA384";
|
||||||
|
public_key_info->signature_algorithm = kBccEcdsaSha384;
|
||||||
} else if (value == DEVICE_KEY_ALGORITHM_EDDSA) {
|
} else if (value == DEVICE_KEY_ALGORITHM_EDDSA) {
|
||||||
kv += "EDDSA";
|
kv += "EDDSA";
|
||||||
|
public_key_info->signature_algorithm = kBccEdDsa;
|
||||||
} else {
|
} else {
|
||||||
AddValidationMessage(kCborValidateError,
|
AddValidationMessage(kCborValidateError,
|
||||||
"Invalid value in public key info map for key "
|
"Invalid value in public key info map for key "
|
||||||
@@ -316,10 +403,13 @@ CborMessageStatus BccValidator::ProcessSubjectPublicKeyInfo(
|
|||||||
std::string kv = "curve: ";
|
std::string kv = "curve: ";
|
||||||
const int64_t value = entry.second->asUint()->value();
|
const int64_t value = entry.second->asUint()->value();
|
||||||
if (value == DEVICE_KEY_CURVE_P256) {
|
if (value == DEVICE_KEY_CURVE_P256) {
|
||||||
|
public_key_info->curve = kBccP256;
|
||||||
kv += "P256";
|
kv += "P256";
|
||||||
} else if (value == DEVICE_KEY_CURVE_P384) {
|
} else if (value == DEVICE_KEY_CURVE_P384) {
|
||||||
|
public_key_info->curve = kBccP384;
|
||||||
kv += "P384";
|
kv += "P384";
|
||||||
} else if (value == DEVICE_KEY_CURVE_ED25519) {
|
} else if (value == DEVICE_KEY_CURVE_ED25519) {
|
||||||
|
public_key_info->curve = kBccEd25519;
|
||||||
kv += "ED25519";
|
kv += "ED25519";
|
||||||
} else {
|
} else {
|
||||||
AddValidationMessage(kCborValidateError,
|
AddValidationMessage(kCborValidateError,
|
||||||
@@ -351,10 +441,11 @@ CborMessageStatus BccValidator::ProcessSubjectPublicKeyInfo(
|
|||||||
std::to_string(key_bytes.size()));
|
std::to_string(key_bytes.size()));
|
||||||
return kCborValidateFatal;
|
return kCborValidateFatal;
|
||||||
}
|
}
|
||||||
std::string& key_bytes_str = map_key == MAP_KEY_DEVICE_KEY_BYTES_0
|
if (map_key == MAP_KEY_DEVICE_KEY_BYTES_0) {
|
||||||
? device_key_bytes_0
|
device_key_bytes_0 = key_bytes;
|
||||||
: device_key_bytes_1;
|
} else {
|
||||||
key_bytes_str.assign(key_bytes.begin(), key_bytes.end());
|
device_key_bytes_1 = key_bytes;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
key_set.insert(map_key);
|
key_set.insert(map_key);
|
||||||
}
|
}
|
||||||
@@ -378,28 +469,34 @@ CborMessageStatus BccValidator::ProcessSubjectPublicKeyInfo(
|
|||||||
"Malformed public key definition. Missing device public key bytes.");
|
"Malformed public key definition. Missing device public key bytes.");
|
||||||
return kCborValidateFatal;
|
return kCborValidateFatal;
|
||||||
}
|
}
|
||||||
std::string device_key_bytes;
|
std::vector<uint8_t> device_key_bytes;
|
||||||
if (key_encoding_format == DEVICE_KEY_OCTET_PAIR) {
|
if (key_encoding_format == DEVICE_KEY_OCTET_PAIR) {
|
||||||
// Key is an ECDSA elliptic key. We need to return the ANSI X9.62
|
// Key is an ECDSA elliptic key. We need to return the ANSI X9.62
|
||||||
// marshaled public key. Generate the marshaled key if needed. The
|
// marshaled public key. Generate the marshaled key if needed. The
|
||||||
// marshaled key is needed to create an ECPublicKey object.
|
// marshaled key is needed to create an ECPublicKey object.
|
||||||
// std::string* device_key_bytes = public_key_info.mutable_key_bytes();
|
device_key_bytes.push_back(kMarshaledECKeyZValue);
|
||||||
device_key_bytes.reserve(kMaxMarshaledECKeySize);
|
device_key_bytes.insert(device_key_bytes.end(), device_key_bytes_0.begin(),
|
||||||
device_key_bytes.resize(1);
|
device_key_bytes_0.end());
|
||||||
device_key_bytes[0] = kMarshaledECKeyZValue;
|
device_key_bytes.insert(device_key_bytes.end(), device_key_bytes_1.begin(),
|
||||||
device_key_bytes.append(device_key_bytes_0);
|
device_key_bytes_1.end());
|
||||||
device_key_bytes.append(device_key_bytes_1);
|
if (device_key_bytes.size() != kMarshaledP384KeySize &&
|
||||||
|
device_key_bytes.size() != kMarshaledP256KeySize) {
|
||||||
|
AddValidationMessage(kCborValidateFatal,
|
||||||
|
"Invalid ECDSA public key size: " +
|
||||||
|
std::to_string(device_key_bytes.size()));
|
||||||
|
return kCborValidateFatal;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
device_key_bytes = device_key_bytes_0;
|
device_key_bytes = std::move(device_key_bytes_0);
|
||||||
}
|
}
|
||||||
fmt_msgs.push_back("public key bytes: " + wvutil::b2a_hex(device_key_bytes));
|
fmt_msgs.push_back("public key bytes: " + wvutil::b2a_hex(device_key_bytes));
|
||||||
*public_key_bytes = device_key_bytes;
|
public_key_info->key_bytes = std::move(device_key_bytes);
|
||||||
return message_status_;
|
return message_status_;
|
||||||
}
|
}
|
||||||
|
|
||||||
CborMessageStatus BccValidator::ProcessDiceChainEntryPayload(
|
CborMessageStatus BccValidator::ProcessDiceChainEntryPayload(
|
||||||
const std::vector<uint8_t>& payload, std::vector<std::string>& fmt_msgs,
|
const std::vector<uint8_t>& payload, std::vector<std::string>& fmt_msgs,
|
||||||
std::string* entry_public_key_bytes) {
|
BccPublicKeyInfo* entry_public_key_info) {
|
||||||
if (payload.empty()) {
|
if (payload.empty()) {
|
||||||
AddValidationMessage(kCborValidateFatal, "Empty bcc entry payload.");
|
AddValidationMessage(kCborValidateFatal, "Empty bcc entry payload.");
|
||||||
return kCborValidateFatal;
|
return kCborValidateFatal;
|
||||||
@@ -449,7 +546,7 @@ CborMessageStatus BccValidator::ProcessDiceChainEntryPayload(
|
|||||||
return kCborValidateFatal;
|
return kCborValidateFatal;
|
||||||
}
|
}
|
||||||
return ProcessSubjectPublicKeyInfo(*subject_public_key_info, fmt_msgs,
|
return ProcessSubjectPublicKeyInfo(*subject_public_key_info, fmt_msgs,
|
||||||
entry_public_key_bytes);
|
entry_public_key_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string BccValidator::GetFormattedMessage() const {
|
std::string BccValidator::GetFormattedMessage() const {
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ using ScopedEvpPkey = ScopedObject<EVP_PKEY, EVP_PKEY_free>;
|
|||||||
using ScopedPrivateKeyInfo =
|
using ScopedPrivateKeyInfo =
|
||||||
ScopedObject<PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO_free>;
|
ScopedObject<PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO_free>;
|
||||||
using ScopedSigPoint = ScopedObject<ECDSA_SIG, ECDSA_SIG_free>;
|
using ScopedSigPoint = ScopedObject<ECDSA_SIG, ECDSA_SIG_free>;
|
||||||
|
using ScopedEcPoint = ScopedObject<EC_POINT, EC_POINT_free>;
|
||||||
|
|
||||||
const EC_GROUP* GetEcGroup(EccCurve curve) {
|
const EC_GROUP* GetEcGroup(EccCurve curve) {
|
||||||
// Creating a named EC_GROUP is an expensive operation, and they
|
// Creating a named EC_GROUP is an expensive operation, and they
|
||||||
@@ -140,6 +141,45 @@ EccCurve GetCurveFromKeyGroup(const EC_KEY* key) {
|
|||||||
return kEccCurveUnknown;
|
return kEccCurveUnknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Creates EC public key from |curve| and |key_point|, and sets the result in
|
||||||
|
// *|public_key|.
|
||||||
|
bool GetPublicKeyFromKeyPoint(EccCurve curve, const uint8_t* key_point,
|
||||||
|
size_t key_point_length, EC_KEY** public_key) {
|
||||||
|
if (key_point == nullptr || key_point_length == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const EC_GROUP* group = GetEcGroup(curve);
|
||||||
|
if (!group) {
|
||||||
|
LOGE("Failed to get ECC group for curve %d", curve);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ScopedEcPoint point(EC_POINT_new(group));
|
||||||
|
if (!point) {
|
||||||
|
LOGE("Failed to new EC_POINT");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!EC_POINT_oct2point(group, point.get(), key_point, key_point_length,
|
||||||
|
nullptr)) {
|
||||||
|
LOGE("Failed to convert the serialized point to EC_POINT");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ScopedEcKey key(EC_KEY_new());
|
||||||
|
if (!key) {
|
||||||
|
LOGE("Failed to allocate key");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!EC_KEY_set_group(key.get(), group)) {
|
||||||
|
LOGE("Failed to set group");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (EC_KEY_set_public_key(key.get(), point.get()) == 0) {
|
||||||
|
LOGE("Failed to convert the EC_POINT to EC_KEY");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*public_key = key.release();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// Compares the public EC points of both keys to see if they are the
|
// Compares the public EC points of both keys to see if they are the
|
||||||
// equal.
|
// equal.
|
||||||
// Both |public_key| and |private_key| must be of the same group.
|
// Both |public_key| and |private_key| must be of the same group.
|
||||||
@@ -457,6 +497,26 @@ std::unique_ptr<EccPublicKey> EccPublicKey::LoadPrivateKeyInfo(
|
|||||||
return LoadPrivateKeyInfo(buffer.data(), buffer.size());
|
return LoadPrivateKeyInfo(buffer.data(), buffer.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
std::unique_ptr<EccPublicKey> EccPublicKey::LoadKeyPoint(EccCurve curve,
|
||||||
|
const uint8_t* buffer,
|
||||||
|
size_t length) {
|
||||||
|
if (buffer == nullptr) {
|
||||||
|
LOGE("Provided key point buffer is null");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
if (length == 0) {
|
||||||
|
LOGE("Provided key point buffer is zero length");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
std::unique_ptr<EccPublicKey> key(new EccPublicKey());
|
||||||
|
if (!key->InitFromKeyPoint(curve, buffer, length)) {
|
||||||
|
LOGE("Failed to initialize public key from KeyPoint");
|
||||||
|
key.reset();
|
||||||
|
}
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
bool EccPublicKey::IsMatchingPrivateKey(
|
bool EccPublicKey::IsMatchingPrivateKey(
|
||||||
const EccPrivateKey& private_key) const {
|
const EccPrivateKey& private_key) const {
|
||||||
if (private_key.curve() != curve_) {
|
if (private_key.curve() != curve_) {
|
||||||
@@ -486,7 +546,7 @@ OEMCryptoResult EccPublicKey::VerifySignature(const uint8_t* message,
|
|||||||
LOGE("Bad message data");
|
LOGE("Bad message data");
|
||||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||||
}
|
}
|
||||||
// Step 1: Parse signature.
|
// Parse signature.
|
||||||
const uint8_t* tp = signature;
|
const uint8_t* tp = signature;
|
||||||
ScopedSigPoint sig_point(d2i_ECDSA_SIG(nullptr, &tp, signature_length));
|
ScopedSigPoint sig_point(d2i_ECDSA_SIG(nullptr, &tp, signature_length));
|
||||||
if (!sig_point) {
|
if (!sig_point) {
|
||||||
@@ -494,24 +554,7 @@ OEMCryptoResult EccPublicKey::VerifySignature(const uint8_t* message,
|
|||||||
// Most likely an invalid signature than an OpenSSL error.
|
// Most likely an invalid signature than an OpenSSL error.
|
||||||
return OEMCrypto_ERROR_SIGNATURE_FAILURE;
|
return OEMCrypto_ERROR_SIGNATURE_FAILURE;
|
||||||
}
|
}
|
||||||
// Step 2: Hash message
|
return DigestAndVerify(message, message_length, sig_point.get());
|
||||||
std::vector<uint8_t> digest;
|
|
||||||
if (!DigestMessage(curve_, message, message_length, &digest)) {
|
|
||||||
LOGE("Failed to digest message");
|
|
||||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
|
||||||
}
|
|
||||||
// Step 3: Verify signature
|
|
||||||
const int res = ECDSA_do_verify(
|
|
||||||
digest.data(), static_cast<int>(digest.size()), sig_point.get(), key_);
|
|
||||||
if (res == -1) {
|
|
||||||
LOGE("Error occurred checking signature");
|
|
||||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
|
||||||
}
|
|
||||||
if (res == 0) {
|
|
||||||
LOGD("Signature did not match");
|
|
||||||
return OEMCrypto_ERROR_SIGNATURE_FAILURE;
|
|
||||||
}
|
|
||||||
return OEMCrypto_SUCCESS;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
OEMCryptoResult EccPublicKey::VerifySignature(
|
OEMCryptoResult EccPublicKey::VerifySignature(
|
||||||
@@ -536,6 +579,37 @@ OEMCryptoResult EccPublicKey::VerifySignature(
|
|||||||
signature.size());
|
signature.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OEMCryptoResult EccPublicKey::VerifyRawSignature(
|
||||||
|
const uint8_t* message, size_t message_length, const uint8_t* signature,
|
||||||
|
size_t signature_length) const {
|
||||||
|
if (signature == nullptr || signature_length == 0) {
|
||||||
|
LOGE("Signature is missing");
|
||||||
|
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||||
|
}
|
||||||
|
if (message == nullptr && message_length > 0) {
|
||||||
|
LOGE("Bad message data");
|
||||||
|
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||||
|
}
|
||||||
|
if (signature_length % 2 == 1) {
|
||||||
|
LOGE("Bad signature size: %zu", signature_length);
|
||||||
|
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||||
|
}
|
||||||
|
// Parse signature.
|
||||||
|
ScopedSigPoint sig_point(ECDSA_SIG_new());
|
||||||
|
if (!sig_point) {
|
||||||
|
LOGE("Error occurred in ECDSA_SIG_new()");
|
||||||
|
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||||
|
}
|
||||||
|
const int r_s_size = static_cast<int>(signature_length) / 2;
|
||||||
|
if (!ECDSA_SIG_set0(sig_point.get(), BN_bin2bn(signature, r_s_size, nullptr),
|
||||||
|
BN_bin2bn(signature + r_s_size, r_s_size, nullptr))) {
|
||||||
|
LOGE("Failed to parse signature");
|
||||||
|
// Most likely an invalid signature than an OpenSSL error.
|
||||||
|
return OEMCrypto_ERROR_SIGNATURE_FAILURE;
|
||||||
|
}
|
||||||
|
return DigestAndVerify(message, message_length, sig_point.get());
|
||||||
|
}
|
||||||
|
|
||||||
EccPublicKey::~EccPublicKey() {
|
EccPublicKey::~EccPublicKey() {
|
||||||
if (key_ != nullptr) {
|
if (key_ != nullptr) {
|
||||||
EC_KEY_free(key_);
|
EC_KEY_free(key_);
|
||||||
@@ -608,6 +682,57 @@ bool EccPublicKey::InitFromPrivateKey(const EccPrivateKey& private_key) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool EccPublicKey::InitFromKeyPoint(EccCurve curve, const uint8_t* buffer,
|
||||||
|
size_t length) {
|
||||||
|
if (buffer == nullptr || length == 0) {
|
||||||
|
LOGE("Provided key point buffer is empty");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
EC_KEY* ec_key;
|
||||||
|
if (!GetPublicKeyFromKeyPoint(curve, buffer, length, &ec_key)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ScopedEcKey key(ec_key);
|
||||||
|
if (EC_KEY_check_key(key.get()) != 1) {
|
||||||
|
LOGE("Invalid public EC key");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Required flags for IETF compliance.
|
||||||
|
EC_KEY_set_asn1_flag(key.get(), OPENSSL_EC_NAMED_CURVE);
|
||||||
|
EC_KEY_set_conv_form(key.get(), POINT_CONVERSION_UNCOMPRESSED);
|
||||||
|
key_ = key.release();
|
||||||
|
curve_ = curve;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
OEMCryptoResult EccPublicKey::DigestAndVerify(
|
||||||
|
const uint8_t* message, size_t message_length,
|
||||||
|
const ECDSA_SIG* sig_point) const {
|
||||||
|
if (message == nullptr && message_length > 0) {
|
||||||
|
LOGE("Bad message data");
|
||||||
|
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||||
|
}
|
||||||
|
// Hash message.
|
||||||
|
std::vector<uint8_t> digest;
|
||||||
|
if (!DigestMessage(curve_, message, message_length, &digest)) {
|
||||||
|
LOGE("Failed to digest message");
|
||||||
|
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||||
|
}
|
||||||
|
// Verify signature.
|
||||||
|
const int res = ECDSA_do_verify(
|
||||||
|
digest.data(), static_cast<int>(digest.size()), sig_point, key_);
|
||||||
|
if (res == -1) {
|
||||||
|
LOGE("Error occurred checking signature");
|
||||||
|
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||||
|
}
|
||||||
|
if (res == 0) {
|
||||||
|
LOGD("Signature did not match");
|
||||||
|
return OEMCrypto_ERROR_SIGNATURE_FAILURE;
|
||||||
|
}
|
||||||
|
return OEMCrypto_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
std::unique_ptr<EccPrivateKey> EccPrivateKey::New(EccCurve curve) {
|
std::unique_ptr<EccPrivateKey> EccPrivateKey::New(EccCurve curve) {
|
||||||
std::unique_ptr<EccPrivateKey> key(new EccPrivateKey());
|
std::unique_ptr<EccPrivateKey> key(new EccPrivateKey());
|
||||||
|
|||||||
Reference in New Issue
Block a user