Refactor OEMCrypto Unit Tests
Merge from widevine repo of http://go/wvgerrit/21681 This CL refactors some oemcrypto unit tests in preparation for adding Provisioning 3.0 tests. - The signature GenerateNonce has changed. Instead of the caller passing in a pointer for the nonce, we store the nonce in a member variable of Session. - GenerateDerivedKeys is being replaced by InstallTestSessionKeys. This sets up and calls the appropriate derive keys method. This function is in the test class, instead of the session class so that multiple sessions in a class can share the same wrapped rsa key. This will be modified for provisioning 3.0 in a future CL. - Rename tests that require a keybox. Some tests are specific for using a keybox to request a DRM cert. These tests are renamed so we can filter them out on devices that use an OEM Cert. Corresponding tests for devices using provisioning 3.0 will be in a future CL. - Some member variables and methods in the class Session were not used. They are removed. - Added openssl smart pointer. - Comments. I added comments. - clang format. Change-Id: Ib579a322858e0ef92652a42167241b35cf85a041
This commit is contained in:
@@ -9,8 +9,10 @@
|
||||
#include <openssl/aes.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/hmac.h>
|
||||
#include <openssl/pem.h>
|
||||
#include <openssl/rand.h>
|
||||
#include <openssl/x509.h>
|
||||
#include <openssl/x509_vfy.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
@@ -18,13 +20,14 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "OEMCryptoCENC.h"
|
||||
#include "log.h"
|
||||
#include "oec_device_features.h"
|
||||
#include "oec_test_data.h"
|
||||
#include "oemcrypto_key_mock.h"
|
||||
#include "OEMCryptoCENC.h"
|
||||
#include "string_conversions.h"
|
||||
#include "wv_cdm_constants.h"
|
||||
#include "wv_cdm_types.h"
|
||||
#include "wv_keybox.h"
|
||||
|
||||
using namespace std;
|
||||
@@ -69,6 +72,23 @@ void dump_openssl_error() {
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, void (*func)(T*)>
|
||||
class openssl_ptr {
|
||||
public:
|
||||
explicit openssl_ptr(T* p = NULL) : ptr_(p) {}
|
||||
~openssl_ptr() {
|
||||
if (ptr_) func(ptr_);
|
||||
}
|
||||
T& operator*() const { return *ptr_; }
|
||||
T* operator->() const { return ptr_; }
|
||||
T* get() const { return ptr_; }
|
||||
bool NotNull() { return ptr_; }
|
||||
|
||||
private:
|
||||
T* ptr_;
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(openssl_ptr);
|
||||
};
|
||||
|
||||
Session::Session()
|
||||
: open_(false),
|
||||
forced_session_id_(false),
|
||||
@@ -113,8 +133,8 @@ void Session::close() {
|
||||
open_ = false;
|
||||
}
|
||||
|
||||
void Session::GenerateNonce(uint32_t* nonce, int* error_counter) {
|
||||
if (OEMCrypto_SUCCESS == OEMCrypto_GenerateNonce(session_id(), nonce)) {
|
||||
void Session::GenerateNonce(int* error_counter) {
|
||||
if (OEMCrypto_SUCCESS == OEMCrypto_GenerateNonce(session_id(), &nonce_)) {
|
||||
return;
|
||||
}
|
||||
if (error_counter) {
|
||||
@@ -122,7 +142,7 @@ void Session::GenerateNonce(uint32_t* nonce, int* error_counter) {
|
||||
} else {
|
||||
sleep(1); // wait a second, then try again.
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS,
|
||||
OEMCrypto_GenerateNonce(session_id(), nonce));
|
||||
OEMCrypto_GenerateNonce(session_id(), &nonce_));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,7 +168,7 @@ void Session::FillDefaultContext(vector<uint8_t>* mac_context,
|
||||
}
|
||||
|
||||
void Session::GenerateDerivedKeysFromKeybox() {
|
||||
GenerateNonce(&nonce_);
|
||||
GenerateNonce();
|
||||
vector<uint8_t> mac_context;
|
||||
vector<uint8_t> enc_context;
|
||||
FillDefaultContext(&mac_context, &enc_context);
|
||||
@@ -168,10 +188,11 @@ void Session::GenerateDerivedKeysFromKeybox() {
|
||||
|
||||
void Session::GenerateDerivedKeysFromSessionKey() {
|
||||
// Uses test certificate.
|
||||
GenerateNonce(&nonce_);
|
||||
GenerateNonce();
|
||||
vector<uint8_t> session_key;
|
||||
vector<uint8_t> enc_session_key;
|
||||
PreparePublicKey();
|
||||
ASSERT_TRUE(GenerateRSASessionKey(&enc_session_key));
|
||||
ASSERT_TRUE(GenerateRSASessionKey(&session_key, &enc_session_key));
|
||||
vector<uint8_t> mac_context;
|
||||
vector<uint8_t> enc_context;
|
||||
FillDefaultContext(&mac_context, &enc_context);
|
||||
@@ -190,14 +211,6 @@ void Session::GenerateDerivedKeysFromSessionKey() {
|
||||
enc_key_ = wvcdm::a2b_hex("CB477D09014D72C9B8DCE76C33EA43B3");
|
||||
}
|
||||
|
||||
void Session::GenerateTestSessionKeys() {
|
||||
if (global_features.derive_key_method == DeviceFeatures::LOAD_TEST_RSA_KEY) {
|
||||
GenerateDerivedKeysFromSessionKey();
|
||||
} else {
|
||||
GenerateDerivedKeysFromKeybox();
|
||||
}
|
||||
}
|
||||
|
||||
void Session::LoadTestKeys(const std::string& pst, bool new_mac_keys) {
|
||||
uint8_t* pst_ptr = NULL;
|
||||
if (pst.length() > 0) {
|
||||
@@ -236,9 +249,11 @@ void Session::VerifyTestKeys() {
|
||||
// control duration and bits stored in network byte order. For printing
|
||||
// we change to host byte order.
|
||||
ASSERT_EQ((htonl_fnc(license_.keys[i].control.duration)),
|
||||
(htonl_fnc(block.duration))) << "For key " << i;
|
||||
(htonl_fnc(block.duration)))
|
||||
<< "For key " << i;
|
||||
ASSERT_EQ(htonl_fnc(license_.keys[i].control.control_bits),
|
||||
htonl_fnc(block.control_bits)) << "For key " << i;
|
||||
htonl_fnc(block.control_bits))
|
||||
<< "For key " << i;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -278,8 +293,8 @@ void Session::SetKeyId(int index, const string& key_id) {
|
||||
|
||||
void Session::FillSimpleMessage(uint32_t duration, uint32_t control,
|
||||
uint32_t nonce, const std::string& pst) {
|
||||
EXPECT_EQ(1, RAND_pseudo_bytes(license_.mac_key_iv,
|
||||
sizeof(license_.mac_key_iv)));
|
||||
EXPECT_EQ(
|
||||
1, RAND_pseudo_bytes(license_.mac_key_iv, sizeof(license_.mac_key_iv)));
|
||||
EXPECT_EQ(1, RAND_pseudo_bytes(license_.mac_keys, sizeof(license_.mac_keys)));
|
||||
for (unsigned int i = 0; i < num_keys_; i++) {
|
||||
memset(license_.keys[i].key_id, 0, kTestKeyIdMaxLength);
|
||||
@@ -353,8 +368,10 @@ void Session::EncryptAndSign() {
|
||||
FillKeyArray(encrypted_license(), key_array_);
|
||||
}
|
||||
|
||||
void Session::EncryptMessage(RSAPrivateKeyMessage* data,
|
||||
RSAPrivateKeyMessage* encrypted) {
|
||||
void Session::EncryptProvisioningMessage(
|
||||
RSAPrivateKeyMessage* data, RSAPrivateKeyMessage* encrypted,
|
||||
const vector<uint8_t>& encryption_key) {
|
||||
ASSERT_EQ(encryption_key.size(), wvcdm::KEY_SIZE);
|
||||
*encrypted = *data;
|
||||
size_t padding = wvcdm::KEY_SIZE - (data->rsa_key_length % wvcdm::KEY_SIZE);
|
||||
memset(data->rsa_key + data->rsa_key_length, static_cast<uint8_t>(padding),
|
||||
@@ -363,7 +380,7 @@ void Session::EncryptMessage(RSAPrivateKeyMessage* data,
|
||||
uint8_t iv_buffer[16];
|
||||
memcpy(iv_buffer, &data->rsa_key_iv[0], wvcdm::KEY_IV_SIZE);
|
||||
AES_KEY aes_key;
|
||||
AES_set_encrypt_key(&enc_key_[0], 128, &aes_key);
|
||||
AES_set_encrypt_key(&encryption_key[0], 128, &aes_key);
|
||||
AES_cbc_encrypt(&data->rsa_key[0], &encrypted->rsa_key[0],
|
||||
encrypted->rsa_key_length, &aes_key, iv_buffer, AES_ENCRYPT);
|
||||
}
|
||||
@@ -385,17 +402,15 @@ void Session::ClientSignMessage(const vector<uint8_t>& data,
|
||||
&(data.front()), data.size(), &(signature->front()), &md_len);
|
||||
}
|
||||
|
||||
// This verifies the signature computed by OEMCrypto using the client mac keys.
|
||||
// This is used when a device requests a license renewal. It is also used for
|
||||
// a license request authenticated by a keybox. The first use case is needed
|
||||
// for devices with a keybox or without.
|
||||
void Session::VerifyClientSignature(size_t data_length) {
|
||||
// In the real world, a message should be signed by the client and
|
||||
// verified by the server. This simulates that.
|
||||
vector<uint8_t> data(data_length);
|
||||
for(int i=0; i < data.size(); i++) data[i] = i % 0xFF;
|
||||
for (int i = 0; i < data.size(); i++) data[i] = i % 0xFF;
|
||||
OEMCryptoResult sts;
|
||||
size_t gen_signature_length = 0;
|
||||
sts = OEMCrypto_GenerateSignature(session_id(), &data[0], data.size(),
|
||||
NULL, &gen_signature_length);
|
||||
sts = OEMCrypto_GenerateSignature(session_id(), &data[0], data.size(), NULL,
|
||||
&gen_signature_length);
|
||||
ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts);
|
||||
ASSERT_EQ(static_cast<size_t>(32), gen_signature_length);
|
||||
vector<uint8_t> gen_signature(gen_signature_length);
|
||||
@@ -520,36 +535,9 @@ void Session::MakeRSACertificate(struct RSAPrivateKeyMessage* encrypted,
|
||||
size_t message_size,
|
||||
std::vector<uint8_t>* signature,
|
||||
uint32_t allowed_schemes,
|
||||
const vector<uint8_t>& rsa_key) {
|
||||
// Dummy context for testing signature generation.
|
||||
vector<uint8_t> context = wvcdm::a2b_hex(
|
||||
"0a4c08001248000000020000101907d9ffde13aa95c122678053362136bdf840"
|
||||
"8f8276e4c2d87ec52b61aa1b9f646e58734930acebe899b3e464189a14a87202"
|
||||
"fb02574e70640bd22ef44b2d7e3912250a230a14080112100915007caa9b5931"
|
||||
"b76a3a85f046523e10011a09393837363534333231180120002a0c3138383637"
|
||||
"38373430350000");
|
||||
|
||||
OEMCryptoResult sts;
|
||||
|
||||
// Generate signature
|
||||
size_t gen_signature_length = 0;
|
||||
sts = OEMCrypto_GenerateSignature(session_id(), &context[0], context.size(),
|
||||
NULL, &gen_signature_length);
|
||||
ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts);
|
||||
ASSERT_EQ(static_cast<size_t>(32), gen_signature_length);
|
||||
vector<uint8_t> gen_signature(gen_signature_length);
|
||||
sts = OEMCrypto_GenerateSignature(session_id(), &context[0], context.size(),
|
||||
&gen_signature[0], &gen_signature_length);
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
|
||||
std::vector<uint8_t> expected_signature;
|
||||
ClientSignMessage(context, &expected_signature);
|
||||
ASSERT_EQ(expected_signature, gen_signature);
|
||||
|
||||
// Rewrap Canned Response
|
||||
|
||||
// In the real world, the signature above would just have been used to
|
||||
// contact the certificate provisioning server to get this response.
|
||||
|
||||
const vector<uint8_t>& rsa_key,
|
||||
const vector<uint8_t>* encryption_key) {
|
||||
if (encryption_key == NULL) encryption_key = &enc_key_;
|
||||
struct RSAPrivateKeyMessage message;
|
||||
if (allowed_schemes != kSign_RSASSA_PSS) {
|
||||
uint32_t algorithm_n = htonl(allowed_schemes);
|
||||
@@ -564,7 +552,7 @@ void Session::MakeRSACertificate(struct RSAPrivateKeyMessage* encrypted,
|
||||
EXPECT_EQ(1, RAND_pseudo_bytes(message.rsa_key_iv, wvcdm::KEY_IV_SIZE));
|
||||
message.nonce = nonce_;
|
||||
|
||||
EncryptMessage(&message, encrypted);
|
||||
EncryptProvisioningMessage(&message, encrypted, *encryption_key);
|
||||
ServerSignBuffer(reinterpret_cast<const uint8_t*>(encrypted), message_size,
|
||||
signature);
|
||||
}
|
||||
@@ -601,35 +589,29 @@ void Session::PreparePublicKey(const uint8_t* rsa_key, size_t rsa_key_length) {
|
||||
rsa_key_length = sizeof(kTestRSAPKCS8PrivateKeyInfo2_2048);
|
||||
}
|
||||
uint8_t* p = const_cast<uint8_t*>(rsa_key);
|
||||
BIO* bio = BIO_new_mem_buf(p, rsa_key_length);
|
||||
ASSERT_TRUE(NULL != bio);
|
||||
PKCS8_PRIV_KEY_INFO* pkcs8_pki = d2i_PKCS8_PRIV_KEY_INFO_bio(bio, NULL);
|
||||
ASSERT_TRUE(NULL != pkcs8_pki);
|
||||
EVP_PKEY* evp = NULL;
|
||||
evp = EVP_PKCS82PKEY(pkcs8_pki);
|
||||
ASSERT_TRUE(NULL != evp);
|
||||
openssl_ptr<BIO, BIO_vfree> bio(BIO_new_mem_buf(p, rsa_key_length));
|
||||
ASSERT_TRUE(bio.NotNull());
|
||||
openssl_ptr<PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO_free> pkcs8_pki(
|
||||
d2i_PKCS8_PRIV_KEY_INFO_bio(bio.get(), NULL));
|
||||
ASSERT_TRUE(pkcs8_pki.NotNull());
|
||||
openssl_ptr<EVP_PKEY, EVP_PKEY_free> evp(EVP_PKCS82PKEY(pkcs8_pki.get()));
|
||||
ASSERT_TRUE(evp.NotNull());
|
||||
if (public_rsa_) RSA_free(public_rsa_);
|
||||
public_rsa_ = EVP_PKEY_get1_RSA(evp);
|
||||
EVP_PKEY_free(evp);
|
||||
PKCS8_PRIV_KEY_INFO_free(pkcs8_pki);
|
||||
BIO_free(bio);
|
||||
public_rsa_ = EVP_PKEY_get1_RSA(evp.get());
|
||||
if (!public_rsa_) {
|
||||
cout << "d2i_RSAPrivateKey failed. ";
|
||||
dump_openssl_error();
|
||||
ASSERT_TRUE(false);
|
||||
FAIL() << "Could not parse public RSA key.";
|
||||
}
|
||||
switch (RSA_check_key(public_rsa_)) {
|
||||
case 1: // valid.
|
||||
ASSERT_TRUE(true);
|
||||
return;
|
||||
case 0: // not valid.
|
||||
cout << "[rsa key not valid] ";
|
||||
dump_openssl_error();
|
||||
ASSERT_TRUE(false);
|
||||
FAIL() << "[rsa key not valid] ";
|
||||
default: // -1 == check failed.
|
||||
cout << "[error checking rsa key] ";
|
||||
dump_openssl_error();
|
||||
ASSERT_TRUE(false);
|
||||
FAIL() << "[error checking rsa key] ";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -693,14 +675,12 @@ void Session::VerifyRSASignature(const vector<uint8_t>& message,
|
||||
EXPECT_EQ(static_cast<size_t>(RSA_size(public_rsa_)), signature_length)
|
||||
<< "Signature size is wrong. " << signature_length << ", should be "
|
||||
<< RSA_size(public_rsa_) << "\n";
|
||||
|
||||
if (padding_scheme == kSign_RSASSA_PSS) {
|
||||
EVP_PKEY* pkey = EVP_PKEY_new();
|
||||
ASSERT_TRUE(EVP_PKEY_set1_RSA(pkey, public_rsa_) == 1);
|
||||
openssl_ptr<EVP_PKEY, EVP_PKEY_free> pkey(EVP_PKEY_new());
|
||||
ASSERT_EQ(1, EVP_PKEY_set1_RSA(pkey.get(), public_rsa_));
|
||||
|
||||
const bool ok = VerifyPSSSignature(pkey, &message[0], message.size(),
|
||||
const bool ok = VerifyPSSSignature(pkey.get(), &message[0], message.size(),
|
||||
signature, signature_length);
|
||||
EVP_PKEY_free(pkey);
|
||||
EXPECT_TRUE(ok) << "PSS signature check failed.";
|
||||
} else if (padding_scheme == kSign_PKCS1_Block1) {
|
||||
vector<uint8_t> padded_digest(signature_length);
|
||||
@@ -717,20 +697,20 @@ void Session::VerifyRSASignature(const vector<uint8_t>& message,
|
||||
}
|
||||
}
|
||||
|
||||
bool Session::GenerateRSASessionKey(vector<uint8_t>* enc_session_key) {
|
||||
bool Session::GenerateRSASessionKey(vector<uint8_t>* session_key,
|
||||
vector<uint8_t>* enc_session_key) {
|
||||
if (!public_rsa_) {
|
||||
cout << "No public RSA key loaded in test code.\n";
|
||||
return false;
|
||||
}
|
||||
vector<uint8_t> session_key =
|
||||
wvcdm::a2b_hex("6fa479c731d2770b6a61a5d1420bb9d1");
|
||||
*session_key = wvcdm::a2b_hex("6fa479c731d2770b6a61a5d1420bb9d1");
|
||||
enc_session_key->assign(RSA_size(public_rsa_), 0);
|
||||
int status = RSA_public_encrypt(session_key.size(), &session_key[0],
|
||||
int status = RSA_public_encrypt(session_key->size(), &(session_key->front()),
|
||||
&(enc_session_key->front()), public_rsa_,
|
||||
RSA_PKCS1_OAEP_PADDING);
|
||||
int size = static_cast<int>(RSA_size(public_rsa_));
|
||||
if (status != size) {
|
||||
cout << "GenerateRSASessionKey error encrypting session key. ";
|
||||
cout << "GenerateRSASessionKey error encrypting session key.\n";
|
||||
dump_openssl_error();
|
||||
return false;
|
||||
}
|
||||
@@ -744,21 +724,6 @@ void Session::InstallRSASessionTestKey(const vector<uint8_t>& wrapped_rsa_key) {
|
||||
GenerateDerivedKeysFromSessionKey();
|
||||
}
|
||||
|
||||
void Session::DisallowDeriveKeys() {
|
||||
GenerateNonce(&nonce_);
|
||||
vector<uint8_t> enc_session_key;
|
||||
PreparePublicKey();
|
||||
ASSERT_TRUE(GenerateRSASessionKey(&enc_session_key));
|
||||
vector<uint8_t> mac_context;
|
||||
vector<uint8_t> enc_context;
|
||||
FillDefaultContext(&mac_context, &enc_context);
|
||||
ASSERT_NE(OEMCrypto_SUCCESS,
|
||||
OEMCrypto_DeriveKeysFromSessionKey(
|
||||
session_id(), &enc_session_key[0], enc_session_key.size(),
|
||||
&mac_context[0], mac_context.size(), &enc_context[0],
|
||||
enc_context.size()));
|
||||
}
|
||||
|
||||
void Session::GenerateReport(const std::string& pst, bool expect_success,
|
||||
Session* other) {
|
||||
if (other) { // If other is specified, copy mac keys.
|
||||
|
||||
Reference in New Issue
Block a user