Files
2020-01-27 16:05:15 -08:00

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