//////////////////////////////////////////////////////////////////////////////// // 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 #include #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 = 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( 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( 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(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( 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(&(*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(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