#ifndef CDM_OEC_SESSION_UTIL_H_ #define CDM_OEC_SESSION_UTIL_H_ // Copyright 2016 Google Inc. All Rights Reserved. // // OEMCrypto unit tests // #include #include #include #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& 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) {} 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; }; 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* mac_context, vector* 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& encryption_key); // Sign the buffer with server's mac key. void ServerSignBuffer(const uint8_t* data, size_t data_length, std::vector* signature); // Sign the buffer with client's known mac key. Known test keys must be // installed first. void ClientSignMessage(const vector& data, std::vector* 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& in_buffer, const uint8_t* key, const uint8_t* starting_iv, vector* 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* signature, uint32_t allowed_schemes, const vector& rsa_key, const vector* 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& signature, vector* 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& 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* session_key, vector* 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& encrypted_message_key, vector* 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& wrapped_rsa_key); // Creates a new usage entry, and keeps track of the index. void CreateNewUsageEntry(); // Copy encrypted usage entry from other session, and then load it. // This session must already be open. void LoadUsageEntry(uint32_t index, const vector& 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* 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& 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* 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); // Generate and 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 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* 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& 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& mac_key_context, const vector& enc_key_context); // Internal utility function to derive key using CMAC-128 void DeriveKey(const uint8_t* key, const vector& context, int counter, vector* out); bool open_; bool forced_session_id_; OEMCrypto_SESSION session_id_; vector mac_key_server_; vector mac_key_client_; vector enc_key_; uint32_t nonce_; RSA* public_rsa_; vector 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 signature_; unsigned int num_keys_; vector 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_