Files
android/libwvdrmengine/oemcrypto/util/src/oemcrypto_ecc_key.cpp
Alex Dale 4a065adc33 Copied OEMCrypto utils to Android.
The OEMCrypto utils have been copied over from the CDM repo.
Tests have been excluded for this CL.

Files represent a snapshot taken from http://go/wvgerrit/148270
and http://go/wvgerrit/148372.

Bug: 205902021
Change-Id: I1a58952cd1436a48974367c5436bf7296163e6f1
2022-03-21 21:22:19 -07:00

932 lines
29 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>;
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;
}
// 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());
}
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;
}
// Step 1: 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;
}
// 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;
}
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());
}
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;
}
// 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