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 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
|
||||
// and validates relevant pieces of information of BCC.
|
||||
// Relevant documents:
|
||||
@@ -37,15 +59,20 @@ class BccValidator : public CborValidator {
|
||||
|
||||
private:
|
||||
// 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(
|
||||
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
|
||||
// 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(
|
||||
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.
|
||||
std::stringstream msg_ss_;
|
||||
};
|
||||
|
||||
@@ -60,6 +60,13 @@ class EccPublicKey {
|
||||
size_t length);
|
||||
static std::unique_ptr<EccPublicKey> Load(const std::string& 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.
|
||||
static std::unique_ptr<EccPublicKey> LoadPrivateKeyInfo(const uint8_t* buffer,
|
||||
@@ -107,6 +114,15 @@ class EccPublicKey {
|
||||
const std::string& signature) const;
|
||||
OEMCryptoResult VerifySignature(const std::vector<uint8_t>& message,
|
||||
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();
|
||||
|
||||
@@ -125,6 +141,13 @@ class EccPublicKey {
|
||||
bool InitFromPrivateKeyInfo(const uint8_t* buffer, size_t length);
|
||||
// Initializes the public key object from a private.
|
||||
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.
|
||||
// As a public key, this will only have key point initialized.
|
||||
|
||||
@@ -14,6 +14,9 @@
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include <openssl/evp.h>
|
||||
|
||||
#include "oemcrypto_ecc_key.h"
|
||||
#include "string_conversions.h"
|
||||
|
||||
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 public key coordinates. X and Y are each 48 bytes.
|
||||
constexpr int kP384KeyComponentSize = 384 / 8;
|
||||
constexpr int kMarshaledP256KeySize = kP256KeyComponentSize * 2 + 1;
|
||||
constexpr int kMarshaledP384KeySize = kP384KeyComponentSize * 2 + 1;
|
||||
constexpr int kMaxMarshaledECKeySize = kMarshaledP384KeySize;
|
||||
constexpr char kMarshaledECKeyZValue = 0x04;
|
||||
constexpr int kED25519KeyDataItemSize = 32;
|
||||
// The Issuer field key in BccEntryPayload.
|
||||
@@ -79,6 +82,8 @@ constexpr int64_t kIssuer = 1;
|
||||
constexpr int64_t kSubject = 2;
|
||||
// The SubjectPublicKey field key in BccEntryPayload.
|
||||
constexpr int64_t kSubjectPublicKey = -4670552;
|
||||
// This signature context is defined by COSE SIGN1.
|
||||
constexpr char kSignatureContextString[] = "Signature1";
|
||||
|
||||
struct IssuerSubject {
|
||||
std::string issuer;
|
||||
@@ -135,6 +140,60 @@ void AddMessages(std::stringstream& ss,
|
||||
}
|
||||
} // 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() {
|
||||
if (message_status_ != kCborParseOk) return message_status_;
|
||||
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
|
||||
// definition.
|
||||
const cppbor::Map* device_public_key_info = (*bcc_array)[0]->asMap();
|
||||
if (device_public_key_info == nullptr) {
|
||||
const cppbor::Map* device_public_key_map = (*bcc_array)[0]->asMap();
|
||||
if (device_public_key_map == nullptr) {
|
||||
AddValidationMessage(
|
||||
kCborValidateFatal,
|
||||
"Device public key info is not a CBOR map. Actual type: " +
|
||||
CppborMajorTypeToString((*bcc_array)[0]->type()));
|
||||
return message_status_;
|
||||
}
|
||||
std::vector<std::string> key_value_texts;
|
||||
std::string device_public_key_bytes;
|
||||
BccPublicKeyInfo root_pub_key;
|
||||
std::vector<std::string> key_value_texts; // for pretty print
|
||||
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);
|
||||
if (status == kCborValidateFatal) return status;
|
||||
|
||||
BccPublicKeyInfo leaf_pub_key = root_pub_key;
|
||||
msg_ss_ << "BCC ENTRY:\n";
|
||||
// Parse each certificate in the chain. The structure of thr entries are
|
||||
// COSE_Sign1 (untagged).
|
||||
// Parse and verify each certificate in the chain. The structure of thr
|
||||
// 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) {
|
||||
msg_ss_ << "- CDI PUBLIC KEY INDEX: " << i << "\n";
|
||||
const cppbor::Array* bcc_entry = (*bcc_array)[i]->asArray();
|
||||
@@ -205,25 +266,48 @@ CborMessageStatus BccValidator::Validate() {
|
||||
return message_status_;
|
||||
}
|
||||
|
||||
const std::vector<uint8_t>& key_payload =
|
||||
(*bcc_entry)[2]->asBstr()->value();
|
||||
// Signature verification Step 1: construct and encode signature input
|
||||
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();
|
||||
std::string entry_public_key_bytes;
|
||||
status = ProcessDiceChainEntryPayload(key_payload, key_value_texts,
|
||||
&entry_public_key_bytes);
|
||||
BccPublicKeyInfo entry_pub_key;
|
||||
status =
|
||||
ProcessDiceChainEntryPayload(payload, key_value_texts, &entry_pub_key);
|
||||
AddMessages(msg_ss_, key_value_texts, 1);
|
||||
if (status == kCborValidateFatal) return status;
|
||||
// If the size of the BCC array (including device pub key) is 2, then it
|
||||
// must be a de-generated BCC, which means the second element in the array
|
||||
// is a self-signed entry. The entry's public key should be identical to the
|
||||
// device's public key.
|
||||
if (bcc_array->size() == 2 && i == 1) {
|
||||
// self-signed BCC entry
|
||||
if (entry_public_key_bytes != device_public_key_bytes) {
|
||||
AddValidationMessage(kCborValidateError,
|
||||
"The public key of a self-signed entry should be "
|
||||
"identical to its device public key.");
|
||||
}
|
||||
leaf_pub_key = std::move(entry_pub_key);
|
||||
}
|
||||
// If the size of the BCC array (including device pub key) is 2, then it
|
||||
// must be a de-generated BCC, which means the second element in the array
|
||||
// is a self-signed entry. The entry's public key should be identical to the
|
||||
// device's public key.
|
||||
if (bcc_array->size() == 2) {
|
||||
// self-signed BCC entry
|
||||
if (leaf_pub_key.key_bytes != root_pub_key.key_bytes) {
|
||||
AddValidationMessage(kCborValidateError,
|
||||
"The public key of a self-signed entry should be "
|
||||
"identical to its device public key.");
|
||||
}
|
||||
}
|
||||
msg_ss_ << "...\n";
|
||||
@@ -233,10 +317,10 @@ CborMessageStatus BccValidator::Validate() {
|
||||
|
||||
CborMessageStatus BccValidator::ProcessSubjectPublicKeyInfo(
|
||||
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;
|
||||
std::string device_key_bytes_0;
|
||||
std::string device_key_bytes_1;
|
||||
std::vector<uint8_t> device_key_bytes_0;
|
||||
std::vector<uint8_t> device_key_bytes_1;
|
||||
std::unordered_set<int64_t> key_set;
|
||||
for (size_t index = 0; index < public_key_info_map.size(); ++index) {
|
||||
std::pair<const std::unique_ptr<cppbor::Item>&,
|
||||
@@ -289,10 +373,13 @@ CborMessageStatus BccValidator::ProcessSubjectPublicKeyInfo(
|
||||
const int64_t value = entry.second->asNint()->value();
|
||||
if (value == DEVICE_KEY_ALGORITHM_ES256) {
|
||||
kv += "ECDSA_SHA256";
|
||||
public_key_info->signature_algorithm = kBccEcdsaSha256;
|
||||
} else if (value == DEVICE_KEY_ALGORITHM_ES384) {
|
||||
kv += "ECDSA_SHA384";
|
||||
public_key_info->signature_algorithm = kBccEcdsaSha384;
|
||||
} else if (value == DEVICE_KEY_ALGORITHM_EDDSA) {
|
||||
kv += "EDDSA";
|
||||
public_key_info->signature_algorithm = kBccEdDsa;
|
||||
} else {
|
||||
AddValidationMessage(kCborValidateError,
|
||||
"Invalid value in public key info map for key "
|
||||
@@ -316,10 +403,13 @@ CborMessageStatus BccValidator::ProcessSubjectPublicKeyInfo(
|
||||
std::string kv = "curve: ";
|
||||
const int64_t value = entry.second->asUint()->value();
|
||||
if (value == DEVICE_KEY_CURVE_P256) {
|
||||
public_key_info->curve = kBccP256;
|
||||
kv += "P256";
|
||||
} else if (value == DEVICE_KEY_CURVE_P384) {
|
||||
public_key_info->curve = kBccP384;
|
||||
kv += "P384";
|
||||
} else if (value == DEVICE_KEY_CURVE_ED25519) {
|
||||
public_key_info->curve = kBccEd25519;
|
||||
kv += "ED25519";
|
||||
} else {
|
||||
AddValidationMessage(kCborValidateError,
|
||||
@@ -351,10 +441,11 @@ CborMessageStatus BccValidator::ProcessSubjectPublicKeyInfo(
|
||||
std::to_string(key_bytes.size()));
|
||||
return kCborValidateFatal;
|
||||
}
|
||||
std::string& key_bytes_str = map_key == MAP_KEY_DEVICE_KEY_BYTES_0
|
||||
? device_key_bytes_0
|
||||
: device_key_bytes_1;
|
||||
key_bytes_str.assign(key_bytes.begin(), key_bytes.end());
|
||||
if (map_key == MAP_KEY_DEVICE_KEY_BYTES_0) {
|
||||
device_key_bytes_0 = key_bytes;
|
||||
} else {
|
||||
device_key_bytes_1 = key_bytes;
|
||||
}
|
||||
}
|
||||
key_set.insert(map_key);
|
||||
}
|
||||
@@ -378,28 +469,34 @@ CborMessageStatus BccValidator::ProcessSubjectPublicKeyInfo(
|
||||
"Malformed public key definition. Missing device public key bytes.");
|
||||
return kCborValidateFatal;
|
||||
}
|
||||
std::string device_key_bytes;
|
||||
std::vector<uint8_t> device_key_bytes;
|
||||
if (key_encoding_format == DEVICE_KEY_OCTET_PAIR) {
|
||||
// Key is an ECDSA elliptic key. We need to return the ANSI X9.62
|
||||
// marshaled public key. Generate the marshaled key if needed. The
|
||||
// marshaled key is needed to create an ECPublicKey object.
|
||||
// std::string* device_key_bytes = public_key_info.mutable_key_bytes();
|
||||
device_key_bytes.reserve(kMaxMarshaledECKeySize);
|
||||
device_key_bytes.resize(1);
|
||||
device_key_bytes[0] = kMarshaledECKeyZValue;
|
||||
device_key_bytes.append(device_key_bytes_0);
|
||||
device_key_bytes.append(device_key_bytes_1);
|
||||
device_key_bytes.push_back(kMarshaledECKeyZValue);
|
||||
device_key_bytes.insert(device_key_bytes.end(), device_key_bytes_0.begin(),
|
||||
device_key_bytes_0.end());
|
||||
device_key_bytes.insert(device_key_bytes.end(), device_key_bytes_1.begin(),
|
||||
device_key_bytes_1.end());
|
||||
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 {
|
||||
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));
|
||||
*public_key_bytes = device_key_bytes;
|
||||
public_key_info->key_bytes = std::move(device_key_bytes);
|
||||
return message_status_;
|
||||
}
|
||||
|
||||
CborMessageStatus BccValidator::ProcessDiceChainEntryPayload(
|
||||
const std::vector<uint8_t>& payload, std::vector<std::string>& fmt_msgs,
|
||||
std::string* entry_public_key_bytes) {
|
||||
BccPublicKeyInfo* entry_public_key_info) {
|
||||
if (payload.empty()) {
|
||||
AddValidationMessage(kCborValidateFatal, "Empty bcc entry payload.");
|
||||
return kCborValidateFatal;
|
||||
@@ -449,7 +546,7 @@ CborMessageStatus BccValidator::ProcessDiceChainEntryPayload(
|
||||
return kCborValidateFatal;
|
||||
}
|
||||
return ProcessSubjectPublicKeyInfo(*subject_public_key_info, fmt_msgs,
|
||||
entry_public_key_bytes);
|
||||
entry_public_key_info);
|
||||
}
|
||||
|
||||
std::string BccValidator::GetFormattedMessage() const {
|
||||
|
||||
@@ -44,6 +44,7 @@ using ScopedEvpPkey = ScopedObject<EVP_PKEY, EVP_PKEY_free>;
|
||||
using ScopedPrivateKeyInfo =
|
||||
ScopedObject<PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO_free>;
|
||||
using ScopedSigPoint = ScopedObject<ECDSA_SIG, ECDSA_SIG_free>;
|
||||
using ScopedEcPoint = ScopedObject<EC_POINT, EC_POINT_free>;
|
||||
|
||||
const EC_GROUP* GetEcGroup(EccCurve curve) {
|
||||
// Creating a named EC_GROUP is an expensive operation, and they
|
||||
@@ -140,6 +141,45 @@ EccCurve GetCurveFromKeyGroup(const EC_KEY* key) {
|
||||
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
|
||||
// equal.
|
||||
// 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());
|
||||
}
|
||||
|
||||
// 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(
|
||||
const EccPrivateKey& private_key) const {
|
||||
if (private_key.curve() != curve_) {
|
||||
@@ -486,7 +546,7 @@ OEMCryptoResult EccPublicKey::VerifySignature(const uint8_t* message,
|
||||
LOGE("Bad message data");
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
// Step 1: Parse signature.
|
||||
// Parse signature.
|
||||
const uint8_t* tp = signature;
|
||||
ScopedSigPoint sig_point(d2i_ECDSA_SIG(nullptr, &tp, signature_length));
|
||||
if (!sig_point) {
|
||||
@@ -494,24 +554,7 @@ OEMCryptoResult EccPublicKey::VerifySignature(const uint8_t* message,
|
||||
// Most likely an invalid signature than an OpenSSL error.
|
||||
return OEMCrypto_ERROR_SIGNATURE_FAILURE;
|
||||
}
|
||||
// Step 2: Hash message
|
||||
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;
|
||||
return DigestAndVerify(message, message_length, sig_point.get());
|
||||
}
|
||||
|
||||
OEMCryptoResult EccPublicKey::VerifySignature(
|
||||
@@ -536,6 +579,37 @@ OEMCryptoResult EccPublicKey::VerifySignature(
|
||||
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() {
|
||||
if (key_ != nullptr) {
|
||||
EC_KEY_free(key_);
|
||||
@@ -608,6 +682,57 @@ bool EccPublicKey::InitFromPrivateKey(const EccPrivateKey& private_key) {
|
||||
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
|
||||
std::unique_ptr<EccPrivateKey> EccPrivateKey::New(EccCurve curve) {
|
||||
std::unique_ptr<EccPrivateKey> key(new EccPrivateKey());
|
||||
|
||||
Reference in New Issue
Block a user