//////////////////////////////////////////////////////////////////////////////// // 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: // Definition of elliptic curve public and private key classes. #include "common/ec_key.h" #include #include "glog/logging.h" #include "absl/memory/memory.h" #include "openssl/base.h" #include "openssl/bn.h" #include "openssl/ec.h" #include "openssl/ecdh.h" #include "openssl/ecdsa.h" #include "openssl/err.h" #include "openssl/evp.h" #include "openssl/sha.h" #include "common/aes_cbc_util.h" #include "common/ec_util.h" #include "common/hash_algorithm.h" #include "common/openssl_util.h" #include "common/sha_util.h" namespace widevine { namespace { void* SHA256KDF(const void* in, size_t inlen, void* out, size_t* outlen) { std::string shared_session_key(static_cast(in), inlen); std::string derived_shared_session_key = Sha256_Hash(shared_session_key); if (*outlen != SHA256_DIGEST_LENGTH) { return nullptr; } memcpy(out, derived_shared_session_key.data(), *outlen); return out; } ECPrivateKey::EllipticCurve ECKeyToCurve(const EC_KEY* key) { if (key == nullptr) { return ECPrivateKey::UNDEFINED_CURVE; } return ec_util::NidToCurve(EC_GROUP_get_curve_name(EC_KEY_get0_group(key))); } std::string OpenSSLErrorString(uint32_t error) { char buf[ERR_ERROR_STRING_BUF_LEN]; ERR_error_string_n(error, buf, sizeof(buf)); return buf; } std::string GetMessageDigest(const std::string& message, widevine::HashAlgorithm hash_algorithm) { switch (hash_algorithm) { case widevine::HashAlgorithm::kUnspecified: case widevine::HashAlgorithm::kSha256: return widevine::Sha256_Hash(message); case widevine::HashAlgorithm::kSha384: return widevine::Sha384_Hash(message); case widevine::HashAlgorithm::kSha1: LOG(ERROR) << "Unexpected hash algorithm: " << static_cast(hash_algorithm); return ""; } LOG(FATAL) << "Unexpected hash algorithm: " << static_cast(hash_algorithm); return ""; } } // namespace ECPrivateKey::ECPrivateKey(EC_KEY* ec_key) : key_(ec_key) { CHECK(key() != nullptr); CHECK(EC_KEY_get0_private_key(key()) != nullptr); CHECK_NE(ECKeyToCurve(key()), ECPrivateKey::UNDEFINED_CURVE); CHECK_EQ(EC_KEY_check_key(key()), 1); } ECPrivateKey::ECPrivateKey(ScopedECKEY ec_key) : key_(std::move(ec_key)) {} ECPrivateKey::ECPrivateKey(const ECPrivateKey& ec_key) : ECPrivateKey(EC_KEY_dup(ec_key.key())) {} std::unique_ptr ECPrivateKey::Create( const std::string& serialized_key) { EC_KEY* key; if (!ec_util::DeserializeECPrivateKey(serialized_key, &key)) { return nullptr; } ScopedECKEY scoped_ec_key(key); if (EC_KEY_check_key(scoped_ec_key.get()) != 1) { LOG(ERROR) << "Invalid private EC key: " << OpenSSLErrorString(ERR_get_error()); return nullptr; } if (ECKeyToCurve(scoped_ec_key.get()) == ECPrivateKey::UNDEFINED_CURVE) { LOG(ERROR) << "Key has unsupported curve"; return nullptr; } return absl::make_unique(scoped_ec_key.release()); } std::unique_ptr ECPrivateKey::PublicKey() const { if (key_ == nullptr) { return nullptr; } return absl::make_unique(ScopedECKEY(EC_KEY_dup(key_.get()))); } bool ECPrivateKey::DeriveSharedSessionKey( const ECPublicKey& public_key, std::string* derived_shared_session_key) const { if (derived_shared_session_key == nullptr) { LOG(ERROR) << "|derived_shared_session_key| cannot be nullptr"; return false; } const EC_GROUP* group1 = EC_KEY_get0_group(key()); const EC_GROUP* group2 = EC_KEY_get0_group(public_key.key()); if (EC_GROUP_cmp(group1, group2, nullptr) != 0) { LOG(ERROR) << "EC_GROUPs do not match"; return false; } const EC_POINT* public_key_point = EC_KEY_get0_public_key(public_key.key()); derived_shared_session_key->resize(SHA256_DIGEST_LENGTH, 0); int result = ECDH_compute_key( reinterpret_cast( const_cast(derived_shared_session_key->data())), derived_shared_session_key->size(), public_key_point, key(), SHA256KDF); if (result == -1) { LOG(ERROR) << "Could not write shared session key: " << OpenSSLErrorString(ERR_get_error()); return false; } if (result != SHA256_DIGEST_LENGTH) { LOG(ERROR) << "Wrote " << result << " bytes to derived shared session key instead of " << SHA256_DIGEST_LENGTH << " bytes"; return false; } return true; } bool ECPrivateKey::GenerateSignature(const std::string& message, HashAlgorithm hash_algorithm, std::string* signature) const { if (message.empty()) { LOG(ERROR) << "|message| cannot be empty"; return false; } if (signature == nullptr) { LOG(ERROR) << "|signature| cannot be nullptr"; return false; } // Hash the message using corresponding hash algorithm. std::string message_digest = GetMessageDigest(message, hash_algorithm); if (message_digest.empty()) { LOG(ERROR) << "Empty message digest"; return false; } size_t max_signature_size = ECDSA_size(key()); if (max_signature_size == 0) { LOG(ERROR) << "key_ does not have a group set"; return false; } signature->resize(max_signature_size); unsigned int bytes_written = 0; int result = ECDSA_sign( 0 /* unused type */, reinterpret_cast(message_digest.data()), message_digest.size(), reinterpret_cast(const_cast(signature->data())), &bytes_written, key()); if (result != 1) { LOG(ERROR) << "Could not calculate signature: " << OpenSSLErrorString(ERR_get_error()); return false; } signature->resize(bytes_written); return true; } bool ECPrivateKey::MatchesPrivateKey(const ECPrivateKey& private_key) const { return BN_cmp(EC_KEY_get0_private_key(key()), EC_KEY_get0_private_key(private_key.key())) == 0; } bool ECPrivateKey::MatchesPublicKey(const ECPublicKey& public_key) const { return EC_POINT_cmp(EC_KEY_get0_group(key()), EC_KEY_get0_public_key(key()), EC_KEY_get0_public_key(public_key.key()), nullptr) == 0; } ECPrivateKey::EllipticCurve ECPrivateKey::Curve() const { return ECKeyToCurve(key()); } bool ECPrivateKey::SerializedKey(std::string* serialized_key) const { CHECK(serialized_key); return ec_util::SerializeECPrivateKey(key_.get(), serialized_key); } bool ECPrivateKey::GetRawPrivateKey(std::string* raw_private_key) const { if (raw_private_key == nullptr) { LOG(WARNING) << "|raw_private_key| cannot be nullptr."; return false; } const EC_GROUP* group = EC_KEY_get0_group(key()); if (group == nullptr) { LOG(WARNING) << "Failed to load EC key group."; return false; } int key_size_bits = EC_GROUP_get_degree(group); if (key_size_bits == 0) { LOG(WARNING) << "Key size is 0."; return false; } const BIGNUM* key_bn = EC_KEY_get0_private_key(key()); int key_size_bytes = (key_size_bits + 7) / 8; std::vector tmp_raw_private_key_data(key_size_bytes); int num_bytes = BN_bn2bin(key_bn, tmp_raw_private_key_data.data()); if (num_bytes != key_size_bytes) { LOG(WARNING) << "Failed to convert EC private key BIGNUM to binary data."; return false; } raw_private_key->assign(tmp_raw_private_key_data.begin(), tmp_raw_private_key_data.end()); return true; } ECPublicKey::ECPublicKey(EC_KEY* ec_key) : key_(ec_key) { CHECK(key() != nullptr); CHECK(EC_KEY_get0_private_key(key()) == nullptr); CHECK_NE(ECKeyToCurve(key()), ECPrivateKey::UNDEFINED_CURVE); CHECK_EQ(EC_KEY_check_key(key()), 1); } ECPublicKey::ECPublicKey(ScopedECKEY ec_key) : key_(std::move(ec_key)) {} ECPublicKey::ECPublicKey(const ECPublicKey& ec_key) : ECPublicKey(EC_KEY_dup(ec_key.key())) {} std::unique_ptr ECPublicKey::Create( const std::string& serialized_key) { EC_KEY* key; if (!ec_util::DeserializeECPublicKey(serialized_key, &key)) { return nullptr; } ScopedECKEY scoped_ec_key(key); if (EC_KEY_check_key(scoped_ec_key.get()) != 1) { LOG(ERROR) << "Invalid public EC key: " << OpenSSLErrorString(ERR_get_error()); return nullptr; } if (ECKeyToCurve(scoped_ec_key.get()) == ECPrivateKey::UNDEFINED_CURVE) { LOG(ERROR) << "Key has unsupported curve"; return nullptr; } return absl::make_unique(scoped_ec_key.release()); } std::unique_ptr ECPublicKey::CreateFromKeyPoint( ECPrivateKey::EllipticCurve curve, const std::string& key_point) { EC_KEY* key; if (!ec_util::GetPublicKeyFromKeyPoint(curve, key_point, &key)) { return nullptr; } ScopedECKEY scoped_ec_key(key); if (EC_KEY_check_key(scoped_ec_key.get()) != 1) { LOG(ERROR) << "Invalid public EC key: " << OpenSSLErrorString(ERR_get_error()); return nullptr; } if (ECKeyToCurve(scoped_ec_key.get()) == ECPrivateKey::UNDEFINED_CURVE) { LOG(ERROR) << "Key has unsupported curve"; return nullptr; } return absl::make_unique(scoped_ec_key.release()); } bool ECPublicKey::VerifySignature(const std::string& message, HashAlgorithm hash_algorithm, const std::string& signature) const { if (message.empty()) { LOG(ERROR) << "|message| cannot be empty"; return false; } if (signature.empty()) { LOG(ERROR) << "|signature| cannot be empty"; return false; } // Hash the message using corresponding hash algorithm. std::string message_digest = GetMessageDigest(message, hash_algorithm); if (message_digest.empty()) { LOG(ERROR) << "Empty message digest"; return false; } int result = ECDSA_verify( 0 /* unused type */, reinterpret_cast(message_digest.data()), message_digest.size(), reinterpret_cast(const_cast(signature.data())), signature.size(), key()); if (result != 1) { LOG(ERROR) << "Could not verify signature: " << OpenSSLErrorString(ERR_get_error()); return false; } return true; } bool ECPublicKey::MatchesPrivateKey(const ECPrivateKey& private_key) const { return private_key.MatchesPublicKey(*this); } bool ECPublicKey::MatchesPublicKey(const ECPublicKey& public_key) const { return EC_POINT_cmp(EC_KEY_get0_group(key()), EC_KEY_get0_public_key(key()), EC_KEY_get0_public_key(public_key.key()), nullptr) == 0; } ECPrivateKey::EllipticCurve ECPublicKey::Curve() const { return ECKeyToCurve(key()); } bool ECPublicKey::SerializedKey(std::string* serialized_key) const { CHECK(serialized_key); return ec_util::SerializeECPublicKey(key_.get(), serialized_key); } bool ECPublicKey::GetPointEncodedKey(std::string* encoded_key) const { CHECK(encoded_key); return ec_util::GetPublicKeyPoint(key_.get(), encoded_key); } bool ECPublicKey::GetRawPublicKey(std::string* raw_public_key) const { if (raw_public_key == nullptr) { LOG(WARNING) << "|raw_public_key| cannot be nullptr."; return false; } std::string tmp_raw_public_key; if (!ec_util::GetPublicKeyPoint(key(), &tmp_raw_public_key) || tmp_raw_public_key.empty()) { LOG(WARNING) << "Failed to encoded EC_KEY"; } CHECK_EQ(tmp_raw_public_key[0], POINT_CONVERSION_UNCOMPRESSED); // Public key is X9.62 uncompressed format. Expected structure is , // where tag = 0x04 indicating uncompressed, and X and Y lengths are equal but // dependent on ECC curve. Tag is removed because Intel Sigma 2.1.0 library // requires only X and Y bytes. raw_public_key->assign(tmp_raw_public_key.begin() + 1, tmp_raw_public_key.end()); return true; } } // namespace widevine