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
1057 lines
33 KiB
C++
1057 lines
33 KiB
C++
// Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
|
|
// source code may only be used and distributed under the Widevine License
|
|
// Agreement.
|
|
//
|
|
// Reference implementation utilities of OEMCrypto APIs
|
|
//
|
|
#include "oemcrypto_ecc_key.h"
|
|
|
|
#include <assert.h>
|
|
#include <string.h>
|
|
|
|
#include <mutex>
|
|
|
|
#include <openssl/bn.h>
|
|
#include <openssl/crypto.h>
|
|
#include <openssl/ec.h>
|
|
#include <openssl/ecdsa.h>
|
|
#include <openssl/evp.h>
|
|
#include <openssl/rand.h>
|
|
#include <openssl/x509.h>
|
|
|
|
#include "log.h"
|
|
#include "scoped_object.h"
|
|
|
|
namespace wvoec {
|
|
namespace util {
|
|
namespace {
|
|
// Estimated max size (in bytes) of a serialized ECC key (public or
|
|
// private). These values are based on rough calculations for
|
|
// secp521r1 (largest of the supported curves) and should be slightly
|
|
// larger needed.
|
|
constexpr size_t kPrivateKeySize = 250;
|
|
constexpr size_t kPublicKeySize = 164;
|
|
|
|
// 256 bit key, intended to be used with CMAC-AES-256.
|
|
constexpr size_t kEccSessionKeySize = 32;
|
|
|
|
using ScopedBigNum = ScopedObject<BIGNUM, BN_free>;
|
|
using ScopedBigNumCtx = ScopedObject<BN_CTX, BN_CTX_free>;
|
|
using ScopedBio = ScopedObject<BIO, BIO_vfree>;
|
|
using ScopedEcKey = ScopedObject<EC_KEY, EC_KEY_free>;
|
|
using ScopedEvpMdCtx = ScopedObject<EVP_MD_CTX, EVP_MD_CTX_free>;
|
|
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
|
|
// are always used in a manner which does not transfer ownership.
|
|
// Maintaining a process-wide set of supported EC groups reduces
|
|
// the overhead of group operations.
|
|
static std::mutex group_mutex;
|
|
static EC_GROUP* group_256 = nullptr;
|
|
static EC_GROUP* group_384 = nullptr;
|
|
static EC_GROUP* group_521 = nullptr;
|
|
std::lock_guard<std::mutex> group_lock(group_mutex);
|
|
switch (curve) {
|
|
case kEccSecp256r1: {
|
|
if (group_256 == nullptr) {
|
|
LOGD("Creating secp256r1 group");
|
|
// The curve secp256r1 was originally named prime256v1
|
|
// in the X9.62 specification.
|
|
group_256 = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
|
|
assert(group_256 != nullptr);
|
|
}
|
|
return group_256;
|
|
}
|
|
case kEccSecp384r1: {
|
|
if (group_384 == nullptr) {
|
|
LOGD("Creating secp384r1 group");
|
|
group_384 = EC_GROUP_new_by_curve_name(NID_secp384r1);
|
|
assert(group_384 != nullptr);
|
|
}
|
|
return group_384;
|
|
}
|
|
case kEccSecp521r1: {
|
|
if (group_521 == nullptr) {
|
|
LOGD("Creating secp521r1 group");
|
|
group_521 = EC_GROUP_new_by_curve_name(NID_secp521r1);
|
|
assert(group_521 != nullptr);
|
|
}
|
|
return group_521;
|
|
}
|
|
default:
|
|
LOGE("Cannot get EC group for unknown curve: curve = %d",
|
|
static_cast<int>(curve));
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
// Determines which of the supported ECC curves the provided |key|
|
|
// belongs to.
|
|
//
|
|
// This is intended to be used on keys that have been deserialized
|
|
// from an ASN.1 structure which may have contained a key which is
|
|
// supported by OpenSSL/BoringSSL but not necessarily by OEMCrypto.
|
|
//
|
|
// If the key group is unknown to OEMCrypto or if an error occurs,
|
|
// kEccCurveUnknown is returned.
|
|
EccCurve GetCurveFromKeyGroup(const EC_KEY* key) {
|
|
ScopedBigNumCtx ctx(BN_CTX_new());
|
|
if (!ctx) {
|
|
LOGE("Failed to allocate BN ctx");
|
|
return kEccCurveUnknown;
|
|
}
|
|
const EC_GROUP* group = EC_KEY_get0_group(key);
|
|
if (group == nullptr) {
|
|
LOGE("Provided key does not have a group");
|
|
return kEccCurveUnknown;
|
|
}
|
|
int rc = EC_GROUP_cmp(group, GetEcGroup(kEccSecp256r1), ctx.get());
|
|
if (rc == 0) {
|
|
return kEccSecp256r1;
|
|
}
|
|
if (rc == -1) {
|
|
LOGE("Error occurred while checking against secp256r1");
|
|
return kEccCurveUnknown;
|
|
}
|
|
|
|
rc = EC_GROUP_cmp(group, GetEcGroup(kEccSecp384r1), ctx.get());
|
|
if (rc == 0) {
|
|
return kEccSecp384r1;
|
|
}
|
|
if (rc == -1) {
|
|
LOGE("Error occurred while checking against secp384r1");
|
|
return kEccCurveUnknown;
|
|
}
|
|
|
|
rc = EC_GROUP_cmp(group, GetEcGroup(kEccSecp521r1), ctx.get());
|
|
if (rc == 0) {
|
|
return kEccSecp521r1;
|
|
}
|
|
if (rc == -1) {
|
|
LOGE("Error occurred while checking against secp521r1");
|
|
return kEccCurveUnknown;
|
|
}
|
|
|
|
LOGW("Unsupported curve group");
|
|
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.
|
|
bool IsMatchingKeyPair(const EC_KEY* public_key, const EC_KEY* private_key) {
|
|
ScopedBigNumCtx ctx(BN_CTX_new());
|
|
if (!ctx) {
|
|
LOGE("Failed to allocate BN ctx");
|
|
return false;
|
|
}
|
|
// Returns: 1 if not equal, 0 if equal, -1 if error.
|
|
const int res = EC_POINT_cmp(EC_KEY_get0_group(public_key),
|
|
EC_KEY_get0_public_key(public_key),
|
|
EC_KEY_get0_public_key(private_key), ctx.get());
|
|
if (res == -1) {
|
|
LOGE("Error occurred comparing keys");
|
|
}
|
|
return res == 0;
|
|
}
|
|
|
|
// Performs a SHA2 digest on the provided |message| and outputs the
|
|
// computed hash to |digest|.
|
|
// The digest algorithm used depends on which curve is used.
|
|
// - secp256r1 -> SHA-256
|
|
// - secp384r1 -> SHA-384
|
|
// - secp521r1 -> SHA-512
|
|
// This function assumes that all parameters are valid.
|
|
// Returns true on success, false otherwise.
|
|
bool DigestMessage(EccCurve curve, const uint8_t* message, size_t message_size,
|
|
std::vector<uint8_t>* digest) {
|
|
const EVP_MD* md_engine = nullptr;
|
|
switch (curve) {
|
|
case kEccSecp256r1: {
|
|
md_engine = EVP_sha256();
|
|
break;
|
|
}
|
|
case kEccSecp384r1: {
|
|
md_engine = EVP_sha384();
|
|
break;
|
|
}
|
|
case kEccSecp521r1: {
|
|
md_engine = EVP_sha512();
|
|
break;
|
|
}
|
|
case kEccCurveUnknown:
|
|
// This case is to suppress compiler warnings. It will never
|
|
// occur.
|
|
break;
|
|
}
|
|
if (md_engine == nullptr) {
|
|
LOGE("Failed to get MD engine: curve = %d", static_cast<int>(curve));
|
|
return false;
|
|
}
|
|
|
|
ScopedEvpMdCtx md_ctx(EVP_MD_CTX_new());
|
|
if (!md_ctx) {
|
|
LOGE("Failed to create MD CTX");
|
|
return false;
|
|
}
|
|
if (!EVP_DigestInit_ex(md_ctx.get(), md_engine, nullptr)) {
|
|
LOGE("Failed to init MD CTX");
|
|
return false;
|
|
}
|
|
if (message_size > 0 &&
|
|
!EVP_DigestUpdate(md_ctx.get(), message, message_size)) {
|
|
LOGE("Failed to update");
|
|
return false;
|
|
}
|
|
digest->resize(EVP_MD_CTX_size(md_ctx.get()), 0);
|
|
const int res = EVP_DigestFinal_ex(md_ctx.get(), digest->data(), nullptr);
|
|
if (!res) {
|
|
LOGE("Failed to finalize");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// This KDF function is defined by OEMCrypto ECC specification.
|
|
// Function signature is based on the |kdf| parameter of
|
|
// ECDH_compute_key(). This function assumes that all pointer
|
|
// parameters are not null.
|
|
void* WidevineEccKdf(const void* secret, size_t secret_length, void* key,
|
|
size_t* key_size) {
|
|
if (*key_size < kEccSessionKeySize) {
|
|
LOGE("Output buffer is too small: required = %zu, size = %zu",
|
|
kEccSessionKeySize, *key_size);
|
|
return nullptr;
|
|
}
|
|
std::vector<uint8_t> digest;
|
|
if (!DigestMessage(kEccSecp256r1 /* SHA-256 */,
|
|
reinterpret_cast<const uint8_t*>(secret), secret_length,
|
|
&digest)) {
|
|
LOGE("Cannot derive key: Failed to hash secret");
|
|
return nullptr;
|
|
}
|
|
if (digest.size() != kEccSessionKeySize) {
|
|
LOGE("Unexpected hash size: actual = %zu, expected = %zu", digest.size(),
|
|
kEccSessionKeySize);
|
|
return nullptr;
|
|
}
|
|
*key_size = kEccSessionKeySize;
|
|
memcpy(key, digest.data(), *key_size);
|
|
return key;
|
|
}
|
|
|
|
void OpensslFreeU8(uint8_t* ptr) { OPENSSL_free(ptr); }
|
|
|
|
// Internal ECC public key serialization.
|
|
OEMCryptoResult SerializeEccPublicKey(const EC_KEY* key, uint8_t* buffer,
|
|
size_t* buffer_size) {
|
|
if (buffer_size == nullptr) {
|
|
LOGE("Output buffer size is null");
|
|
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
|
}
|
|
if (buffer == nullptr && *buffer_size > 0) {
|
|
LOGE("Output buffer is null");
|
|
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
|
}
|
|
|
|
uint8_t* der_key_raw = nullptr;
|
|
const int der_res = i2d_EC_PUBKEY(
|
|
const_cast<EC_KEY*>(key) /* Does not get modified */, &der_key_raw);
|
|
if (der_res < 0) {
|
|
LOGE("Public key serialization failed");
|
|
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
|
}
|
|
ScopedObject<uint8_t, OpensslFreeU8> der_key(der_key_raw);
|
|
der_key_raw = nullptr;
|
|
if (!der_key) {
|
|
LOGE("Encoded key is unexpectedly null");
|
|
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
|
}
|
|
if (der_res == 0) {
|
|
LOGE("Unexpected DER encoded size");
|
|
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
|
}
|
|
const size_t required_size = static_cast<size_t>(der_res);
|
|
if (buffer == nullptr || *buffer_size < required_size) {
|
|
*buffer_size = required_size;
|
|
return OEMCrypto_ERROR_SHORT_BUFFER;
|
|
}
|
|
memcpy(buffer, der_key.get(), required_size);
|
|
*buffer_size = required_size;
|
|
return OEMCrypto_SUCCESS;
|
|
}
|
|
|
|
std::vector<uint8_t> SerializeEccPublicKey(const EC_KEY* key) {
|
|
size_t key_size = kPublicKeySize;
|
|
std::vector<uint8_t> key_data(key_size, 0);
|
|
const OEMCryptoResult res =
|
|
SerializeEccPublicKey(key, key_data.data(), &key_size);
|
|
if (res != OEMCrypto_SUCCESS) {
|
|
LOGE("Failed to serialize public key: result = %d", static_cast<int>(res));
|
|
key_data.clear();
|
|
} else {
|
|
key_data.resize(key_size);
|
|
}
|
|
return key_data;
|
|
}
|
|
|
|
bool ParseEccPrivateKeyInfo(const uint8_t* buffer, size_t length,
|
|
ScopedEcKey* key, EccCurve* curve) {
|
|
if (length == 0) {
|
|
LOGE("Public key is too small: length = %zu", length);
|
|
return false;
|
|
}
|
|
ScopedBio bio(BIO_new_mem_buf(buffer, static_cast<int>(length)));
|
|
if (!bio) {
|
|
LOGE("Failed to allocate BIO buffer");
|
|
return false;
|
|
}
|
|
// Step 1: Deserializes PKCS8 PrivateKeyInfo containing an ECC key.
|
|
ScopedPrivateKeyInfo priv_info(
|
|
d2i_PKCS8_PRIV_KEY_INFO_bio(bio.get(), nullptr));
|
|
if (!priv_info) {
|
|
LOGE("Failed to parse private key");
|
|
return false;
|
|
}
|
|
// Step 2: Convert to EC_KEY.
|
|
ScopedEvpPkey pkey(EVP_PKCS82PKEY(priv_info.get()));
|
|
if (!pkey) {
|
|
LOGE("Failed to convert PKCS8 to EVP");
|
|
return false;
|
|
}
|
|
const int key_type = EVP_PKEY_base_id(pkey.get());
|
|
if (key_type != EVP_PKEY_EC) {
|
|
LOGE("Decoded private key is not ECC");
|
|
return false;
|
|
}
|
|
key->reset(EVP_PKEY_get1_EC_KEY(pkey.get()));
|
|
if (!*key) {
|
|
LOGE("Failed to get ECC key");
|
|
return false;
|
|
}
|
|
// Step 3: Verify key parameters and curve family.
|
|
const int check = EC_KEY_check_key(key->get());
|
|
if (check == 0) {
|
|
LOGE("ECC key parameters are invalid");
|
|
return false;
|
|
} else if (check == -1) {
|
|
LOGE("Failed to check ECC key");
|
|
return false;
|
|
}
|
|
*curve = GetCurveFromKeyGroup(key->get());
|
|
if (*curve == kEccCurveUnknown) {
|
|
LOGE("Failed to determine key group");
|
|
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);
|
|
return true;
|
|
}
|
|
} // namespace
|
|
|
|
std::string EccCurveToString(EccCurve curve) {
|
|
switch (curve) {
|
|
case kEccSecp256r1:
|
|
return "secp256r1";
|
|
case kEccSecp384r1:
|
|
return "secp384r1";
|
|
case kEccSecp521r1:
|
|
return "secp521r1";
|
|
case kEccCurveUnknown:
|
|
return "Unknown";
|
|
}
|
|
return "Unknown(" + std::to_string(static_cast<int>(curve)) + ")";
|
|
}
|
|
|
|
// static
|
|
std::unique_ptr<EccPublicKey> EccPublicKey::New(
|
|
const EccPrivateKey& private_key) {
|
|
std::unique_ptr<EccPublicKey> key(new EccPublicKey());
|
|
if (!key->InitFromPrivateKey(private_key)) {
|
|
LOGE("Failed to initialize public key from private key");
|
|
key.reset();
|
|
}
|
|
return key;
|
|
}
|
|
|
|
// static
|
|
std::unique_ptr<EccPublicKey> EccPublicKey::Load(const uint8_t* buffer,
|
|
size_t length) {
|
|
if (buffer == nullptr) {
|
|
LOGE("Provided public key buffer is null");
|
|
return nullptr;
|
|
}
|
|
if (length == 0) {
|
|
LOGE("Provided public key buffer is zero length");
|
|
return nullptr;
|
|
}
|
|
std::unique_ptr<EccPublicKey> key(new EccPublicKey());
|
|
if (!key->InitFromSubjectPublicKeyInfo(buffer, length)) {
|
|
LOGE("Failed to initialize public key from SubjectPublicKeyInfo");
|
|
key.reset();
|
|
}
|
|
return key;
|
|
}
|
|
|
|
// static
|
|
std::unique_ptr<EccPublicKey> EccPublicKey::Load(const std::string& buffer) {
|
|
if (buffer.empty()) {
|
|
LOGE("Provided public key buffer is empty");
|
|
return std::unique_ptr<EccPublicKey>();
|
|
}
|
|
return Load(reinterpret_cast<const uint8_t*>(buffer.data()), buffer.size());
|
|
}
|
|
|
|
// static
|
|
std::unique_ptr<EccPublicKey> EccPublicKey::Load(
|
|
const std::vector<uint8_t>& buffer) {
|
|
if (buffer.empty()) {
|
|
LOGE("Provided public key buffer is empty");
|
|
return std::unique_ptr<EccPublicKey>();
|
|
}
|
|
return Load(buffer.data(), buffer.size());
|
|
}
|
|
|
|
// static
|
|
std::unique_ptr<EccPublicKey> EccPublicKey::LoadPrivateKeyInfo(
|
|
const uint8_t* buffer, size_t length) {
|
|
if (buffer == nullptr) {
|
|
LOGE("Provided public key buffer is null");
|
|
return nullptr;
|
|
}
|
|
if (length == 0) {
|
|
LOGE("Provided public key buffer is zero length");
|
|
return nullptr;
|
|
}
|
|
std::unique_ptr<EccPublicKey> key(new EccPublicKey());
|
|
if (!key->InitFromPrivateKeyInfo(buffer, length)) {
|
|
LOGE("Failed to initialize public key from PrivateKeyInfo");
|
|
key.reset();
|
|
}
|
|
return key;
|
|
}
|
|
|
|
// static
|
|
std::unique_ptr<EccPublicKey> EccPublicKey::LoadPrivateKeyInfo(
|
|
const std::string& buffer) {
|
|
if (buffer.empty()) {
|
|
LOGE("Provided public key buffer is empty");
|
|
return std::unique_ptr<EccPublicKey>();
|
|
}
|
|
return LoadPrivateKeyInfo(reinterpret_cast<const uint8_t*>(buffer.data()),
|
|
buffer.size());
|
|
}
|
|
|
|
// static
|
|
std::unique_ptr<EccPublicKey> EccPublicKey::LoadPrivateKeyInfo(
|
|
const std::vector<uint8_t>& buffer) {
|
|
if (buffer.empty()) {
|
|
LOGE("Provided public key buffer is empty");
|
|
return std::unique_ptr<EccPublicKey>();
|
|
}
|
|
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_) {
|
|
return false;
|
|
}
|
|
return IsMatchingKeyPair(GetEcKey(), private_key.GetEcKey());
|
|
}
|
|
|
|
OEMCryptoResult EccPublicKey::Serialize(uint8_t* buffer,
|
|
size_t* buffer_size) const {
|
|
return SerializeEccPublicKey(key_, buffer, buffer_size);
|
|
}
|
|
|
|
std::vector<uint8_t> EccPublicKey::Serialize() const {
|
|
return SerializeEccPublicKey(key_);
|
|
}
|
|
|
|
OEMCryptoResult EccPublicKey::VerifySignature(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;
|
|
}
|
|
// Parse signature.
|
|
const uint8_t* tp = signature;
|
|
ScopedSigPoint sig_point(d2i_ECDSA_SIG(nullptr, &tp, signature_length));
|
|
if (!sig_point) {
|
|
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());
|
|
}
|
|
|
|
OEMCryptoResult EccPublicKey::VerifySignature(
|
|
const std::string& message, const std::string& signature) const {
|
|
if (signature.empty()) {
|
|
LOGE("Signature should not be empty");
|
|
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
|
}
|
|
return VerifySignature(
|
|
reinterpret_cast<const uint8_t*>(message.data()), message.size(),
|
|
reinterpret_cast<const uint8_t*>(signature.data()), signature.size());
|
|
}
|
|
|
|
OEMCryptoResult EccPublicKey::VerifySignature(
|
|
const std::vector<uint8_t>& message,
|
|
const std::vector<uint8_t>& signature) const {
|
|
if (signature.empty()) {
|
|
LOGE("Signature should not be empty");
|
|
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
|
}
|
|
return VerifySignature(message.data(), message.size(), signature.data(),
|
|
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_);
|
|
key_ = nullptr;
|
|
}
|
|
curve_ = kEccCurveUnknown;
|
|
}
|
|
|
|
bool EccPublicKey::InitFromSubjectPublicKeyInfo(const uint8_t* buffer,
|
|
size_t length) {
|
|
// Deserialize SubjectPublicKeyInfo
|
|
const uint8_t* tp = buffer;
|
|
ScopedEcKey key(d2i_EC_PUBKEY(nullptr, &tp, length));
|
|
if (!key) {
|
|
LOGE("Failed to parse ECC key");
|
|
return false;
|
|
}
|
|
// Verify key parameters and curve family.
|
|
const int check = EC_KEY_check_key(key.get());
|
|
if (check == 0) {
|
|
LOGE("ECC key parameters are invalid");
|
|
return false;
|
|
} else if (check == -1) {
|
|
LOGE("Failed to check ECC key");
|
|
return false;
|
|
}
|
|
curve_ = GetCurveFromKeyGroup(key.get());
|
|
if (curve_ == kEccCurveUnknown) {
|
|
LOGE("Failed to determine key group");
|
|
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();
|
|
return true;
|
|
}
|
|
|
|
bool EccPublicKey::InitFromPrivateKeyInfo(const uint8_t* buffer,
|
|
size_t length) {
|
|
ScopedEcKey private_key;
|
|
if (!ParseEccPrivateKeyInfo(buffer, length, &private_key, &curve_)) {
|
|
return false;
|
|
}
|
|
// TODO(sigquit): Strip private information.
|
|
key_ = private_key.release();
|
|
return true;
|
|
}
|
|
|
|
bool EccPublicKey::InitFromPrivateKey(const EccPrivateKey& private_key) {
|
|
ScopedEcKey key(EC_KEY_new());
|
|
if (!key) {
|
|
LOGE("Failed to allocate key");
|
|
return false;
|
|
}
|
|
if (!EC_KEY_set_group(key.get(), EC_KEY_get0_group(private_key.GetEcKey()))) {
|
|
LOGE("Failed to set group");
|
|
return false;
|
|
}
|
|
if (!EC_KEY_set_public_key(key.get(),
|
|
EC_KEY_get0_public_key(private_key.GetEcKey()))) {
|
|
LOGE("Failed to set public point");
|
|
return false;
|
|
}
|
|
curve_ = private_key.curve();
|
|
// 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();
|
|
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());
|
|
if (!key->InitFromCurve(curve)) {
|
|
LOGE("Failed to initialize private key from curve");
|
|
key.reset();
|
|
}
|
|
return key;
|
|
}
|
|
|
|
// static
|
|
std::unique_ptr<EccPrivateKey> EccPrivateKey::Load(const uint8_t* buffer,
|
|
size_t length) {
|
|
if (buffer == nullptr) {
|
|
LOGE("Provided private key buffer is null");
|
|
return nullptr;
|
|
}
|
|
if (length == 0) {
|
|
LOGE("Provided private key buffer is zero length");
|
|
return nullptr;
|
|
}
|
|
std::unique_ptr<EccPrivateKey> key(new EccPrivateKey());
|
|
if (!key->InitFromPrivateKeyInfo(buffer, length)) {
|
|
LOGE("Failed to initialize private key from PrivateKeyInfo");
|
|
key.reset();
|
|
}
|
|
return key;
|
|
}
|
|
|
|
// static
|
|
std::unique_ptr<EccPrivateKey> EccPrivateKey::Load(const std::string& buffer) {
|
|
if (buffer.empty()) {
|
|
LOGE("Provided private key buffer is empty");
|
|
return std::unique_ptr<EccPrivateKey>();
|
|
}
|
|
return Load(reinterpret_cast<const uint8_t*>(buffer.data()), buffer.size());
|
|
}
|
|
|
|
// static
|
|
std::unique_ptr<EccPrivateKey> EccPrivateKey::Load(
|
|
const std::vector<uint8_t>& buffer) {
|
|
if (buffer.empty()) {
|
|
LOGE("Provided private key buffer is empty");
|
|
return std::unique_ptr<EccPrivateKey>();
|
|
}
|
|
return Load(buffer.data(), buffer.size());
|
|
}
|
|
|
|
std::unique_ptr<EccPublicKey> EccPrivateKey::MakePublicKey() const {
|
|
return EccPublicKey::New(*this);
|
|
}
|
|
|
|
bool EccPrivateKey::IsMatchingPublicKey(const EccPublicKey& public_key) const {
|
|
if (public_key.curve() != curve_) {
|
|
return false;
|
|
}
|
|
return IsMatchingKeyPair(public_key.GetEcKey(), GetEcKey());
|
|
}
|
|
|
|
OEMCryptoResult EccPrivateKey::Serialize(uint8_t* buffer,
|
|
size_t* buffer_size) const {
|
|
if (buffer_size == nullptr) {
|
|
LOGE("Output buffer size is null");
|
|
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
|
}
|
|
if (buffer == nullptr && *buffer_size > 0) {
|
|
LOGE("Output buffer is null");
|
|
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
|
}
|
|
// Step 1: Convert EC_KEY key to EVP.
|
|
ScopedEvpPkey pkey(EVP_PKEY_new());
|
|
if (!pkey) {
|
|
LOGE("Failed to allocate EVP");
|
|
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
|
}
|
|
if (!EVP_PKEY_set1_EC_KEY(pkey.get(), key_)) {
|
|
LOGE("Failed to set EVP ECC key");
|
|
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
|
}
|
|
// Step 2: Convert ECC EVP to PKCS8 format.
|
|
ScopedPrivateKeyInfo priv_info(EVP_PKEY2PKCS8(pkey.get()));
|
|
if (!priv_info) {
|
|
LOGE("Failed to convert ECC key to PKCS8 info");
|
|
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
|
}
|
|
// Step 3: Serialize PKCS8 to DER encoding.
|
|
ScopedBio bio(BIO_new(BIO_s_mem()));
|
|
if (!bio) {
|
|
LOGE("Failed to allocate IO buffer for ECC key");
|
|
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
|
}
|
|
if (!i2d_PKCS8_PRIV_KEY_INFO_bio(bio.get(), priv_info.get())) {
|
|
LOGE("Failed to serialize ECC key");
|
|
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
|
}
|
|
// Step 4: Determine key size and copy.
|
|
char* key_ptr = nullptr;
|
|
const long key_size = BIO_get_mem_data(bio.get(), &key_ptr);
|
|
if (key_size < 0) {
|
|
LOGE("Failed to get ECC key size");
|
|
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
|
}
|
|
if (key_ptr == nullptr) {
|
|
LOGE("Encoded key is unexpectedly null");
|
|
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
|
}
|
|
const size_t required_size = static_cast<size_t>(key_size);
|
|
if (*buffer_size < required_size) {
|
|
*buffer_size = required_size;
|
|
return OEMCrypto_ERROR_SHORT_BUFFER;
|
|
}
|
|
*buffer_size = required_size;
|
|
memcpy(buffer, key_ptr, required_size);
|
|
return OEMCrypto_SUCCESS;
|
|
}
|
|
|
|
std::vector<uint8_t> EccPrivateKey::Serialize() const {
|
|
size_t key_size = kPrivateKeySize;
|
|
std::vector<uint8_t> key_data(key_size, 0);
|
|
const OEMCryptoResult res = Serialize(key_data.data(), &key_size);
|
|
if (res != OEMCrypto_SUCCESS) {
|
|
LOGE("Failed to serialize private key: result = %d", static_cast<int>(res));
|
|
key_data.clear();
|
|
} else {
|
|
key_data.resize(key_size);
|
|
}
|
|
return key_data;
|
|
}
|
|
|
|
OEMCryptoResult EccPrivateKey::SerializeAsPublicKey(uint8_t* buffer,
|
|
size_t* buffer_size) const {
|
|
return SerializeEccPublicKey(key_, buffer, buffer_size);
|
|
}
|
|
|
|
std::vector<uint8_t> EccPrivateKey::SerializeAsPublicKey() const {
|
|
return SerializeEccPublicKey(key_);
|
|
}
|
|
|
|
OEMCryptoResult EccPrivateKey::GenerateSignature(
|
|
const uint8_t* message, size_t message_length, uint8_t* signature,
|
|
size_t* signature_length) const {
|
|
if (signature_length == nullptr) {
|
|
LOGE("Output signature size is null");
|
|
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
|
}
|
|
if (signature == nullptr && *signature_length > 0) {
|
|
LOGE("Output signature is null");
|
|
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
|
}
|
|
if (message == nullptr && message_length > 0) {
|
|
LOGE("Invalid message data");
|
|
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
|
}
|
|
const size_t expected_signature_length = ECDSA_size(key_);
|
|
if (*signature_length < expected_signature_length) {
|
|
*signature_length = expected_signature_length;
|
|
return OEMCrypto_ERROR_SHORT_BUFFER;
|
|
}
|
|
|
|
// Step 1: 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 2: Generate signature point.
|
|
ScopedSigPoint sig_point(
|
|
ECDSA_do_sign(digest.data(), static_cast<int>(digest.size()), key_));
|
|
if (!sig_point) {
|
|
LOGE("Failed to perform ECDSA");
|
|
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
|
}
|
|
// Step 3: Serialize
|
|
std::vector<uint8_t> temp(expected_signature_length);
|
|
uint8_t* sig_ptr = temp.data();
|
|
const int res = i2d_ECDSA_SIG(sig_point.get(), &sig_ptr);
|
|
if (res <= 0) {
|
|
LOGE("Failed to serialize signature");
|
|
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
|
}
|
|
const size_t required_size = static_cast<size_t>(res);
|
|
if (signature == nullptr || *signature_length < required_size) {
|
|
*signature_length = required_size;
|
|
return OEMCrypto_ERROR_SHORT_BUFFER;
|
|
}
|
|
memcpy(signature, temp.data(), required_size);
|
|
*signature_length = required_size;
|
|
return OEMCrypto_SUCCESS;
|
|
}
|
|
|
|
std::vector<uint8_t> EccPrivateKey::GenerateSignature(
|
|
const std::string& message) const {
|
|
size_t signature_size = SignatureSize();
|
|
std::vector<uint8_t> signature(signature_size, 0);
|
|
const OEMCryptoResult res =
|
|
GenerateSignature(reinterpret_cast<const uint8_t*>(message.data()),
|
|
message.size(), signature.data(), &signature_size);
|
|
if (res != OEMCrypto_SUCCESS) {
|
|
LOGE("Failed to generate signature: result = %d", static_cast<int>(res));
|
|
signature.clear();
|
|
} else {
|
|
signature.resize(signature_size);
|
|
}
|
|
return signature;
|
|
}
|
|
|
|
std::vector<uint8_t> EccPrivateKey::GenerateSignature(
|
|
const std::vector<uint8_t>& message) const {
|
|
size_t signature_size = SignatureSize();
|
|
std::vector<uint8_t> signature(signature_size, 0);
|
|
const OEMCryptoResult res = GenerateSignature(
|
|
message.data(), message.size(), signature.data(), &signature_size);
|
|
if (res != OEMCrypto_SUCCESS) {
|
|
LOGE("Failed to generate signature: result = %d", static_cast<int>(res));
|
|
signature.clear();
|
|
} else {
|
|
signature.resize(signature_size);
|
|
}
|
|
return signature;
|
|
}
|
|
|
|
size_t EccPrivateKey::SignatureSize() const { return ECDSA_size(key_); }
|
|
|
|
OEMCryptoResult EccPrivateKey::DeriveSessionKey(
|
|
const EccPublicKey& public_key, uint8_t* session_key,
|
|
size_t* session_key_size) const {
|
|
if (public_key.curve() != curve_) {
|
|
LOGE("Incompatible ECC keys: public = %s, private = %s",
|
|
EccCurveToString(public_key.curve()).c_str(),
|
|
EccCurveToString(curve_).c_str());
|
|
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
|
}
|
|
if (session_key_size == nullptr) {
|
|
LOGE("Output session key size buffer is null");
|
|
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
|
}
|
|
if (session_key == nullptr && *session_key_size > 0) {
|
|
LOGE("Output session key buffer is null");
|
|
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
|
}
|
|
if (*session_key_size < kEccSessionKeySize) {
|
|
*session_key_size = kEccSessionKeySize;
|
|
return OEMCrypto_ERROR_SHORT_BUFFER;
|
|
}
|
|
const int res = ECDH_compute_key(
|
|
session_key, kEccSessionKeySize,
|
|
EC_KEY_get0_public_key(public_key.GetEcKey()), key_, WidevineEccKdf);
|
|
if (res < 0) {
|
|
LOGE("ECDH error occurred");
|
|
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
|
}
|
|
if (static_cast<size_t>(res) != kEccSessionKeySize) {
|
|
LOGE("Unexpected key size: size = %d", res);
|
|
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
|
}
|
|
*session_key_size = kEccSessionKeySize;
|
|
return OEMCrypto_SUCCESS;
|
|
}
|
|
|
|
std::vector<uint8_t> EccPrivateKey::DeriveSessionKey(
|
|
const EccPublicKey& public_key) const {
|
|
size_t session_key_size = kEccSessionKeySize;
|
|
std::vector<uint8_t> session_key(session_key_size, 0);
|
|
const OEMCryptoResult res =
|
|
DeriveSessionKey(public_key, session_key.data(), &session_key_size);
|
|
if (res != OEMCrypto_SUCCESS) {
|
|
LOGE("Failed to derive session key: result = %d", static_cast<int>(res));
|
|
session_key.clear();
|
|
} else {
|
|
session_key.resize(session_key_size);
|
|
}
|
|
return session_key;
|
|
}
|
|
|
|
size_t EccPrivateKey::SessionKeyLength() const { return kEccSessionKeySize; }
|
|
|
|
EccPrivateKey::~EccPrivateKey() {
|
|
if (key_ != nullptr) {
|
|
EC_KEY_free(key_);
|
|
key_ = nullptr;
|
|
}
|
|
curve_ = kEccCurveUnknown;
|
|
}
|
|
|
|
bool EccPrivateKey::InitFromPrivateKeyInfo(const uint8_t* buffer,
|
|
size_t length) {
|
|
ScopedEcKey key;
|
|
if (!ParseEccPrivateKeyInfo(buffer, length, &key, &curve_)) return false;
|
|
key_ = key.release();
|
|
return true;
|
|
}
|
|
|
|
bool EccPrivateKey::InitFromCurve(EccCurve curve) {
|
|
const EC_GROUP* group = GetEcGroup(curve);
|
|
if (group == nullptr) {
|
|
LOGE("Failed to get ECC group");
|
|
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;
|
|
}
|
|
// Generate random key.
|
|
if (!EC_KEY_generate_key(key.get())) {
|
|
LOGE("Failed to generate random key");
|
|
return false;
|
|
}
|
|
curve_ = curve;
|
|
// 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();
|
|
return true;
|
|
}
|
|
} // namespace util
|
|
} // namespace wvoec
|