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

Change-Id: Iae7409c53eeea9c3892a32c180d7181d72467dcb
This commit is contained in:
Adam Langley
2015-03-31 15:02:25 -07:00
parent 5f70024388
commit 4f01ef23d1
5 changed files with 135 additions and 65 deletions

View File

@@ -11,10 +11,9 @@ LOCAL_C_INCLUDES := \
vendor/widevine/libwvdrmengine/third_party/stringencoders/src vendor/widevine/libwvdrmengine/third_party/stringencoders/src
LOCAL_C_INCLUDES += \ LOCAL_C_INCLUDES += \
external/openssl/include \
external/protobuf/src external/protobuf/src
LOCAL_STATIC_LIBRARIES := libcdm_protos LOCAL_STATIC_LIBRARIES := libcdm_protos libcrypto_static
LOCAL_ADDITIONAL_DEPENDENCIES := $(cdm_proto_gen_headers) LOCAL_ADDITIONAL_DEPENDENCIES := $(cdm_proto_gen_headers)
SRC_DIR := src SRC_DIR := src

View File

@@ -108,7 +108,7 @@ bool AesCbcKey::Encrypt(const std::string& in, std::string* out,
} }
int padding = 0; int padding = 0;
if (EVP_EncryptFinal(&ctx, reinterpret_cast<uint8_t*>(&(*out)[out_length]), if (EVP_EncryptFinal_ex(&ctx, reinterpret_cast<uint8_t*>(&(*out)[out_length]),
&padding) == 0) { &padding) == 0) {
LOGE("AesCbcKey::Encrypt: PKCS7 padding failure: %s", LOGE("AesCbcKey::Encrypt: PKCS7 padding failure: %s",
ERR_error_string(ERR_get_error(), NULL)); ERR_error_string(ERR_get_error(), NULL));
@@ -180,6 +180,64 @@ bool RsaPublicKey::Encrypt(const std::string& clear_message,
return true; 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<uint8_t *>(
reinterpret_cast<const uint8_t *>(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, bool RsaPublicKey::VerifySignature(const std::string& message,
const std::string& signature) { const std::string& signature) {
if (serialized_key_.empty()) { if (serialized_key_.empty()) {
@@ -190,50 +248,25 @@ bool RsaPublicKey::VerifySignature(const std::string& message,
LOGE("RsaPublicKey::VerifySignature: signed message is empty"); LOGE("RsaPublicKey::VerifySignature: signed message is empty");
return false; return false;
} }
RSA* key = GetKey(serialized_key_); RSA* rsa_key = GetKey(serialized_key_);
if (key == NULL) { if (rsa_key == NULL) {
// Error already logged by GetKey. // Error already logged by GetKey.
return false; return false;
} }
EVP_PKEY *pkey = EVP_PKEY_new();
int rsa_size = RSA_size(key); if (pkey == NULL ||
if (static_cast<int>(signature.size()) != rsa_size) { EVP_PKEY_set1_RSA(pkey, rsa_key) != 1) {
LOGE( FreeKey(rsa_key);
"RsaPublicKey::VerifySignature: message signature is of the wrong " LOGE("RsaPublicKey::VerifySignature: failed to wrap key in an EVP_PKEY");
"size (expected %d, actual %d)",
rsa_size, signature.size());
FreeKey(key);
return false; return false;
} }
FreeKey(rsa_key);
// Decrypt the signature. const bool ok = VerifyPSSSignature(pkey, message, signature);
std::string padded_digest(signature.size(), 0); EVP_PKEY_free(pkey);
if (RSA_public_decrypt(
signature.size(),
const_cast<unsigned char*>(
reinterpret_cast<const unsigned char*>(signature.data())),
reinterpret_cast<unsigned char*>(&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;
}
// Hash the message using SHA1. if (!ok) {
std::string message_digest(SHA_DIGEST_LENGTH, 0); LOGE("RsaPublicKey::VerifySignature: RSA verify failure");
SHA1(reinterpret_cast<const unsigned char*>(message.data()), message.size(),
reinterpret_cast<unsigned char*>(&message_digest[0]));
// Verify PSS padding.
if (RSA_verify_PKCS1_PSS(
key, reinterpret_cast<const unsigned char*>(message_digest.data()),
EVP_sha1(),
reinterpret_cast<const unsigned char*>(padded_digest.data()),
kPssSaltLength) == 0) {
LOGE("RsaPublicKey::VerifySignature: RSA verify failure: %s",
ERR_error_string(ERR_get_error(), NULL));
FreeKey(key);
return false; return false;
} }

View File

@@ -5,7 +5,6 @@ LOCAL_SRC_FILES := \
src/WVCryptoPlugin.cpp \ src/WVCryptoPlugin.cpp \
LOCAL_C_INCLUDES := \ LOCAL_C_INCLUDES := \
external/openssl/include \
frameworks/av/include \ frameworks/av/include \
frameworks/native/include \ frameworks/native/include \
vendor/widevine/libwvdrmengine/cdm/core/include \ vendor/widevine/libwvdrmengine/cdm/core/include \
@@ -20,4 +19,6 @@ LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_TARGET_ARCH := arm x86 LOCAL_MODULE_TARGET_ARCH := arm x86
LOCAL_STATIC_LIBRARIES := libcrypto_static
include $(BUILD_STATIC_LIBRARY) include $(BUILD_STATIC_LIBRARY)

View File

@@ -888,7 +888,8 @@ static const uint8_t kTestRSAPublicKey3_2048[] = {
static void dump_openssl_error() { static void dump_openssl_error() {
while (unsigned long err = ERR_get_error()) { while (unsigned long err = ERR_get_error()) {
char buffer[120]; 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";
} }
} }
@@ -1390,6 +1391,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<uint8_t*>(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, void VerifyRSASignature(const uint8_t* message, size_t message_length,
const uint8_t* signature, size_t signature_length, const uint8_t* signature, size_t signature_length,
RSA_Padding_Scheme padding_scheme) { RSA_Padding_Scheme padding_scheme) {
@@ -1400,28 +1452,13 @@ class Session {
<< RSA_size(public_rsa_) << "\n"; << RSA_size(public_rsa_) << "\n";
if (padding_scheme == kSign_RSASSA_PSS) { if (padding_scheme == kSign_RSASSA_PSS) {
// Hash the message using SHA1. EVP_PKEY *pkey = EVP_PKEY_new();
uint8_t hash[SHA_DIGEST_LENGTH]; ASSERT_TRUE(EVP_PKEY_set1_RSA(pkey, public_rsa_) == 1);
if (!SHA1(message, message_length, hash)) {
dump_openssl_error();
EXPECT_TRUE(false) << "Error computing SHA1. ";
}
// Decrypt signature to padded digest. const bool ok = VerifyPSSSignature(pkey, message, message_length,
uint8_t padded_digest[signature_length]; signature, signature_length);
int status; EVP_PKEY_free(pkey);
status = RSA_public_decrypt(signature_length, signature, padded_digest, EXPECT_TRUE(ok) << "PSS signature check failed";
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 ";
}
} else if (padding_scheme == kSign_PKCS1_Block1) { } else if (padding_scheme == kSign_PKCS1_Block1) {
uint8_t padded_digest[signature_length]; uint8_t padded_digest[signature_length];
int status; int status;

View File

@@ -10,12 +10,12 @@ LOCAL_SRC_FILES := \
LOCAL_C_INCLUDES := \ LOCAL_C_INCLUDES := \
$(TOP)/vendor/widevine/proprietary/wvm/include \ $(TOP)/vendor/widevine/proprietary/wvm/include \
$(TOP)/external/openssl/include
LOCAL_CFLAGS += -Wno-unused-parameter LOCAL_CFLAGS += -Wno-unused-parameter
LOCAL_MODULE:= libwvdecryptcommon LOCAL_MODULE:= libwvdecryptcommon
LOCAL_MODULE_TAGS := optional LOCAL_MODULE_TAGS := optional
LOCAL_STATIC_LIBRARIES := libcrypto_static
# Not 64-bit compatible, WVCryptoPlugin::decrypt stores a pointer in a uint32 # Not 64-bit compatible, WVCryptoPlugin::decrypt stores a pointer in a uint32
LOCAL_MULTILIB := 32 LOCAL_MULTILIB := 32