Allow Apps to Voluntarily Downgrade to L3 Crypto
This merges the following changes from the Widevine CDM repository: 564f4cc Add CdmClientPropertySet to CDM Adds an interface to the CDM that allows it to query its client for certain properties. In this case, this includes the ability to specify what security level is desired, as well as support for service ceritifcate privacy mode. 9cfbd3e Force Level 3 fallback Adds support for voluntarily invoking L3 crypto to the OEMCrypto wrapper. 95d12c1 Add pointer to CdmClientPropertySet class to OpenSession. Adds support for storing the property set on a session-by-session basis and choosing the appropriate crypto level. 17de442 Add Settable Properties for Clank to Android Adds support for setting the aforementioned properties to the DrmEngine bbe704d Fixes to force fallback to level three security Corrections to invoke provisioning, OEMCrypto API with configured security level rather than the default. Unit tests were also revised. Note that some parts of this are also support for the ability to use a service certificate-based privacy mode. The remaining code for supporting this mode is still forthcoming. Bug: 10109249 Change-Id: I2755e4dea1de3e8a56cff237360298f7b7f1bddc
This commit is contained in:
201
libwvdrmengine/cdm/core/src/privacy_crypto.cpp
Normal file
201
libwvdrmengine/cdm/core/src/privacy_crypto.cpp
Normal file
@@ -0,0 +1,201 @@
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Original code at //depot/google3/video/widevine/common/rsa_key.cc by
|
||||
// tinskip@google.com. Modified for core CDM usage.
|
||||
//
|
||||
// Description:
|
||||
// Definition of classes representing RSA public keys used
|
||||
// for signature verification and encryption and decryption.
|
||||
//
|
||||
|
||||
#include "privacy_crypto.h"
|
||||
|
||||
#include "log.h"
|
||||
#include "openssl/bio.h"
|
||||
#include "openssl/err.h"
|
||||
#include "openssl/evp.h"
|
||||
#include "openssl/pem.h"
|
||||
#include "openssl/rsa.h"
|
||||
#include "openssl/sha.h"
|
||||
|
||||
namespace {
|
||||
const int kPssSaltLength = 20;
|
||||
const int kRsaPkcs1OaepPaddingLength = 41;
|
||||
const int kBitsInAByte = 8;
|
||||
} // namespace
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
bool AesCbcKey::Init(const std::string& key) {
|
||||
if (key.empty()) {
|
||||
LOGE("AesCbcKey::Init: no key provided");
|
||||
return false;
|
||||
}
|
||||
if (key.size() != AES_BLOCK_SIZE) {
|
||||
LOGE("AesCbcKey::Init: unexpected key size: %d", key.size());
|
||||
return false;
|
||||
}
|
||||
if (AES_set_encrypt_key(reinterpret_cast<const uint8_t*>(&key[0]),
|
||||
key.size() * kBitsInAByte, &key_) != 0) {
|
||||
LOGE("AesCbcKey::Init: AES CBC key setup failure: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
return false;
|
||||
}
|
||||
initialized_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AesCbcKey::Encrypt(const std::string& in, std::string* out,
|
||||
std::string* iv) {
|
||||
if (in.empty()) {
|
||||
LOGE("AesCbcKey::Encrypt: no cleartext provided");
|
||||
return false;
|
||||
}
|
||||
if (in.size() % AES_BLOCK_SIZE) {
|
||||
LOGE("AesCbcKey::Encrypt: cleartext not a block multiple: %d", in.size());
|
||||
return false;
|
||||
}
|
||||
if (iv == NULL) {
|
||||
LOGE("AesCbcKey::Encrypt: initialization vector destination not provided");
|
||||
return false;
|
||||
}
|
||||
if (iv->size() != AES_BLOCK_SIZE) {
|
||||
LOGE("AesCbcKey::Encrypt: invalid iv size: %d", iv->size());
|
||||
return false;
|
||||
}
|
||||
if (out == NULL) {
|
||||
LOGE("AesCbcKey::Encrypt: crypttext destination not provided");
|
||||
return false;
|
||||
}
|
||||
if (!initialized_) {
|
||||
LOGE("AesCbcKey::Encrypt: AES key not initialized");
|
||||
return false;
|
||||
}
|
||||
|
||||
out->resize(in.size());
|
||||
|
||||
AES_cbc_encrypt(reinterpret_cast<const uint8_t*>(&in[0]),
|
||||
reinterpret_cast<uint8_t*>(&out[0]), in.size(), &key_,
|
||||
reinterpret_cast<uint8_t*>(&iv[0]), AES_ENCRYPT);
|
||||
return true;
|
||||
}
|
||||
|
||||
RsaPublicKey::~RsaPublicKey() {
|
||||
if (key_ != NULL) {
|
||||
RSA_free(key_);
|
||||
}
|
||||
}
|
||||
|
||||
bool RsaPublicKey::Init(const std::string& serialized_key) {
|
||||
|
||||
if (serialized_key.empty()) {
|
||||
LOGE("RsaPublicKey::Init: no serialized key provided");
|
||||
return false;
|
||||
}
|
||||
|
||||
BIO* bio = BIO_new_mem_buf(const_cast<char*>(serialized_key.data()),
|
||||
serialized_key.size());
|
||||
if (bio == NULL) {
|
||||
LOGE("RsaPublicKey::Init: BIO_new_mem_buf returned NULL");
|
||||
return false;
|
||||
}
|
||||
key_ = d2i_RSAPublicKey_bio(bio, NULL);
|
||||
BIO_free(bio);
|
||||
|
||||
if (key_ == NULL) {
|
||||
LOGE("RsaPublicKey::Init: RSA key deserialization failure");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RsaPublicKey::Encrypt(const std::string& clear_message,
|
||||
std::string* encrypted_message) {
|
||||
if (clear_message.empty()) {
|
||||
LOGE("RsaPublicKey::Encrypt: message to be encrypted is empty");
|
||||
return false;
|
||||
}
|
||||
if (encrypted_message == NULL) {
|
||||
LOGE("RsaPublicKey::Encrypt: no encrypt message buffer provided");
|
||||
return false;
|
||||
}
|
||||
if (key_ == NULL) {
|
||||
LOGE("RsaPublicKey::Encrypt: RSA key not initialized");
|
||||
return false;
|
||||
}
|
||||
|
||||
int rsa_size = RSA_size(key_);
|
||||
if (static_cast<int>(clear_message.size()) >
|
||||
rsa_size - kRsaPkcs1OaepPaddingLength) {
|
||||
LOGE("RsaPublicKey::Encrypt: message too large to be encrypted (actual %d",
|
||||
" max allowed %d)", clear_message.size(),
|
||||
rsa_size - kRsaPkcs1OaepPaddingLength);
|
||||
return false;
|
||||
}
|
||||
encrypted_message->assign(rsa_size, 0);
|
||||
if (RSA_public_encrypt(
|
||||
clear_message.size(),
|
||||
const_cast<unsigned char*>(
|
||||
reinterpret_cast<const unsigned char*>(clear_message.data())),
|
||||
reinterpret_cast<unsigned char*>(&(*encrypted_message)[0]), key_,
|
||||
RSA_PKCS1_OAEP_PADDING) != rsa_size) {
|
||||
LOGE("RsaPublicKey::Encrypt: encrypt failure: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RsaPublicKey::VerifySignature(const std::string& message,
|
||||
const std::string& signature) {
|
||||
if (key_ == NULL) {
|
||||
LOGE("RsaPublicKey::VerifySignature: RSA key not initialized");
|
||||
return false;
|
||||
}
|
||||
if (message.empty()) {
|
||||
LOGE("RsaPublicKey::VerifySignature: signed message is empty");
|
||||
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());
|
||||
return false;
|
||||
}
|
||||
// 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));
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
Reference in New Issue
Block a user