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:
@@ -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
|
||||
|
||||
@@ -108,8 +108,8 @@ bool AesCbcKey::Encrypt(const std::string& in, std::string* out,
|
||||
}
|
||||
|
||||
int padding = 0;
|
||||
if (EVP_EncryptFinal(&ctx, reinterpret_cast<uint8_t*>(&(*out)[out_length]),
|
||||
&padding) == 0) {
|
||||
if (EVP_EncryptFinal_ex(&ctx, reinterpret_cast<uint8_t*>(&(*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<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,
|
||||
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<int>(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<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;
|
||||
}
|
||||
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<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);
|
||||
if (!ok) {
|
||||
LOGE("RsaPublicKey::VerifySignature: RSA verify failure");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user