Merge from Widevine repo of http://go/wvgerrit/58440 This CL modifies the oemcrypto test TwoHundredEntries so that it attempts to create more than 200 entries. A device is allowed to fail when such an attempt is made, but it must return an insufficient resources error. The test then verifies that each of the entries that were succesfully created can be used to reload its license and the keys can be used for decryption. It then shrinks the usage table header, and verifies that the remaining licenses can still be used for decryption. bug: 112486006 test: unit tests (test code only) Change-Id: I6e6edfb00f0553724e0f99fb4e5ea5c817450937
423 lines
20 KiB
C++
423 lines
20 KiB
C++
#ifndef CDM_OEC_SESSION_UTIL_H_
|
|
#define CDM_OEC_SESSION_UTIL_H_
|
|
|
|
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
|
|
// source code may only be used and distributed under the Widevine Master
|
|
// License Agreement.
|
|
//
|
|
// OEMCrypto unit tests
|
|
//
|
|
#include <openssl/rsa.h>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "oec_device_features.h"
|
|
#include "pst_report.h"
|
|
#include "wv_cdm_constants.h"
|
|
|
|
using namespace std;
|
|
|
|
// GTest requires PrintTo to be in the same namespace as the thing it prints,
|
|
// which is std::vector in this case.
|
|
namespace std {
|
|
|
|
void PrintTo(const vector<uint8_t>& value, ostream* os);
|
|
|
|
} // namespace std
|
|
|
|
namespace wvoec {
|
|
|
|
const size_t kMaxNumKeys = 20;
|
|
|
|
namespace {
|
|
#if defined(TEST_SPEED_MULTIPLIER) // Can slow test time limits when
|
|
// debugging is slowing everything.
|
|
const int kSpeedMultiplier = TEST_SPEED_MULTIPLIER;
|
|
#else
|
|
const int kSpeedMultiplier = 1;
|
|
#endif
|
|
const int kShortSleep = 1 * kSpeedMultiplier;
|
|
const int kLongSleep = 2 * kSpeedMultiplier;
|
|
const uint32_t kDuration = 2 * kSpeedMultiplier;
|
|
const uint32_t kLongDuration = 5 * kSpeedMultiplier;
|
|
const int32_t kTimeTolerance = 3 * kSpeedMultiplier;
|
|
const time_t kUsageTableTimeTolerance = 10 * kSpeedMultiplier;
|
|
} // namespace
|
|
|
|
typedef struct {
|
|
uint8_t verification[4];
|
|
uint32_t duration;
|
|
uint32_t nonce;
|
|
uint32_t control_bits;
|
|
} KeyControlBlock;
|
|
|
|
// Note: The API does not specify a maximum key id length. We specify a
|
|
// maximum just for these tests, so that we have a fixed message size.
|
|
const size_t kTestKeyIdMaxLength = 16;
|
|
|
|
// Most content will use a key id that is 16 bytes long.
|
|
const int kDefaultKeyIdLength = 16;
|
|
|
|
const size_t kMaxTestRSAKeyLength = 2000; // Rough estimate.
|
|
const size_t kMaxPSTLength = 255; // In specification.
|
|
const size_t kMaxMessageSize = 8 * 1024; // In specification.
|
|
const size_t kMaxDecryptSize = 100 * 1024; // In specification.
|
|
|
|
typedef struct {
|
|
uint8_t key_id[kTestKeyIdMaxLength];
|
|
size_t key_id_length;
|
|
uint8_t key_data[wvcdm::MAC_KEY_SIZE];
|
|
size_t key_data_length;
|
|
uint8_t key_iv[wvcdm::KEY_IV_SIZE];
|
|
uint8_t control_iv[wvcdm::KEY_IV_SIZE];
|
|
KeyControlBlock control;
|
|
// Note: cipher_mode may not be part of a real signed message. For these
|
|
// tests, it is convenient to keep it in this structure anyway.
|
|
OEMCryptoCipherMode cipher_mode;
|
|
} MessageKeyData;
|
|
|
|
// This structure will be signed to simulate a message from the server.
|
|
struct MessageData {
|
|
MessageKeyData keys[kMaxNumKeys];
|
|
uint8_t mac_key_iv[wvcdm::KEY_IV_SIZE];
|
|
uint8_t mac_keys[2 * wvcdm::MAC_KEY_SIZE];
|
|
uint8_t pst[kMaxPSTLength];
|
|
};
|
|
|
|
// This structure will be signed to simulate a provisioning response from the
|
|
// server.
|
|
struct RSAPrivateKeyMessage {
|
|
uint8_t rsa_key[kMaxTestRSAKeyLength];
|
|
uint8_t rsa_key_iv[wvcdm::KEY_IV_SIZE];
|
|
size_t rsa_key_length;
|
|
uint32_t nonce;
|
|
};
|
|
|
|
struct Test_PST_Report {
|
|
Test_PST_Report(const std::string& pst_in,
|
|
OEMCrypto_Usage_Entry_Status status_in)
|
|
: status(status_in), pst(pst_in), time_created(time(NULL)) {}
|
|
|
|
OEMCrypto_Usage_Entry_Status status;
|
|
int64_t seconds_since_license_received;
|
|
int64_t seconds_since_first_decrypt;
|
|
int64_t seconds_since_last_decrypt;
|
|
std::string pst;
|
|
time_t time_created;
|
|
};
|
|
|
|
struct EntitledContentKeyData {
|
|
uint8_t entitlement_key_id[wvcdm::KEY_SIZE];
|
|
uint8_t content_key_id[wvcdm::KEY_SIZE];
|
|
uint8_t content_key_data_iv[wvcdm::KEY_SIZE];
|
|
uint8_t content_key_data[wvcdm::KEY_SIZE];
|
|
};
|
|
|
|
// Increment counter for AES-CTR. The CENC spec specifies we increment only
|
|
// the low 64 bits of the IV counter, and leave the high 64 bits alone. This
|
|
// is different from the OpenSSL implementation, so we implement the CTR loop
|
|
// ourselves.
|
|
void ctr128_inc64(int64_t increaseBy, uint8_t* iv);
|
|
|
|
// Some compilers don't like the macro htonl within an ASSERT_EQ.
|
|
uint32_t htonl_fnc(uint32_t x);
|
|
|
|
// Prints error string from BoringSSL
|
|
void dump_boringssl_error();
|
|
|
|
class Session {
|
|
public:
|
|
Session();
|
|
~Session();
|
|
|
|
// Returns the most recently generated nonce.
|
|
// Valid after call to GenerateNonce.
|
|
uint32_t get_nonce() { return nonce_; }
|
|
// Valid after call to open().
|
|
uint32_t session_id() { return (uint32_t)session_id_; }
|
|
// Call OEMCrypto_OpenSession, with GTest ASSERTs.
|
|
void open();
|
|
// Call OEMCrypto_CloseSession, with GTest ASSERTs.
|
|
void close();
|
|
// Artifically set session id without calling OEMCrypto_OpenSession. This is
|
|
// used in core/test/generic_crypto_unittest.cpp.
|
|
void SetSessionId(uint32_t session_id);
|
|
uint32_t GetOecSessionId() { return session_id_; }
|
|
// Generates one nonce. If error_counter is null, this will sleep 1 second
|
|
// and try again if a nonce flood has been detected. If error_counter is
|
|
// not null, it will be incremented when a nonce flood is detected.
|
|
void GenerateNonce(int* error_counter = NULL);
|
|
// Fill the vectors with test context which generate known mac and enc keys.
|
|
void FillDefaultContext(vector<uint8_t>* mac_context,
|
|
vector<uint8_t>* enc_context);
|
|
// Generate known mac and enc keys using OEMCrypto_GenerateDerivedKeys and
|
|
// also fill out enc_key_, mac_key_server_, and mac_key_client_.
|
|
void GenerateDerivedKeysFromKeybox(const wvoec_mock::WidevineKeybox& keybox);
|
|
// Generate known mac and enc keys using OEMCrypto_DeriveKeysFromSessionKey
|
|
// and also fill out enc_key_, mac_key_server_, and mac_key_client_.
|
|
void GenerateDerivedKeysFromSessionKey();
|
|
// Loads and verifies the keys in the message pointed to by message_ptr()
|
|
// using OEMCrypto_LoadKeys. This message should have already been created
|
|
// by FillSimpleMessage, modified if needed, and then encrypted and signed by
|
|
// the server's mac key in EncryptAndSign.
|
|
void LoadTestKeys(const std::string& pst = "", bool new_mac_keys = true);
|
|
// Loads the entitlement keys in the message pointed to by message_ptr()
|
|
// using OEMCrypto_LoadKeys. This message should have already been created
|
|
// by FillSimpleEntitlementMessage, modified if needed, and then encrypted
|
|
// and signed by the server's mac key in EncryptAndSign.
|
|
void LoadEnitlementTestKeys(const std::string& pst = "",
|
|
bool new_mac_keys = true,
|
|
OEMCryptoResult expected_sts = OEMCrypto_SUCCESS);
|
|
// Fills an OEMCrypto_EntitledContentKeyObject using the information from
|
|
// the license_ and randomly generated content keys. This method should be
|
|
// called after LoadEnitlementTestKeys.
|
|
void FillEntitledKeyArray();
|
|
// Encrypts and loads the entitled content keys via
|
|
// OEMCrypto_LoadEntitledContentKeys.
|
|
void LoadEntitledContentKeys(
|
|
OEMCryptoResult expected_sts = OEMCrypto_SUCCESS);
|
|
// This uses OEMCrypto_QueryKeyControl to check that the keys in OEMCrypto
|
|
// have the correct key control data.
|
|
void VerifyTestKeys();
|
|
// This uses OEMCrypto_QueryKeyControl to check that the keys in OEMCrypto
|
|
// have the correct key control data.
|
|
void VerifyEntitlementTestKeys();
|
|
// This creates a refresh key or license renewal message, signs it with the
|
|
// server's mac key, and calls OEMCrypto_RefreshKeys.
|
|
void RefreshTestKeys(const size_t key_count, uint32_t control_bits,
|
|
uint32_t nonce, OEMCryptoResult expected_result);
|
|
// This sets the key id in the current message data to the specified string.
|
|
// This is used to test with different key id lengths.
|
|
void SetKeyId(int index, const string& key_id);
|
|
// This fills the data structure license_ with key information. This data
|
|
// can be modified, and then should be encrypted and signed in EncryptAndSign
|
|
// before being loaded in LoadTestKeys.
|
|
void FillSimpleMessage(uint32_t duration, uint32_t control, uint32_t nonce,
|
|
const std::string& pst = "");
|
|
// This fills the data structure license_ with entitlement key information.
|
|
// This data can be modified, and then should be encrypted and signed in
|
|
// EncryptAndSign before being loaded in LoadEnitlementTestKeys.
|
|
void FillSimpleEntitlementMessage(
|
|
uint32_t duration, uint32_t control,
|
|
uint32_t nonce, const std::string& pst = "");
|
|
// Like FillSimpleMessage, this fills encrypted_license_ with data. The name
|
|
// is a little misleading: the license renewal message is not encrypted, it
|
|
// is just signed. The signature is computed in RefreshTestKeys, above.
|
|
void FillRefreshMessage(size_t key_count, uint32_t control_bits,
|
|
uint32_t nonce);
|
|
// This copies data from license_ to encrypted_license_, and then encrypts
|
|
// each field in the key array appropriately. It then signes the buffer with
|
|
// the server mac keys. It then fills out the key_array_ so that pointers in
|
|
// that array point to the locations in the encrypted message.
|
|
void EncryptAndSign();
|
|
// This encrypts an RSAPrivateKeyMessage with encryption_key so that it may be
|
|
// loaded with OEMCrypto_RewrapDeviceRSAKey.
|
|
void EncryptProvisioningMessage(RSAPrivateKeyMessage* data,
|
|
RSAPrivateKeyMessage* encrypted,
|
|
const vector<uint8_t>& encryption_key);
|
|
// Sign the buffer with server's mac key.
|
|
void ServerSignBuffer(const uint8_t* data, size_t data_length,
|
|
std::vector<uint8_t>* signature);
|
|
// Sign the buffer with client's known mac key. Known test keys must be
|
|
// installed first.
|
|
void ClientSignMessage(const vector<uint8_t>& data,
|
|
std::vector<uint8_t>* signature);
|
|
// This checks the signature generated by OEMCrypto_GenerateSignature against
|
|
// that generaged by ClientSignMessage.
|
|
void VerifyClientSignature(size_t data_length = 400);
|
|
// Set the pointers in key_array[*] to point values inside data. This is
|
|
// needed to satisfy range checks in OEMCrypto_LoadKeys.
|
|
void FillKeyArray(const MessageData& data, OEMCrypto_KeyObject* key_array);
|
|
// As in FillKeyArray but for the license renewal message passed to
|
|
// OEMCrypto_RefreshKeys.
|
|
void FillRefreshArray(OEMCrypto_KeyRefreshObject* key_array,
|
|
size_t key_count);
|
|
// Encrypt a block of data using CTR mode.
|
|
void EncryptCTR(const vector<uint8_t>& in_buffer, const uint8_t* key,
|
|
const uint8_t* starting_iv, vector<uint8_t>* out_buffer);
|
|
// Encrypt some data and pass to OEMCrypto_DecryptCENC to verify decryption.
|
|
void TestDecryptCTR(bool select_key_first = true,
|
|
OEMCryptoResult expected_result = OEMCrypto_SUCCESS,
|
|
int key_index = 0);
|
|
// This compares the actual result with the expected result. If OEMCrypto is
|
|
// an older version, we allow it to report an equivalent error code.
|
|
void TestDecryptResult(OEMCryptoResult expected_result,
|
|
OEMCryptoResult actual_result);
|
|
// Verify that an attempt to select an expired key either succeeds, or gives
|
|
// an actionable error code.
|
|
void TestSelectExpired(unsigned int key_index);
|
|
// Calls OEMCrypto_GetOEMPublicCertificate and loads the OEM cert's public
|
|
// rsa key into public_rsa_.
|
|
void LoadOEMCert(bool verify_cert = false);
|
|
// Creates RSAPrivateKeyMessage for the specified rsa_key, encrypts it with
|
|
// the specified encryption key, and then signs it with the server's mac key.
|
|
// If encryption_key is null, use the session's enc_key_.
|
|
void MakeRSACertificate(struct RSAPrivateKeyMessage* encrypted,
|
|
size_t message_size, std::vector<uint8_t>* signature,
|
|
uint32_t allowed_schemes,
|
|
const vector<uint8_t>& rsa_key,
|
|
const vector<uint8_t>* encryption_key = NULL);
|
|
// Calls OEMCrypto_RewrapDeviceRSAKey with the given provisioning response
|
|
// message. If force is true, we assert that the key loads successfully.
|
|
void RewrapRSAKey(const struct RSAPrivateKeyMessage& encrypted,
|
|
size_t message_size, const std::vector<uint8_t>& signature,
|
|
vector<uint8_t>* wrapped_key, bool force);
|
|
// Loads the specified RSA public key into public_rsa_. If rsa_key is null,
|
|
// the default test key is loaded.
|
|
void PreparePublicKey(const uint8_t* rsa_key = NULL,
|
|
size_t rsa_key_length = 0);
|
|
// Verifies the given signature is from the given message and RSA key, pkey.
|
|
static bool VerifyPSSSignature(EVP_PKEY* pkey, const uint8_t* message,
|
|
size_t message_length,
|
|
const uint8_t* signature,
|
|
size_t signature_length);
|
|
// Verify that the message was signed by the private key associated with
|
|
// |public_rsa_| using the specified padding scheme.
|
|
void VerifyRSASignature(const vector<uint8_t>& message,
|
|
const uint8_t* signature, size_t signature_length,
|
|
RSA_Padding_Scheme padding_scheme);
|
|
// Encrypts a known session key with public_rsa_ for use in future calls to
|
|
// OEMCrypto_DeriveKeysFromSessionKey or OEMCrypto_RewrapDeviceRSAKey30.
|
|
// The unencrypted session key is stored in session_key.
|
|
bool GenerateRSASessionKey(vector<uint8_t>* session_key,
|
|
vector<uint8_t>* enc_session_key);
|
|
// Calls OEMCrypto_RewrapDeviceRSAKey30 with the given provisioning response
|
|
// message. If force is true, we assert that the key loads successfully.
|
|
void RewrapRSAKey30(const struct RSAPrivateKeyMessage& encrypted,
|
|
const std::vector<uint8_t>& encrypted_message_key,
|
|
vector<uint8_t>* wrapped_key, bool force);
|
|
// Loads the specified wrapped_rsa_key into OEMCrypto, and then runs
|
|
// GenerateDerivedKeysFromSessionKey to install known encryption and mac keys.
|
|
void InstallRSASessionTestKey(const vector<uint8_t>& wrapped_rsa_key);
|
|
// Creates a new usage entry, and keeps track of the index.
|
|
// If status is null, we expect success, otherwise status is set to the
|
|
// return value.
|
|
void CreateNewUsageEntry(OEMCryptoResult *status = NULL);
|
|
// Copy encrypted usage entry from other session, and then load it.
|
|
// This session must already be open.
|
|
void LoadUsageEntry(uint32_t index, const vector<uint8_t>& buffer);
|
|
// Copy encrypted usage entry from other session.
|
|
// This session must already be open.
|
|
void LoadUsageEntry(const Session& other) {
|
|
LoadUsageEntry(other.usage_entry_number(), other.encrypted_usage_entry());
|
|
}
|
|
// Reload previously used usage entry.
|
|
void ReloadUsageEntry() { LoadUsageEntry(*this); }
|
|
// Update the usage entry and save the header to the specified buffer.
|
|
void UpdateUsageEntry(std::vector<uint8_t>* header_buffer);
|
|
// Deactivate this session's usage entry.
|
|
void DeactivateUsageEntry(const std::string& pst);
|
|
// The usage entry number for this session's usage entry.
|
|
uint32_t usage_entry_number() const { return usage_entry_number_; }
|
|
void set_usage_entry_number(uint32_t v) { usage_entry_number_ = v; }
|
|
// The encrypted buffer holding the recently updated and saved usage entry.
|
|
const vector<uint8_t>& encrypted_usage_entry() const {
|
|
return encrypted_usage_entry_;
|
|
}
|
|
// Generates a usage report for the specified pst. If there is success,
|
|
// the report's signature is verified, and several fields are given sanity
|
|
// checks. If other is not null, then the mac keys are copied from other in
|
|
// order to verify signatures.
|
|
void GenerateReport(const std::string& pst,
|
|
OEMCryptoResult expected_result = OEMCrypto_SUCCESS,
|
|
Session* other = 0);
|
|
// Move this usage entry to a new index.
|
|
void MoveUsageEntry(uint32_t new_index, std::vector<uint8_t>* header_buffer,
|
|
OEMCryptoResult expect_result = OEMCrypto_SUCCESS);
|
|
// PST used in FillSimpleMesage.
|
|
string pst() const { return pst_; }
|
|
// Returns a pointer-like thing to the usage report generated by the previous
|
|
// call to GenerateReport.
|
|
wvcdm::Unpacked_PST_Report pst_report() {
|
|
return wvcdm::Unpacked_PST_Report(&pst_report_buffer_[0]);
|
|
}
|
|
// Verify the values in the PST report. The signature should have been
|
|
// verified in GenerateReport, above.
|
|
void VerifyPST(const Test_PST_Report& report);
|
|
// Verify the Usage Report. If any time is greater than 10 minutes, it is
|
|
// assumed to be an absolute time, and time_since will be computed relative to
|
|
// now.
|
|
void VerifyReport(Test_PST_Report report,
|
|
int64_t time_license_received = 0,
|
|
int64_t time_first_decrypt = 0,
|
|
int64_t time_last_decrypt = 0);
|
|
// Same as above, but generates the report with the given status.
|
|
void GenerateVerifyReport(const std::string& pst,
|
|
OEMCrypto_Usage_Entry_Status status,
|
|
int64_t time_license_received = 0,
|
|
int64_t time_first_decrypt = 0,
|
|
int64_t time_last_decrypt = 0);
|
|
// Create an entry in the old usage table based on the given report.
|
|
void CreateOldEntry(const Test_PST_Report &report);
|
|
// Create a new entry and copy the old entry into it. Then very the report
|
|
// is right.
|
|
void CopyAndVerifyOldEntry(const Test_PST_Report &report,
|
|
std::vector<uint8_t>* header_buffer);
|
|
|
|
// The unencrypted license response or license renewal response.
|
|
MessageData& license() { return license_; }
|
|
// The encrypted license response or license renewal response.
|
|
MessageData& encrypted_license() { return padded_message_; }
|
|
|
|
// A pointer to the buffer holding encrypted_license.
|
|
const uint8_t* message_ptr();
|
|
|
|
// An array of key objects for use in LoadKeys.
|
|
OEMCrypto_KeyObject* key_array() { return key_array_; }
|
|
// The last signature generated with the server's mac key.
|
|
std::vector<uint8_t>& signature() { return signature_; }
|
|
|
|
// Set the number of keys to use in the license(), encrypted_license()
|
|
// and key_array().
|
|
void set_num_keys(int num_keys) { num_keys_ = num_keys; }
|
|
// The current number of keys to use in the license(), encrypted_license()
|
|
// and key_array().
|
|
unsigned int num_keys() const { return num_keys_; }
|
|
|
|
// Set the size of the buffer used the encrypted license.
|
|
// Must be between sizeof(MessageData) and kMaxMessageSize.
|
|
void set_message_size(size_t size);
|
|
// The size of the encrypted message.
|
|
size_t message_size() { return message_size_; }
|
|
|
|
private:
|
|
// Generate mac and enc keys give the master key.
|
|
void DeriveKeys(const uint8_t* master_key,
|
|
const vector<uint8_t>& mac_key_context,
|
|
const vector<uint8_t>& enc_key_context);
|
|
// Internal utility function to derive key using CMAC-128
|
|
void DeriveKey(const uint8_t* key, const vector<uint8_t>& context,
|
|
int counter, vector<uint8_t>* out);
|
|
|
|
bool open_;
|
|
bool forced_session_id_;
|
|
OEMCrypto_SESSION session_id_;
|
|
vector<uint8_t> mac_key_server_;
|
|
vector<uint8_t> mac_key_client_;
|
|
vector<uint8_t> enc_key_;
|
|
uint32_t nonce_;
|
|
RSA* public_rsa_;
|
|
vector<uint8_t> pst_report_buffer_;
|
|
MessageData license_;
|
|
struct PaddedMessageData : public MessageData {
|
|
uint8_t padding[kMaxMessageSize - sizeof(MessageData)];
|
|
} padded_message_;
|
|
size_t message_size_; // How much of the padded message to use.
|
|
OEMCrypto_KeyObject key_array_[kMaxNumKeys];
|
|
std::vector<uint8_t> signature_;
|
|
unsigned int num_keys_;
|
|
vector<uint8_t> encrypted_usage_entry_;
|
|
uint32_t usage_entry_number_;
|
|
string pst_;
|
|
|
|
// Clear Entitlement key data. This is the backing data for
|
|
// |entitled_key_array_|.
|
|
EntitledContentKeyData entitled_key_data_[kMaxNumKeys];
|
|
// Entitled key object. Pointers are backed by |entitled_key_data_|.
|
|
OEMCrypto_EntitledContentKeyObject entitled_key_array_[kMaxNumKeys];
|
|
};
|
|
|
|
} // namespace wvoec
|
|
|
|
#endif // CDM_OEC_SESSION_UTIL_H_
|