199 lines
6.5 KiB
C++
199 lines
6.5 KiB
C++
////////////////////////////////////////////////////////////////////////////////
|
|
// Copyright 2019 Google LLC.
|
|
//
|
|
// This software is licensed under the terms defined in the Widevine Master
|
|
// License Agreement. For a copy of this agreement, please contact
|
|
// widevine-licensing@google.com.
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Description:
|
|
// Implementation of EC utility functions.
|
|
|
|
#include "common/ec_util.h"
|
|
|
|
#include <cstdint>
|
|
#include <string>
|
|
|
|
#include "glog/logging.h"
|
|
#include "absl/memory/memory.h"
|
|
#include "openssl/ec.h"
|
|
#include "openssl/ec_key.h"
|
|
#include "openssl/pem.h"
|
|
#include "common/ec_key.h"
|
|
#include "common/openssl_util.h"
|
|
#include "common/private_key_util.h"
|
|
|
|
namespace widevine {
|
|
namespace ec_util {
|
|
|
|
namespace {
|
|
// Expected X9.62 format. <tag><X><Y>.
|
|
// tag = 0x04 indicating uncompressed format.
|
|
// size of X and Y equal. But differ per curve.
|
|
// Secp256R1 32 bytes per value.
|
|
// Secp384R1 48 bytes per value.
|
|
// Secp521R1 66 bytes per value.
|
|
const size_t kSecp256R1ExpectedPubKeySize = 256 / 8 * 2 + 1; // 65.
|
|
const size_t kSecp384R1ExpectedPubKeySize = 384 / 8 * 2 + 1; // 97.
|
|
// 521 has to be rounded up to next block size, 528.
|
|
const size_t kSecp521R1ExpectedPubKeySize = (521 + 7) / 8 * 2 + 1; // 133.
|
|
|
|
std::string OpenSSLErrorString(uint32_t error) {
|
|
char buf[ERR_ERROR_STRING_BUF_LEN];
|
|
ERR_error_string_n(error, buf, sizeof(buf));
|
|
return buf;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
bool SerializeECPrivateKey(const EC_KEY* private_key,
|
|
std::string* serialized_private_key) {
|
|
return private_key_util::SerializeKey<EC_KEY>(
|
|
private_key, i2d_ECPrivateKey_bio, serialized_private_key);
|
|
}
|
|
|
|
bool DeserializeECPrivateKey(const std::string& serialized_private_key,
|
|
EC_KEY** private_key) {
|
|
return private_key_util::DeserializeKey<EC_KEY>(
|
|
serialized_private_key, d2i_ECPrivateKey_bio, private_key);
|
|
}
|
|
|
|
bool SerializeECPublicKey(const EC_KEY* public_key,
|
|
std::string* serialized_public_key) {
|
|
return private_key_util::SerializeKey<EC_KEY>(public_key, i2d_EC_PUBKEY_bio,
|
|
serialized_public_key);
|
|
}
|
|
|
|
bool DeserializeECPublicKey(const std::string& serialized_public_key,
|
|
EC_KEY** public_key) {
|
|
return private_key_util::DeserializeKey<EC_KEY>(
|
|
serialized_public_key, d2i_EC_PUBKEY_bio, public_key);
|
|
}
|
|
|
|
size_t GetPublicKeyPointSize(ECPrivateKey::EllipticCurve curve) {
|
|
switch (curve) {
|
|
case widevine::ECPrivateKey::SECP256R1:
|
|
return kSecp256R1ExpectedPubKeySize;
|
|
case widevine::ECPrivateKey::SECP384R1:
|
|
return kSecp384R1ExpectedPubKeySize;
|
|
case widevine::ECPrivateKey::SECP521R1:
|
|
return kSecp521R1ExpectedPubKeySize;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
bool GetPublicKeyPoint(const EC_KEY* ec_key, std::string* encoded_key) {
|
|
CHECK(ec_key);
|
|
CHECK(encoded_key);
|
|
const EC_POINT* public_key = EC_KEY_get0_public_key(ec_key);
|
|
if (public_key == nullptr) {
|
|
LOG(ERROR) << "Could not retrieve EC_POINT from EC_KEY.";
|
|
return false;
|
|
}
|
|
|
|
size_t len =
|
|
EC_POINT_point2oct(EC_KEY_get0_group(ec_key), public_key,
|
|
POINT_CONVERSION_UNCOMPRESSED, nullptr, 0, nullptr);
|
|
if (len == 0) {
|
|
LOG(ERROR) << "Unexpected error getting the size of the public key.";
|
|
return false;
|
|
}
|
|
encoded_key->resize(len);
|
|
uint8_t* encoded_data = reinterpret_cast<uint8_t*>(&(*encoded_key)[0]);
|
|
len = EC_POINT_point2oct(EC_KEY_get0_group(ec_key), public_key,
|
|
POINT_CONVERSION_UNCOMPRESSED, encoded_data,
|
|
encoded_key->size(), nullptr);
|
|
if (len == 0) {
|
|
LOG(ERROR) << "Unexpected error encoding the public key.";
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool GetPublicKeyFromKeyPoint(ECPrivateKey::EllipticCurve curve,
|
|
const std::string& key_point,
|
|
EC_KEY** public_key) {
|
|
CHECK(public_key);
|
|
ScopedECGROUP group(EC_GROUP_new_by_curve_name(CurveToNid(curve)));
|
|
if (group == nullptr) {
|
|
LOG(ERROR) << "Could not load the EC Group for curve " << curve;
|
|
return false;
|
|
}
|
|
|
|
ScopedECPOINT point(EC_POINT_new(group.get()));
|
|
if (EC_POINT_oct2point(group.get(), point.get(),
|
|
reinterpret_cast<const uint8_t*>(key_point.data()),
|
|
key_point.size(), nullptr) == 0) {
|
|
LOG(ERROR) << "Failed to convert the serialized point to EC_POINT.";
|
|
return false;
|
|
}
|
|
|
|
ScopedECKEY key(EC_KEY_new_by_curve_name(ec_util::CurveToNid(curve)));
|
|
if (EC_KEY_set_public_key(key.get(), point.get()) == 0) {
|
|
LOG(ERROR) << "Failed to convert the EC_POINT to EC_KEY";
|
|
return false;
|
|
}
|
|
|
|
*public_key = key.release();
|
|
return true;
|
|
}
|
|
|
|
ECPrivateKey::EllipticCurve NidToCurve(int nid) {
|
|
switch (nid) {
|
|
case NID_X9_62_prime256v1:
|
|
return ECPrivateKey::SECP256R1;
|
|
case NID_secp384r1:
|
|
return ECPrivateKey::SECP384R1;
|
|
case NID_secp521r1:
|
|
return ECPrivateKey::SECP521R1;
|
|
default:
|
|
LOG(ERROR) << "Unaccepted nid: " << nid;
|
|
return ECPrivateKey::UNDEFINED_CURVE;
|
|
}
|
|
}
|
|
|
|
int CurveToNid(ECPrivateKey::EllipticCurve curve) {
|
|
switch (curve) {
|
|
case ECPrivateKey::SECP256R1:
|
|
// This is the ANSI X9.62 name for secp256r1. openssl uses prime256v1
|
|
// instead of secp256r1.
|
|
// https://tools.ietf.org/search/rfc4492#appendix-A
|
|
return NID_X9_62_prime256v1;
|
|
case ECPrivateKey::SECP384R1:
|
|
return NID_secp384r1;
|
|
case ECPrivateKey::SECP521R1:
|
|
return NID_secp521r1;
|
|
default:
|
|
LOG(ERROR) << "EllipticCurve " << curve << " not accepted";
|
|
return NID_undef;
|
|
}
|
|
}
|
|
|
|
// TODO(user): Benchmark ecc key generation and evaluate whether additional
|
|
// changes are needed to improve the implementation. Currently an ephemeral key
|
|
// pair is generated per license request. We will also test for the impact of
|
|
// multiple threads generating keys.
|
|
ScopedECKEY GenerateKeyWithCurve(ECPrivateKey::EllipticCurve curve) {
|
|
if (curve == ECPrivateKey::UNDEFINED_CURVE) {
|
|
LOG(ERROR) << "Requested key uses unsupported curve";
|
|
return nullptr;
|
|
}
|
|
ScopedECKEY new_ec_key(EC_KEY_new_by_curve_name(ec_util::CurveToNid(curve)));
|
|
if (EC_KEY_generate_key(new_ec_key.get()) == 0) {
|
|
LOG(ERROR) << "Unable to generate private key: "
|
|
<< OpenSSLErrorString(ERR_get_error());
|
|
return nullptr;
|
|
}
|
|
if (EC_KEY_check_key(new_ec_key.get()) == 0) {
|
|
LOG(ERROR) << "Invalid private EC key: "
|
|
<< OpenSSLErrorString(ERR_get_error());
|
|
return nullptr;
|
|
}
|
|
return new_ec_key;
|
|
}
|
|
|
|
} // namespace ec_util
|
|
} // namespace widevine
|