This is the initial code drop of the reference implementation and test cases for the Widevine Whitebox API. In this drop, the full reference implementation for the AEAD white-box is provided and all test cases verifying the top-level behave have are enabled. Since the implementations can vary so much the testing is mostly left to verifying the return codes for specific parameter conditions. A full reference implementation for the license white-box is provided, however not all tests are implemented or enabled. A number of tests have been disabled as they required a loaded license and test licenses are still being worked on. The two license white-box API functions that are the further from competition are ProcessLicenseResponse() and MaskedDecryt(). ProcessLicenseResponse() is still being worked on and MaskedDecrypt() is waiting on Decrypt() to be fully functional. Most tests focus on verifying return code for specific parameter conditions, but as test licenses are created, tests looking to test the internal behaviour of license management will be added to ProcessLicenseResponse(), Decrypt(), and MaskedDecrypt().
336 lines
11 KiB
C++
336 lines
11 KiB
C++
////////////////////////////////////////////////////////////////////////////////
|
|
// Copyright 2016 Google LLC.
|
|
//
|
|
// This software is licensed under the terms defined in the Widevine Master
|
|
// License Agreement. For a copy of this agreement, please contact
|
|
// widevine-licensing@google.com.
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
// Description:
|
|
// Definition of classes representing RSA private and public keys used
|
|
// for message signing, signature verification, encryption and decryption.
|
|
//
|
|
// RSA signature details:
|
|
// Algorithm: RSASSA-PSS
|
|
// Hash algorithm: SHA1
|
|
// Mask generation function: mgf1SHA1
|
|
// Salt length: 20 bytes
|
|
// Trailer field: 0xbc
|
|
//
|
|
// RSA encryption details:
|
|
// Algorithm: RSA-OAEP
|
|
// Mask generation function: mgf1SHA1
|
|
// Label (encoding paramter): empty std::string
|
|
|
|
#include "crypto_utils/rsa_key.h"
|
|
|
|
#include "base/logging.h"
|
|
#include "third_party/boringssl/src/include/openssl/bn.h"
|
|
#include "third_party/boringssl/src/include/openssl/err.h"
|
|
#include "third_party/boringssl/src/include/openssl/evp.h"
|
|
#include "third_party/boringssl/src/include/openssl/rsa.h"
|
|
#include "third_party/boringssl/src/include/openssl/sha.h"
|
|
#include "crypto_utils/rsa_util.h"
|
|
#include "crypto_utils/sha_util.h"
|
|
|
|
static const int kPssSaltLength = 20;
|
|
|
|
namespace {
|
|
|
|
// Check if two RSA keys match. If matches, they are either a public-private key
|
|
// pair or the same public key or the same private key.
|
|
bool RsaKeyMatch(const RSA* key1, const RSA* key2) {
|
|
if (!key1 || !key2)
|
|
return false;
|
|
return BN_cmp(key1->n, key2->n) == 0;
|
|
}
|
|
|
|
std::string OpenSSLErrorString(uint32_t error) {
|
|
char buf[ERR_ERROR_STRING_BUF_LEN];
|
|
ERR_error_string_n(error, buf, sizeof(buf));
|
|
return buf;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
namespace widevine {
|
|
|
|
RsaPrivateKey::RsaPrivateKey(RSA* key) : key_(key) {
|
|
CHECK(key_ != nullptr);
|
|
}
|
|
|
|
RsaPrivateKey::RsaPrivateKey(const RsaPrivateKey& rsa_key)
|
|
: key_(RSAPrivateKey_dup(rsa_key.key_)) {
|
|
CHECK(key_ != nullptr);
|
|
}
|
|
|
|
RsaPrivateKey::~RsaPrivateKey() {
|
|
RSA_free(key_);
|
|
}
|
|
|
|
RsaPrivateKey* RsaPrivateKey::Create(const std::string& serialized_key) {
|
|
RSA* key;
|
|
if (!rsa_util::DeserializeRsaPrivateKey(serialized_key, &key))
|
|
return nullptr;
|
|
if (RSA_check_key(key) != 1) {
|
|
LOG(ERROR) << "Invalid private RSA key: "
|
|
<< OpenSSLErrorString(ERR_get_error());
|
|
RSA_free(key);
|
|
}
|
|
return new RsaPrivateKey(key);
|
|
}
|
|
|
|
bool RsaPrivateKey::Decrypt(const std::string& encrypted_message,
|
|
std::string* decrypted_message) const {
|
|
DCHECK(decrypted_message);
|
|
|
|
size_t rsa_size = RSA_size(key_);
|
|
if (encrypted_message.size() != rsa_size) {
|
|
LOG(ERROR) << "Encrypted RSA message has the wrong size (expected "
|
|
<< rsa_size << ", actual " << encrypted_message.size() << ")";
|
|
return false;
|
|
}
|
|
decrypted_message->assign(rsa_size, 0);
|
|
int decrypted_size = RSA_private_decrypt(
|
|
rsa_size,
|
|
const_cast<unsigned char*>(
|
|
reinterpret_cast<const unsigned char*>(encrypted_message.data())),
|
|
reinterpret_cast<unsigned char*>(&(*decrypted_message)[0]), key_,
|
|
RSA_PKCS1_OAEP_PADDING);
|
|
if (decrypted_size == -1) {
|
|
LOG(ERROR) << "RSA private decrypt failure: "
|
|
<< OpenSSLErrorString(ERR_get_error());
|
|
return false;
|
|
}
|
|
decrypted_message->resize(decrypted_size);
|
|
return true;
|
|
}
|
|
|
|
bool RsaPrivateKey::GenerateSignature(const std::string& message,
|
|
std::string* signature) const {
|
|
DCHECK(signature);
|
|
|
|
if (message.empty()) {
|
|
LOG(ERROR) << "Message to be signed is empty";
|
|
return false;
|
|
}
|
|
// Hash the message using SHA1.
|
|
std::string message_digest = Sha1_Hash(message);
|
|
|
|
// Add PSS padding.
|
|
size_t rsa_size = RSA_size(key_);
|
|
std::string padded_digest(rsa_size, 0);
|
|
if (!RSA_padding_add_PKCS1_PSS_mgf1(
|
|
key_, reinterpret_cast<unsigned char*>(&padded_digest[0]),
|
|
reinterpret_cast<unsigned char*>(&message_digest[0]), EVP_sha1(),
|
|
EVP_sha1(), kPssSaltLength)) {
|
|
LOG(ERROR) << "RSA padding failure: "
|
|
<< OpenSSLErrorString(ERR_get_error());
|
|
return false;
|
|
}
|
|
// Encrypt PSS padded digest.
|
|
signature->assign(rsa_size, 0);
|
|
if (RSA_private_encrypt(padded_digest.size(),
|
|
reinterpret_cast<unsigned char*>(&padded_digest[0]),
|
|
reinterpret_cast<unsigned char*>(&(*signature)[0]),
|
|
key_, RSA_NO_PADDING) !=
|
|
static_cast<int>(signature->size())) {
|
|
LOG(ERROR) << "RSA private encrypt failure: "
|
|
<< OpenSSLErrorString(ERR_get_error());
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool RsaPrivateKey::GenerateSignatureSha256Pkcs7(const std::string& message,
|
|
std::string* signature) const {
|
|
DCHECK(signature);
|
|
if (message.empty()) {
|
|
LOG(ERROR) << "Empty signature verification message";
|
|
return false;
|
|
}
|
|
|
|
unsigned char digest[SHA256_DIGEST_LENGTH];
|
|
SHA256(reinterpret_cast<const unsigned char*>(message.data()), message.size(),
|
|
digest);
|
|
unsigned int sig_len = RSA_size(key_);
|
|
signature->resize(sig_len);
|
|
return RSA_sign(NID_sha256, digest, sizeof(digest),
|
|
reinterpret_cast<unsigned char*>(&(*signature)[0]), &sig_len,
|
|
key_) == 1;
|
|
}
|
|
|
|
bool RsaPrivateKey::MatchesPrivateKey(const RsaPrivateKey& private_key) const {
|
|
return RsaKeyMatch(key(), private_key.key());
|
|
}
|
|
|
|
bool RsaPrivateKey::MatchesPublicKey(const RsaPublicKey& public_key) const {
|
|
return RsaKeyMatch(key(), public_key.key());
|
|
}
|
|
|
|
uint32_t RsaPrivateKey::KeySize() const {
|
|
return RSA_size(key_);
|
|
}
|
|
|
|
RsaPublicKey::RsaPublicKey(RSA* key) : key_(key) {
|
|
CHECK(key_ != nullptr);
|
|
}
|
|
|
|
RsaPublicKey::RsaPublicKey(const RsaPublicKey& rsa_key)
|
|
: key_(RSAPublicKey_dup(rsa_key.key_)) {
|
|
CHECK(key_ != nullptr);
|
|
}
|
|
|
|
RsaPublicKey::RsaPublicKey(const RsaPrivateKey& rsa_key)
|
|
: key_(RSAPublicKey_dup(rsa_key.key_)) {
|
|
CHECK(key_ != nullptr);
|
|
}
|
|
|
|
RsaPublicKey::~RsaPublicKey() {
|
|
RSA_free(key_);
|
|
}
|
|
|
|
RsaPublicKey* RsaPublicKey::Create(const std::string& serialized_key) {
|
|
RSA* key;
|
|
if (!rsa_util::DeserializeRsaPublicKey(serialized_key, &key))
|
|
return nullptr;
|
|
if (RSA_size(key) == 0) {
|
|
LOG(ERROR) << "Invalid public RSA key: "
|
|
<< OpenSSLErrorString(ERR_get_error());
|
|
RSA_free(key);
|
|
}
|
|
return new RsaPublicKey(key);
|
|
}
|
|
|
|
bool RsaPublicKey::Encrypt(const std::string& clear_message,
|
|
std::string* encrypted_message) const {
|
|
DCHECK(encrypted_message);
|
|
|
|
if (clear_message.empty()) {
|
|
LOG(ERROR) << "Message to be encrypted is empty";
|
|
return false;
|
|
}
|
|
size_t rsa_size = RSA_size(key_);
|
|
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) != static_cast<int>(rsa_size)) {
|
|
LOG(ERROR) << "RSA public encrypt failure: "
|
|
<< OpenSSLErrorString(ERR_get_error());
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool RsaPublicKey::VerifySignature(const std::string& message,
|
|
const std::string& signature) const {
|
|
if (message.empty()) {
|
|
LOG(ERROR) << "Signed message is empty";
|
|
return false;
|
|
}
|
|
size_t rsa_size = RSA_size(key_);
|
|
if (signature.size() != rsa_size) {
|
|
LOG(ERROR) << "Message signature is of the wrong size (expected "
|
|
<< rsa_size << ", actual " << 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) != static_cast<int>(rsa_size)) {
|
|
LOG(ERROR) << "RSA public decrypt failure: "
|
|
<< OpenSSLErrorString(ERR_get_error());
|
|
return false;
|
|
}
|
|
// Hash the message using SHA1.
|
|
std::string message_digest = Sha1_Hash(message);
|
|
|
|
// Verify PSS padding.
|
|
if (RSA_verify_PKCS1_PSS_mgf1(
|
|
key_, reinterpret_cast<unsigned char*>(&message_digest[0]),
|
|
EVP_sha1(), EVP_sha1(),
|
|
reinterpret_cast<unsigned char*>(&padded_digest[0]),
|
|
kPssSaltLength) == 0) {
|
|
LOG(ERROR) << "RSA Verify PSS padding failure: "
|
|
<< OpenSSLErrorString(ERR_get_error());
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool RsaPublicKey::VerifySignatureSha256Pkcs7(
|
|
const std::string& message,
|
|
const std::string& signature) const {
|
|
if (message.empty()) {
|
|
LOG(ERROR) << "Empty signature verification message";
|
|
return false;
|
|
}
|
|
if (signature.empty()) {
|
|
LOG(ERROR) << "Empty signature";
|
|
return false;
|
|
}
|
|
|
|
if (signature.size() != RSA_size(key_)) {
|
|
LOG(ERROR) << "RSA signature has the wrong size";
|
|
return false;
|
|
}
|
|
unsigned char digest[SHA256_DIGEST_LENGTH];
|
|
SHA256(reinterpret_cast<const unsigned char*>(message.data()), message.size(),
|
|
digest);
|
|
return RSA_verify(NID_sha256, digest, sizeof(digest),
|
|
reinterpret_cast<const unsigned char*>(signature.data()),
|
|
signature.size(), key_) == 1;
|
|
}
|
|
|
|
bool RsaPublicKey::MatchesPrivateKey(const RsaPrivateKey& private_key) const {
|
|
return RsaKeyMatch(key(), private_key.key());
|
|
}
|
|
|
|
bool RsaPublicKey::MatchesPublicKey(const RsaPublicKey& public_key) const {
|
|
return RsaKeyMatch(key(), public_key.key());
|
|
}
|
|
|
|
uint32_t RsaPublicKey::KeySize() const {
|
|
return RSA_size(key_);
|
|
}
|
|
|
|
RsaKeyFactory::RsaKeyFactory() {}
|
|
|
|
RsaKeyFactory::~RsaKeyFactory() {}
|
|
|
|
std::unique_ptr<RsaPrivateKey> RsaKeyFactory::CreateFromPkcs1PrivateKey(
|
|
const std::string& private_key) const {
|
|
return std::unique_ptr<RsaPrivateKey>(RsaPrivateKey::Create(private_key));
|
|
}
|
|
|
|
std::unique_ptr<RsaPrivateKey> RsaKeyFactory::CreateFromPkcs8PrivateKey(
|
|
const std::string& private_key,
|
|
const std::string& private_key_passphrase) const {
|
|
std::string pkcs1_key;
|
|
const bool result =
|
|
private_key_passphrase.empty()
|
|
? rsa_util::PrivateKeyInfoToRsaPrivateKey(private_key, &pkcs1_key)
|
|
: rsa_util::EncryptedPrivateKeyInfoToRsaPrivateKey(
|
|
private_key, private_key_passphrase, &pkcs1_key);
|
|
if (!result) {
|
|
LOG(WARNING) << "Failed to get pkcs1_key.";
|
|
return std::unique_ptr<RsaPrivateKey>();
|
|
}
|
|
return std::unique_ptr<RsaPrivateKey>(RsaPrivateKey::Create(pkcs1_key));
|
|
}
|
|
|
|
std::unique_ptr<RsaPublicKey> RsaKeyFactory::CreateFromPkcs1PublicKey(
|
|
const std::string& public_key) const {
|
|
return std::unique_ptr<RsaPublicKey>(RsaPublicKey::Create(public_key));
|
|
}
|
|
|
|
} // namespace widevine
|