From f934d6fb41f5e75fb9fbd44fb74be2be1a2cd803 Mon Sep 17 00:00:00 2001 From: Adam Langley Date: Tue, 31 Mar 2015 15:02:25 -0700 Subject: [PATCH] widevine: update to work with BoringSSL. This change: 1) Switches the Makefiles over to using LOCAL_STATIC_LIBRARIES, which I understand is the new hotness, rather than setting a -I flag directly. 2) Switches to the non-deprecated _ex versions for EVP_EncryptFinal. 3) Uses the EVP_PKEY interface for checking PSS signatures. This is the only supported interface in OpenSSL: the PSS padding check functions are only exported in upstream OpenSSL because it's a library from the 90s and they don't have a concept of "unexported". Also, by using the EVP functions, OpenSSL/BoringSSL can do a better job of being constant-time. Since there aren't any obvious tests for checking that the signtaure verification still works, I tested with the code in the referenced paste, which includes both the old and new verification functions and checks that they both work on a sample signature. (And I also checked that they both fail when a bit in the signature is changed.) https://paste.googleplex.com/5747976139964416 (cherry picked from commit 4f01ef23d18f8034870cb6907dc1088dc074fad2) Change-Id: Iae7409c53eeea9c3892a32c180d7181d72467dcb --- libwvdrmengine/cdm/Android.mk | 3 +- .../cdm/core/src/privacy_crypto.cpp | 111 ++++++++++++------ libwvdrmengine/mediacrypto/Android.mk | 3 +- .../oemcrypto/test/oemcrypto_test.cpp | 81 +++++++++---- proprietary/cryptoPlugin/Android.mk | 2 +- 5 files changed, 135 insertions(+), 65 deletions(-) diff --git a/libwvdrmengine/cdm/Android.mk b/libwvdrmengine/cdm/Android.mk index b0cdeb17..109b48ca 100644 --- a/libwvdrmengine/cdm/Android.mk +++ b/libwvdrmengine/cdm/Android.mk @@ -11,10 +11,9 @@ LOCAL_C_INCLUDES := \ vendor/widevine/libwvdrmengine/third_party/stringencoders/src LOCAL_C_INCLUDES += \ - external/openssl/include \ external/protobuf/src -LOCAL_STATIC_LIBRARIES := libcdm_protos +LOCAL_STATIC_LIBRARIES := libcdm_protos libcrypto_static LOCAL_ADDITIONAL_DEPENDENCIES := $(cdm_proto_gen_headers) SRC_DIR := src diff --git a/libwvdrmengine/cdm/core/src/privacy_crypto.cpp b/libwvdrmengine/cdm/core/src/privacy_crypto.cpp index 70bfd5d3..9425e593 100644 --- a/libwvdrmengine/cdm/core/src/privacy_crypto.cpp +++ b/libwvdrmengine/cdm/core/src/privacy_crypto.cpp @@ -108,8 +108,8 @@ bool AesCbcKey::Encrypt(const std::string& in, std::string* out, } int padding = 0; - if (EVP_EncryptFinal(&ctx, reinterpret_cast(&(*out)[out_length]), - &padding) == 0) { + if (EVP_EncryptFinal_ex(&ctx, reinterpret_cast(&(*out)[out_length]), + &padding) == 0) { LOGE("AesCbcKey::Encrypt: PKCS7 padding failure: %s", ERR_error_string(ERR_get_error(), NULL)); return false; @@ -180,6 +180,64 @@ bool RsaPublicKey::Encrypt(const std::string& clear_message, return true; } +// LogOpenSSLError is a callback from OpenSSL which is called with each error +// in the thread's error queue. +static int LogOpenSSLError(const char *msg, size_t len, void *ctx) { + LOGE(" %s", msg); + return 1; +} + +static bool VerifyPSSSignature(EVP_PKEY *pkey, const std::string &message, + const std::string &signature) { + EVP_MD_CTX ctx; + EVP_MD_CTX_init(&ctx); + EVP_PKEY_CTX *pctx = NULL; + + if (EVP_DigestVerifyInit(&ctx, &pctx, EVP_sha1(), NULL /* no ENGINE */, + pkey) != 1) { + LOGE("EVP_DigestVerifyInit failed in VerifyPSSSignature"); + goto err; + } + + if (EVP_PKEY_CTX_set_signature_md(pctx, EVP_sha1()) != 1) { + LOGE("EVP_PKEY_CTX_set_signature_md failed in VerifyPSSSignature"); + goto err; + } + + if (EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING) != 1) { + LOGE("EVP_PKEY_CTX_set_rsa_padding failed in VerifyPSSSignature"); + goto err; + } + + if (EVP_PKEY_CTX_set_rsa_pss_saltlen(pctx, kPssSaltLength) != 1) { + LOGE("EVP_PKEY_CTX_set_rsa_pss_saltlen failed in VerifyPSSSignature"); + goto err; + } + + if (EVP_DigestVerifyUpdate(&ctx, message.data(), message.size()) != 1) { + LOGE("EVP_DigestVerifyUpdate failed in VerifyPSSSignature"); + goto err; + } + + if (EVP_DigestVerifyFinal( + &ctx, const_cast( + reinterpret_cast(signature.data())), + signature.size()) != 1) { + LOGE( + "EVP_DigestVerifyFinal failed in VerifyPSSSignature. (Probably a bad " + "signature.)"); + goto err; + } + + EVP_MD_CTX_cleanup(&ctx); + return true; + +err: + ERR_print_errors_cb(LogOpenSSLError, NULL); + EVP_MD_CTX_cleanup(&ctx); + return false; +} + bool RsaPublicKey::VerifySignature(const std::string& message, const std::string& signature) { if (serialized_key_.empty()) { @@ -190,50 +248,25 @@ bool RsaPublicKey::VerifySignature(const std::string& message, LOGE("RsaPublicKey::VerifySignature: signed message is empty"); return false; } - RSA* key = GetKey(serialized_key_); - if (key == NULL) { + RSA* rsa_key = GetKey(serialized_key_); + if (rsa_key == NULL) { // Error already logged by GetKey. return false; } - - int rsa_size = RSA_size(key); - if (static_cast(signature.size()) != rsa_size) { - LOGE( - "RsaPublicKey::VerifySignature: message signature is of the wrong " - "size (expected %d, actual %d)", - rsa_size, signature.size()); - FreeKey(key); + EVP_PKEY *pkey = EVP_PKEY_new(); + if (pkey == NULL || + EVP_PKEY_set1_RSA(pkey, rsa_key) != 1) { + FreeKey(rsa_key); + LOGE("RsaPublicKey::VerifySignature: failed to wrap key in an EVP_PKEY"); return false; } + FreeKey(rsa_key); - // Decrypt the signature. - std::string padded_digest(signature.size(), 0); - if (RSA_public_decrypt( - signature.size(), - const_cast( - reinterpret_cast(signature.data())), - reinterpret_cast(&padded_digest[0]), key, - RSA_NO_PADDING) != rsa_size) { - LOGE("RsaPublicKey::VerifySignature: RSA public decrypt failure: %s", - ERR_error_string(ERR_get_error(), NULL)); - FreeKey(key); - return false; - } + const bool ok = VerifyPSSSignature(pkey, message, signature); + EVP_PKEY_free(pkey); - // Hash the message using SHA1. - std::string message_digest(SHA_DIGEST_LENGTH, 0); - SHA1(reinterpret_cast(message.data()), message.size(), - reinterpret_cast(&message_digest[0])); - - // Verify PSS padding. - if (RSA_verify_PKCS1_PSS( - key, reinterpret_cast(message_digest.data()), - EVP_sha1(), - reinterpret_cast(padded_digest.data()), - kPssSaltLength) == 0) { - LOGE("RsaPublicKey::VerifySignature: RSA verify failure: %s", - ERR_error_string(ERR_get_error(), NULL)); - FreeKey(key); + if (!ok) { + LOGE("RsaPublicKey::VerifySignature: RSA verify failure"); return false; } diff --git a/libwvdrmengine/mediacrypto/Android.mk b/libwvdrmengine/mediacrypto/Android.mk index 11ccb613..0a350667 100644 --- a/libwvdrmengine/mediacrypto/Android.mk +++ b/libwvdrmengine/mediacrypto/Android.mk @@ -5,7 +5,6 @@ LOCAL_SRC_FILES := \ src/WVCryptoPlugin.cpp \ LOCAL_C_INCLUDES := \ - external/openssl/include \ frameworks/av/include \ frameworks/native/include \ vendor/widevine/libwvdrmengine/cdm/core/include \ @@ -22,4 +21,6 @@ LOCAL_MODULE_TARGET_ARCH := arm mips x86 LOCAL_CXX_STL := stlport +LOCAL_STATIC_LIBRARIES := libcrypto_static + include $(BUILD_STATIC_LIBRARY) diff --git a/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp b/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp index c246ea54..9874b79f 100644 --- a/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp +++ b/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp @@ -886,7 +886,8 @@ static const uint8_t kTestRSAPublicKey3_2048[] = { static void dump_openssl_error() { while (unsigned long err = ERR_get_error()) { char buffer[120]; - cout << "openssl error -- " << ERR_error_string(err, buffer) << "\n"; + ERR_error_string_n(err, buffer, sizeof(buffer)); + cout << "openssl error -- " << buffer << "\n"; } } @@ -1366,6 +1367,57 @@ class Session { } } + static bool VerifyPSSSignature(EVP_PKEY* pkey, const uint8_t* message, + size_t message_length, + const uint8_t* signature, + size_t signature_length) { + EVP_MD_CTX ctx; + EVP_MD_CTX_init(&ctx); + EVP_PKEY_CTX* pctx = NULL; + + if (EVP_DigestVerifyInit(&ctx, &pctx, EVP_sha1(), NULL /* no ENGINE */, + pkey) != 1) { + LOGE("EVP_DigestVerifyInit failed in VerifyPSSSignature"); + goto err; + } + + if (EVP_PKEY_CTX_set_signature_md(pctx, EVP_sha1()) != 1) { + LOGE("EVP_PKEY_CTX_set_signature_md failed in VerifyPSSSignature"); + goto err; + } + + if (EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING) != 1) { + LOGE("EVP_PKEY_CTX_set_rsa_padding failed in VerifyPSSSignature"); + goto err; + } + + if (EVP_PKEY_CTX_set_rsa_pss_saltlen(pctx, SHA_DIGEST_LENGTH) != 1) { + LOGE("EVP_PKEY_CTX_set_rsa_pss_saltlen failed in VerifyPSSSignature"); + goto err; + } + + if (EVP_DigestVerifyUpdate(&ctx, message, message_length) != 1) { + LOGE("EVP_DigestVerifyUpdate failed in VerifyPSSSignature"); + goto err; + } + + if (EVP_DigestVerifyFinal(&ctx, const_cast(signature), + signature_length) != 1) { + LOGE( + "EVP_DigestVerifyFinal failed in VerifyPSSSignature. (Probably a bad " + "signature.)"); + goto err; + } + + EVP_MD_CTX_cleanup(&ctx); + return true; + + err: + dump_openssl_error(); + EVP_MD_CTX_cleanup(&ctx); + return false; + } + void VerifyRSASignature(const uint8_t* message, size_t message_length, const uint8_t* signature, size_t signature_length, RSA_Padding_Scheme padding_scheme) { @@ -1376,28 +1428,13 @@ class Session { << RSA_size(public_rsa_) << "\n"; if (padding_scheme == kSign_RSASSA_PSS) { - // Hash the message using SHA1. - uint8_t hash[SHA_DIGEST_LENGTH]; - if (!SHA1(message, message_length, hash)) { - dump_openssl_error(); - EXPECT_TRUE(false) << "Error computing SHA1. "; - } + EVP_PKEY *pkey = EVP_PKEY_new(); + ASSERT_TRUE(EVP_PKEY_set1_RSA(pkey, public_rsa_) == 1); - // Decrypt signature to padded digest. - uint8_t padded_digest[signature_length]; - int status; - status = RSA_public_decrypt(signature_length, signature, padded_digest, - public_rsa_, RSA_NO_PADDING); - if (status == -1) { - dump_openssl_error(); - EXPECT_TRUE(false) << "VerifyRSASignature. in RSA_Public_digest "; - } - status = RSA_verify_PKCS1_PSS(public_rsa_, hash, EVP_sha1(), - padded_digest, SHA_DIGEST_LENGTH); - if (status != 1) { - dump_openssl_error(); - EXPECT_TRUE(false) << "VerifyRSASignature. in RSA_verify_PKCS1_PSS "; - } + const bool ok = VerifyPSSSignature(pkey, message, message_length, + signature, signature_length); + EVP_PKEY_free(pkey); + EXPECT_TRUE(ok) << "PSS signature check failed"; } else if (padding_scheme == kSign_PKCS1_Block1) { uint8_t padded_digest[signature_length]; int status; diff --git a/proprietary/cryptoPlugin/Android.mk b/proprietary/cryptoPlugin/Android.mk index 85ed6fee..79016d8a 100644 --- a/proprietary/cryptoPlugin/Android.mk +++ b/proprietary/cryptoPlugin/Android.mk @@ -10,12 +10,12 @@ LOCAL_SRC_FILES := \ LOCAL_C_INCLUDES := \ $(TOP)/vendor/widevine/proprietary/wvm/include \ - $(TOP)/external/openssl/include LOCAL_CFLAGS += -Wno-unused-parameter LOCAL_MODULE:= libwvdecryptcommon LOCAL_MODULE_TAGS := optional +LOCAL_STATIC_LIBRARIES := libcrypto_static # Not 64-bit compatible, WVCryptoPlugin::decrypt stores a pointer in a uint32 LOCAL_MULTILIB := 32