// 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 #include #include #include #include #include #include #include #include #include #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; using ScopedBigNumCtx = ScopedObject; using ScopedBio = ScopedObject; using ScopedEcKey = ScopedObject; using ScopedEvpMdCtx = ScopedObject; using ScopedEvpPkey = ScopedObject; using ScopedPrivateKeyInfo = ScopedObject; using ScopedSigPoint = ScopedObject; 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 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(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* 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(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 digest; if (!DigestMessage(kEccSecp256r1 /* SHA-256 */, reinterpret_cast(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(key) /* Does not get modified */, &der_key_raw); if (der_res < 0) { LOGE("Public key serialization failed"); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } ScopedObject 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(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 SerializeEccPublicKey(const EC_KEY* key) { size_t key_size = kPublicKeySize; std::vector 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(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(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(curve)) + ")"; } // static std::unique_ptr EccPublicKey::New( const EccPrivateKey& private_key) { std::unique_ptr 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::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 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::Load(const std::string& buffer) { if (buffer.empty()) { LOGE("Provided public key buffer is empty"); return std::unique_ptr(); } return Load(reinterpret_cast(buffer.data()), buffer.size()); } // static std::unique_ptr EccPublicKey::Load( const std::vector& buffer) { if (buffer.empty()) { LOGE("Provided public key buffer is empty"); return std::unique_ptr(); } return Load(buffer.data(), buffer.size()); } // static std::unique_ptr 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 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::LoadPrivateKeyInfo( const std::string& buffer) { if (buffer.empty()) { LOGE("Provided public key buffer is empty"); return std::unique_ptr(); } return LoadPrivateKeyInfo(reinterpret_cast(buffer.data()), buffer.size()); } // static std::unique_ptr EccPublicKey::LoadPrivateKeyInfo( const std::vector& buffer) { if (buffer.empty()) { LOGE("Provided public key buffer is empty"); return std::unique_ptr(); } 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 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 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(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(message.data()), message.size(), reinterpret_cast(signature.data()), signature.size()); } OEMCryptoResult EccPublicKey::VerifySignature( const std::vector& message, const std::vector& 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::New(EccCurve curve) { std::unique_ptr key(new EccPrivateKey()); if (!key->InitFromCurve(curve)) { LOGE("Failed to initialize private key from curve"); key.reset(); } return key; } // static std::unique_ptr 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 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::Load(const std::string& buffer) { if (buffer.empty()) { LOGE("Provided private key buffer is empty"); return std::unique_ptr(); } return Load(reinterpret_cast(buffer.data()), buffer.size()); } // static std::unique_ptr EccPrivateKey::Load( const std::vector& buffer) { if (buffer.empty()) { LOGE("Provided private key buffer is empty"); return std::unique_ptr(); } return Load(buffer.data(), buffer.size()); } std::unique_ptr 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(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 EccPrivateKey::Serialize() const { size_t key_size = kPrivateKeySize; std::vector 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(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 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 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(digest.size()), key_)); if (!sig_point) { LOGE("Failed to perform ECDSA"); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } // Step 3: Serialize std::vector 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(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 EccPrivateKey::GenerateSignature( const std::string& message) const { size_t signature_size = SignatureSize(); std::vector signature(signature_size, 0); const OEMCryptoResult res = GenerateSignature(reinterpret_cast(message.data()), message.size(), signature.data(), &signature_size); if (res != OEMCrypto_SUCCESS) { LOGE("Failed to generate signature: result = %d", static_cast(res)); signature.clear(); } else { signature.resize(signature_size); } return signature; } std::vector EccPrivateKey::GenerateSignature( const std::vector& message) const { size_t signature_size = SignatureSize(); std::vector 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(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(res) != kEccSessionKeySize) { LOGE("Unexpected key size: size = %d", res); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } *session_key_size = kEccSessionKeySize; return OEMCrypto_SUCCESS; } std::vector EccPrivateKey::DeriveSessionKey( const EccPublicKey& public_key) const { size_t session_key_size = kEccSessionKeySize; std::vector 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(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