Source release 17.1.0
This commit is contained in:
@@ -2,23 +2,27 @@
|
||||
#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
|
||||
// source code may only be used and distributed under the Widevine
|
||||
// License Agreement.
|
||||
//
|
||||
// OEMCrypto unit tests
|
||||
//
|
||||
#include <gtest/gtest.h>
|
||||
#include <openssl/rsa.h>
|
||||
#include <time.h>
|
||||
#include <unordered_map>
|
||||
|
||||
#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"
|
||||
|
||||
@@ -54,13 +58,6 @@ constexpr int32_t kTimeTolerance = 3 * kSpeedMultiplier;
|
||||
constexpr int64_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.
|
||||
constexpr size_t kTestKeyIdMaxLength = 16;
|
||||
@@ -117,7 +114,7 @@ struct EntitledContentKeyData {
|
||||
};
|
||||
|
||||
// returns 1 on success, -1 if not supported, or 0 if other failure.
|
||||
int GetRandBytes(unsigned char* buf, int num);
|
||||
int GetRandBytes(unsigned char* buf, size_t num);
|
||||
|
||||
void GenerateSimpleSampleDescription(const std::vector<uint8_t>& in,
|
||||
std::vector<uint8_t>& out,
|
||||
@@ -155,12 +152,27 @@ class RoundTrip {
|
||||
core_response_(),
|
||||
response_data_(),
|
||||
encrypted_response_data_(),
|
||||
required_message_size_(0) {}
|
||||
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();
|
||||
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);
|
||||
@@ -189,12 +201,25 @@ class RoundTrip {
|
||||
|
||||
// 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.
|
||||
|
||||
@@ -208,6 +233,11 @@ class RoundTrip {
|
||||
// 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_;
|
||||
@@ -217,9 +247,16 @@ class RoundTrip {
|
||||
// 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
|
||||
@@ -239,6 +276,8 @@ class ProvisioningRoundTrip
|
||||
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();
|
||||
@@ -254,6 +293,7 @@ class ProvisioningRoundTrip
|
||||
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;
|
||||
@@ -316,8 +356,21 @@ class LicenseRoundTrip
|
||||
// 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);
|
||||
@@ -360,6 +413,7 @@ class LicenseRoundTrip
|
||||
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;
|
||||
@@ -423,6 +477,7 @@ class RenewalRoundTrip
|
||||
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;
|
||||
@@ -441,16 +496,35 @@ class EntitledMessage {
|
||||
: license_messages_(license_messages), num_keys_() {}
|
||||
void FillKeyArray();
|
||||
void MakeOneKey(size_t entitlement_key_index);
|
||||
void LoadKeys(OEMCryptoResult expected_sts);
|
||||
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_;
|
||||
@@ -459,6 +533,7 @@ class EntitledMessage {
|
||||
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 {
|
||||
@@ -469,6 +544,8 @@ class 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.
|
||||
@@ -492,16 +569,13 @@ class Session {
|
||||
// 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 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);
|
||||
size_t key_index = 0);
|
||||
// Verify that an attempt to select an expired key either succeeds, or gives
|
||||
// an actionable error code.
|
||||
void TestSelectExpired(unsigned int key_index);
|
||||
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);
|
||||
@@ -510,33 +584,75 @@ class Session {
|
||||
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 = nullptr,
|
||||
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);
|
||||
// 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,
|
||||
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,
|
||||
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);
|
||||
// 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);
|
||||
|
||||
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.
|
||||
@@ -566,7 +682,7 @@ class Session {
|
||||
// order to verify signatures.
|
||||
void GenerateReport(const std::string& pst,
|
||||
OEMCryptoResult expected_result = OEMCrypto_SUCCESS,
|
||||
Session* other = 0);
|
||||
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);
|
||||
@@ -574,8 +690,8 @@ class Session {
|
||||
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]);
|
||||
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.
|
||||
@@ -610,19 +726,34 @@ class Session {
|
||||
OEMCryptoResult actual_select_result,
|
||||
OEMCryptoResult actual_decryt_result);
|
||||
|
||||
bool open_;
|
||||
bool forced_session_id_;
|
||||
OEMCrypto_SESSION session_id_;
|
||||
bool open_ = false;
|
||||
bool forced_session_id_ = false;
|
||||
OEMCrypto_SESSION session_id_ = 0;
|
||||
KeyDeriver key_deriver_;
|
||||
uint32_t nonce_;
|
||||
RSA* public_rsa_;
|
||||
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_;
|
||||
MessageData license_ = {};
|
||||
|
||||
vector<uint8_t> encrypted_usage_entry_;
|
||||
uint32_t usage_entry_number_;
|
||||
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.
|
||||
@@ -631,6 +762,10 @@ bool ConvertByteToValidBoolean(const bool* in);
|
||||
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_
|
||||
|
||||
Reference in New Issue
Block a user