OEMCrypto v15.2

See the file docs/Widevine_Modular_DRM_Version_15_Delta.pdf for
changes since version 15.1.
This commit is contained in:
Fred Gylys-Colwell
2019-05-08 13:37:45 -07:00
parent 88d6b53ba3
commit ded4417dd4
24 changed files with 430 additions and 196 deletions

View File

@@ -0,0 +1,158 @@
# Copyright 2017 Google LLC. All Rights Reserved.
"""Common test utility functions for OEM certificate generation."""
import datetime
import StringIO
from cryptography import x509
from cryptography.hazmat import backends
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.x509 import oid
import oem_certificate
_COUNTRY_NAME = 'US'
_STATE_OR_PROVINCE_NAME = 'WA'
_LOCALITY_NAME = 'Kirkland'
_ORGANIZATION_NAME = 'CompanyXYZ'
_ORGANIZATIONAL_UNIT_NAME = 'ContentProtection'
_NOT_VALID_BEFORE = datetime.datetime(2001, 8, 9)
_VALID_DURATION = 100
_LEAF_CERT_VALID_DURATION = 8000
_SYSTEM_ID = 2001
_ROOT_PRIVATE_KEY_PASSPHRASE = 'root_passphrase'
class ArgParseObject(object):
"""A convenient object to allow adding arbitrary attribute to it."""
def create_root_certificate_and_key():
"""Creates a root certificate and key."""
key = rsa.generate_private_key(
public_exponent=65537,
key_size=3072,
backend=backends.default_backend())
subject_name = x509.Name(
[x509.NameAttribute(oid.NameOID.COMMON_NAME, u'root_cert')])
certificate = oem_certificate.build_certificate(
subject_name, subject_name, None,
datetime.datetime(2001, 8, 9), 1000, key.public_key(), key, True)
return (key, certificate)
def setup_csr_args(country_name=_COUNTRY_NAME,
state_or_province_name=_STATE_OR_PROVINCE_NAME,
locality_name=_LOCALITY_NAME,
organization_name=_ORGANIZATION_NAME,
organizational_unit_name=_ORGANIZATIONAL_UNIT_NAME,
key_size=4096,
output_csr_file=None,
output_private_key_file=None,
passphrase=None,
common_name=None):
"""Sets up arguments to OEM Certificate generator for generating csr."""
args = ArgParseObject()
args.key_size = key_size
args.country_name = country_name
args.state_or_province_name = state_or_province_name
args.locality_name = locality_name
args.organization_name = organization_name
args.organizational_unit_name = organizational_unit_name
args.common_name = common_name
if output_csr_file:
args.output_csr_file = output_csr_file
else:
args.output_csr_file = StringIO.StringIO()
if output_private_key_file:
args.output_private_key_file = output_private_key_file
else:
args.output_private_key_file = StringIO.StringIO()
args.passphrase = passphrase
return args
def setup_intermediate_cert_args(
csr_bytes, root_key, root_certificate, not_valid_before=_NOT_VALID_BEFORE,
valid_duration=_VALID_DURATION, system_id=_SYSTEM_ID,
root_private_key_passphrase=_ROOT_PRIVATE_KEY_PASSPHRASE,
output_certificate_file=None):
"""Sets up args to OEM Cert generator for generating intermediate cert."""
args = ArgParseObject()
args.not_valid_before = not_valid_before
args.valid_duration = valid_duration
args.system_id = system_id
args.csr_file = StringIO.StringIO(csr_bytes)
args.root_private_key_passphrase = root_private_key_passphrase
if output_certificate_file:
args.output_certificate_file = output_certificate_file
else:
args.output_certificate_file = StringIO.StringIO()
serialized_private_key = root_key.private_bytes(
serialization.Encoding.DER,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.BestAvailableEncryption(
args.root_private_key_passphrase))
serialized_certificate = root_certificate.public_bytes(
serialization.Encoding.DER)
args.root_certificate_file = StringIO.StringIO(serialized_certificate)
args.root_private_key_file = StringIO.StringIO(serialized_private_key)
return args
def setup_leaf_cert_args(intermediate_key_bytes,
intermediate_certificate_bytes,
key_size=1024,
passphrase=None,
not_valid_before=_NOT_VALID_BEFORE,
valid_duration=_LEAF_CERT_VALID_DURATION,
output_certificate_file=None,
output_private_key_file=None):
"""Sets up args to OEM Certificate generator for generating leaf cert."""
args = ArgParseObject()
args.key_size = key_size
args.not_valid_before = not_valid_before
args.valid_duration = valid_duration
args.intermediate_private_key_passphrase = None
if output_certificate_file:
args.output_certificate_file = output_certificate_file
else:
args.output_certificate_file = StringIO.StringIO()
if output_private_key_file:
args.output_private_key_file = output_private_key_file
else:
args.output_private_key_file = StringIO.StringIO()
args.passphrase = passphrase
args.intermediate_private_key_file = StringIO.StringIO(
intermediate_key_bytes)
args.intermediate_certificate_file = StringIO.StringIO(
intermediate_certificate_bytes)
return args
def create_intermediate_certificate_and_key_bytes(key_size=4096,
passphrase=None,
pem_format=True):
"""Creates an intermediate certificate and key."""
csr_args = setup_csr_args(key_size=key_size, passphrase=passphrase)
oem_certificate.generate_csr(csr_args)
csr_bytes = csr_args.output_csr_file.getvalue()
root_key, root_certificate = create_root_certificate_and_key()
args = setup_intermediate_cert_args(csr_bytes, root_key, root_certificate)
oem_certificate.generate_intermediate_certificate(args)
cert_bytes = args.output_certificate_file.getvalue()
if pem_format:
cert = x509.load_der_x509_certificate(cert_bytes,
backends.default_backend())
cert_bytes = cert.public_bytes(serialization.Encoding.PEM)
return (csr_args.output_private_key_file.getvalue(), cert_bytes)

View File

@@ -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

View File

@@ -182,6 +182,14 @@ time_t CryptoEngine::RollbackCorrectedOfflineTime() {
return current_time;
}
bool CryptoEngine::NonceCollision(uint32_t nonce) {
for (const auto & session_pair : sessions_) {
const SessionContext* session = session_pair.second;
if (session->NonceCollision(nonce)) return true;
}
return false;
}
OEMCrypto_HDCP_Capability CryptoEngine::config_current_hdcp_capability() {
return config_local_display_only() ? HDCP_NO_DIGITAL_OUTPUT : HDCP_V1;
}

View File

@@ -91,6 +91,10 @@ class CryptoEngine {
time_t RollbackCorrectedOfflineTime();
// Verify that this nonce does not collide with another nonce in any session's
// nonce table.
virtual bool NonceCollision(uint32_t nonce);
// Returns the HDCP version currently in use.
virtual OEMCrypto_HDCP_Capability config_current_hdcp_capability();

View File

@@ -58,6 +58,13 @@ bool NonceTable::CheckNonce(uint32_t nonce) {
return false;
}
bool NonceTable::NonceCollision(uint32_t nonce) const {
for (int i = 0; i < kTableSize; ++i) {
if (nonce == nonces_[i]) return true;
}
return false;
}
void NonceTable::Flush() {
for (int i = 0; i < kTableSize; ++i) {
if (kNTStateFlushPending == state_[i]) {

View File

@@ -13,7 +13,7 @@ namespace wvoec_ref {
class NonceTable {
public:
static const int kTableSize = 16;
static const int kTableSize = 4;
NonceTable() {
for (int i = 0; i < kTableSize; ++i) {
state_[i] = kNTStateInvalid;
@@ -22,6 +22,8 @@ class NonceTable {
~NonceTable() {}
void AddNonce(uint32_t nonce);
bool CheckNonce(uint32_t nonce);
// Verify that the nonce is not the same as any in this table.
bool NonceCollision(uint32_t nonce) const;
void Flush();
private:

View File

@@ -189,13 +189,15 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_GenerateNonce(OEMCrypto_SESSION session,
last_nonce_time = now;
}
uint32_t nonce_value;
uint32_t nonce_value = 0;
uint8_t* nonce_string = reinterpret_cast<uint8_t*>(&nonce_value);
// Generate 4 bytes of random data
if (!RAND_bytes(nonce_string, 4)) {
LOGE("[OEMCrypto_GenerateNonce(): Random bytes failure]");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
while (nonce_value == 0 || crypto_engine->NonceCollision(nonce_value)) {
// Generate 4 bytes of random data
if (!RAND_bytes(nonce_string, 4)) {
LOGE("[OEMCrypto_GenerateNonce(): Random bytes failure]");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
}
session_ctx->AddNonce(nonce_value);
*nonce = nonce_value;
@@ -281,9 +283,8 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_LoadKeys(
!RangeCheck(message_length, enc_mac_keys, true) ||
!RangeCheck(message_length, pst, true) ||
!RangeCheck(message_length, srm_restriction_data, true)) {
LOGE(
"[OEMCrypto_LoadKeys(): OEMCrypto_ERROR_INVALID_CONTEXT - range "
"check.]");
LOGE("[OEMCrypto_LoadKeys(): OEMCrypto_ERROR_INVALID_CONTEXT - "
"range check.]");
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
@@ -293,13 +294,25 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_LoadKeys(
!RangeCheck(message_length, key_array[i].key_data_iv, false) ||
!RangeCheck(message_length, key_array[i].key_control, false) ||
!RangeCheck(message_length, key_array[i].key_control_iv, false)) {
LOGE(
"[OEMCrypto_LoadKeys(): OEMCrypto_ERROR_INVALID_CONTEXT -range "
"check %d]",
i);
LOGE("[OEMCrypto_LoadKeys(): OEMCrypto_ERROR_INVALID_CONTEXT - "
"range check %d]", i);
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
}
if (enc_mac_keys.offset >= wvoec::KEY_IV_SIZE && enc_mac_keys.length > 0) {
if (enc_mac_keys_iv.offset + wvoec::KEY_IV_SIZE == enc_mac_keys.offset) {
LOGE("[OEMCrypto_LoadKeys(): OEMCrypto_ERROR_INVALID_CONTEXT - "
"range check iv]");
return OEMCrypto_ERROR_INVALID_CONTEXT;
} else {
if (memcmp(message + enc_mac_keys.offset - wvoec::KEY_IV_SIZE,
message + enc_mac_keys_iv.offset, wvoec::KEY_IV_SIZE) == 0) {
LOGE("[OEMCrypto_LoadKeys(): OEMCrypto_ERROR_INVALID_CONTEXT - "
"suspicious iv]");
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
}
}
return session_ctx->LoadKeys(message, message_length, signature,
signature_length, enc_mac_keys_iv, enc_mac_keys,
num_keys, key_array, pst, srm_restriction_data,

View File

@@ -1,48 +0,0 @@
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
#ifndef OEMCRYPTO_SCOPED_PTR_H_
#define OEMCRYPTO_SCOPED_PTR_H_
#include <stddef.h>
#include <memory>
#include <arpa/inet.h>
namespace wvoec_ref {
// TODO(fredgc, jfore): scoped_ptr may not be the best name for this smart
// pointer type. It basically works like auto_ptr which is deprecated.
#if __cplusplus < 201103L
template <typename T>
class scoped_ptr {
public:
explicit scoped_ptr(T* p = NULL) : ptr_(p) {}
T* get() const { return ptr_.get(); }
void reset(T* p = NULL) { ptr_.reset(p); }
private:
std::auto_ptr<T> ptr_;
};
#else
template <typename T>
class scoped_ptr {
public:
explicit scoped_ptr(T* p = nullptr) : ptr_(p) {}
scoped_ptr(scoped_ptr& r) { ptr_ = std::move(r.ptr_); }
T& operator*() const { return *ptr_; }
T* operator->() const { return ptr_.get(); }
T* get() const { return ptr_.get(); }
void reset(T* p = NULL) { ptr_.reset(p); }
private:
std::unique_ptr<T> ptr_;
};
#endif
} // namespace wvoec_ref
#endif // OEMCRYPTO_SCOPED_PTR_H_

View File

@@ -636,6 +636,7 @@ OEMCryptoResult SessionContext::LoadKeys(
break;
}
}
encryption_key_.clear();
return OEMCrypto_SUCCESS;
}

View File

@@ -173,6 +173,10 @@ class SessionContext {
void AddNonce(uint32_t nonce);
bool CheckNonce(uint32_t nonce);
// Verify that the nonce does not match any in this session's nonce table.
bool NonceCollision(uint32_t nonce) const {
return nonce_table_.NonceCollision(nonce);
}
void FlushNonces();
virtual OEMCryptoResult CreateNewUsageEntry(uint32_t* usage_entry_number);

View File

@@ -153,33 +153,12 @@ 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.

View File

@@ -539,6 +539,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);
@@ -1268,7 +1269,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 +1278,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 +1359,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) {

View File

@@ -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];
};

View File

@@ -15,6 +15,41 @@
namespace wvoec {
// TODO(fredgc, b/119316243): REMOVE THIS KEYBOX!
// This test keybox is used for testing with OEMCrypto v13.
// It should be removed before release!
static const WidevineKeybox kTestKeyboxForV13 = {
// Sample keybox used for test vectors
{
// deviceID
0x54, 0x65, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x30, // TestKey01
0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........
}, {
// key
0xfb, 0xda, 0x04, 0x89, 0xa1, 0x58, 0x16, 0x0e,
0xa4, 0x02, 0xe9, 0x29, 0xe3, 0xb6, 0x8f, 0x04,
}, {
// data
0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x10, 0x19,
0x07, 0xd9, 0xff, 0xde, 0x13, 0xaa, 0x95, 0xc1,
0x22, 0x67, 0x80, 0x53, 0x36, 0x21, 0x36, 0xbd,
0xf8, 0x40, 0x8f, 0x82, 0x76, 0xe4, 0xc2, 0xd8,
0x7e, 0xc5, 0x2b, 0x61, 0xaa, 0x1b, 0x9f, 0x64,
0x6e, 0x58, 0x73, 0x49, 0x30, 0xac, 0xeb, 0xe8,
0x99, 0xb3, 0xe4, 0x64, 0x18, 0x9a, 0x14, 0xa8,
0x72, 0x02, 0xfb, 0x02, 0x57, 0x4e, 0x70, 0x64,
0x0b, 0xd2, 0x2e, 0xf4, 0x4b, 0x2d, 0x7e, 0x39,
}, {
// magic
0x6b, 0x62, 0x6f, 0x78,
}, {
// Crc
0x0a, 0x7a, 0x2c, 0x35,
}
};
// This is a test keybox. It will not be accepted by production systems. By
// using a known keybox for these tests, the results for a given set of inputs
// to a test are predictable and can be compared to the actual results.

View File

@@ -105,6 +105,8 @@ void SessionUtil::EnsureTestKeys() {
switch (global_features.derive_key_method) {
case DeviceFeatures::LOAD_TEST_KEYBOX:
keybox_ = kTestKeybox;
// TODO(fredgc, b/119316243): REMOVE FOLLOWING LINE:
if (global_features.api_version < 14) keybox_ = kTestKeyboxForV13;
ASSERT_EQ(OEMCrypto_SUCCESS,
OEMCrypto_LoadTestKeybox(
reinterpret_cast<const uint8_t*>(&keybox_),

View File

@@ -871,6 +871,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 +900,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 +1215,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;
@@ -5396,9 +5453,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;

View File

@@ -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

View File

@@ -1,53 +0,0 @@
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
//
// Lock - Platform independent interface for a Mutex class
//
#ifndef WVCDM_UTIL_LOCK_H_
#define WVCDM_UTIL_LOCK_H_
#include "disallow_copy_and_assign.h"
namespace wvcdm {
// Simple lock class. The implementation is platform dependent.
//
// The lock must be unlocked by the thread that locked it.
// The lock is also not recursive (ie. cannot be taken multiple times).
class Lock {
public:
Lock();
~Lock();
void Acquire();
void Release();
friend class AutoLock;
private:
class Impl;
Impl* impl_;
CORE_DISALLOW_COPY_AND_ASSIGN(Lock);
};
// Manages the lock automatically. It will be locked when AutoLock
// is constructed and release when AutoLock goes out of scope.
class AutoLock {
public:
explicit AutoLock(Lock& lock) : lock_(&lock) { lock_->Acquire(); }
explicit AutoLock(Lock* lock) : lock_(lock) { lock_->Acquire(); }
~AutoLock() { lock_->Release(); }
private:
Lock* lock_;
CORE_DISALLOW_COPY_AND_ASSIGN(AutoLock);
};
} // namespace wvcdm
#endif // WVCDM_UTIL_LOCK_H_

View File

@@ -14,11 +14,15 @@ namespace wvcdm {
// Simple logging class. The implementation is platform dependent.
typedef enum {
LOG_ERROR,
LOG_WARN,
LOG_INFO,
LOG_DEBUG,
LOG_VERBOSE
// This log level should only be used for |g_cutoff|, in order to silence all
// logging. It should never be passed to |Log()| as a log level.
LOG_SILENT = -1,
LOG_ERROR = 0,
LOG_WARN = 1,
LOG_INFO = 2,
LOG_DEBUG = 3,
LOG_VERBOSE = 4,
} LogPriority;
extern LogPriority g_cutoff;