Source release 15.2.0
This commit is contained in:
@@ -8,7 +8,7 @@
|
||||
* Reference APIs needed to support Widevine's crypto algorithms.
|
||||
*
|
||||
* See the document "WV Modular DRM Security Integration Guide for Common
|
||||
* Encryption (CENC) -- version 15" for a description of this API. You
|
||||
* Encryption (CENC) -- version 15.2" for a description of this API. You
|
||||
* can find this document in the widevine repository as
|
||||
* docs/WidevineModularDRMSecurityIntegrationGuideforCENC_v15.pdf
|
||||
* Changes between different versions of this API are documented in the files
|
||||
@@ -339,9 +339,9 @@ typedef enum OEMCrypto_Clock_Security_Level {
|
||||
|
||||
typedef uint8_t RSA_Padding_Scheme;
|
||||
// RSASSA-PSS with SHA1.
|
||||
const RSA_Padding_Scheme kSign_RSASSA_PSS = 0x1;
|
||||
#define kSign_RSASSA_PSS ((RSA_Padding_Scheme)0x1)
|
||||
// PKCS1 with block type 1 padding (only).
|
||||
const RSA_Padding_Scheme kSign_PKCS1_Block1 = 0x2;
|
||||
#define kSign_PKCS1_Block1 ((RSA_Padding_Scheme)0x2)
|
||||
|
||||
/*
|
||||
* OEMCrypto_HDCP_Capability is used in the key control block to enforce HDCP
|
||||
@@ -376,9 +376,9 @@ typedef enum OEMCrypto_ProvisioningMethod {
|
||||
/*
|
||||
* Flags indicating full decrypt path hash supported.
|
||||
*/
|
||||
const uint32_t OEMCrypto_Hash_Not_Supported = 0;
|
||||
const uint32_t OEMCrypto_CRC_Clear_Buffer = 1;
|
||||
const uint32_t OEMCrypto_Partner_Defined_Hash = 2;
|
||||
#define OEMCrypto_Hash_Not_Supported 0
|
||||
#define OEMCrypto_CRC_Clear_Buffer 1
|
||||
#define OEMCrypto_Partner_Defined_Hash 2
|
||||
|
||||
/*
|
||||
* Return values from OEMCrypto_GetAnalogOutputFlags.
|
||||
@@ -800,13 +800,17 @@ OEMCryptoResult OEMCrypto_DeriveKeysFromSessionKey(
|
||||
* before requesting more nonces, then OEMCrypto will reset the error
|
||||
* condition and generate valid nonces again.
|
||||
*
|
||||
* To prevent Birthday Paradox attacks, OEMCrypto shall verify that the value
|
||||
* generated is not in this session's nonce table, and that it is not in the
|
||||
* nonce table of any other session.
|
||||
*
|
||||
* Parameters:
|
||||
* [in] session: handle for the session to be used.
|
||||
* [out] nonce: pointer to memory to receive the computed nonce.
|
||||
*
|
||||
* Results:
|
||||
* nonce: the nonce is also stored in secure memory. At least 4 nonces should
|
||||
* be stored for each session.
|
||||
* nonce: the nonce is also stored in secure memory. Each session should
|
||||
* store 4 nonces.
|
||||
*
|
||||
* Returns:
|
||||
* OEMCrypto_SUCCESS success
|
||||
@@ -817,11 +821,9 @@ OEMCryptoResult OEMCrypto_DeriveKeysFromSessionKey(
|
||||
* OEMCrypto_ERROR_SYSTEM_INVALIDATED
|
||||
*
|
||||
* Threading:
|
||||
* This is a "Session Function" and may be called simultaneously with session
|
||||
* functions for other sessions but not simultaneously with other functions
|
||||
* for this session. It will not be called simultaneously with initialization
|
||||
* or usage table functions. It is as if the CDM holds a write lock for this
|
||||
* session, and a read lock on the OEMCrypto system.
|
||||
* This is a "Session Initialization Function" and will not be called
|
||||
* simultaneously with any other function, as if the CDM holds a write lock
|
||||
* on the OEMCrypto system.
|
||||
*
|
||||
* Version:
|
||||
* This method changed in API version 5.
|
||||
@@ -1049,6 +1051,9 @@ OEMCryptoResult OEMCrypto_LoadSRM(const uint8_t* buffer, size_t buffer_length);
|
||||
* entry is marked as "inactive" (either kInactiveUsed or
|
||||
* kInactiveUnused), then the keys are not loaded, and the error
|
||||
* OEMCrypto_ERROR_LICENSE_INACTIVE is returned.
|
||||
* 12. The data in enc_mac_keys_iv is not identical to the 16 bytes before
|
||||
* enc_mac_keys. If it is, return OEMCrypto_ERROR_INVALID_CONTEXT.
|
||||
*
|
||||
* Usage Table and Provider Session Token (pst)
|
||||
*
|
||||
* If a key control block has a nonzero value for Replay_Control, then all
|
||||
@@ -1627,8 +1632,8 @@ OEMCryptoResult OEMCrypto_SelectKey(OEMCrypto_SESSION session,
|
||||
* 6. If the current session has an entry in the Usage Table, and the
|
||||
* status of that entry is either kInactiveUsed or kInactiveUnused, then
|
||||
* return the error OEMCrypto_ERROR_LICENSE_INACTIVE.
|
||||
* 7. If an Decrypt Hash has been initialized via OEMCrypto_SetDecryptHash,
|
||||
* and the current key's control block does not have the
|
||||
* 7. If a Decrypt Hash has been initialized via OEMCrypto_SetDecryptHash,
|
||||
* and the current key's control block does not have the
|
||||
* Allow_Hash_Verification bit set, then do not compute a hash and
|
||||
* return OEMCrypto_ERROR_UNKNOWN_FAILURE.
|
||||
* If the flag is_encrypted is false, then no verification is performed. This
|
||||
@@ -2201,7 +2206,7 @@ OEMCryptoResult OEMCrypto_InstallKeyboxOrOEMCert(const uint8_t* rot,
|
||||
* Version:
|
||||
* This method is new API version 12.
|
||||
*/
|
||||
OEMCrypto_ProvisioningMethod OEMCrypto_GetProvisioningMethod();
|
||||
OEMCrypto_ProvisioningMethod OEMCrypto_GetProvisioningMethod(void);
|
||||
|
||||
/*
|
||||
* OEMCrypto_IsKeyboxOrOEMCertValid
|
||||
@@ -2257,7 +2262,7 @@ OEMCryptoResult OEMCrypto_IsKeyboxOrOEMCertValid(void);
|
||||
* system upgrade.
|
||||
*
|
||||
* This function is optional but recommended for Provisioning 3.0 in API v15.
|
||||
* It may be required for future version of this API.
|
||||
* It may be required for a future version of this API.
|
||||
*
|
||||
* Parameters:
|
||||
* [out] deviceId - pointer to the buffer that receives the Device ID
|
||||
@@ -2459,7 +2464,7 @@ OEMCryptoResult OEMCrypto_GetRandom(uint8_t* randomData, size_t dataLength);
|
||||
* Version:
|
||||
* This method changed in each API version.
|
||||
*/
|
||||
uint32_t OEMCrypto_APIVersion();
|
||||
uint32_t OEMCrypto_APIVersion(void);
|
||||
|
||||
/*
|
||||
* OEMCrypto_BuildInformation
|
||||
@@ -2497,7 +2502,7 @@ uint32_t OEMCrypto_APIVersion();
|
||||
* Version:
|
||||
* This method changed in each API version.
|
||||
*/
|
||||
const char* OEMCrypto_BuildInformation();
|
||||
const char* OEMCrypto_BuildInformation(void);
|
||||
|
||||
/*
|
||||
* OEMCrypto_Security_Patch_Level
|
||||
@@ -2524,7 +2529,7 @@ const char* OEMCrypto_BuildInformation();
|
||||
* Version:
|
||||
* This method was introduced in API version 11.
|
||||
*/
|
||||
uint8_t OEMCrypto_Security_Patch_Level();
|
||||
uint8_t OEMCrypto_Security_Patch_Level(void);
|
||||
|
||||
/*
|
||||
* OEMCrypto_SecurityLevel
|
||||
@@ -2550,7 +2555,7 @@ uint8_t OEMCrypto_Security_Patch_Level();
|
||||
* Version:
|
||||
* This method changed in API version 6.
|
||||
*/
|
||||
const char* OEMCrypto_SecurityLevel();
|
||||
const char* OEMCrypto_SecurityLevel(void);
|
||||
|
||||
/*
|
||||
* OEMCrypto_GetHDCPCapability
|
||||
@@ -2565,12 +2570,6 @@ const char* OEMCrypto_SecurityLevel();
|
||||
* instead of HDMI output. Notice that HDCP must use flag Type 1: all
|
||||
* downstream devices will also use the same version or higher.
|
||||
*
|
||||
* The current HDCP should be the minimum value of any display currently
|
||||
* connected through any channel, either through HDMI or a supported wireless
|
||||
* format. The current value can be used by the application or server to
|
||||
* decide which license can currently be used. If the key control block
|
||||
* requires the current HDCP level, we expect the key to be usable.
|
||||
*
|
||||
* The maximum HDCP level should be the maximum value that the device can
|
||||
* enforce. For example, if the device has an HDCP 1.0 port and an HDCP 2.0
|
||||
* port, and the first port can be disabled, then the maximum is HDCP 2.0. If
|
||||
@@ -2581,6 +2580,30 @@ const char* OEMCrypto_SecurityLevel();
|
||||
* user intends to view the content on a local display. The user will want to
|
||||
* download the higher quality content.
|
||||
*
|
||||
* The current HDCP level should be the level of HDCP currently negotiated
|
||||
* with any connected receivers or repeaters either through HDMI or a
|
||||
* supported wireless format. If multiple ports are connected, the current
|
||||
* level should be the minimum HDCP level of all ports. If the key control
|
||||
* block requires an HDCP level equal to or lower than the current HDCP
|
||||
* level, the key is expected to be usable. If the key control block requires
|
||||
* a higher HDCP level, the key is expected to be forbidden.
|
||||
*
|
||||
* When a key has version HDCP_V2_3 required in the key control block, the
|
||||
* transmitter must have HDCP version 2.3 and have negotiated a connection
|
||||
* with a version 2.3 receiver or repeater. The transmitter must configure
|
||||
* the content stream to be Type 1. Since the transmitter cannot distinguish
|
||||
* between 2.2 and 2.3 downstream receivers when connected to a repeater, it
|
||||
* may transmit to both 2.2 and 2.3 receivers, but not 2.1 receivers.
|
||||
*
|
||||
* For example, if the transmitter is 2.3, and is connected to a receiver
|
||||
* that supports 2.3 then the current level is HDCP_V2_3. If the transmitter
|
||||
* is 2.3 and is connected to a 2.3 repeater, the current level is HDCP_V2_3
|
||||
* even though the repeater can negotiate a connection with a 2.2 downstream
|
||||
* receiver for a Type 1 Content Stream.
|
||||
*
|
||||
* As another example, if the transmitter can support 2.3, but a receiver
|
||||
* supports 2.0, then the current level is HDCP_V2.
|
||||
*
|
||||
* When a license requires HDCP, a device may use a wireless protocol to
|
||||
* connect to a display only if that protocol supports the version of HDCP as
|
||||
* required by the license. Both WirelessHD (formerly WiFi Display) and
|
||||
@@ -2633,7 +2656,7 @@ OEMCryptoResult OEMCrypto_GetHDCPCapability(OEMCrypto_HDCP_Capability* current,
|
||||
* Version:
|
||||
* This method changed in API version 9.
|
||||
*/
|
||||
bool OEMCrypto_SupportsUsageTable();
|
||||
bool OEMCrypto_SupportsUsageTable(void);
|
||||
|
||||
/*
|
||||
* OEMCrypto_IsAntiRollbackHwPresent
|
||||
@@ -2661,7 +2684,7 @@ bool OEMCrypto_SupportsUsageTable();
|
||||
* Version:
|
||||
* This method is new in API version 10.
|
||||
*/
|
||||
bool OEMCrypto_IsAntiRollbackHwPresent();
|
||||
bool OEMCrypto_IsAntiRollbackHwPresent(void);
|
||||
|
||||
/*
|
||||
* OEMCrypto_GetNumberOfOpenSessions
|
||||
@@ -2762,7 +2785,7 @@ OEMCryptoResult OEMCrypto_GetMaxNumberOfSessions(size_t* max);
|
||||
* Version:
|
||||
* This method changed in API version 13.
|
||||
*/
|
||||
uint32_t OEMCrypto_SupportedCertificates();
|
||||
uint32_t OEMCrypto_SupportedCertificates(void);
|
||||
|
||||
/*
|
||||
* OEMCrypto_IsSRMUpdateSupported
|
||||
@@ -2789,7 +2812,7 @@ uint32_t OEMCrypto_SupportedCertificates();
|
||||
* Version:
|
||||
* This method changed in API version 13.
|
||||
*/
|
||||
bool OEMCrypto_IsSRMUpdateSupported();
|
||||
bool OEMCrypto_IsSRMUpdateSupported(void);
|
||||
|
||||
/*
|
||||
* OEMCrypto_GetCurrentSRMVersion
|
||||
@@ -2856,7 +2879,7 @@ OEMCryptoResult OEMCrypto_GetCurrentSRMVersion(uint16_t* version);
|
||||
* Version:
|
||||
* This method is new in API version 14.
|
||||
*/
|
||||
uint32_t OEMCrypto_GetAnalogOutputFlags();
|
||||
uint32_t OEMCrypto_GetAnalogOutputFlags(void);
|
||||
|
||||
/*
|
||||
* OEMCrypto_ResourceRatingTier
|
||||
@@ -2865,7 +2888,7 @@ uint32_t OEMCrypto_GetAnalogOutputFlags();
|
||||
* This function returns a positive number indicating which resource rating
|
||||
* it supports. This value will bubble up to the application level as a
|
||||
* property. This will allow applications to estimate what resolution and
|
||||
* bandwidth the device expects to support.
|
||||
* bandwidth the device is expected to support.
|
||||
*
|
||||
* OEMCrypto unit tests and Android GTS tests will verify that devices do
|
||||
* support the resource values specified in the table below at the tier
|
||||
@@ -2925,8 +2948,6 @@ uint32_t OEMCrypto_GetAnalogOutputFlags();
|
||||
* +-----------------------------------+-----------+------------+-----------+
|
||||
* |Number of keys per session |4 |20 |20 |
|
||||
* +-----------------------------------+-----------+------------+-----------+
|
||||
* |Simultaneous secure playback |1 |2 |2 |
|
||||
* +-----------------------------------+-----------+------------+-----------+
|
||||
* |Decrypted Frames per Second |30 fps SD |30 fps HD |60 fps HD |
|
||||
* +-----------------------------------+-----------+------------+-----------+
|
||||
*
|
||||
@@ -2945,7 +2966,7 @@ uint32_t OEMCrypto_GetAnalogOutputFlags();
|
||||
* Version:
|
||||
* This method is new in API version 15.
|
||||
*/
|
||||
uint32_t OEMCrypto_ResourceRatingTier();
|
||||
uint32_t OEMCrypto_ResourceRatingTier(void);
|
||||
|
||||
/*
|
||||
* OEMCrypto_RewrapDeviceRSAKey30
|
||||
@@ -3306,7 +3327,7 @@ OEMCryptoResult OEMCrypto_LoadDeviceRSAKey(OEMCrypto_SESSION session,
|
||||
* Version:
|
||||
* This method is new in API version 10.
|
||||
*/
|
||||
OEMCryptoResult OEMCrypto_LoadTestRSAKey();
|
||||
OEMCryptoResult OEMCrypto_LoadTestRSAKey(void);
|
||||
|
||||
/*
|
||||
* OEMCrypto_GenerateRSASignature
|
||||
@@ -3648,8 +3669,10 @@ OEMCryptoResult OEMCrypto_UpdateUsageEntry(OEMCrypto_SESSION session,
|
||||
* means that the state of the usage entry is changed to InactiveUsed if it
|
||||
* was Active, or InactiveUnused if it was Unused. This also increments the
|
||||
* entry's generation number, and the header's master generation number. The
|
||||
* entry's flag ForbidReport will be set. This flag prevents an application
|
||||
* from generating a report of a deactivated license without first saving the
|
||||
* corresponding generation number in the usage table header is also
|
||||
* incremented so that it matches the one in the entry. The entry's flag
|
||||
* ForbidReport will be set. This flag prevents an application from
|
||||
* generating a report of a deactivated license without first saving the
|
||||
* entry.
|
||||
*
|
||||
* It is allowed to call this function multiple times. If the state is
|
||||
@@ -3954,14 +3977,14 @@ OEMCryptoResult OEMCrypto_CopyOldUsageEntry(OEMCrypto_SESSION session,
|
||||
* Version:
|
||||
* This method is new in API version 13.
|
||||
*/
|
||||
OEMCryptoResult OEMCrypto_DeleteOldUsageTable();
|
||||
OEMCryptoResult OEMCrypto_DeleteOldUsageTable(void);
|
||||
|
||||
/*
|
||||
* OEMCrypto_RemoveSRM
|
||||
*
|
||||
* Description:
|
||||
* Delete the current SRM. Any valid SRM, regardless of version number, will
|
||||
* be installable after this via OEMCrypto_LoadSRM.
|
||||
* Delete the current SRM. Any valid SRM, regardless of its version number,
|
||||
* will be installable after this via OEMCrypto_LoadSRM.
|
||||
*
|
||||
* This function should not be implemented on production devices, and will
|
||||
* only be used to verify unit tests on a test device.
|
||||
@@ -3981,7 +4004,7 @@ OEMCryptoResult OEMCrypto_DeleteOldUsageTable();
|
||||
* Version:
|
||||
* This method is new in API version 13.
|
||||
*/
|
||||
OEMCryptoResult OEMCrypto_RemoveSRM();
|
||||
OEMCryptoResult OEMCrypto_RemoveSRM(void);
|
||||
|
||||
/*
|
||||
* OEMCrypto_CreateOldUsageEntry
|
||||
@@ -4027,7 +4050,7 @@ OEMCryptoResult OEMCrypto_CreateOldUsageEntry(uint64_t time_since_license_receiv
|
||||
* supported. OEMCrypto is not required by Google to support this feature,
|
||||
* but support will greatly improve automated testing. A hash type of
|
||||
* OEMCrypto_CRC_Clear_Buffer = 1 means the device will be able to compute
|
||||
* the CRC32 checksum of the decrypted content in the secure buffer after a
|
||||
* the CRC 32 checksum of the decrypted content in the secure buffer after a
|
||||
* call to OEMCrypto_DecryptCENC. Google intends to provide test applications
|
||||
* on some platforms, such as Android, that will automate decryption testing
|
||||
* using the CRC 32 checksum of all frames in some test content.
|
||||
@@ -4055,7 +4078,7 @@ OEMCryptoResult OEMCrypto_CreateOldUsageEntry(uint64_t time_since_license_receiv
|
||||
* Version:
|
||||
* This method is new in API version 15.
|
||||
*/
|
||||
uint32_t OEMCrypto_SupportsDecryptHash();
|
||||
uint32_t OEMCrypto_SupportsDecryptHash(void);
|
||||
|
||||
/*
|
||||
* OEMCrypto_SetDecryptHash
|
||||
|
||||
@@ -7,8 +7,13 @@
|
||||
#include "oec_device_features.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
# include <windows.h>
|
||||
#else
|
||||
# include <sys/types.h>
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <cstring>
|
||||
|
||||
@@ -18,6 +23,43 @@ namespace wvoec {
|
||||
|
||||
DeviceFeatures global_features;
|
||||
|
||||
bool CanChangeTime() {
|
||||
#ifdef _WIN32
|
||||
LUID desired_id;
|
||||
if (!LookupPrivilegeValue(nullptr, SE_SYSTEMTIME_NAME, &desired_id))
|
||||
return false;
|
||||
HANDLE token;
|
||||
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &token))
|
||||
return false;
|
||||
std::unique_ptr<void, decltype(&CloseHandle)> safe_token(token, &CloseHandle);
|
||||
|
||||
// This queries all the permissions given to the token to determine if we can
|
||||
// change the system time. Note this is subtly different from PrivilegeCheck
|
||||
// as that only checks "enabled" privileges; even with admin rights, the
|
||||
// privilege is default disabled, even when granted.
|
||||
|
||||
DWORD size = 0;
|
||||
// Determine how big we need to allocate first.
|
||||
GetTokenInformation(token, TokenPrivileges, nullptr, 0, &size);
|
||||
// Since TOKEN_PRIVILEGES uses a variable-length array, we need to use malloc
|
||||
std::unique_ptr<TOKEN_PRIVILEGES, decltype(&free)> privileges(
|
||||
(TOKEN_PRIVILEGES*)malloc(size), &free);
|
||||
if (privileges && GetTokenInformation(token, TokenPrivileges,
|
||||
privileges.get(), size, &size)) {
|
||||
for (int i = 0; i < privileges->PrivilegeCount; i++) {
|
||||
if (privileges->Privileges[i].Luid.HighPart == desired_id.HighPart &&
|
||||
privileges->Privileges[i].Luid.LowPart == desired_id.LowPart) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
#else
|
||||
return getuid() == 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void DeviceFeatures::Initialize(bool is_cast_receiver,
|
||||
bool force_load_test_keybox) {
|
||||
cast_receiver = is_cast_receiver;
|
||||
@@ -153,37 +195,16 @@ std::string DeviceFeatures::RestrictFilter(const std::string& initial_filter) {
|
||||
if (provisioning_method
|
||||
!= OEMCrypto_OEMCertificate) FilterOut(&filter, "*Prov30*");
|
||||
if (!supports_rsa_3072) FilterOut(&filter, "*RSAKey3072*");
|
||||
if (api_version < 14) {
|
||||
// Because API 13 uses an old hard coded test keybox, none of these tests
|
||||
// will pass. Partners who wish to test with a v13 OEMCrypto should use
|
||||
// code on an older v13 branch.
|
||||
printf("These unit tests are designed for OEMCrypto API 15 and above.\n");
|
||||
printf("This device has an OEMCrypto with API version %d.\n", api_version);
|
||||
printf("To verify correctness, please build unit tests from a "
|
||||
"compatible branch.\n");
|
||||
FilterOut(&filter, "*API09*");
|
||||
FilterOut(&filter, "*API10*");
|
||||
FilterOut(&filter, "*API11*");
|
||||
FilterOut(&filter, "*API12*");
|
||||
FilterOut(&filter, "*API13*");
|
||||
FilterOut(&filter, "*API14*");
|
||||
FilterOut(&filter, "*TestKeyboxTest*");
|
||||
FilterOut(&filter, "*SessionTest*");
|
||||
FilterOut(&filter, "*UsageTable*");
|
||||
FilterOut(&filter, "*GenericCrypto*");
|
||||
FilterOut(&filter, "*LoadsCertificate*");
|
||||
FilterOut(&filter, "*UsesCertificate*");
|
||||
// We also expect some CDM tests to fail without a new test keybox:
|
||||
FilterOut(&filter, "*WvCdmRequestLicenseTest*");
|
||||
FilterOut(&filter, "*WvGenericOperations*");
|
||||
FilterOut(&filter, "*WvCdmEngine*");
|
||||
FilterOut(&filter, "*Cdm/WvCdm*");
|
||||
FilterOut(&filter, "*Cdm/WvHls*");
|
||||
}
|
||||
if (api_version < 9) FilterOut(&filter, "*API09*");
|
||||
if (api_version < 10) FilterOut(&filter, "*API10*");
|
||||
if (api_version < 11) FilterOut(&filter, "*API11*");
|
||||
if (api_version < 12) FilterOut(&filter, "*API12*");
|
||||
if (api_version < 13) FilterOut(&filter, "*API13*");
|
||||
if (api_version < 14) FilterOut(&filter, "*API14*");
|
||||
if (api_version < 15) FilterOut(&filter, "*API15*");
|
||||
// Some tests may require root access. If user is not root, filter these tests
|
||||
// out.
|
||||
if (getuid()) {
|
||||
if (!CanChangeTime()) {
|
||||
FilterOut(&filter, "UsageTableTest.TimeRollbackPrevention");
|
||||
}
|
||||
// Performance tests take a long time. Filter them out if they are not
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#include "OEMCryptoCENC.h"
|
||||
@@ -539,6 +538,7 @@ void Session::FillSimpleMessage(uint32_t duration, uint32_t control,
|
||||
uint32_t nonce, const std::string& pst) {
|
||||
EXPECT_EQ(
|
||||
1, GetRandBytes(license_.mac_key_iv, sizeof(license_.mac_key_iv)));
|
||||
memset(license_.padding, 0, sizeof(license_.padding));
|
||||
EXPECT_EQ(1, GetRandBytes(license_.mac_keys, sizeof(license_.mac_keys)));
|
||||
for (unsigned int i = 0; i < num_keys_; i++) {
|
||||
memset(license_.keys[i].key_id, 0, kTestKeyIdMaxLength);
|
||||
@@ -554,9 +554,9 @@ void Session::FillSimpleMessage(uint32_t duration, uint32_t control,
|
||||
if (global_features.api_version >= 12) {
|
||||
// For version 12 and above, we require OEMCrypto to handle kcNN for all
|
||||
// licenses.
|
||||
std::stringstream stream;
|
||||
stream << "kc" << global_features.api_version;
|
||||
memcpy(license_.keys[i].control.verification, stream.str().c_str(), 4);
|
||||
std::string kcVersion =
|
||||
"kc" + std::to_string(global_features.api_version);
|
||||
memcpy(license_.keys[i].control.verification, kcVersion.c_str(), 4);
|
||||
} else if (control & wvoec::kControlSecurityPatchLevelMask) {
|
||||
// For versions before 12, we require the special key control block only
|
||||
// when there are newer features present.
|
||||
@@ -598,9 +598,9 @@ void Session::FillSimpleEntitlementMessage(
|
||||
if (global_features.api_version >= 12) {
|
||||
// For version 12 and above, we require OEMCrypto to handle kcNN for all
|
||||
// licenses.
|
||||
std::stringstream stream;
|
||||
stream << "kc" << global_features.api_version;
|
||||
memcpy(license_.keys[i].control.verification, stream.str().c_str(), 4);
|
||||
std::string kcVersion =
|
||||
"kc" + std::to_string(global_features.api_version);
|
||||
memcpy(license_.keys[i].control.verification, kcVersion.c_str(), 4);
|
||||
} else if (control & wvoec::kControlSecurityPatchLevelMask) {
|
||||
// For versions before 12, we require the special key control block only
|
||||
// when there are newer features present.
|
||||
@@ -631,10 +631,10 @@ void Session::FillRefreshMessage(size_t key_count, uint32_t control_bits,
|
||||
if (global_features.api_version >= 12) {
|
||||
// For version 12 and above, we require OEMCrypto to handle kcNN for all
|
||||
// licenses.
|
||||
std::stringstream stream;
|
||||
stream << "kc" << global_features.api_version;
|
||||
std::string kcVersion =
|
||||
"kc" + std::to_string(global_features.api_version);
|
||||
memcpy(encrypted_license().keys[i].control.verification,
|
||||
stream.str().c_str(), 4);
|
||||
kcVersion.c_str(), 4);
|
||||
} else {
|
||||
// For versions before 12, we require the special key control block only
|
||||
// when there are newer features present.
|
||||
@@ -1268,7 +1268,6 @@ void Session::GenerateReport(const std::string& pst,
|
||||
ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts);
|
||||
}
|
||||
if (sts == OEMCrypto_ERROR_SHORT_BUFFER) {
|
||||
ASSERT_EQ(wvcdm::Unpacked_PST_Report::report_size(pst.length()), length);
|
||||
pst_report_buffer_.assign(length, 0xFF); // Fill with garbage values.
|
||||
}
|
||||
sts = OEMCrypto_ReportUsage(session_id(),
|
||||
@@ -1278,7 +1277,7 @@ void Session::GenerateReport(const std::string& pst,
|
||||
if (expected_result != OEMCrypto_SUCCESS) {
|
||||
return;
|
||||
}
|
||||
ASSERT_EQ(pst_report_buffer_.size(), length);
|
||||
EXPECT_EQ(wvcdm::Unpacked_PST_Report::report_size(pst.length()), length);
|
||||
vector<uint8_t> computed_signature(SHA_DIGEST_LENGTH);
|
||||
unsigned int sig_len = SHA_DIGEST_LENGTH;
|
||||
HMAC(EVP_sha1(), mac_key_client_.data(), mac_key_client_.size(),
|
||||
@@ -1359,6 +1358,9 @@ void Session::GenerateVerifyReport(const std::string& pst,
|
||||
Test_PST_Report expected(pst, status);
|
||||
ASSERT_NO_FATAL_FAILURE(VerifyReport(expected, time_license_received,
|
||||
time_first_decrypt, time_last_decrypt));
|
||||
// The PST report was signed above. Below we verify that the entire message
|
||||
// that is sent to the server will be signed by the right mac keys.
|
||||
ASSERT_NO_FATAL_FAILURE(VerifyClientSignature());
|
||||
}
|
||||
|
||||
void Session::CreateOldEntry(const Test_PST_Report& report) {
|
||||
|
||||
@@ -81,6 +81,7 @@ typedef struct {
|
||||
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];
|
||||
};
|
||||
|
||||
@@ -13,16 +13,20 @@
|
||||
#include <openssl/sha.h>
|
||||
#include <openssl/x509.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
# include <windows.h>
|
||||
#else
|
||||
# include <sys/time.h>
|
||||
#endif
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
@@ -87,6 +91,37 @@ int GetRandBytes(unsigned char* buf, int num) {
|
||||
// returns 1 on success, -1 if not supported, or 0 if other failure.
|
||||
return RAND_bytes(buf, num);
|
||||
}
|
||||
|
||||
/** @return The Unix time of the given time point. */
|
||||
template <typename Duration>
|
||||
uint64_t UnixTime(const std::chrono::time_point<std::chrono::system_clock,
|
||||
Duration>& point) {
|
||||
return point.time_since_epoch() / std::chrono::seconds(1);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
using NativeTime = SYSTEMTIME;
|
||||
#else
|
||||
using NativeTime = timeval;
|
||||
#endif
|
||||
|
||||
void AddNativeTime(int64_t delta_seconds, NativeTime* time) {
|
||||
#ifdef _WIN32
|
||||
// See remarks from this for why this series is used.
|
||||
// https://msdn.microsoft.com/en-us/f77cdf86-0f97-4a89-b565-95b46fa7d65b
|
||||
FILETIME file_time;
|
||||
ASSERT_TRUE(SystemTimeToFileTime(time, &file_time));
|
||||
uint64_t long_time = static_cast<uint64_t>(file_time.dwLowDateTime) |
|
||||
(static_cast<uint64_t>(file_time.dwHighDateTime) << 32);
|
||||
long_time += delta_seconds * 1e7; // long_time is in 100-nanosecond intervals.
|
||||
file_time.dwLowDateTime = long_time & ((1ull << 32) - 1);
|
||||
file_time.dwHighDateTime = long_time >> 32;
|
||||
ASSERT_TRUE(FileTimeToSystemTime(&file_time, time));
|
||||
#else
|
||||
time->tv_sec += delta_seconds;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
class OEMCryptoClientTest : public ::testing::Test, public SessionUtil {
|
||||
@@ -95,7 +130,6 @@ class OEMCryptoClientTest : public ::testing::Test, public SessionUtil {
|
||||
|
||||
void SetUp() override {
|
||||
::testing::Test::SetUp();
|
||||
wvcdm::g_cutoff = wvcdm::LOG_INFO;
|
||||
const ::testing::TestInfo* const test_info =
|
||||
::testing::UnitTest::GetInstance()->current_test_info();
|
||||
LOGD("Running test %s.%s", test_info->test_case_name(), test_info->name());
|
||||
@@ -871,6 +905,24 @@ TEST_F(OEMCryptoSessionTests, LoadKeyNoNonce) {
|
||||
ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys());
|
||||
}
|
||||
|
||||
// Verify that a second license may be not be loaded in a session.
|
||||
TEST_F(OEMCryptoSessionTests, LoadKeyNoNonceTwice) {
|
||||
Session s;
|
||||
ASSERT_NO_FATAL_FAILURE(s.open());
|
||||
ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s));
|
||||
ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(kDuration, 0, 42));
|
||||
ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign());
|
||||
ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys());
|
||||
ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign());
|
||||
ASSERT_NE(
|
||||
OEMCrypto_SUCCESS,
|
||||
OEMCrypto_LoadKeys(s.session_id(), s.message_ptr(), s.message_size(),
|
||||
s.signature().data(), s.signature().size(),
|
||||
s.enc_mac_keys_iv_substr(), s.enc_mac_keys_substr(),
|
||||
s.num_keys(), s.key_array(), s.pst_substr(),
|
||||
GetSubstring(), OEMCrypto_ContentLicense));
|
||||
}
|
||||
|
||||
// Verify that a license may be loaded with a nonce.
|
||||
TEST_F(OEMCryptoSessionTests, LoadKeyWithNonce) {
|
||||
Session s;
|
||||
@@ -882,6 +934,24 @@ TEST_F(OEMCryptoSessionTests, LoadKeyWithNonce) {
|
||||
ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys());
|
||||
}
|
||||
|
||||
// Verify that a second license may be not be loaded in a session.
|
||||
TEST_F(OEMCryptoSessionTests, LoadKeyWithNonceTwice) {
|
||||
Session s;
|
||||
ASSERT_NO_FATAL_FAILURE(s.open());
|
||||
ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s));
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
s.FillSimpleMessage(0, wvoec::kControlNonceEnabled, s.get_nonce()));
|
||||
ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign());
|
||||
ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys());
|
||||
ASSERT_NE(
|
||||
OEMCrypto_SUCCESS,
|
||||
OEMCrypto_LoadKeys(s.session_id(), s.message_ptr(), s.message_size(),
|
||||
s.signature().data(), s.signature().size(),
|
||||
s.enc_mac_keys_iv_substr(), s.enc_mac_keys_substr(),
|
||||
s.num_keys(), s.key_array(), s.pst_substr(),
|
||||
GetSubstring(), OEMCrypto_ContentLicense));
|
||||
}
|
||||
|
||||
// This asks for several nonce. This simulates several license requests being
|
||||
// lost. OEMCrypto is required to keep up to four nonce in the nonce table.
|
||||
TEST_F(OEMCryptoSessionTests, LoadKeySeveralNonce) {
|
||||
@@ -1179,6 +1249,27 @@ TEST_F(OEMCryptoSessionTests, LoadKeyWithBadRange7) {
|
||||
ASSERT_NE(OEMCrypto_SUCCESS, sts);
|
||||
}
|
||||
|
||||
// The IV should not be identical to the data right before the encrypted mac
|
||||
// keys.
|
||||
TEST_F(OEMCryptoSessionTests, LoadKeyWithSuspiciousIV) {
|
||||
Session s;
|
||||
ASSERT_NO_FATAL_FAILURE(s.open());
|
||||
ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s));
|
||||
ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(0, 0, 0));
|
||||
// This is suspicious: the data right before the mac keys is identical to the
|
||||
// iv.
|
||||
memcpy(s.license().padding, s.license().mac_key_iv,
|
||||
sizeof(s.license().padding));
|
||||
ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign());
|
||||
|
||||
OEMCryptoResult sts = OEMCrypto_LoadKeys(
|
||||
s.session_id(), s.message_ptr(), s.message_size(), s.signature().data(),
|
||||
s.signature().size(), s.enc_mac_keys_iv_substr(), s.enc_mac_keys_substr(),
|
||||
s.num_keys(), s.key_array(), GetSubstring(), GetSubstring(),
|
||||
OEMCrypto_ContentLicense);
|
||||
ASSERT_NE(OEMCrypto_SUCCESS, sts);
|
||||
}
|
||||
|
||||
// Test that LoadKeys fails when a key is loaded with no key control block.
|
||||
TEST_F(OEMCryptoSessionTests, LoadKeyWithNullKeyControl) {
|
||||
Session s;
|
||||
@@ -1483,10 +1574,36 @@ TEST_F(OEMCryptoSessionTests, SelectKeyNotThereAPI15) {
|
||||
ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign());
|
||||
ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys());
|
||||
const char* key_id = "no_key";
|
||||
ASSERT_EQ(OEMCrypto_ERROR_NO_CONTENT_KEY,
|
||||
OEMCrypto_SelectKey(
|
||||
s.session_id(), reinterpret_cast<const uint8_t*>(key_id),
|
||||
strlen(key_id), OEMCrypto_CipherMode_CTR));
|
||||
OEMCryptoResult sts = OEMCrypto_SelectKey(
|
||||
s.session_id(), reinterpret_cast<const uint8_t*>(key_id), strlen(key_id),
|
||||
OEMCrypto_CipherMode_CTR);
|
||||
if (sts != OEMCrypto_SUCCESS) {
|
||||
EXPECT_EQ(OEMCrypto_ERROR_NO_CONTENT_KEY, sts);
|
||||
} else {
|
||||
// Delayed error code. If select key was a success, then we should
|
||||
// eventually see the error when we decrypt.
|
||||
vector<uint8_t> in_buffer(256);
|
||||
for (size_t i = 0; i < in_buffer.size(); i++) in_buffer[i] = i % 256;
|
||||
vector<uint8_t> encryptionIv(AES_BLOCK_SIZE);
|
||||
EXPECT_EQ(1, GetRandBytes(encryptionIv.data(), AES_BLOCK_SIZE));
|
||||
// Describe the output
|
||||
vector<uint8_t> out_buffer(in_buffer.size());
|
||||
const bool is_encrypted = true;
|
||||
OEMCrypto_DestBufferDesc destBuffer;
|
||||
destBuffer.type = OEMCrypto_BufferType_Clear;
|
||||
destBuffer.buffer.clear.address = out_buffer.data();
|
||||
destBuffer.buffer.clear.max_length = out_buffer.size();
|
||||
OEMCrypto_CENCEncryptPatternDesc pattern;
|
||||
pattern.encrypt = 0;
|
||||
pattern.skip = 0;
|
||||
pattern.offset = 0;
|
||||
// Decrypt the data
|
||||
sts = OEMCrypto_DecryptCENC(
|
||||
s.session_id(), in_buffer.data(), in_buffer.size(), is_encrypted,
|
||||
encryptionIv.data(), 0, &destBuffer, &pattern,
|
||||
OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample);
|
||||
EXPECT_EQ(OEMCrypto_ERROR_NO_CONTENT_KEY, sts);
|
||||
}
|
||||
}
|
||||
|
||||
// After loading keys, we should be able to query the key control block. If we
|
||||
@@ -1952,6 +2069,18 @@ class OEMCryptoSessionTestsDecryptTests
|
||||
pattern_ = ::testing::get<0>(GetParam());
|
||||
cipher_mode_ = ::testing::get<1>(GetParam());
|
||||
decrypt_inplace_ = ::testing::get<2>(GetParam());
|
||||
verify_crc_ = global_features.supports_crc;
|
||||
// Pick a random key.
|
||||
EXPECT_EQ(1, GetRandBytes(key_, AES_BLOCK_SIZE));
|
||||
// Pick a random starting iv. Some tests override this before using it.
|
||||
starting_iv_.resize(AES_BLOCK_SIZE);
|
||||
EXPECT_EQ(1, GetRandBytes(starting_iv_.data(), starting_iv_.size()));
|
||||
total_size_ = -1;
|
||||
}
|
||||
|
||||
virtual void TearDown() {
|
||||
ASSERT_NO_FATAL_FAILURE(session_.close());
|
||||
OEMCryptoSessionTests::TearDown();
|
||||
}
|
||||
|
||||
void FindTotalSize() {
|
||||
@@ -1962,24 +2091,47 @@ class OEMCryptoSessionTestsDecryptTests
|
||||
}
|
||||
}
|
||||
|
||||
void EncryptData(const vector<uint8_t>& key,
|
||||
const vector<uint8_t>& starting_iv,
|
||||
const vector<uint8_t>& in_buffer,
|
||||
vector<uint8_t>* out_buffer) {
|
||||
// Set up the input buffer and output buffer.
|
||||
// This should be called after FindTotalSize().
|
||||
void MakeBuffers() {
|
||||
ASSERT_GT(total_size_, 0u);
|
||||
encrypted_buffer_.resize(total_size_);
|
||||
truth_buffer_.resize(total_size_);
|
||||
for (size_t i = 0; i < total_size_; i++) truth_buffer_[i] = i % 256;
|
||||
output_descriptor_.type = OEMCrypto_BufferType_Clear;
|
||||
if (decrypt_inplace_) {
|
||||
output_descriptor_.buffer.clear.address = encrypted_buffer_.data();
|
||||
} else {
|
||||
// Add some padding to verify there is no overrun.
|
||||
clear_buffer_.resize(total_size_ + 16, 0xaa);
|
||||
output_descriptor_.buffer.clear.address = clear_buffer_.data();
|
||||
}
|
||||
output_descriptor_.buffer.clear.max_length = total_size_;
|
||||
}
|
||||
|
||||
void UpdateOutputOffset(size_t offset) {
|
||||
if (decrypt_inplace_) {
|
||||
output_descriptor_.buffer.clear.address =
|
||||
encrypted_buffer_.data() + offset;
|
||||
} else {
|
||||
output_descriptor_.buffer.clear.address = clear_buffer_.data() + offset;
|
||||
}
|
||||
output_descriptor_.buffer.clear.max_length = total_size_ - offset;
|
||||
}
|
||||
|
||||
void EncryptData() {
|
||||
AES_KEY aes_key;
|
||||
AES_set_encrypt_key(key.data(), AES_BLOCK_SIZE * 8, &aes_key);
|
||||
out_buffer->resize(in_buffer.size());
|
||||
AES_set_encrypt_key(key_, AES_BLOCK_SIZE * 8, &aes_key);
|
||||
|
||||
uint8_t iv[AES_BLOCK_SIZE]; // Current iv.
|
||||
|
||||
memcpy(iv, starting_iv.data(), AES_BLOCK_SIZE);
|
||||
memcpy(iv, starting_iv_.data(), AES_BLOCK_SIZE);
|
||||
|
||||
size_t buffer_index = 0; // byte index into in and out.
|
||||
size_t block_offset = 0; // byte index into current block.
|
||||
for (size_t i = 0; i < subsample_size_.size(); i++) {
|
||||
// Copy clear content.
|
||||
if (subsample_size_[i].clear_size > 0) {
|
||||
memcpy(&(*out_buffer)[buffer_index], &in_buffer[buffer_index],
|
||||
memcpy(&encrypted_buffer_[buffer_index], &truth_buffer_[buffer_index],
|
||||
subsample_size_[i].clear_size);
|
||||
buffer_index += subsample_size_[i].clear_size;
|
||||
}
|
||||
@@ -2006,15 +2158,17 @@ class OEMCryptoSessionTestsDecryptTests
|
||||
// can put whatever we want in the output buffer.
|
||||
if (skip_block || ((cipher_mode_ == OEMCrypto_CipherMode_CBC) &&
|
||||
(size < AES_BLOCK_SIZE))) {
|
||||
memcpy(&(*out_buffer)[buffer_index], &in_buffer[buffer_index], size);
|
||||
memcpy(&encrypted_buffer_[buffer_index], &truth_buffer_[buffer_index],
|
||||
size);
|
||||
block_offset = 0; // Next block should be complete.
|
||||
} else {
|
||||
if (cipher_mode_ == OEMCrypto_CipherMode_CTR) {
|
||||
uint8_t aes_output[AES_BLOCK_SIZE];
|
||||
AES_encrypt(iv, aes_output, &aes_key);
|
||||
for (size_t n = 0; n < size; n++) {
|
||||
(*out_buffer)[buffer_index + n] =
|
||||
aes_output[n + block_offset] ^ in_buffer[buffer_index + n];
|
||||
encrypted_buffer_[buffer_index + n] =
|
||||
aes_output[n + block_offset] ^
|
||||
truth_buffer_[buffer_index + n];
|
||||
}
|
||||
if (size + block_offset < AES_BLOCK_SIZE) {
|
||||
// Partial block. Don't increment iv. Compute next block offset.
|
||||
@@ -2029,10 +2183,10 @@ class OEMCryptoSessionTestsDecryptTests
|
||||
} else {
|
||||
uint8_t aes_input[AES_BLOCK_SIZE];
|
||||
for (size_t n = 0; n < size; n++) {
|
||||
aes_input[n] = in_buffer[buffer_index + n] ^ iv[n];
|
||||
aes_input[n] = truth_buffer_[buffer_index + n] ^ iv[n];
|
||||
}
|
||||
AES_encrypt(aes_input, &(*out_buffer)[buffer_index], &aes_key);
|
||||
memcpy(iv, &(*out_buffer)[buffer_index], AES_BLOCK_SIZE);
|
||||
AES_encrypt(aes_input, &encrypted_buffer_[buffer_index], &aes_key);
|
||||
memcpy(iv, &encrypted_buffer_[buffer_index], AES_BLOCK_SIZE);
|
||||
// CBC mode should always start on block boundary.
|
||||
block_offset = 0;
|
||||
}
|
||||
@@ -2042,73 +2196,58 @@ class OEMCryptoSessionTestsDecryptTests
|
||||
}
|
||||
}
|
||||
|
||||
void TestDecryptCENC(const vector<uint8_t>& key,
|
||||
const vector<uint8_t>& /* encryptionIv */,
|
||||
const vector<uint8_t>& encryptedData,
|
||||
const vector<uint8_t>& unencryptedData) {
|
||||
void LoadLicense() {
|
||||
// First we open a session and load a license.
|
||||
ASSERT_NO_FATAL_FAILURE(session_.open());
|
||||
ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&session_));
|
||||
uint32_t control = 0;
|
||||
if (verify_crc_) control |= kControlAllowHashVerification;
|
||||
ASSERT_NO_FATAL_FAILURE(session_.FillSimpleMessage(
|
||||
kDuration, control, 0));
|
||||
memcpy(session_.license().keys[0].key_data, key_, sizeof(key_));
|
||||
session_.license().keys[0].cipher_mode = cipher_mode_;
|
||||
ASSERT_NO_FATAL_FAILURE(session_.EncryptAndSign());
|
||||
ASSERT_NO_FATAL_FAILURE(session_.LoadTestKeys());
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS,
|
||||
OEMCrypto_SelectKey(
|
||||
session_.session_id(), session_.license().keys[0].key_id,
|
||||
session_.license().keys[0].key_id_length, cipher_mode_));
|
||||
}
|
||||
|
||||
void TestDecryptCENC() {
|
||||
OEMCryptoResult sts;
|
||||
Session s;
|
||||
ASSERT_NO_FATAL_FAILURE(s.open());
|
||||
ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s));
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
s.FillSimpleMessage(kDuration, kControlAllowHashVerification, 0));
|
||||
memcpy(s.license().keys[0].key_data, key.data(), key.size());
|
||||
s.license().keys[0].cipher_mode = cipher_mode_;
|
||||
ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign());
|
||||
ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys());
|
||||
if (global_features.supports_crc) {
|
||||
uint32_t hash =
|
||||
wvcrc32(unencryptedData.data(), unencryptedData.size());
|
||||
// If supported, initialize the decrypt hash.
|
||||
if (verify_crc_) {
|
||||
uint32_t hash = wvcrc32(truth_buffer_.data(), truth_buffer_.size());
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS,
|
||||
OEMCrypto_SetDecryptHash(
|
||||
s.session_id(), 1, reinterpret_cast<const uint8_t*>(&hash),
|
||||
sizeof(hash)));
|
||||
}
|
||||
sts = OEMCrypto_SelectKey(s.session_id(), s.license().keys[0].key_id,
|
||||
s.license().keys[0].key_id_length,
|
||||
cipher_mode_);
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
|
||||
// We decrypt each subsample.
|
||||
vector<uint8_t> output_buffer(total_size_ + 16, 0xaa);
|
||||
const uint8_t *input_buffer = NULL;
|
||||
if (decrypt_inplace_) { // Use same buffer for input and output.
|
||||
// Copy the useful data from encryptedData to output_buffer, which
|
||||
// will be the same as input_buffer. Leave the 0xaa padding at the end.
|
||||
for(size_t i=0; i < total_size_; i++) output_buffer[i] = encryptedData[i];
|
||||
// Now let input_buffer point to the same data.
|
||||
input_buffer = output_buffer.data();
|
||||
} else {
|
||||
input_buffer = encryptedData.data();
|
||||
session_.session_id(), 1,
|
||||
reinterpret_cast<const uint8_t*>(&hash), sizeof(hash)));
|
||||
}
|
||||
size_t buffer_offset = 0;
|
||||
for (size_t i = 0; i < subsample_size_.size(); i++) {
|
||||
OEMCrypto_CENCEncryptPatternDesc pattern = pattern_;
|
||||
pattern.offset = 0; // Final CENC spec says pattern offset always 0.
|
||||
bool is_encrypted = false;
|
||||
OEMCrypto_DestBufferDesc destBuffer;
|
||||
size_t block_offset = 0;
|
||||
uint8_t subsample_flags = 0;
|
||||
if (subsample_size_[i].clear_size > 0) {
|
||||
destBuffer.type = OEMCrypto_BufferType_Clear;
|
||||
destBuffer.buffer.clear.address = &output_buffer[buffer_offset];
|
||||
destBuffer.buffer.clear.max_length = total_size_ - buffer_offset;
|
||||
ASSERT_NO_FATAL_FAILURE(UpdateOutputOffset(buffer_offset));
|
||||
if (i == 0) subsample_flags |= OEMCrypto_FirstSubsample;
|
||||
if ((i == subsample_size_.size() - 1) &&
|
||||
(subsample_size_[i].encrypted_size == 0)) {
|
||||
subsample_flags |= OEMCrypto_LastSubsample;
|
||||
}
|
||||
sts =
|
||||
OEMCrypto_DecryptCENC(s.session_id(), input_buffer + buffer_offset,
|
||||
subsample_size_[i].clear_size, is_encrypted,
|
||||
sample_init_data_[i].iv, block_offset,
|
||||
&destBuffer, &pattern, subsample_flags);
|
||||
sts = OEMCrypto_DecryptCENC(
|
||||
session_.session_id(), &encrypted_buffer_[buffer_offset],
|
||||
subsample_size_[i].clear_size, is_encrypted,
|
||||
sample_init_data_[i].iv, block_offset, &output_descriptor_,
|
||||
&pattern, subsample_flags);
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
|
||||
buffer_offset += subsample_size_[i].clear_size;
|
||||
}
|
||||
if (subsample_size_[i].encrypted_size > 0) {
|
||||
destBuffer.type = OEMCrypto_BufferType_Clear;
|
||||
destBuffer.buffer.clear.address = &output_buffer[buffer_offset];
|
||||
destBuffer.buffer.clear.max_length = total_size_ - buffer_offset;
|
||||
ASSERT_NO_FATAL_FAILURE(UpdateOutputOffset(buffer_offset));
|
||||
is_encrypted = true;
|
||||
block_offset = sample_init_data_[i].block_offset;
|
||||
subsample_flags = 0;
|
||||
@@ -2119,10 +2258,10 @@ class OEMCryptoSessionTestsDecryptTests
|
||||
subsample_flags |= OEMCrypto_LastSubsample;
|
||||
}
|
||||
sts = OEMCrypto_DecryptCENC(
|
||||
s.session_id(), input_buffer + buffer_offset,
|
||||
session_.session_id(), &encrypted_buffer_[buffer_offset],
|
||||
subsample_size_[i].encrypted_size, is_encrypted,
|
||||
sample_init_data_[i].iv, block_offset, &destBuffer, &pattern,
|
||||
subsample_flags);
|
||||
sample_init_data_[i].iv, block_offset, &output_descriptor_,
|
||||
&pattern, subsample_flags);
|
||||
// CBC mode should not accept a block offset.
|
||||
if ((block_offset > 0) && (cipher_mode_ == OEMCrypto_CipherMode_CBC)) {
|
||||
ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, sts)
|
||||
@@ -2133,14 +2272,23 @@ class OEMCryptoSessionTestsDecryptTests
|
||||
buffer_offset += subsample_size_[i].encrypted_size;
|
||||
}
|
||||
}
|
||||
EXPECT_EQ(0xaa, output_buffer[total_size_]) << "Buffer overrun.";
|
||||
output_buffer.resize(total_size_);
|
||||
EXPECT_EQ(unencryptedData, output_buffer);
|
||||
if (output_descriptor_.type == OEMCrypto_BufferType_Clear) {
|
||||
if (decrypt_inplace_) {
|
||||
// We expect encrypted buffer to have been changed by OEMCrypto.
|
||||
EXPECT_EQ(encrypted_buffer_, truth_buffer_);
|
||||
} else {
|
||||
// If we are not decrypting in place, then look at the one byte just
|
||||
// after the data that was written. It should not have changed from the
|
||||
// original 0xaa that we set in MakeBuffersession_.
|
||||
EXPECT_EQ(0xaa, clear_buffer_[total_size_]) << "Buffer overrun.";
|
||||
clear_buffer_.resize(total_size_); // Remove padding.
|
||||
EXPECT_EQ(clear_buffer_, truth_buffer_);
|
||||
}
|
||||
}
|
||||
if (global_features.supports_crc) {
|
||||
uint32_t frame;
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS,
|
||||
OEMCrypto_GetHashErrorCode(s.session_id(), &frame));
|
||||
|
||||
OEMCrypto_GetHashErrorCode(session_.session_id(), &frame));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2149,7 +2297,17 @@ class OEMCryptoSessionTestsDecryptTests
|
||||
bool decrypt_inplace_; // If true, input and output buffers are the same.
|
||||
vector<SampleSize> subsample_size_;
|
||||
size_t total_size_;
|
||||
bool verify_crc_;
|
||||
vector<SampleInitData> sample_init_data_;
|
||||
// Encrypted data -- this is input to OEMCrypto, and output from EncryptData.
|
||||
std::vector<uint8_t> encrypted_buffer_;
|
||||
std::vector<uint8_t> clear_buffer_; // OEMCrypto store clear output here.
|
||||
void* secure_handle_; // OEMCrypto stores secure output here.
|
||||
std::vector<uint8_t> truth_buffer_; // Truth data for clear text.
|
||||
OEMCrypto_DestBufferDesc output_descriptor_;
|
||||
uint8_t key_[AES_BLOCK_SIZE]; // Encryption Key.
|
||||
std::vector<uint8_t> starting_iv_; // Starting IV.
|
||||
Session session_;
|
||||
};
|
||||
|
||||
// Tests that generate partial ending blocks. These tests should not be used
|
||||
@@ -2163,15 +2321,10 @@ TEST_P(OEMCryptoSessionTestsDecryptTests, SingleLargeSubsample) {
|
||||
// full patterns if we have more than 320 -- round up to 400.
|
||||
subsample_size_.push_back(SampleSize(0, 400));
|
||||
FindTotalSize();
|
||||
vector<uint8_t> unencryptedData(total_size_);
|
||||
vector<uint8_t> encryptedData(total_size_);
|
||||
vector<uint8_t> encryptionIv(AES_BLOCK_SIZE);
|
||||
vector<uint8_t> key(AES_BLOCK_SIZE);
|
||||
EXPECT_EQ(1, GetRandBytes(encryptionIv.data(), AES_BLOCK_SIZE));
|
||||
EXPECT_EQ(1, GetRandBytes(key.data(), AES_BLOCK_SIZE));
|
||||
for (size_t i = 0; i < total_size_; i++) unencryptedData[i] = i % 256;
|
||||
EncryptData(key, encryptionIv, unencryptedData, &encryptedData);
|
||||
TestDecryptCENC(key, encryptionIv, encryptedData, unencryptedData);
|
||||
ASSERT_NO_FATAL_FAILURE(MakeBuffers());
|
||||
ASSERT_NO_FATAL_FAILURE(LoadLicense());
|
||||
ASSERT_NO_FATAL_FAILURE(EncryptData());
|
||||
ASSERT_NO_FATAL_FAILURE(TestDecryptCENC());
|
||||
}
|
||||
|
||||
// When the pattern length is 10 blocks, there is a discrepancy between the
|
||||
@@ -2181,30 +2334,20 @@ TEST_P(OEMCryptoSessionTestsDecryptTests, SingleLargeSubsample) {
|
||||
TEST_P(OEMCryptoSessionTestsDecryptTests, PatternPlusOneBlock) {
|
||||
subsample_size_.push_back(SampleSize(0, 160 + 16));
|
||||
FindTotalSize();
|
||||
vector<uint8_t> unencryptedData(total_size_);
|
||||
vector<uint8_t> encryptedData(total_size_);
|
||||
vector<uint8_t> encryptionIv(AES_BLOCK_SIZE);
|
||||
vector<uint8_t> key(AES_BLOCK_SIZE);
|
||||
EXPECT_EQ(1, GetRandBytes(encryptionIv.data(), AES_BLOCK_SIZE));
|
||||
EXPECT_EQ(1, GetRandBytes(key.data(), AES_BLOCK_SIZE));
|
||||
for (size_t i = 0; i < total_size_; i++) unencryptedData[i] = i % 256;
|
||||
EncryptData(key, encryptionIv, unencryptedData, &encryptedData);
|
||||
TestDecryptCENC(key, encryptionIv, encryptedData, unencryptedData);
|
||||
ASSERT_NO_FATAL_FAILURE(MakeBuffers());
|
||||
ASSERT_NO_FATAL_FAILURE(LoadLicense());
|
||||
ASSERT_NO_FATAL_FAILURE(EncryptData());
|
||||
ASSERT_NO_FATAL_FAILURE(TestDecryptCENC());
|
||||
}
|
||||
|
||||
// Test that a single block can be decrypted.
|
||||
TEST_P(OEMCryptoSessionTestsDecryptTests, OneBlock) {
|
||||
subsample_size_.push_back(SampleSize(0, 16));
|
||||
FindTotalSize();
|
||||
vector<uint8_t> unencryptedData(total_size_);
|
||||
vector<uint8_t> encryptedData(total_size_);
|
||||
vector<uint8_t> encryptionIv(AES_BLOCK_SIZE);
|
||||
vector<uint8_t> key(AES_BLOCK_SIZE);
|
||||
EXPECT_EQ(1, GetRandBytes(encryptionIv.data(), AES_BLOCK_SIZE));
|
||||
EXPECT_EQ(1, GetRandBytes(key.data(), AES_BLOCK_SIZE));
|
||||
for (size_t i = 0; i < total_size_; i++) unencryptedData[i] = i % 256;
|
||||
EncryptData(key, encryptionIv, unencryptedData, &encryptedData);
|
||||
TestDecryptCENC(key, encryptionIv, encryptedData, unencryptedData);
|
||||
ASSERT_NO_FATAL_FAILURE(MakeBuffers());
|
||||
ASSERT_NO_FATAL_FAILURE(LoadLicense());
|
||||
ASSERT_NO_FATAL_FAILURE(EncryptData());
|
||||
ASSERT_NO_FATAL_FAILURE(TestDecryptCENC());
|
||||
}
|
||||
|
||||
// This tests the ability to decrypt multiple subsamples with no offset.
|
||||
@@ -2215,41 +2358,36 @@ TEST_P(OEMCryptoSessionTestsDecryptTests, NoOffset) {
|
||||
subsample_size_.push_back(SampleSize(50, 256));
|
||||
subsample_size_.push_back(SampleSize(25, 160));
|
||||
FindTotalSize();
|
||||
vector<uint8_t> unencryptedData(total_size_);
|
||||
vector<uint8_t> encryptedData(total_size_);
|
||||
vector<uint8_t> encryptionIv(AES_BLOCK_SIZE);
|
||||
vector<uint8_t> key(AES_BLOCK_SIZE);
|
||||
EXPECT_EQ(1, GetRandBytes(encryptionIv.data(), AES_BLOCK_SIZE));
|
||||
EXPECT_EQ(1, GetRandBytes(key.data(), AES_BLOCK_SIZE));
|
||||
for (size_t i = 0; i < total_size_; i++) unencryptedData[i] = i % 256;
|
||||
EncryptData(key, encryptionIv, unencryptedData, &encryptedData);
|
||||
TestDecryptCENC(key, encryptionIv, encryptedData, unencryptedData);
|
||||
ASSERT_NO_FATAL_FAILURE(MakeBuffers());
|
||||
ASSERT_NO_FATAL_FAILURE(LoadLicense());
|
||||
ASSERT_NO_FATAL_FAILURE(EncryptData());
|
||||
ASSERT_NO_FATAL_FAILURE(TestDecryptCENC());
|
||||
}
|
||||
|
||||
// This tests an offset into the block for the second encrypted subsample.
|
||||
// This should only work for CTR mode, for CBC mode an error is expected in
|
||||
// the decrypt step.
|
||||
// If this test fails for CTR mode, then it is probably handleing the
|
||||
// If this test fails for CTR mode, then it is probably handling the
|
||||
// block_offset incorrectly.
|
||||
TEST_P(OEMCryptoSessionTestsPartialBlockTests, EvenOffset) {
|
||||
subsample_size_.push_back(SampleSize(25, 8));
|
||||
subsample_size_.push_back(SampleSize(25, 32));
|
||||
subsample_size_.push_back(SampleSize(25, 50));
|
||||
FindTotalSize();
|
||||
vector<uint8_t> unencryptedData(total_size_);
|
||||
vector<uint8_t> encryptedData(total_size_);
|
||||
vector<uint8_t> encryptionIv(AES_BLOCK_SIZE);
|
||||
vector<uint8_t> key(AES_BLOCK_SIZE);
|
||||
EXPECT_EQ(1, GetRandBytes(key.data(), AES_BLOCK_SIZE));
|
||||
ASSERT_NO_FATAL_FAILURE(MakeBuffers());
|
||||
ASSERT_NO_FATAL_FAILURE(LoadLicense());
|
||||
// CTR Mode is self-inverse -- i.e. We can pick the encrypted data and
|
||||
// compute the unencrypted data. By picking the encrypted data to be all 0,
|
||||
// it is easier to re-encrypt the data and debug problems. Similarly, we
|
||||
// pick an iv = 0.
|
||||
EncryptData(key, encryptionIv, encryptedData, &unencryptedData);
|
||||
// Run EncryptData again to correctly compute intermediate IV vectors.
|
||||
// For CBC mode, this also computes the real encrypted data.
|
||||
EncryptData(key, encryptionIv, unencryptedData, &encryptedData);
|
||||
TestDecryptCENC(key, encryptionIv, encryptedData, unencryptedData);
|
||||
starting_iv_.assign(AES_BLOCK_SIZE, 0);
|
||||
truth_buffer_.assign(total_size_, 0);
|
||||
ASSERT_NO_FATAL_FAILURE(EncryptData());
|
||||
truth_buffer_ = encrypted_buffer_; // truth_buffer_ = encrypted zero buffer.
|
||||
// Run EncryptData to re-encrypt this buffer. For CTR mode, we should get
|
||||
// back to zeros.
|
||||
ASSERT_NO_FATAL_FAILURE(EncryptData());
|
||||
ASSERT_NO_FATAL_FAILURE(TestDecryptCENC());
|
||||
}
|
||||
|
||||
// If the EvenOffset test passes, but this one doesn't, then DecryptCTR might
|
||||
@@ -2264,15 +2402,10 @@ TEST_P(OEMCryptoSessionTestsPartialBlockTests, OddOffset) {
|
||||
subsample_size_.push_back(SampleSize(10, 75));
|
||||
subsample_size_.push_back(SampleSize(10, 25));
|
||||
FindTotalSize();
|
||||
vector<uint8_t> unencryptedData(total_size_);
|
||||
vector<uint8_t> encryptedData(total_size_);
|
||||
vector<uint8_t> encryptionIv(AES_BLOCK_SIZE);
|
||||
vector<uint8_t> key(AES_BLOCK_SIZE);
|
||||
EXPECT_EQ(1, GetRandBytes(encryptionIv.data(), AES_BLOCK_SIZE));
|
||||
EXPECT_EQ(1, GetRandBytes(key.data(), AES_BLOCK_SIZE));
|
||||
for (size_t i = 0; i < total_size_; i++) unencryptedData[i] = i % 256;
|
||||
EncryptData(key, encryptionIv, unencryptedData, &encryptedData);
|
||||
TestDecryptCENC(key, encryptionIv, encryptedData, unencryptedData);
|
||||
ASSERT_NO_FATAL_FAILURE(MakeBuffers());
|
||||
ASSERT_NO_FATAL_FAILURE(LoadLicense());
|
||||
ASSERT_NO_FATAL_FAILURE(EncryptData());
|
||||
ASSERT_NO_FATAL_FAILURE(TestDecryptCENC());
|
||||
}
|
||||
|
||||
// This tests that the algorithm used to increment the counter for
|
||||
@@ -2286,17 +2419,13 @@ TEST_P(OEMCryptoSessionTestsPartialBlockTests, OddOffset) {
|
||||
// If you start with an IV of 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE, after you
|
||||
// increment twice, you should get 0xFFFFFFFFFFFFFFFF0000000000000000.
|
||||
TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptWithNearWrap) {
|
||||
starting_iv_ = wvcdm::a2b_hex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE");
|
||||
subsample_size_.push_back(SampleSize(0, 256));
|
||||
FindTotalSize();
|
||||
vector<uint8_t> unencryptedData(total_size_);
|
||||
vector<uint8_t> encryptedData(total_size_);
|
||||
vector<uint8_t> encryptionIv(AES_BLOCK_SIZE);
|
||||
vector<uint8_t> key(AES_BLOCK_SIZE);
|
||||
EXPECT_EQ(1, GetRandBytes(key.data(), AES_BLOCK_SIZE));
|
||||
encryptionIv = wvcdm::a2b_hex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE");
|
||||
for (size_t i = 0; i < total_size_; i++) unencryptedData[i] = i % 256;
|
||||
EncryptData(key, encryptionIv, unencryptedData, &encryptedData);
|
||||
TestDecryptCENC(key, encryptionIv, encryptedData, unencryptedData);
|
||||
ASSERT_NO_FATAL_FAILURE(MakeBuffers());
|
||||
ASSERT_NO_FATAL_FAILURE(LoadLicense());
|
||||
ASSERT_NO_FATAL_FAILURE(EncryptData());
|
||||
ASSERT_NO_FATAL_FAILURE(TestDecryptCENC());
|
||||
}
|
||||
|
||||
// This tests the case where an encrypted sample is not an even number of
|
||||
@@ -2308,15 +2437,10 @@ TEST_P(OEMCryptoSessionTestsPartialBlockTests, PartialBlock) {
|
||||
// other tests, e.g. (7, 3). 3*16 < 50 and 7*16 > 50.
|
||||
subsample_size_.push_back(SampleSize(0, 50));
|
||||
FindTotalSize();
|
||||
vector<uint8_t> unencryptedData(total_size_);
|
||||
vector<uint8_t> encryptedData(total_size_);
|
||||
vector<uint8_t> encryptionIv(AES_BLOCK_SIZE);
|
||||
vector<uint8_t> key(AES_BLOCK_SIZE);
|
||||
EXPECT_EQ(1, GetRandBytes(encryptionIv.data(), AES_BLOCK_SIZE));
|
||||
EXPECT_EQ(1, GetRandBytes(key.data(), AES_BLOCK_SIZE));
|
||||
for (size_t i = 0; i < total_size_; i++) unencryptedData[i] = i % 256;
|
||||
EncryptData(key, encryptionIv, unencryptedData, &encryptedData);
|
||||
TestDecryptCENC(key, encryptionIv, encryptedData, unencryptedData);
|
||||
ASSERT_NO_FATAL_FAILURE(MakeBuffers());
|
||||
ASSERT_NO_FATAL_FAILURE(LoadLicense());
|
||||
ASSERT_NO_FATAL_FAILURE(EncryptData());
|
||||
ASSERT_NO_FATAL_FAILURE(TestDecryptCENC());
|
||||
}
|
||||
|
||||
// Based on the resource rating, oemcrypto should handle at least
|
||||
@@ -2333,15 +2457,10 @@ TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptMaxSample) {
|
||||
subsample_size_.push_back(SampleSize(0, max_subsample_size));
|
||||
}
|
||||
FindTotalSize();
|
||||
vector<uint8_t> unencryptedData(total_size_);
|
||||
vector<uint8_t> encryptedData(total_size_);
|
||||
vector<uint8_t> encryptionIv(AES_BLOCK_SIZE);
|
||||
vector<uint8_t> key(AES_BLOCK_SIZE);
|
||||
EXPECT_EQ(1, GetRandBytes(encryptionIv.data(), AES_BLOCK_SIZE));
|
||||
EXPECT_EQ(1, GetRandBytes(key.data(), AES_BLOCK_SIZE));
|
||||
for (size_t i = 0; i < total_size_; i++) unencryptedData[i] = i % 256;
|
||||
EncryptData(key, encryptionIv, unencryptedData, &encryptedData);
|
||||
TestDecryptCENC(key, encryptionIv, encryptedData, unencryptedData);
|
||||
ASSERT_NO_FATAL_FAILURE(MakeBuffers());
|
||||
ASSERT_NO_FATAL_FAILURE(LoadLicense());
|
||||
ASSERT_NO_FATAL_FAILURE(EncryptData());
|
||||
ASSERT_NO_FATAL_FAILURE(TestDecryptCENC());
|
||||
}
|
||||
|
||||
// This tests that we can decrypt the required maximum number of subsamples.
|
||||
@@ -2350,30 +2469,20 @@ TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptMaxSubsample) {
|
||||
subsample_size_.push_back(SampleSize(max_subsample_size, 0));
|
||||
subsample_size_.push_back(SampleSize(0, max_subsample_size));
|
||||
FindTotalSize();
|
||||
vector<uint8_t> unencryptedData(total_size_);
|
||||
vector<uint8_t> encryptedData(total_size_);
|
||||
vector<uint8_t> encryptionIv(AES_BLOCK_SIZE);
|
||||
vector<uint8_t> key(AES_BLOCK_SIZE);
|
||||
EXPECT_EQ(1, GetRandBytes(encryptionIv.data(), AES_BLOCK_SIZE));
|
||||
EXPECT_EQ(1, GetRandBytes(key.data(), AES_BLOCK_SIZE));
|
||||
for (size_t i = 0; i < total_size_; i++) unencryptedData[i] = i % 256;
|
||||
EncryptData(key, encryptionIv, unencryptedData, &encryptedData);
|
||||
TestDecryptCENC(key, encryptionIv, encryptedData, unencryptedData);
|
||||
ASSERT_NO_FATAL_FAILURE(MakeBuffers());
|
||||
ASSERT_NO_FATAL_FAILURE(LoadLicense());
|
||||
ASSERT_NO_FATAL_FAILURE(EncryptData());
|
||||
ASSERT_NO_FATAL_FAILURE(TestDecryptCENC());
|
||||
}
|
||||
|
||||
// There are probably no frames this small, but we should handle them anyway.
|
||||
TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptSmallBuffer) {
|
||||
subsample_size_.push_back(SampleSize(5, 5));
|
||||
FindTotalSize();
|
||||
vector<uint8_t> unencryptedData(total_size_);
|
||||
vector<uint8_t> encryptedData(total_size_);
|
||||
vector<uint8_t> encryptionIv(AES_BLOCK_SIZE);
|
||||
vector<uint8_t> key(AES_BLOCK_SIZE);
|
||||
EXPECT_EQ(1, GetRandBytes(encryptionIv.data(), AES_BLOCK_SIZE));
|
||||
EXPECT_EQ(1, GetRandBytes(key.data(), AES_BLOCK_SIZE));
|
||||
for (size_t i = 0; i < total_size_; i++) unencryptedData[i] = i % 256;
|
||||
EncryptData(key, encryptionIv, unencryptedData, &encryptedData);
|
||||
TestDecryptCENC(key, encryptionIv, encryptedData, unencryptedData);
|
||||
ASSERT_NO_FATAL_FAILURE(MakeBuffers());
|
||||
ASSERT_NO_FATAL_FAILURE(LoadLicense());
|
||||
ASSERT_NO_FATAL_FAILURE(EncryptData());
|
||||
ASSERT_NO_FATAL_FAILURE(TestDecryptCENC());
|
||||
}
|
||||
|
||||
// Test the case where there is only a clear subsample and no encrypted
|
||||
@@ -2381,47 +2490,25 @@ TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptSmallBuffer) {
|
||||
TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptUnencrypted) {
|
||||
subsample_size_.push_back(SampleSize(256, 0));
|
||||
FindTotalSize();
|
||||
vector<uint8_t> unencryptedData(total_size_);
|
||||
vector<uint8_t> encryptedData(total_size_);
|
||||
vector<uint8_t> encryptionIv(AES_BLOCK_SIZE);
|
||||
vector<uint8_t> key(AES_BLOCK_SIZE);
|
||||
EXPECT_EQ(1, GetRandBytes(encryptionIv.data(), AES_BLOCK_SIZE));
|
||||
EXPECT_EQ(1, GetRandBytes(key.data(), AES_BLOCK_SIZE));
|
||||
for (size_t i = 0; i < total_size_; i++) unencryptedData[i] = i % 256;
|
||||
EncryptData(key, encryptionIv, unencryptedData, &encryptedData);
|
||||
TestDecryptCENC(key, encryptionIv, encryptedData, unencryptedData);
|
||||
ASSERT_NO_FATAL_FAILURE(MakeBuffers());
|
||||
ASSERT_NO_FATAL_FAILURE(LoadLicense());
|
||||
ASSERT_NO_FATAL_FAILURE(EncryptData());
|
||||
ASSERT_NO_FATAL_FAILURE(TestDecryptCENC());
|
||||
}
|
||||
|
||||
TEST_F(OEMCryptoSessionTests, DecryptUnencryptedNoKey) {
|
||||
OEMCryptoResult sts;
|
||||
Session s;
|
||||
ASSERT_NO_FATAL_FAILURE(s.open());
|
||||
// Clear data should be copied even if there is no key selected.
|
||||
// Set up our expected input and output
|
||||
// This is dummy decrypted data.
|
||||
vector<uint8_t> in_buffer(256);
|
||||
for (size_t i = 0; i < in_buffer.size(); i++) in_buffer[i] = i % 256;
|
||||
vector<uint8_t> encryptionIv(AES_BLOCK_SIZE);
|
||||
EXPECT_EQ(1, GetRandBytes(encryptionIv.data(), AES_BLOCK_SIZE));
|
||||
// Describe the output
|
||||
vector<uint8_t> out_buffer(in_buffer.size());
|
||||
OEMCrypto_DestBufferDesc destBuffer;
|
||||
destBuffer.type = OEMCrypto_BufferType_Clear;
|
||||
destBuffer.buffer.clear.address = out_buffer.data();
|
||||
destBuffer.buffer.clear.max_length = out_buffer.size();
|
||||
OEMCrypto_CENCEncryptPatternDesc pattern;
|
||||
pattern.encrypt = 0;
|
||||
pattern.skip = 0;
|
||||
pattern.offset = 0;
|
||||
|
||||
// Decrypt the data
|
||||
sts =
|
||||
OEMCrypto_DecryptCENC(s.session_id(), in_buffer.data(), in_buffer.size(),
|
||||
false, encryptionIv.data(), 0, &destBuffer,
|
||||
&pattern,
|
||||
OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample);
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
|
||||
ASSERT_EQ(in_buffer, out_buffer);
|
||||
TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptUnencryptedNoKey) {
|
||||
ASSERT_NO_FATAL_FAILURE(session_.open());
|
||||
// Single clear subsample
|
||||
subsample_size_.push_back(SampleSize(400, 0));
|
||||
// Do not try to compute the CRC because we have not loaded a license.
|
||||
verify_crc_ = false;
|
||||
FindTotalSize();
|
||||
ASSERT_NO_FATAL_FAILURE(MakeBuffers());
|
||||
// Clear data should be copied even if there is no key selected, and no
|
||||
// license loaded.
|
||||
// ASSERT_NO_FATAL_FAILURE(LoadLicense());
|
||||
ASSERT_NO_FATAL_FAILURE(EncryptData());
|
||||
ASSERT_NO_FATAL_FAILURE(TestDecryptCENC());
|
||||
}
|
||||
|
||||
// Used to construct a specific pattern.
|
||||
@@ -4882,6 +4969,22 @@ class UsageTableTest : public GenericCryptoTest {
|
||||
void SetUp() override {
|
||||
GenericCryptoTest::SetUp();
|
||||
new_mac_keys_ = true;
|
||||
did_change_system_time_ = false;
|
||||
test_start_steady_ = steady_clock_.now();
|
||||
#ifdef _WIN32
|
||||
GetSystemTime(&test_start_wall_);
|
||||
#else
|
||||
ASSERT_EQ(0, gettimeofday(&test_start_wall_, nullptr));
|
||||
#endif
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
if (did_change_system_time_) {
|
||||
const auto delta = steady_clock_.now() - test_start_steady_;
|
||||
const int64_t delta_sec = delta / std::chrono::seconds(1);
|
||||
ASSERT_NO_FATAL_FAILURE(SetWallTimeDelta(delta_sec));
|
||||
}
|
||||
GenericCryptoTest::TearDown();
|
||||
}
|
||||
|
||||
virtual void ShutDown() {
|
||||
@@ -4926,7 +5029,26 @@ class UsageTableTest : public GenericCryptoTest {
|
||||
}
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Sets the current wall-clock time to a delta based on the start of the
|
||||
* test.
|
||||
*/
|
||||
void SetWallTimeDelta(int64_t delta_seconds) {
|
||||
did_change_system_time_ = true;
|
||||
NativeTime time = test_start_wall_;
|
||||
ASSERT_NO_FATAL_FAILURE(AddNativeTime(delta_seconds, &time));
|
||||
#ifdef _WIN32
|
||||
ASSERT_TRUE(SetSystemTime(&time));
|
||||
#else
|
||||
ASSERT_EQ(0, settimeofday(&time, nullptr));
|
||||
#endif
|
||||
}
|
||||
|
||||
bool new_mac_keys_;
|
||||
bool did_change_system_time_;
|
||||
std::chrono::steady_clock steady_clock_;
|
||||
std::chrono::time_point<std::chrono::steady_clock> test_start_steady_;
|
||||
NativeTime test_start_wall_;
|
||||
};
|
||||
|
||||
// Some usage tables we want to check a license either with or without a
|
||||
@@ -5396,9 +5518,9 @@ TEST_P(UsageTableTestWithMAC, ReloadOfflineLicenseWithRefresh) {
|
||||
ASSERT_NO_FATAL_FAILURE(s.close());
|
||||
}
|
||||
|
||||
// Verify that a license that has been we can still reload an offline license
|
||||
// after OEMCrypto_Terminate and Initialize are called. This is as close to a
|
||||
// reboot as we can do in a unit test.
|
||||
// Verify that we can still reload an offline license after OEMCrypto_Terminate
|
||||
// and Initialize are called. This is as close to a reboot as we can do in a
|
||||
// unit test.
|
||||
TEST_P(UsageTableTestWithMAC, ReloadOfflineLicenseWithTerminate) {
|
||||
std::string pst = "my_pst";
|
||||
Session s;
|
||||
@@ -5856,9 +5978,7 @@ TEST_F(UsageTableDefragTest, ReloadUsageEntryBadData) {
|
||||
}
|
||||
|
||||
static std::string MakePST(size_t n) {
|
||||
std::stringstream stream;
|
||||
stream << "pst-" << n;
|
||||
return stream.str();
|
||||
return "pst-" + std::to_string(n);
|
||||
}
|
||||
|
||||
// This verifies we can actually create two hundered usage table entries.
|
||||
@@ -6287,23 +6407,19 @@ TEST_F(UsageTableTest, TimeRollbackPrevention) {
|
||||
cout << "This test temporarily rolls back the system time in order to verify "
|
||||
<< "that the usage report accounts for the change. It then rolls "
|
||||
<< "the time back forward to the absolute time." << endl;
|
||||
// We use clock_gettime(CLOCK_REALTIME, ...) over time(...) so we can easily
|
||||
// set the time using clock_settime.
|
||||
timespec current_time;
|
||||
ASSERT_EQ(0, clock_gettime(CLOCK_REALTIME, ¤t_time));
|
||||
time_t loaded = current_time.tv_sec;
|
||||
std::chrono::system_clock wall_clock;
|
||||
std::chrono::steady_clock monotonic_clock;
|
||||
const auto loaded = wall_clock.now();
|
||||
ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s1, pst));
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(s1.open());
|
||||
ASSERT_NO_FATAL_FAILURE(s1.ReloadUsageEntry());
|
||||
ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s1));
|
||||
ASSERT_NO_FATAL_FAILURE(s1.LoadTestKeys(pst, new_mac_keys_));
|
||||
ASSERT_EQ(0, clock_gettime(CLOCK_REALTIME, ¤t_time));
|
||||
time_t first_decrypt = current_time.tv_sec;
|
||||
const auto first_decrypt = wall_clock.now();
|
||||
// Monotonic clock can't be changed. We use this since system clock will be
|
||||
// unreliable.
|
||||
ASSERT_EQ(0, clock_gettime(CLOCK_MONOTONIC, ¤t_time));
|
||||
time_t first_decrypt_monotonic = current_time.tv_sec;
|
||||
const auto first_decrypt_monotonic = monotonic_clock.now();
|
||||
ASSERT_NO_FATAL_FAILURE(s1.TestDecryptCTR());
|
||||
ASSERT_NO_FATAL_FAILURE(s1.UpdateUsageEntry(&encrypted_usage_header_));
|
||||
ASSERT_NO_FATAL_FAILURE(s1.close());
|
||||
@@ -6319,20 +6435,17 @@ TEST_F(UsageTableTest, TimeRollbackPrevention) {
|
||||
ASSERT_NO_FATAL_FAILURE(s1.UpdateUsageEntry(&encrypted_usage_header_));
|
||||
ASSERT_NO_FATAL_FAILURE(s1.close());
|
||||
|
||||
ASSERT_EQ(0, clock_gettime(CLOCK_REALTIME, ¤t_time));
|
||||
// Rollback the wall clock time.
|
||||
cout << "Rolling the system time back..." << endl;
|
||||
timeval current_time_of_day = {};
|
||||
current_time_of_day.tv_sec = current_time.tv_sec - kLongDuration * 10;
|
||||
ASSERT_EQ(0, settimeofday(¤t_time_of_day, NULL));
|
||||
ASSERT_NO_FATAL_FAILURE(SetWallTimeDelta(
|
||||
-static_cast<int64_t>(kLongDuration) * 10));
|
||||
|
||||
// Try to playback again.
|
||||
ASSERT_NO_FATAL_FAILURE(s1.open());
|
||||
ASSERT_NO_FATAL_FAILURE(s1.ReloadUsageEntry());
|
||||
ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s1));
|
||||
ASSERT_NO_FATAL_FAILURE(s1.LoadTestKeys(pst, new_mac_keys_));
|
||||
ASSERT_EQ(0, clock_gettime(CLOCK_MONOTONIC, ¤t_time));
|
||||
time_t third_decrypt = current_time.tv_sec;
|
||||
const auto third_decrypt_monotonic = monotonic_clock.now();
|
||||
ASSERT_NO_FATAL_FAILURE(s1.TestDecryptCTR());
|
||||
ASSERT_NO_FATAL_FAILURE(s1.UpdateUsageEntry(&encrypted_usage_header_));
|
||||
ASSERT_NO_FATAL_FAILURE(s1.GenerateReport(pst));
|
||||
@@ -6340,18 +6453,18 @@ TEST_F(UsageTableTest, TimeRollbackPrevention) {
|
||||
|
||||
// Restore wall clock to its original position to verify that OEMCrypto does
|
||||
// not report negative times.
|
||||
ASSERT_EQ(0, clock_gettime(CLOCK_MONOTONIC, ¤t_time));
|
||||
current_time_of_day.tv_sec =
|
||||
first_decrypt + current_time.tv_sec - first_decrypt_monotonic;
|
||||
const auto test_duration = third_decrypt_monotonic - first_decrypt_monotonic;
|
||||
cout << "Rolling the system time forward to the absolute time..." << endl;
|
||||
ASSERT_EQ(0, settimeofday(¤t_time_of_day, NULL));
|
||||
ASSERT_NO_FATAL_FAILURE(SetWallTimeDelta(
|
||||
test_duration / std::chrono::seconds(1)));
|
||||
// Need to update time created since the verification checks the time of PST
|
||||
// report creation.
|
||||
expected.time_created = current_time_of_day.tv_sec;
|
||||
expected.time_created = UnixTime(wall_clock.now());
|
||||
|
||||
const auto end_time = first_decrypt + test_duration;
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
s1.VerifyReport(expected, loaded, first_decrypt,
|
||||
first_decrypt + third_decrypt - first_decrypt_monotonic));
|
||||
s1.VerifyReport(expected, UnixTime(loaded), UnixTime(first_decrypt),
|
||||
UnixTime(end_time)));
|
||||
ASSERT_NO_FATAL_FAILURE(s1.close());
|
||||
}
|
||||
|
||||
|
||||
@@ -50,6 +50,11 @@ TEST_F(OEMCryptoAndroidLMPTest, ValidKeybox) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(OEMCryptoAndroidLMPTest, MinVersionNumber9) {
|
||||
uint32_t version = OEMCrypto_APIVersion();
|
||||
ASSERT_LE(9u, version);
|
||||
}
|
||||
|
||||
TEST_F(OEMCryptoAndroidLMPTest, ValidKeyboxTest) {
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_IsKeyboxValid());
|
||||
}
|
||||
@@ -108,6 +113,11 @@ TEST_F(OEMCryptoAndroidLMPTest, Level1Required) {
|
||||
// These tests are required for M Android devices.
|
||||
class OEMCryptoAndroidMNCTest : public OEMCryptoAndroidLMPTest {};
|
||||
|
||||
TEST_F(OEMCryptoAndroidMNCTest, MinVersionNumber10) {
|
||||
uint32_t version = OEMCrypto_APIVersion();
|
||||
ASSERT_GE(version, 10u);
|
||||
}
|
||||
|
||||
// Android devices using Provisioning 2.0 must be able to load a test keybox.
|
||||
// If they are not using Provisioning 2.0, then they must use Provisioning 3.0.
|
||||
TEST_F(OEMCryptoAndroidMNCTest, LoadsTestKeyboxImplemented) {
|
||||
@@ -135,4 +145,28 @@ TEST_F(OEMCryptoAndroidMNCTest, QueryKeyControlImplemented) {
|
||||
OEMCrypto_QueryKeyControl(0, NULL, 0, NULL, NULL));
|
||||
}
|
||||
|
||||
// These tests are required for N Android devices.
|
||||
class OEMCryptoAndroidNYCTest : public OEMCryptoAndroidMNCTest {};
|
||||
|
||||
TEST_F(OEMCryptoAndroidNYCTest, MinVersionNumber11) {
|
||||
uint32_t version = OEMCrypto_APIVersion();
|
||||
ASSERT_GE(version, 11u);
|
||||
}
|
||||
|
||||
// These tests are required for O MR1 Android devices.
|
||||
class OEMCryptoAndroidOCTest : public OEMCryptoAndroidNYCTest {};
|
||||
|
||||
TEST_F(OEMCryptoAndroidOCTest, MinVersionNumber13) {
|
||||
uint32_t version = OEMCrypto_APIVersion();
|
||||
ASSERT_GE(version, 13u);
|
||||
}
|
||||
|
||||
// These tests are required for Q Android devices.
|
||||
class OEMCryptoAndroidQTest : public OEMCryptoAndroidOCTest {};
|
||||
|
||||
TEST_F(OEMCryptoAndroidQTest, MinVersionNumber14) {
|
||||
uint32_t version = OEMCrypto_APIVersion();
|
||||
ASSERT_GE(version, 15u);
|
||||
}
|
||||
|
||||
} // namespace wvoec
|
||||
|
||||
@@ -17,12 +17,14 @@ static void acknowledge_cast() {
|
||||
// Also, the test filter is updated based on the feature list.
|
||||
int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
wvcdm::g_cutoff = wvcdm::LOG_INFO;
|
||||
bool is_cast_receiver = false;
|
||||
bool force_load_test_keybox = false;
|
||||
bool filter_tests = true;
|
||||
int verbosity = 0;
|
||||
for (int i = 0; i < argc; i++) {
|
||||
if (!strcmp(argv[i], "--cast")) {
|
||||
if (arg == "--verbose" || arg == "-v") {
|
||||
++verbosity;
|
||||
} else if (!strcmp(argv[i], "--cast")) {
|
||||
acknowledge_cast();
|
||||
is_cast_receiver = true;
|
||||
}
|
||||
@@ -33,6 +35,7 @@ int main(int argc, char** argv) {
|
||||
filter_tests = false;
|
||||
}
|
||||
}
|
||||
wvcdm::g_cutoff = static_cast<LogPriority>(verbosity);
|
||||
wvoec::global_features.Initialize(is_cast_receiver, force_load_test_keybox);
|
||||
// If the user requests --no_filter, we don't change the filter, otherwise, we
|
||||
// filter out features that are not supported.
|
||||
|
||||
@@ -3,14 +3,14 @@
|
||||
{
|
||||
'variables': {
|
||||
# Override the variables below for the location of various gyp files.
|
||||
# Alternatively, set the environment variable CDM_DIR to point to a recent
|
||||
# version of the source CDM.
|
||||
'boringssl_dependency%': '<!(echo $CDM_DIR)/third_party/boringssl/boringssl.gyp:ssl',
|
||||
'gtest_dependency%': '<!(echo $CDM_DIR)/third_party/gmock.gyp:gtest',
|
||||
'gmock_dependency%': '<!(echo $CDM_DIR)/third_party/gmock.gyp:gmock',
|
||||
# Alternatively, set the environment variable PATH_TO_CDM_DIR to point to a
|
||||
# recent version of the source CDM. This *must* be a relative path.
|
||||
'boringssl_dependency%': '<!(echo $PATH_TO_CDM_DIR)/third_party/boringssl/boringssl.gyp:ssl',
|
||||
'gtest_dependency%': '<!(echo $PATH_TO_CDM_DIR)/third_party/gmock.gyp:gtest',
|
||||
'gmock_dependency%': '<!(echo $PATH_TO_CDM_DIR)/third_party/gmock.gyp:gmock',
|
||||
'oemcrypto_dir%': '..',
|
||||
'util_dir%': '../../util',
|
||||
'platform_specific_dir%': '<!(echo $CDM_DIR)/linux/src',
|
||||
'platform_specific_dir%': '<!(echo $PATH_TO_CDM_DIR)/linux/src',
|
||||
},
|
||||
'targets': [
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user