779 lines
35 KiB
C++
779 lines
35 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
|
|
// License Agreement.
|
|
//
|
|
// OEMCrypto unit tests
|
|
//
|
|
#include <gtest/gtest.h>
|
|
#include <time.h>
|
|
#include <unordered_map>
|
|
|
|
#include <mutex>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "core_message_deserialize.h"
|
|
#include "core_message_features.h"
|
|
#include "core_message_serialize.h"
|
|
#include "odk.h"
|
|
#include "oec_device_features.h"
|
|
#include "oec_key_deriver.h"
|
|
#include "oemcrypto_ecc_key.h"
|
|
#include "oemcrypto_fuzz_structs.h"
|
|
#include "oemcrypto_rsa_key.h"
|
|
#include "oemcrypto_types.h"
|
|
#include "pst_report.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 {
|
|
// OEMCrypto Fuzzing: Set max signture length to 1mb.
|
|
const size_t MB = 1024 * 1024;
|
|
|
|
// Make sure this is larger than kMaxKeysPerSession, in oemcrypto_test.cpp
|
|
constexpr size_t kMaxNumKeys = 30;
|
|
|
|
namespace {
|
|
#if defined(TEST_SPEED_MULTIPLIER) // Can slow test time limits when
|
|
// debugging is slowing everything.
|
|
constexpr int kSpeedMultiplier = TEST_SPEED_MULTIPLIER;
|
|
#else
|
|
constexpr int kSpeedMultiplier = 1;
|
|
#endif
|
|
constexpr int kShortSleep = 1 * kSpeedMultiplier;
|
|
constexpr int kLongSleep = 2 * kSpeedMultiplier;
|
|
constexpr uint32_t kDuration = 2 * kSpeedMultiplier;
|
|
constexpr uint32_t kLongDuration = 5 * kSpeedMultiplier;
|
|
constexpr int32_t kTimeTolerance = 3 * kSpeedMultiplier;
|
|
constexpr int64_t kUsageTableTimeTolerance = 10 * kSpeedMultiplier;
|
|
} // namespace
|
|
|
|
// 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.
|
|
constexpr size_t kTestKeyIdMaxLength = 16;
|
|
|
|
// Most content will use a key id that is 16 bytes long.
|
|
constexpr int kDefaultKeyIdLength = 16;
|
|
constexpr size_t kMaxPSTLength = 255; // In specification.
|
|
constexpr size_t kMaxCoreMessage = 200 * kMaxNumKeys + 200; // Rough estimate.
|
|
|
|
typedef struct {
|
|
uint8_t key_id[kTestKeyIdMaxLength];
|
|
size_t key_id_length;
|
|
uint8_t key_data[MAC_KEY_SIZE];
|
|
size_t key_data_length;
|
|
uint8_t key_iv[KEY_IV_SIZE];
|
|
uint8_t control_iv[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[KEY_IV_SIZE];
|
|
uint8_t padding[KEY_IV_SIZE];
|
|
uint8_t mac_keys[2 * MAC_KEY_SIZE];
|
|
uint8_t pst[kMaxPSTLength];
|
|
SRM_Restriction_Data srm_restriction_data;
|
|
};
|
|
|
|
struct Test_PST_Report {
|
|
Test_PST_Report(const std::string& pst_in,
|
|
OEMCrypto_Usage_Entry_Status status_in);
|
|
|
|
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;
|
|
int64_t time_created;
|
|
};
|
|
|
|
struct EntitledContentKeyData {
|
|
uint8_t entitlement_key_id[kTestKeyIdMaxLength];
|
|
size_t entitlement_key_id_length;
|
|
uint8_t content_key_id[kTestKeyIdMaxLength];
|
|
size_t content_key_id_length;
|
|
uint8_t content_key_data_iv[KEY_IV_SIZE];
|
|
uint8_t content_key_data[KEY_SIZE];
|
|
uint8_t encrypted_content_key_data[KEY_SIZE];
|
|
size_t key_index; // Index into the license's key array. Only for testing.
|
|
};
|
|
|
|
// returns 1 on success, -1 if not supported, or 0 if other failure.
|
|
int GetRandBytes(unsigned char* buf, size_t num);
|
|
|
|
void GenerateSimpleSampleDescription(const std::vector<uint8_t>& in,
|
|
std::vector<uint8_t>& out,
|
|
OEMCrypto_SampleDescription* sample,
|
|
OEMCrypto_SubSampleDescription* subsample);
|
|
|
|
// 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;
|
|
// The prototype of the OEMCrypto function to prepare and sign a request.
|
|
typedef OEMCryptoResult (*PrepAndSignRequest_t)(
|
|
OEMCrypto_SESSION session, uint8_t* message, size_t message_length,
|
|
size_t* core_message_length, uint8_t* signature, size_t* signature_length);
|
|
|
|
// A RoundTrip helps generate and verify a request message, helps generate the
|
|
// corresponding response, and then helps verify loading the response.
|
|
template <class CoreRequest, PrepAndSignRequest_t PrepAndSignRequest,
|
|
class CoreResponse, class ResponseData>
|
|
class RoundTrip {
|
|
public:
|
|
RoundTrip() = delete;
|
|
RoundTrip(Session* session)
|
|
: session_(session),
|
|
core_request_(),
|
|
core_response_(),
|
|
response_data_(),
|
|
encrypted_response_data_(),
|
|
required_message_size_(0),
|
|
required_core_message_size_(0),
|
|
required_request_signature_size_(0),
|
|
encrypted_response_length_(0),
|
|
response_signature_length_(0) {}
|
|
virtual ~RoundTrip() {}
|
|
|
|
// Have OEMCrypto sign a request message and then verify the signature and the
|
|
// core message.
|
|
virtual void SignAndVerifyRequest() {
|
|
// Boolean true generates core request and verifies the request.
|
|
// Custom message sizes are 0 by default, so the behavior of following
|
|
// functions will be sign and verify request without any custom buffers
|
|
// sizes.
|
|
ASSERT_EQ(SignAndCreateRequestWithCustomBufferLengths(true),
|
|
OEMCrypto_SUCCESS);
|
|
}
|
|
// Have OEMCrypto sign and call create request APIs. Buffer parameters in API
|
|
// can be set to custom values to test with varying lengths of buffers.
|
|
virtual OEMCryptoResult SignAndCreateRequestWithCustomBufferLengths(
|
|
bool verify_request = false);
|
|
// Used for OEMCrypto Fuzzing: Function to convert fuzzer data to valid
|
|
// License/Provisioning/Renwal request data that can be serialized.
|
|
virtual void InjectFuzzedRequestData(uint8_t* data, size_t size);
|
|
// Create a default |response_data| and |core_response|.
|
|
virtual void CreateDefaultResponse() = 0;
|
|
// Copy fields from |response_data| to |padded_response_data|, encrypting
|
|
// those that should be encrypted. Serialize the core message. Then sign the
|
|
// response.
|
|
virtual void EncryptAndSignResponse() = 0;
|
|
// Attempt to load the response and return the error. Short buffer errors are
|
|
// handled by LoadResponse, not the caller. All other errors should be
|
|
// handled by the caller.
|
|
virtual OEMCryptoResult LoadResponse() { return LoadResponse(session_); }
|
|
// As with LoadResponse, but load into a different session.
|
|
virtual OEMCryptoResult LoadResponse(Session* session) = 0;
|
|
|
|
// Accessors are all read/write because tests modify default values.
|
|
Session* session() { return session_; }
|
|
void set_session(Session* session) { session_ = session; }
|
|
CoreRequest& core_request() { return core_request_; }
|
|
CoreResponse& core_response() { return core_response_; }
|
|
ResponseData& response_data() { return response_data_; }
|
|
std::vector<uint8_t>& encrypted_response_buffer() {
|
|
return encrypted_response_;
|
|
}
|
|
|
|
// Set the size of the buffer used the encrypted license.
|
|
void set_message_size(size_t size) { required_message_size_ = size; }
|
|
// Set core message size to test OEMCrypto request APIs for varying core
|
|
// message lengths.
|
|
void set_core_message_size(size_t size) {
|
|
required_core_message_size_ = size;
|
|
}
|
|
// Set signature size to test OEMCrypto request APIs for varying signature
|
|
// lengths.
|
|
void set_request_signature_size(size_t size) {
|
|
required_request_signature_size_ = size;
|
|
}
|
|
std::vector<uint8_t>& response_signature() { return response_signature_; }
|
|
const std::string& serialized_core_message() const {
|
|
return serialized_core_message_;
|
|
}
|
|
|
|
protected:
|
|
// Returns true if a nonce should be generated before signing the request.
|
|
virtual bool RequestHasNonce() = 0;
|
|
|
|
// ----------------------------------------------------------------------
|
|
// Specialized functionality for each message type.
|
|
|
|
// Verify the signature of the request.
|
|
virtual void VerifyRequestSignature(
|
|
const vector<uint8_t>& data, const vector<uint8_t>& generated_signature,
|
|
size_t core_message_length) = 0;
|
|
// Verify the values of the core response.
|
|
virtual void FillAndVerifyCoreRequest(
|
|
const std::string& core_message_string) = 0;
|
|
// Find the given pointer in the response_data_.
|
|
virtual OEMCrypto_Substring FindSubstring(const void* pointer, size_t length);
|
|
|
|
// Set EncryptAndSignResponse output lengths for later verification.
|
|
void SetEncryptAndSignResponseLengths();
|
|
// Verify EncryptAndSignResponse output lengths are unchanged.
|
|
void VerifyEncryptAndSignResponseLengths() const;
|
|
|
|
// ----------------------------------------------------------------------
|
|
// Member variables.
|
|
Session* session_;
|
|
CoreRequest core_request_;
|
|
CoreResponse core_response_;
|
|
ResponseData response_data_, encrypted_response_data_;
|
|
// Message buffers will be at least this big. Tests for loading and signing
|
|
// messages will increase all buffers to this size.
|
|
size_t required_message_size_;
|
|
size_t required_core_message_size_;
|
|
size_t required_request_signature_size_;
|
|
std::vector<uint8_t> response_signature_;
|
|
std::string serialized_core_message_;
|
|
std::vector<uint8_t> encrypted_response_;
|
|
|
|
private:
|
|
// EncryptAndSignResponse output lengths.
|
|
size_t encrypted_response_length_;
|
|
size_t response_signature_length_;
|
|
};
|
|
|
|
class ProvisioningRoundTrip
|
|
: public RoundTrip<
|
|
/* CoreRequest */ oemcrypto_core_message::ODK_ProvisioningRequest,
|
|
OEMCrypto_PrepAndSignProvisioningRequest,
|
|
/* CoreResponse */ ODK_ParsedProvisioning,
|
|
/* ResponseData */ RSAPrivateKeyMessage> {
|
|
public:
|
|
ProvisioningRoundTrip(Session* session,
|
|
const std::vector<uint8_t>& encoded_rsa_key)
|
|
: RoundTrip(session),
|
|
allowed_schemes_(kSign_RSASSA_PSS),
|
|
encryptor_(),
|
|
encoded_rsa_key_(encoded_rsa_key) {}
|
|
// Prepare the session for signing the request.
|
|
virtual void PrepareSession(const wvoec::WidevineKeybox& keybox);
|
|
void CreateDefaultResponse() override;
|
|
void EncryptAndSignResponse() override;
|
|
void EncryptAndSignResponseWithoutUpdatingEncPrivateKeyLength();
|
|
void SignResponse();
|
|
OEMCryptoResult LoadResponse() override { return LoadResponse(session_); }
|
|
OEMCryptoResult LoadResponse(Session* session) override;
|
|
void VerifyLoadFailed();
|
|
const std::vector<uint8_t>& encoded_rsa_key() { return encoded_rsa_key_; }
|
|
const std::vector<uint8_t>& wrapped_rsa_key() { return wrapped_rsa_key_; }
|
|
void set_allowed_schemes(uint32_t allowed_schemes) {
|
|
allowed_schemes_ = allowed_schemes;
|
|
}
|
|
// Used for OEMCrypto Fuzzing: Function to convert fuzzer data to valid
|
|
// provisioning response data that can be parsed. Calculates signature for
|
|
// data generated by fuzzer, so that signature validation passes when parsing
|
|
// provisioning response.
|
|
void InjectFuzzedResponseData(const uint8_t* data, size_t size);
|
|
|
|
protected:
|
|
bool RequestHasNonce() override { return true; }
|
|
void VerifyRequestSignature(const vector<uint8_t>& data,
|
|
const vector<uint8_t>& generated_signature,
|
|
size_t core_message_length) override;
|
|
// Verify the values of the core response.
|
|
virtual void FillAndVerifyCoreRequest(
|
|
const std::string& core_message_string) override;
|
|
// Load the response, without the retry. Called by LoadResponse.
|
|
OEMCryptoResult LoadResponseNoRetry(Session* session,
|
|
size_t* wrapped_key_length);
|
|
// This takes a pointer in the response_data_ and remaps it to the same
|
|
// pointer within the encrypted message. This is used for backwards
|
|
// compatibliity testing, so that a v15 oemcrypto will accept range checks.
|
|
template <typename T>
|
|
const T* RemapPointer(const T* response_pointer) const;
|
|
|
|
uint32_t allowed_schemes_;
|
|
Encryptor encryptor_;
|
|
// The message key used for Prov 3.0.
|
|
std::vector<uint8_t> message_key_;
|
|
std::vector<uint8_t> encrypted_message_key_;
|
|
std::vector<uint8_t> encoded_rsa_key_;
|
|
std::vector<uint8_t> wrapped_rsa_key_;
|
|
};
|
|
|
|
class LicenseRoundTrip
|
|
: public RoundTrip<
|
|
/* CoreRequest */ oemcrypto_core_message::ODK_LicenseRequest,
|
|
OEMCrypto_PrepAndSignLicenseRequest,
|
|
/* CoreResponse */ ODK_ParsedLicense,
|
|
/* ResponseData */ MessageData> {
|
|
public:
|
|
LicenseRoundTrip(Session* session)
|
|
: RoundTrip(session),
|
|
control_(wvoec::kControlNonceEnabled),
|
|
num_keys_(4),
|
|
pst_(""),
|
|
minimum_srm_version_(0),
|
|
update_mac_keys_(true),
|
|
api_version_(kCurrentAPI),
|
|
expect_request_has_correct_nonce_(true),
|
|
license_type_(OEMCrypto_ContentLicense),
|
|
request_hash_() {}
|
|
void CreateDefaultResponse() override;
|
|
// Used for OEMCrypto Fuzzing: Function to inject fuzzed timer limits
|
|
// into timer_limits field from core_response. We need to fuzz timer
|
|
// limits in order to efficiently fuzz load renewal response API.
|
|
void InjectFuzzedTimerLimits(OEMCrypto_Renewal_Response_Fuzz& fuzzed_data);
|
|
// Used for OEMCrypto Fuzzing: Function to convert fuzzer data to valid
|
|
// License response data that can be parsed. Calculates signature for data
|
|
// generated by fuzzer, so that signature validation passes when parsing
|
|
// license response.
|
|
void InjectFuzzedResponseData(const uint8_t* data, size_t size);
|
|
// Used for OEMCrypto Fuzzing: Convert boolean flags in parsed_license to
|
|
// valid bytes to avoid errors from msan.
|
|
void ConvertDataToValidBools(ODK_ParsedLicense* t);
|
|
// Create a license with four keys. Each key is responsible for one of generic
|
|
// encrypt (key 0), decrypt (key 1), sign (key 2) and verify (key 3). Each key
|
|
// is allowed only one type of operation.
|
|
void CreateResponseWithGenericCryptoKeys();
|
|
// Fill the |core_response| substrings.
|
|
virtual void FillCoreResponseSubstrings();
|
|
void EncryptAndSignResponse() override;
|
|
// Encrypt and sign license response created from a specific odk version.
|
|
void EncryptAndSignResponseWithCoreMessageFeatures(
|
|
const oemcrypto_core_message::features::CoreMessageFeatures& features,
|
|
bool force_clear_kcb);
|
|
// Encrypt license response. This is used in EncryptAndSignResponse().
|
|
void EncryptResponse(bool force_clear_kcb = false);
|
|
// Create core license response with a specific ODK version. This is used in
|
|
// EncryptAndSignResponse().
|
|
void CreateCoreLicenseResponseWithFeatures(
|
|
const oemcrypto_core_message::features::CoreMessageFeatures& features);
|
|
// Sign license response. This is used in EncryptAndSignResponse().
|
|
void SignEncryptedResponse();
|
|
OEMCryptoResult LoadResponse() override { return LoadResponse(session_); }
|
|
OEMCryptoResult LoadResponse(Session* session) override;
|
|
OEMCryptoResult LoadResponse(Session* session, bool verify_keys);
|
|
// Reload an offline license into a different session. This derives new mac
|
|
// keys and then calls LoadResponse.
|
|
OEMCryptoResult ReloadResponse(Session* session);
|
|
void VerifyTestKeys(Session* session);
|
|
// Set the default key control block for all keys. This is used in
|
|
// CreateDefaultResponse. The key control block determines the restrictions
|
|
// that OEMCrypto should place on a key's use. For example, it specifies the
|
|
// minimum HDCP requirement and whether the key can only be used with a secure
|
|
// video path. See the section "Key Control Block" in the document "Widevine
|
|
// Modular DRM Security Integration Guide for CENC".
|
|
void set_control(uint32_t control) { control_ = control; }
|
|
uint32_t control() const { return control_; }
|
|
// Set the number of keys to use in the license.
|
|
void set_num_keys(uint32_t num_keys) { num_keys_ = num_keys; }
|
|
uint32_t num_keys() const { return num_keys_; }
|
|
// Get/Set the pst for the license and usage table entry.
|
|
const std::string& pst() const { return pst_; }
|
|
void set_pst(const std::string& pst) { pst_ = pst; }
|
|
// Set the minimum SRM version for the license.
|
|
void set_minimum_srm_version(uint32_t minimum_srm_version) {
|
|
minimum_srm_version_ = minimum_srm_version;
|
|
}
|
|
// Change the hash of the core request. This should cause the response to be
|
|
// rejected.
|
|
void BreakRequestHash() { request_hash_[3] ^= 42; }
|
|
// Set the API version for the license itself. This will be used in
|
|
// CreateDefaultResponse.
|
|
void set_api_version(uint32_t api_version) { api_version_ = api_version; }
|
|
uint32_t api_version() const { return api_version_; }
|
|
void set_update_mac_keys(bool update_mac_keys) {
|
|
update_mac_keys_ = update_mac_keys;
|
|
}
|
|
void set_license_type(OEMCrypto_LicenseType license_type) {
|
|
license_type_ = license_type;
|
|
}
|
|
// Skip the nonce check when verifying the license request.
|
|
void skip_nonce_check() { expect_request_has_correct_nonce_ = false; }
|
|
// This sets the key id of the specified key to the specified string.
|
|
// This is used to test with different key id lengths.
|
|
void SetKeyId(size_t index, const string& key_id);
|
|
|
|
protected:
|
|
bool RequestHasNonce() override { return true; }
|
|
void VerifyRequestSignature(const vector<uint8_t>& data,
|
|
const vector<uint8_t>& generated_signature,
|
|
size_t core_message_length) override;
|
|
// Verify the values of the core response.
|
|
virtual void FillAndVerifyCoreRequest(
|
|
const std::string& core_message_string) override;
|
|
|
|
// The default key control bits used with CreateDefaultResponse.
|
|
uint32_t control_;
|
|
// The number of keys in the license response.
|
|
uint32_t num_keys_;
|
|
// If non-empty, the license's provider session token.
|
|
std::string pst_;
|
|
// If non-zero, the minimum SRM version.
|
|
uint32_t minimum_srm_version_;
|
|
// If true, the license contains new mac keys for signing renewals.
|
|
bool update_mac_keys_;
|
|
// API version for the license itself. If this is 0 when the license request
|
|
// is signed, it will be set to the same as OEMCrypto's API version. It may
|
|
// be set to a lower value in order to test backwards compatibility.
|
|
uint32_t api_version_;
|
|
// If true, then we expect the nonce in the core request to match that in
|
|
// session. This is usually true, but when we are testing how OEMCrypto
|
|
// handles a bad nonce, we don't want to.
|
|
bool expect_request_has_correct_nonce_;
|
|
// Whether this is a content license or an entitlement license. Used in
|
|
// CreateDefaultResponse.
|
|
OEMCrypto_LicenseType license_type_;
|
|
uint8_t request_hash_[ODK_SHA256_HASH_SIZE];
|
|
};
|
|
|
|
class RenewalRoundTrip
|
|
: public RoundTrip<
|
|
/* CoreRequest */ oemcrypto_core_message::ODK_RenewalRequest,
|
|
OEMCrypto_PrepAndSignRenewalRequest,
|
|
// Renewal response info is same as request:
|
|
/* CoreResponse */ oemcrypto_core_message::ODK_RenewalRequest,
|
|
/* ResponseData */ MessageData> {
|
|
public:
|
|
RenewalRoundTrip(LicenseRoundTrip* license_messages)
|
|
: RoundTrip(license_messages->session()),
|
|
license_messages_(license_messages),
|
|
refresh_object_(),
|
|
renewal_duration_seconds_(
|
|
license_messages->core_response()
|
|
.timer_limits.initial_renewal_duration_seconds),
|
|
is_release_(false) {}
|
|
void CreateDefaultResponse() override;
|
|
void EncryptAndSignResponse() override;
|
|
void InjectFuzzedResponseData(OEMCrypto_Renewal_Response_Fuzz& fuzzed_data,
|
|
const uint8_t* renewal_response,
|
|
const size_t renewal_response_size);
|
|
OEMCryptoResult LoadResponse() override { return LoadResponse(session_); }
|
|
OEMCryptoResult LoadResponse(Session* session) override;
|
|
uint64_t renewal_duration_seconds() const {
|
|
return renewal_duration_seconds_;
|
|
}
|
|
void set_renewal_duration_seconds(uint64_t renewal_duration_seconds) {
|
|
renewal_duration_seconds_ = renewal_duration_seconds;
|
|
}
|
|
void set_is_release(bool is_release) { is_release_ = is_release; }
|
|
|
|
protected:
|
|
bool RequestHasNonce() override { return false; }
|
|
void VerifyRequestSignature(const vector<uint8_t>& data,
|
|
const vector<uint8_t>& generated_signature,
|
|
size_t core_message_length) override;
|
|
// Verify the values of the core response.
|
|
virtual void FillAndVerifyCoreRequest(
|
|
const std::string& core_message_string) override;
|
|
LicenseRoundTrip* license_messages_;
|
|
OEMCrypto_KeyRefreshObject refresh_object_;
|
|
uint64_t renewal_duration_seconds_;
|
|
bool is_release_; // If this is a license release, and not a real renewal.
|
|
};
|
|
|
|
class EntitledMessage {
|
|
public:
|
|
EntitledMessage(LicenseRoundTrip* license_messages)
|
|
: license_messages_(license_messages), num_keys_() {}
|
|
void FillKeyArray();
|
|
void MakeOneKey(size_t entitlement_key_index);
|
|
void SetEntitledKeySession(uint32_t key_session) {
|
|
entitled_key_session_ = key_session;
|
|
}
|
|
void LoadKeys(bool expected_success);
|
|
OEMCryptoResult LoadKeys(const vector<uint8_t>& message);
|
|
OEMCryptoResult LoadKeys();
|
|
void EncryptContentKey();
|
|
void LoadCasKeys(bool load_even, bool load_odd, OEMCryptoResult expected_sts);
|
|
void set_num_keys(uint32_t num_keys) { num_keys_ = num_keys; }
|
|
uint32_t num_keys() const { return num_keys_; }
|
|
void SetEntitlementKeyId(unsigned int index, const std::string& key_id);
|
|
void SetContentKeyId(unsigned int index, const std::string& key_id);
|
|
OEMCrypto_EntitledContentKeyObject* entitled_key_array();
|
|
// Returns entitled_key_data_ which is used as input message buffer to
|
|
// load entitled content keys API.
|
|
EntitledContentKeyData* entitled_key_data();
|
|
size_t entitled_key_data_size();
|
|
// Verify that key control blocks of the loaded keys.
|
|
void VerifyEntitlementTestKeys();
|
|
void VerifyEntitlementTestKey(size_t index);
|
|
|
|
private:
|
|
// Find the offset of the give pointer, relative to |entitled_key_data_|.
|
|
OEMCrypto_Substring FindSubstring(const void* ptr, size_t size);
|
|
// Verify that key control blocks of the loaded keys matches their entitlement
|
|
// key.
|
|
void VerifyKCBs();
|
|
// Verify that decryption with the entitled keys works.
|
|
void VerifyDecrypt();
|
|
|
|
LicenseRoundTrip* license_messages_;
|
|
uint32_t num_keys_;
|
|
// 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];
|
|
uint32_t entitled_key_session_;
|
|
};
|
|
|
|
class Session {
|
|
public:
|
|
Session();
|
|
~Session();
|
|
|
|
// Returns the most recently generated nonce.
|
|
// Valid after call to GenerateNonce.
|
|
uint32_t nonce() const { return nonce_; }
|
|
// The nonce can be overridden.
|
|
void set_nonce(uint32_t nonce) { nonce_ = nonce; }
|
|
// Valid after call to open().
|
|
uint32_t session_id() const { 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 = nullptr);
|
|
// 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::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();
|
|
// Encrypt some data and pass to OEMCrypto_DecryptCENC to verify decryption.
|
|
void TestDecryptCTR(bool select_key_first = true,
|
|
OEMCryptoResult expected_result = OEMCrypto_SUCCESS,
|
|
size_t key_index = 0);
|
|
// Encrypt some data and pass to OEMCrypto_DecryptCENC to verify decryption
|
|
// for entitled sessions.
|
|
void TestDecryptEntitled(OEMCryptoResult expected_result = OEMCrypto_SUCCESS,
|
|
OEMCrypto_SESSION session = 0,
|
|
const uint8_t* content_key_id = nullptr,
|
|
size_t content_key_id_length = 0);
|
|
// Verify that an attempt to select an expired key either succeeds, or gives
|
|
// an actionable error code.
|
|
void TestSelectExpired(size_t key_index);
|
|
// Calls OEMCrypto_GetOEMPublicCertificate and OEMCrypto_LoadOEMPrivateKey and
|
|
// loads the OEM cert's public rsa key into public_rsa_.
|
|
void LoadOEMCert(bool verify_cert = false);
|
|
// 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 default test RSA public key into public_rsa_.
|
|
void SetTestRsaPublicKey();
|
|
// Loads the specified DRM public key into the appropriate key.
|
|
// The provided key is serialized as an ASN.1 DER encoded PrivateKeyInfo.
|
|
void SetPublicKeyFromPrivateKeyInfo(OEMCrypto_PrivateKeyType key_type,
|
|
const uint8_t* buffer, size_t length);
|
|
// Loads the specified RSA public key into public_rsa_.
|
|
// The provided key is serialized as an ASN.1 DER encoded PrivateKeyInfo.
|
|
void SetRsaPublicKeyFromPrivateKeyInfo(const uint8_t* buffer, size_t length);
|
|
// Loads the specified EC public key into public_ec_.
|
|
// The provided key is serialized as an ASN.1 DER encoded PrivateKeyInfo.
|
|
void SetEccPublicKeyFromPrivateKeyInfo(const uint8_t* buffer, size_t length);
|
|
|
|
// Loads the specified DRM public key into the appropriate key.
|
|
// The provided key is serialized as an ASN.1 DER encoded SubjectPublicKey.
|
|
void SetPublicKeyFromSubjectPublicKey(OEMCrypto_PrivateKeyType key_type,
|
|
const uint8_t* buffer, size_t length);
|
|
// Loads the specified RSA public key into public_rsa_.
|
|
// The provided key is serialized as an ASN.1 DER encoded SubjectPublicKey.
|
|
void SetRsaPublicKeyFromSubjectPublicKey(const uint8_t* buffer,
|
|
size_t length);
|
|
// Loads the specified EC public key into public_ec_.
|
|
// The provided key is serialized as an ASN.1 DER encoded SubjectPublicKey.
|
|
void SetEccPublicKeyFromSubjectPublicKey(const uint8_t* buffer,
|
|
size_t 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);
|
|
// Verify that the message was signed by the private key associated with
|
|
// |public_ecc_| using Widevine ECDSA.
|
|
void VerifyEccSignature(const vector<uint8_t>& message,
|
|
const uint8_t* signature, size_t signature_length);
|
|
// Verify RSA or ECC signature based on the key type installed. The
|
|
// padding_scheme will be ignored in case of ECC key.
|
|
void VerifySignature(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);
|
|
// Derives a session key with public_ec_ and a ephemeral "server" ECC key
|
|
// for use in future calls to OEMCrypto_DeriveKeysFromSessionKey.
|
|
// The unencrypted session key is stored in session_key.
|
|
bool GenerateEccSessionKey(vector<uint8_t>* session_key,
|
|
vector<uint8_t>* ecdh_public_key_data);
|
|
// Based on the key type installed, call GenerateRsaSessionKey or
|
|
// GenerateEccSessionKey.
|
|
bool GenerateSessionKey(vector<uint8_t>* session_key,
|
|
vector<uint8_t>* key_material);
|
|
|
|
// 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);
|
|
|
|
void LoadWrappedDrmKey(OEMCrypto_PrivateKeyType key_type,
|
|
const vector<uint8_t>& wrapped_drm_key);
|
|
// Loads the specified wrapped_rsa_key into OEMCrypto.
|
|
void LoadWrappedRsaDrmKey(const vector<uint8_t>& wrapped_rsa_key);
|
|
// Loads the specified wrapped_ecc_key into OEMCrypto.
|
|
void LoadWrappedEccDrmKey(const vector<uint8_t>& wrapped_ecc_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 = nullptr);
|
|
// 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);
|
|
// 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 = nullptr);
|
|
// 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.
|
|
wvutil::Unpacked_PST_Report pst_report() {
|
|
return wvutil::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);
|
|
// 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_; }
|
|
|
|
void set_license(const MessageData& license) { license_ = license; }
|
|
|
|
const KeyDeriver& key_deriver() const { return key_deriver_; }
|
|
void set_mac_keys(const uint8_t* mac_keys) {
|
|
key_deriver_.set_mac_keys(mac_keys);
|
|
}
|
|
|
|
private:
|
|
// 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_select_result,
|
|
OEMCryptoResult actual_decryt_result);
|
|
|
|
bool open_ = false;
|
|
bool forced_session_id_ = false;
|
|
OEMCrypto_SESSION session_id_ = 0;
|
|
KeyDeriver key_deriver_;
|
|
uint32_t nonce_ = 0;
|
|
// Only one of RSA or EC should be set.
|
|
std::unique_ptr<util::RsaPublicKey> public_rsa_;
|
|
std::unique_ptr<util::EccPublicKey> public_ec_;
|
|
// In provisioning 4.0, the shared session key is derived from either
|
|
// 1. (client side) client private key + server ephemeral public key, or
|
|
// 2. (server side) server ephemeral private key + client public key
|
|
// Encryption key and mac keys are derived from the shared session key, and
|
|
// are inserted in to the default license response which simulates the
|
|
// response from a license server. In order for these keys to be deterministic
|
|
// across multiple test calls of GenerateDerivedKeysFromSessionKey(), which
|
|
// simulates how the server derives keys, the ephemeral keys used by the
|
|
// "server" need to be stored for re-use.
|
|
static std::unordered_map<
|
|
util::EccCurve, std::unique_ptr<util::EccPrivateKey>, std::hash<int>>
|
|
server_ephemeral_keys_;
|
|
static std::mutex ephemeral_key_map_lock_;
|
|
vector<uint8_t> pst_report_buffer_;
|
|
MessageData license_ = {};
|
|
|
|
vector<uint8_t> encrypted_usage_entry_;
|
|
uint32_t usage_entry_number_ = 0;
|
|
string pst_;
|
|
}; // class Session
|
|
|
|
// Used for OEMCrypto Fuzzing: Convert byte to a valid boolean to avoid errors
|
|
// generated by msan.
|
|
bool ConvertByteToValidBoolean(const bool* in);
|
|
// Used for OEMCrypto Fuzzing: Generates corpus for request APIs.
|
|
template <class CoreRequest>
|
|
void WriteRequestApiCorpus(size_t signature_length, size_t core_message_length,
|
|
vector<uint8_t>& data);
|
|
template <PrepAndSignRequest_t PrepAndSignRequest>
|
|
void GetDefaultRequestSignatureAndCoreMessageLengths(
|
|
uint32_t& session_id, const size_t& small_size,
|
|
size_t* gen_signature_length, size_t* core_message_length);
|
|
} // namespace wvoec
|
|
|
|
#endif // CDM_OEC_SESSION_UTIL_H_
|