OEMCrypto v16.2 unit tests and reference code

This commit is contained in:
Fred Gylys-Colwell
2020-02-04 16:59:41 -08:00
parent d597f863e9
commit 1a9765171b
49 changed files with 2819 additions and 2215 deletions

View File

@@ -8,9 +8,9 @@
* Reference APIs needed to support Widevine's crypto algorithms. * Reference APIs needed to support Widevine's crypto algorithms.
* *
* See the document "WV Modular DRM Security Integration Guide for Common * See the document "WV Modular DRM Security Integration Guide for Common
* Encryption (CENC) -- version 15.2" for a description of this API. You * Encryption (CENC) -- version 16.2" for a description of this API. You
* can find this document in the widevine repository as * can find this document in the widevine repository as
* docs/WidevineModularDRMSecurityIntegrationGuideforCENC_v15.pdf * docs/WidevineModularDRMSecurityIntegrationGuideforCENC_v16.pdf
* Changes between different versions of this API are documented in the files * Changes between different versions of this API are documented in the files
* docs/Widevine_Modular_DRM_Version_*_Delta.pdf * docs/Widevine_Modular_DRM_Version_*_Delta.pdf
* *
@@ -147,13 +147,22 @@ typedef struct {
* OEMCrypto_SubSampleDescription Structure * OEMCrypto_SubSampleDescription Structure
* *
* Description: * Description:
* This structure is used as parameters in the OEMCrypto_DecryptCENC function. * This structure is used as parameters in the OEMCrypto_DecryptCENC
* function. In the DASH specification, a sample is composed of multiple
* samples, and each subsample is composed of two regions. The first region
* is clear unprotected data. We also call this clear data or unencrypted
* data. Immediately following the clear region is the protected region. The
* protected region is encrypted or encrypted with a pattern. The pattern and
* number of bytes that are encrypted in the protected region is discussed in
* this document when we talk about the function OEMCryptoDecryptCENC. For
* historic reasons, this document also calls the protected region the
* encrypted region.
* *
* Fields: * Fields:
* [in] num_bytes_clear: The number of clear bytes in this subsample. The * [in] num_bytes_clear: The number of unprotected bytes in this subsample.
* clear bytes come before the encrypted bytes. * The clear bytes come before the encrypted bytes.
* [in] num_bytes_encrypted: The number of encrypted bytes in this subsample. * [in] num_bytes_encrypted: The number of protected bytes in this subsample.
* The encrypted bytes come after the clear bytes. * The protected bytes come after the clear bytes.
* [in] subsample_flags: bitwise flags indicating if this is the first, * [in] subsample_flags: bitwise flags indicating if this is the first,
* middle, or last subsample in a sample. 1 = first subsample, 2 = last * middle, or last subsample in a sample. 1 = first subsample, 2 = last
* subsample, 3 = both first and last subsample, 0 = neither first nor last * subsample, 3 = both first and last subsample, 0 = neither first nor last
@@ -231,14 +240,6 @@ typedef enum OEMCryptoCipherMode {
OEMCrypto_CipherMode_CBC, OEMCrypto_CipherMode_CBC,
} OEMCryptoCipherMode; } OEMCryptoCipherMode;
/** OEMCrypto_LicenseType is used in LoadKeys to indicate if the key objects
* are for content keys, or for entitlement keys.
*/
typedef enum OEMCrypto_LicenseType {
OEMCrypto_ContentLicense = 0,
OEMCrypto_EntitlementLicense = 1
} OEMCrypto_LicenseType;
/* /*
* OEMCrypto_EntitledContentKeyObject * OEMCrypto_EntitledContentKeyObject
* Contains encrypted content key data for loading into the sessions keytable. * Contains encrypted content key data for loading into the sessions keytable.
@@ -358,12 +359,6 @@ typedef enum OEMCrypto_ProvisioningMethod {
OEMCrypto_OEMCertificate = 3 // Device has factory installed OEM certificate. OEMCrypto_OEMCertificate = 3 // Device has factory installed OEM certificate.
} OEMCrypto_ProvisioningMethod; } OEMCrypto_ProvisioningMethod;
/* Private key type used in OEMCrypto_LoadDRMPrivateKey. */
typedef enum OEMCrypto_PrivateKeyType {
OEMCrypto_RSA_Private_Key,
OEMCrypto_ECC_Private_Key,
} OEMCrypto_PrivateKeyType;
/* /*
* Flags indicating public/private key types supported. * Flags indicating public/private key types supported.
*/ */
@@ -833,6 +828,11 @@ OEMCryptoResult OEMCrypto_DeriveKeysFromSessionKey(
* the function ODK_SetNonceValue(&nonce_values, nonce). The ODK functions * the function ODK_SetNonceValue(&nonce_values, nonce). The ODK functions
* are documented in "Widevine Core Message Serialization". * are documented in "Widevine Core Message Serialization".
* *
* This function shall only be called at most once per open session. It shall
* only be called before signing either a provisioning request or a license
* request. If an attempt is made to generate a nonce while in the wrong
* state, an error of OEMCrypto_ERROR_INVALID_CONTEXT is returned.
*
* Parameters: * Parameters:
* [in] session: handle for the session to be used. * [in] session: handle for the session to be used.
* [out] nonce: pointer to memory to receive the computed nonce. * [out] nonce: pointer to memory to receive the computed nonce.
@@ -957,10 +957,12 @@ OEMCryptoResult OEMCrypto_PrepAndSignLicenseRequest(
* key. The entire message is the buffer starting at message with length * key. The entire message is the buffer starting at message with length
* message_length. * message_length.
* *
* If nonce_values.api_level is 15, then OEMCrypto shall compute the * If nonce_values.api_major_version is 15, then OEMCrypto shall compute the
* signature of the message body using the session's client renewal mac key. * signature of the message body using the session's client renewal mac key.
* The message body is the buffer starting at message+core_message_size with * The message body is the buffer starting at message+core_message_size with
* length message_length-core_message_size. * length message_length-core_message_size. If the session has not had a
* license loaded, it will use the usage entries client mac key to sign the
* message body.
* *
* This function generates a HMAC-SHA256 signature using the mac_key[client] * This function generates a HMAC-SHA256 signature using the mac_key[client]
* for license request signing under the license server protocol for CENC. * for license request signing under the license server protocol for CENC.
@@ -1162,7 +1164,8 @@ OEMCryptoResult OEMCrypto_LoadSRM(const uint8_t* buffer, size_t buffer_length);
* The mac_key and encrypt_key were generated and stored by the previous call * The mac_key and encrypt_key were generated and stored by the previous call
* to OEMCrypto_GenerateDerivedKeys() or * to OEMCrypto_GenerateDerivedKeys() or
* OEMCrypto_DeriveKeysFromSessionKey(). The nonce was generated and stored * OEMCrypto_DeriveKeysFromSessionKey(). The nonce was generated and stored
* by the previous call to OEMCrypto_GenerateNonce(). * in the session's nonce_values by the previous call to
* OEMCrypto_GenerateNonce().
* *
* This session's elapsed time clock is started at 0. The clock will be used * This session's elapsed time clock is started at 0. The clock will be used
* in OEMCrypto_DecryptCENC(). * in OEMCrypto_DecryptCENC().
@@ -1492,10 +1495,13 @@ OEMCryptoResult OEMCrypto_LoadKeys(
* OEMCrypto_ERROR_LICENSE_INACTIVE is returned. * OEMCrypto_ERROR_LICENSE_INACTIVE is returned.
* 24. The data in enc_mac_keys_iv is not identical to the 16 bytes before * 24. 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. * enc_mac_keys. If it is, return OEMCrypto_ERROR_INVALID_CONTEXT.
*
* Usage Table and Provider Session Token (pst) * Usage Table and Provider Session Token (pst)
* The function ODK_ParseLicense takes several parameters that may need more
* explanation.
* The parameter usage_entry_present shall be set to true if a usage entry * The parameter usage_entry_present shall be set to true if a usage entry
* was created or loaded for this session. This parameter is passed into * was created or loaded for this session. This parameter is used by
* ODK_ParseLicense and used for usage entry verification. * ODK_ParseLicense for usage entry verification.
* The parameter initial_license_load shall be false if the usage entry was * The parameter initial_license_load shall be false if the usage entry was
* loaded. If there is no usage entry or if the usage entry was created with * loaded. If there is no usage entry or if the usage entry was created with
* OEMCrypto_CreateNewUsageEntry, then initial_license_load shall be true. * OEMCrypto_CreateNewUsageEntry, then initial_license_load shall be true.
@@ -1712,6 +1718,29 @@ OEMCryptoResult OEMCrypto_LoadEntitledContentKeys(
* signature verification shall use a constant-time algorithm (a signature * signature verification shall use a constant-time algorithm (a signature
* mismatch will always take the same time as a successful comparison). * mismatch will always take the same time as a successful comparison).
* *
* The key control from the first OEMCrypto_KeyRefreshObject in the key_array
* shall be extracted. If it is encrypted, as described below, it shall be
* decrypted. The duration from the key control shall be extracted and
* converted to host byte order. This duration shall be passed to the
* function ODK_RefreshV15Values as the parameter new_key_duration.
*
* If the KeyRefreshObject's key_control_iv has zero length, then the
* key_control is not encrypted. If the key_control_iv is specified, then
* key_control is encrypted with the first 128 bits of the corresponding
* content key.
*
* If the KeyRefreshObject's key_id has zero length, then it is an error for
* the key_control_iv to have nonzero length. OEMCrypto shall return an error
* of OEMCrypto_ERROR_INVALID_CONTEXT.
*
* If the session's license_type is OEMCrypto_ContentLicense, and the
* KeyRefreshObject's key_id is not null, then the entry in the keytable with
* the matching content_key_id is used.
*
* If the session's license_type is OEMCrypto_EntitlementLicense, and the
* KeyRefreshObject's key_id is not null, then the entry in the keytable with
* the matching entitlment_key_id is used.
*
* The function ODK_RefreshV15Values shall be called to update the clock * The function ODK_RefreshV15Values shall be called to update the clock
* values. See the document "Widevine Core Message Serialization" for the * values. See the document "Widevine Core Message Serialization" for the
* documentation of the ODK library functions. * documentation of the ODK library functions.
@@ -1782,19 +1811,14 @@ OEMCryptoResult OEMCrypto_RefreshKeys(
* Updates the clock values and resets the renewal timer for the current * Updates the clock values and resets the renewal timer for the current
* session. * session.
* *
* OEMCrypto shall verify the signature of the message using the session's * OEMCrypto shall verify the signature of the entire message using the
* renewal mac key for the server. If the signature does not match, OEMCrypto * session's renewal mac key for the server. The entire message is the buffer
* returns OEMCrypto_ERROR_SIGNATURE_FAILURE. * starting at message with length message_length. If the signature does not
* match, OEMCrypto returns OEMCrypto_ERROR_SIGNATURE_FAILURE.
* *
* If nonce_values.api_level is 16, then OEMCrypto shall verify the signature * OEMCrypto shall verify that nonce_values.api_major_version is 16. If not,
* of the entire message using the session's server renewal mac key. The * return the error OEMCrypto_ERROR_INVALID_CONTEXT. Legacy licenses will use
* entire message is the buffer starting at message with length * the function OEMCrypto_RefreshKeys instead of OEMCrypto_LoadRenewal.
* message_length.
*
* If nonce_values.api_level is 15, then OEMCrypto shall compute the
* signature of the message body using the session's server renewal mac key.
* The entire message is the buffer starting at message+core_message_size
* with length message_length-core_message_size.
* *
* If the signature passes, OEMCrypto shall use the function * If the signature passes, OEMCrypto shall use the function
* ODK_ParseRenewal, as described in the document "Widevine Core Message * ODK_ParseRenewal, as described in the document "Widevine Core Message
@@ -2178,51 +2202,34 @@ OEMCryptoResult OEMCrypto_SelectKey(OEMCrypto_SESSION session,
* The 'cbcs' scheme is OEMCrypto_CipherMode_CBC with an encryption pattern. * The 'cbcs' scheme is OEMCrypto_CipherMode_CBC with an encryption pattern.
* Only some of the bytes in the encrypted portion of each subsample are * Only some of the bytes in the encrypted portion of each subsample are
* encrypted. In the pattern parameter, the encrypt and skip fields will * encrypted. In the pattern parameter, the encrypt and skip fields will
* usually be non-zero. This mode allows devices to decrypt FMP4 HLS content * usually be non-zero. This mode allows devices to decrypt FMP4 HLS content,
* and SAMPLE-AES HLS content. * SAMPLE-AES HLS content, as well as content using the DASH 'cbcs' scheme.
* *
* The skip field of OEMCrypto_CENCEncryptPatternDesc may also be zero. If * The skip field of OEMCrypto_CENCEncryptPatternDesc may also be zero. If
* the skip field is zero, then patterns are not in use and all crypto blocks * the skip field is zero, then patterns are not in use and all crypto blocks
* in the encrypted subsample are encrypted. It is not valid for the encrypt * in the encrypted part of the subsample are encrypted. It is not valid for
* field to be zero. * the encrypt field to be zero.
* *
* The length of a crypto block in AES-128 is 16 bytes. In the 'cbcs' scheme, * The length of a crypto block in AES-128 is 16 bytes. In the 'cbcs' scheme,
* if an encrypted subsample has a length that is not a multiple of 16 bytes, * if the encrypted part of a subsample has a length that is not a multiple
* then the final bytes that do not make up a full crypto block should be * of 16 bytes, then the final bytes that do not make up a full crypto block
* treated as clear and should not be decrypted. The following diagram * are clear and should never be decrypted. The following diagram provides an
* provides an example: * example:
* *
* (See drawing in "Widevine Modular DRM Security Integration Guide") * (See drawing in "Widevine Modular DRM Security Integration Guide")
* *
* Whether any given protected block is actually encrypted also depends on
* the pattern. But the bytes at the end that do not make up a full crypto
* block will never be encrypted, regardless of what the pattern is. Even if
* the pattern says to decrypt every protected block, these bytes are clear
* and should not be decrypted.
*
* Of course, if the encrypted subsample has a length that is a multiple of * Of course, if the encrypted subsample has a length that is a multiple of
* 16 bytes, the final bytes should be decrypted. The following diagram * 16 bytes, all the bytes in it are protected, and they may need to be
* provides an example: * decrypted following the pattern. The following diagram provides an example:
* *
* (See drawing in "Widevine Modular DRM Security Integration Guide") * (See drawing in "Widevine Modular DRM Security Integration Guide")
* *
* If the encrypted subsample has a length that is not an even multiple of
* the pattern length, then there may also be extra clear blocks at the end.
*
* If there are not enough bytes at the end of the encrypted subsample to
* complete an iteration of the encrypted part of the pattern, even if there
* are enough bytes to make a full crypto block, then the final bytes that do
* not fill the encrypted part of the pattern should be treated as clear and
* should not be decrypted. The following diagram provides an example:
*
* (See drawing in "Widevine Modular DRM Security Integration Guide")
*
* If there are enough bytes at the end of the encrypted subsample to
* complete an iteration of the encrypted part of the pattern, even if there
* are not enough bytes to complete the clear part of the pattern, then the
* bytes that fill the encrypted part of the pattern should be treated as
* encrypted. The following diagram provides an example:
*
* (See drawing in "Widevine Modular DRM Security Integration Guide")
*
* This behavior is specified by the ISO-CENC standard. Refer to the ISO-CENC
* standard, section 9.6, for full details of how patterns are to be applied
* to content.
*
* INITIALIZATION VECTOR BETWEEN SUBSAMPLES: * INITIALIZATION VECTOR BETWEEN SUBSAMPLES:
* *
* The IV is specified for the initial subsample in a sample in the iv field * The IV is specified for the initial subsample in a sample in the iv field
@@ -2491,7 +2498,6 @@ OEMCryptoResult OEMCrypto_DecryptCENC(
* OEMCrypto_ERROR_SYSTEM_INVALIDATED * OEMCrypto_ERROR_SYSTEM_INVALIDATED
* *
* Buffer Sizes: * Buffer Sizes:
* OEMCrypto shall support subsample sizes and total input buffer sizes as * OEMCrypto shall support subsample sizes and total input buffer sizes as
* specified by its resource rating tier. * specified by its resource rating tier.
* OEMCrypto shall return OEMCrypto_ERROR_BUFFER_TOO_LARGE if the buffer is * OEMCrypto shall return OEMCrypto_ERROR_BUFFER_TOO_LARGE if the buffer is
@@ -3235,7 +3241,7 @@ uint32_t OEMCrypto_APIVersion(void);
* number allows the calling application to avoid version mis-match errors, * number allows the calling application to avoid version mis-match errors,
* because this API is part of a shared library. * because this API is part of a shared library.
* *
* The minor version specified in this document is 1. Any OEM that returns * The minor version specified in this document is 2. Any OEM that returns
* this version number guarantees it passes all unit tests associated with * this version number guarantees it passes all unit tests associated with
* this version. * this version.
* *
@@ -3762,13 +3768,6 @@ uint32_t OEMCrypto_GetAnalogOutputFlags(void);
* sessions with 4 keys each (80 total), but it does not need to support 20 * sessions with 4 keys each (80 total), but it does not need to support 20
* sessions with 20 keys each. * sessions with 20 keys each.
* *
* Living room devices refer to devices such as TVs, set top boxes, game
* consoles, blu-ray players etc. These devices tend to have reduced UI and
* frequently are dedicated to playing video. For these devices, we expect
* there to be more memory dedicated to video playback and video
* applications. The number of sessions required for living room devices is
* larger than for mobile devices.
*
* The message size that is needed for a license with a large number of keys * The message size that is needed for a license with a large number of keys
* is larger than in previous versions. The message size limit applies to all * is larger than in previous versions. The message size limit applies to all
* functions that sign or verify messages. It also applies to the size of * functions that sign or verify messages. It also applies to the size of
@@ -3780,11 +3779,14 @@ uint32_t OEMCrypto_GetAnalogOutputFlags(void);
* should also support a higher frame rate. Platforms may enforce these * should also support a higher frame rate. Platforms may enforce these
* values. For example Android will enforce a frame rate via a GTS test. * values. For example Android will enforce a frame rate via a GTS test.
* *
* Note on units: We will use KiB to mean 1024 bytes and MiB to mean 1024 KiB,
* as described at https://en.wikipedia.org/wiki/Kibibyte.
*
* +--------------------------------+---------+----------+---------+---------+ * +--------------------------------+---------+----------+---------+---------+
* |Resource Rating Tier |1 - Low |2 - Medium|3 - High |4 - Very | * |Resource Rating Tier |1 - Low |2 - Medium|3 - High |4 - Very |
* | | | | | High | * | | | | | High |
* +--------------------------------+---------+----------+---------+---------+ * +--------------------------------+---------+----------+---------+---------+
* |Minimum Sample size |1 MB |2 MB |4 MB |16 MB | * |Minimum Sample size |1 MiB |2 MiB |4 MiB |16 MiB |
* +--------------------------------+---------+----------+---------+---------+ * +--------------------------------+---------+----------+---------+---------+
* |Minimum Number of Subsamples |10 |16 |32 |64 | * |Minimum Number of Subsamples |10 |16 |32 |64 |
* | (H264 or HEVC) | | | | | * | (H264 or HEVC) | | | | |
@@ -3795,16 +3797,12 @@ uint32_t OEMCrypto_GetAnalogOutputFlags(void);
* |Minimum Number of Subsamples |72 |144 |288 |576 | * |Minimum Number of Subsamples |72 |144 |288 |576 |
* |(AV1) | | | | | * |(AV1) | | | | |
* +--------------------------------+---------+----------+---------+---------+ * +--------------------------------+---------+----------+---------+---------+
* |Minimum subsample buffer size |100 KB |500 KB |1 MB |4 MB | * |Minimum subsample buffer size |100 KiB |500 KiB |1 MiB |4 MiB |
* +--------------------------------+---------+----------+---------+---------+ * +--------------------------------+---------+----------+---------+---------+
* |Minimum Generic crypto buffer |10 KB |100 KB |500 KB |1 MB | * |Minimum Generic crypto buffer |10 KiB |100 KiB |500 KiB |1 MiB |
* |size | | | | | * |size | | | | |
* +--------------------------------+---------+----------+---------+---------+ * +--------------------------------+---------+----------+---------+---------+
* |Minimum number of open sessions |10 |20 |20 |30 | * |Minimum number of open sessions |10 |20 |30 |40 |
* |(mobile devices) | | | | |
* +--------------------------------+---------+----------+---------+---------+
* |Minimum number of open sessions |10 |100 |100 |100 |
* |(living room devices) | | | | |
* +--------------------------------+---------+----------+---------+---------+ * +--------------------------------+---------+----------+---------+---------+
* |Minimum number of keys per |4 |20 |20 |30 | * |Minimum number of keys per |4 |20 |20 |30 |
* |session | | | | | * |session | | | | |
@@ -4304,6 +4302,11 @@ OEMCryptoResult OEMCrypto_CreateNewUsageEntry(OEMCrypto_SESSION session,
* usage entry associated with it, the error * usage entry associated with it, the error
* OEMCrypto_ERROR_MULTIPLE_USAGE_ENTRIES is returned. * OEMCrypto_ERROR_MULTIPLE_USAGE_ENTRIES is returned.
* *
* Before version API 16, the usage entry stored the time that the license
* was loaded. This value is now interpreted as the time that the licence
* request was signed. This can be achieved by simply renaming the field and
* using the same value when reloading an older entry.
*
* Parameters: * Parameters:
* [in] session: handle for the session to be used. * [in] session: handle for the session to be used.
* [in] usage_entry_number: index of existing usage entry. * [in] usage_entry_number: index of existing usage entry.
@@ -4522,6 +4525,10 @@ OEMCryptoResult OEMCrypto_DeactivateUsageEntry(OEMCrypto_SESSION session,
* HMAC SHA1 signature is used to prevent a rogue application from using * HMAC SHA1 signature is used to prevent a rogue application from using
* OMECrypto_GenerateSignature to forge a Usage Report. * OMECrypto_GenerateSignature to forge a Usage Report.
* *
* Before version 16 of this API, seconds_since_license_received was reported
* instead of seconds_since_license_signed. For any practical bookkeeping
* purposes, these events are essentially at the same time.
*
* Devices that do not implement a Session Usage Table may return * Devices that do not implement a Session Usage Table may return
* OEMCrypto_ERROR_NOT_IMPLEMENTED. * OEMCrypto_ERROR_NOT_IMPLEMENTED.
* *
@@ -4985,7 +4992,6 @@ OEMCryptoResult OEMCrypto_LoadDeviceRSAKey(OEMCrypto_SESSION session,
/****************************************************************************/ /****************************************************************************/
/****************************************************************************/ /****************************************************************************/
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@@ -1,4 +1,6 @@
// Copyright 2019 Google LLC. All rights reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
// ---------------------------------------------------------------- // ----------------------------------------------------------------
// Builds libwv_odk.a, The ODK Library (libwv_odk) is used by // Builds libwv_odk.a, The ODK Library (libwv_odk) is used by
@@ -16,6 +18,7 @@ cc_library_static {
"src/odk_overflow.c", "src/odk_overflow.c",
"src/odk_serialize.c", "src/odk_serialize.c",
"src/odk_timer.c", "src/odk_timer.c",
"src/odk_util.c",
"src/serialization_base.c", "src/serialization_base.c",
], ],
proprietary: true, proprietary: true,

View File

@@ -1,6 +1,6 @@
The ODK Library is used to generate and parse core OEMCrypto messages. The ODK Library is used to generate and parse core OEMCrypto messages.
This library is used by both OEMcrypto on a device, and by Widvine license and This library is used by both OEMCrypto on a device, and by Widvine license and
provisioning servers. provisioning servers.
The source of truth for these files is in the server code base on piper. Do not The source of truth for these files is in the server code base on piper. Do not

View File

@@ -106,6 +106,21 @@ typedef enum OEMCrypto_Usage_Entry_Status {
kInactiveUnused = 4, kInactiveUnused = 4,
} OEMCrypto_Usage_Entry_Status; } OEMCrypto_Usage_Entry_Status;
/*
* OEMCrypto_LicenseType is used in the license message to indicate if the key
* objects are for content keys, or for entitlement keys.
*/
typedef enum OEMCrypto_LicenseType {
OEMCrypto_ContentLicense = 0,
OEMCrypto_EntitlementLicense = 1
} OEMCrypto_LicenseType;
/* Private key type used in the provisioning response. */
typedef enum OEMCrypto_PrivateKeyType {
OEMCrypto_RSA_Private_Key = 0,
OEMCrypto_ECC_Private_Key = 1,
} OEMCrypto_PrivateKeyType;
/* /*
* OEMCrypto_Substring * OEMCrypto_Substring
* *

View File

@@ -30,10 +30,12 @@ namespace serialize {
* Parameters: * Parameters:
* [in] parsed_lic * [in] parsed_lic
* [in] core_request * [in] core_request
* [in] core_request_sha256
* [out] oemcrypto_core_message * [out] oemcrypto_core_message
*/ */
bool CreateCoreLicenseResponse(const ODK_ParsedLicense& parsed_lic, bool CreateCoreLicenseResponse(const ODK_ParsedLicense& parsed_lic,
const ODK_LicenseRequest& core_request, const ODK_LicenseRequest& core_request,
const std::string& core_request_sha256,
std::string* oemcrypto_core_message); std::string* oemcrypto_core_message);
/** /**
@@ -41,9 +43,11 @@ bool CreateCoreLicenseResponse(const ODK_ParsedLicense& parsed_lic,
* *
* Parameters: * Parameters:
* [in] core_request * [in] core_request
* [in] renewal_duration_seconds
* [out] oemcrypto_core_message * [out] oemcrypto_core_message
*/ */
bool CreateCoreRenewalResponse(const ODK_RenewalRequest& core_request, bool CreateCoreRenewalResponse(const ODK_RenewalRequest& core_request,
uint64_t renewal_duration_seconds,
std::string* oemcrypto_core_message); std::string* oemcrypto_core_message);
/** /**

View File

@@ -30,13 +30,16 @@ namespace serialize {
* *
* Parameters: * Parameters:
* [in] serialized_license * [in] serialized_license
* serialized video_widevine::License serialized video_widevine::License
* [in] core_request * [in] core_request oemcrypto core message from request.
* [out] oemcrypto_core_message * [in] core_request_sha256 - hash of serialized core request.
* [in] nonce_required - if the device should require a nonce match.
* [out] oemcrypto_core_message - the serialized oemcrypto core response.
*/ */
bool CreateCoreLicenseResponseFromProto(const std::string& serialized_license, bool CreateCoreLicenseResponseFromProto(const std::string& serialized_license,
const ODK_LicenseRequest& core_request, const ODK_LicenseRequest& core_request,
const std::string& core_request_sha256, const std::string& core_request_sha256,
const bool nonce_required,
std::string* oemcrypto_core_message); std::string* oemcrypto_core_message);
/** /**

View File

@@ -64,7 +64,8 @@ namespace oemcrypto_core_message {
* Input structure for CreateCoreLicenseResponse * Input structure for CreateCoreLicenseResponse
*/ */
struct ODK_LicenseRequest { struct ODK_LicenseRequest {
uint32_t api_version; uint16_t api_minor_version;
uint16_t api_major_version;
uint32_t nonce; uint32_t nonce;
uint32_t session_id; uint32_t session_id;
}; };
@@ -74,7 +75,8 @@ struct ODK_LicenseRequest {
* Input structure for CreateCoreRenewalResponse * Input structure for CreateCoreRenewalResponse
*/ */
struct ODK_RenewalRequest { struct ODK_RenewalRequest {
uint32_t api_version; uint16_t api_minor_version;
uint16_t api_major_version;
uint32_t nonce; uint32_t nonce;
uint32_t session_id; uint32_t session_id;
uint64_t playback_time_seconds; uint64_t playback_time_seconds;
@@ -85,7 +87,8 @@ struct ODK_RenewalRequest {
* Input structure for CreateCoreProvisioningResponse * Input structure for CreateCoreProvisioningResponse
*/ */
struct ODK_ProvisioningRequest { struct ODK_ProvisioningRequest {
uint32_t api_version; uint16_t api_minor_version;
uint16_t api_major_version;
uint32_t nonce; uint32_t nonce;
uint32_t session_id; uint32_t session_id;
std::string device_id; std::string device_id;

View File

@@ -65,7 +65,7 @@ extern "C" {
* [out] timer_limits: the session's timer limits. * [out] timer_limits: the session's timer limits.
* [out] clock_values: the session's clock values. * [out] clock_values: the session's clock values.
* [out] nonce_values: the session's ODK nonce values. * [out] nonce_values: the session's ODK nonce values.
* [in] api_version: the API version of OEMCrypto. * [in] api_major_version: the API version of OEMCrypto.
* [in] session_id: the session id of the newly created session. * [in] session_id: the session id of the newly created session.
* *
* Returns: * Returns:
@@ -78,7 +78,7 @@ extern "C" {
OEMCryptoResult ODK_InitializeSessionValues(ODK_TimerLimits* timer_limits, OEMCryptoResult ODK_InitializeSessionValues(ODK_TimerLimits* timer_limits,
ODK_ClockValues* clock_values, ODK_ClockValues* clock_values,
ODK_NonceValues* nonce_values, ODK_NonceValues* nonce_values,
uint32_t api_version, uint32_t api_major_version,
uint32_t session_id); uint32_t session_id);
/* /*
@@ -127,7 +127,9 @@ OEMCryptoResult ODK_InitializeClockValues(ODK_ClockValues* clock_values,
* *
* Description: * Description:
* This function sets the values in the clock_values structure. It shall be * This function sets the values in the clock_values structure. It shall be
* called from OEMCrypto_LoadUsageEntry. * called from OEMCrypto_LoadUsageEntry. When a usage entry from a v15 or
* earlier license is loaded, the value time_of_license_loaded shall be used
* in place of time_of_license_signed.
* *
* Parameters: * Parameters:
* [in/out] clock_values: the session's clock data. * [in/out] clock_values: the session's clock data.
@@ -231,7 +233,7 @@ OEMCryptoResult ODK_UpdateLastPlaybackTime(uint64_t system_time_seconds,
* *
* Description: * Description:
* This function modifies the session's clock values to indicate that the * This function modifies the session's clock values to indicate that the
* license has been deactiviated. It shall be called from * license has been deactivated. It shall be called from
* OEMCrypto_DeactivateUsageEntry * OEMCrypto_DeactivateUsageEntry
* *
* Parameters: * Parameters:
@@ -256,8 +258,8 @@ OEMCryptoResult ODK_DeactivateUsageEntry(ODK_ClockValues* clock_values);
* *
* This shall be called by OEMCrypto from OEMCrypto_PrepAndSignLicenseRequest. * This shall be called by OEMCrypto from OEMCrypto_PrepAndSignLicenseRequest.
* *
* NOTE: if message pointer is null and/or input core_message_size is zero, * NOTE: if the message pointer is null and/or input core_message_size is
* this function returns OEMCrypto_ERROR_SHORT_BUFFER and sets output * zero, this function returns OEMCrypto_ERROR_SHORT_BUFFER and sets output
* core_message_size to the size needed. * core_message_size to the size needed.
* *
* Parameters: * Parameters:
@@ -271,7 +273,7 @@ OEMCryptoResult ODK_DeactivateUsageEntry(ODK_ClockValues* clock_values);
* *
* Returns: * Returns:
* OEMCrypto_SUCCESS * OEMCrypto_SUCCESS
* OEMCrypto_ERROR_SHORT_BUFFER if core_message_size is too small * OEMCrypto_ERROR_SHORT_BUFFER: core_message_size is too small
* OEMCrypto_ERROR_INVALID_CONTEXT * OEMCrypto_ERROR_INVALID_CONTEXT
* *
* Version: * Version:
@@ -292,8 +294,14 @@ OEMCryptoResult ODK_PrepareCoreLicenseRequest(
* *
* This shall be called by OEMCrypto from OEMCrypto_PrepAndSignRenewalRequest. * This shall be called by OEMCrypto from OEMCrypto_PrepAndSignRenewalRequest.
* *
* NOTE: if message pointer is null and/or input core_message_size is zero, * If status in clock_values indicates that a license has not been loaded,
* this function returns OEMCrypto_ERROR_SHORT_BUFFER and sets output * then this is a license release. The ODK library will change the value of
* nonce_values.api_major_version to 15. This will make
* OEMCrypto_PrepAndSignRenewalRequest sign just the message body, as it does
* for all legacy licenses.
*
* NOTE: if the message pointer is null and/or input core_message_size is
* zero, this function returns OEMCrypto_ERROR_SHORT_BUFFER and sets output
* core_message_size to the size needed. * core_message_size to the size needed.
* *
* Parameters: * Parameters:
@@ -303,31 +311,33 @@ OEMCryptoResult ODK_PrepareCoreLicenseRequest(
* [in/out] core_message_size: length of the core message at the beginning of * [in/out] core_message_size: length of the core message at the beginning of
* the message. (in) size of buffer reserved for the core message, in * the message. (in) size of buffer reserved for the core message, in
* bytes. (out) actual length of the core message, in bytes. * bytes. (out) actual length of the core message, in bytes.
* [in] nonce_values: pointer to the session's nonce data. * [in/out] nonce_values: pointer to the session's nonce data.
* [in] clock_values: the session's clock values. * [in/out] clock_values: the session's clock values.
* [in] system_time_seconds: the current time on OEMCrypto's clock, in * [in] system_time_seconds: the current time on OEMCrypto's clock, in
* seconds. * seconds.
* *
* Returns: * Returns:
* OEMCrypto_SUCCESS * OEMCrypto_SUCCESS
* OEMCrypto_ERROR_SHORT_BUFFER if core_message_size is too small * OEMCrypto_ERROR_SHORT_BUFFER: core_message_size is too small
* OEMCrypto_ERROR_INVALID_CONTEXT * OEMCrypto_ERROR_INVALID_CONTEXT
* *
* Version: * Version:
* This method is new in version 16 of the API. * This method is new in version 16 of the API.
*/ */
OEMCryptoResult ODK_PrepareCoreRenewalRequest( OEMCryptoResult ODK_PrepareCoreRenewalRequest(uint8_t* message,
uint8_t* message, size_t message_length, size_t* core_message_size, size_t message_length,
const ODK_NonceValues* nonce_values, ODK_ClockValues* clock_values, size_t* core_message_size,
uint64_t system_time_seconds); ODK_NonceValues* nonce_values,
ODK_ClockValues* clock_values,
uint64_t system_time_seconds);
/* /*
* ODK_PrepareCoreProvisioningRequest * ODK_PrepareCoreProvisioningRequest
* *
* Description: * Description:
* Modifies the message to include a core provisioning request at the * Modifies the message to include a core provisioning request at the
* beginning of the message buffer. The values in nonce_values, clock_values * beginning of the message buffer. The values in nonce_values are used to
* and system_time_seconds are used to populate the message. * populate the message.
* *
* This shall be called by OEMCrypto from * This shall be called by OEMCrypto from
* OEMCrypto_PrepAndSignProvisioningRequest. * OEMCrypto_PrepAndSignProvisioningRequest.
@@ -336,8 +346,8 @@ OEMCryptoResult ODK_PrepareCoreRenewalRequest(
* OEMCrypto_GetDeviceID. The device ID shall be unique to the device, and * OEMCrypto_GetDeviceID. The device ID shall be unique to the device, and
* stable across reboots and factory resets for an L1 device. * stable across reboots and factory resets for an L1 device.
* *
* NOTE: if message pointer is null and/or input core_message_size is zero, * NOTE: if the message pointer is null and/or input core_message_size is
* this function returns OEMCrypto_ERROR_SHORT_BUFFER and sets output * zero, this function returns OEMCrypto_ERROR_SHORT_BUFFER and sets output
* core_message_size to the size needed. * core_message_size to the size needed.
* *
* Parameters: * Parameters:
@@ -356,7 +366,7 @@ OEMCryptoResult ODK_PrepareCoreRenewalRequest(
* *
* Returns: * Returns:
* OEMCrypto_SUCCESS * OEMCrypto_SUCCESS
* OEMCrypto_ERROR_SHORT_BUFFER if core_message_size is too small * OEMCrypto_ERROR_SHORT_BUFFER: core_message_size is too small
* OEMCrypto_ERROR_INVALID_CONTEXT * OEMCrypto_ERROR_INVALID_CONTEXT
* *
* Version: * Version:
@@ -373,7 +383,7 @@ OEMCryptoResult ODK_PrepareCoreProvisioningRequest(
* Description: * Description:
* This function sets all limits in the timer_limits struct to the * This function sets all limits in the timer_limits struct to the
* key_duration and initializes the other values. The field * key_duration and initializes the other values. The field
* nonce_values.api_level will be set to 15. It shall be called from * nonce_values.api_major_version will be set to 15. It shall be called from
* OEMCrypto_LoadKeys when loading a legacy license. * OEMCrypto_LoadKeys when loading a legacy license.
* *
* Parameters: * Parameters:
@@ -403,8 +413,12 @@ OEMCryptoResult ODK_InitializeV15Values(ODK_TimerLimits* timer_limits,
* ODK_RefreshV15Values * ODK_RefreshV15Values
* *
* Description: * Description:
* This function updates the clock_values as needed if the renewal is * This function updates the clock_values as needed if a v15 renewal is
* accepted. The field nonce_values.api_level is verified to be 15. * accepted. The field nonce_values.api_major_version is verified to be 15.
*
* This is called from OEMCrypto_RefreshKeys for a valid license renewal.
* OEMCrypto shall pass in the current system time, and the key duration from
* the first object in the OEMCrypto_KeyRefreshObject.
* *
* Parameters: * Parameters:
* [in] timer_limits: The session's timer limits. * [in] timer_limits: The session's timer limits.
@@ -412,6 +426,8 @@ OEMCryptoResult ODK_InitializeV15Values(ODK_TimerLimits* timer_limits,
* [in] nonce_values: The session's ODK nonce values. * [in] nonce_values: The session's ODK nonce values.
* [in] system_time_seconds: The current time on the system clock, as * [in] system_time_seconds: The current time on the system clock, as
* described in the document "License Duration and Renewal". * described in the document "License Duration and Renewal".
* [in] new_key_duration: The duration from the first
* OEMCrypto_KeyRefreshObject in key_array.
* [out] timer_value: set to the new timer value. Only used if the return * [out] timer_value: set to the new timer value. Only used if the return
* value is ODK_SET_TIMER. This must be non-null if OEMCrypto uses a * value is ODK_SET_TIMER. This must be non-null if OEMCrypto uses a
* hardware timer. * hardware timer.
@@ -432,27 +448,43 @@ OEMCryptoResult ODK_RefreshV15Values(const ODK_TimerLimits* timer_limits,
ODK_ClockValues* clock_values, ODK_ClockValues* clock_values,
const ODK_NonceValues* nonce_values, const ODK_NonceValues* nonce_values,
uint64_t system_time_seconds, uint64_t system_time_seconds,
uint32_t new_key_duration,
uint64_t* timer_value); uint64_t* timer_value);
/* /*
* ODK_ParseLicense * ODK_ParseLicense
* *
* Description: * Description:
* The function ODK_ParseLicense will parse the message and verify * The function ODK_ParseLicense will parse the message and verify fields in
* * the message.
* 1. Either the nonce matches the one passed in or the license does not
* require a nonce.
* 2. The API version of the message matches.
* 3. The session id matches.
* The function ODK_ParseLicense will parse the message and set each
* substring pointer to point to a location in the message body. The message
* body is the buffer starting at message + core_message_length with size
* message_length - core_message_length.
* *
* If the message does not parse correctly, ODK_VerifyAndParseLicense will * If the message does not parse correctly, ODK_VerifyAndParseLicense will
* return ODK_ERROR_CORE_MESSAGE that OEMCrypto should return to the CDM * return ODK_ERROR_CORE_MESSAGE that OEMCrypto should return to the CDM
* layer above. If the api in the message is larger than 16, then * layer above.
* ODK_UNSUPPORTED_API is returned. *
* If the API in the message is not 16, then ODK_UNSUPPORTED_API is returned.
*
* If initial_license_load is true, and nonce_required in the license is
* true, then the ODK library shall verify that nonce_values->nonce and
* nonce_values->session_id are the same as those in the message. If
* verification fails, then it shall return OEMCrypto_ERROR_INVALID_NONCE.
*
* If initial_license_load is false, and nonce_required is true, then
* ODK_ParseLicense will set the values in nonce_values from those in the
* message.
*
* The function ODK_ParseLicense will verify that each substring points to a
* location in the message body. The message body is the buffer starting at
* message + core_message_length with size message_length -
* core_message_length.
*
* If initial_license_load is true, then ODK_ParseLicense shall verify that
* the parameter request_hash matches request_hash in the parsed license. If
* verification fails, then it shall return ODK_ERROR_CORE_MESSAGE. This was
* computed by OEMCrypto when the license was requested.
*
* If usage_entry_present is true, then ODK_ParseLicense shall verify that
* the pst in the license has a nonzero length.
* *
* Parameters: * Parameters:
* [in] message: pointer to the message buffer. * [in] message: pointer to the message buffer.
@@ -463,6 +495,8 @@ OEMCryptoResult ODK_RefreshV15Values(const ODK_TimerLimits* timer_limits,
* false when called for OEMCrypto_ReloadLicense. * false when called for OEMCrypto_ReloadLicense.
* [in] usage_entry_present: true if the session has a new usage entry * [in] usage_entry_present: true if the session has a new usage entry
* associated with it created via OEMCrypto_CreateNewUsageEntry. * associated with it created via OEMCrypto_CreateNewUsageEntry.
* [in] request_hash: the hash of the license request core message. This was
* computed by OEMCrypto when the license request was signed.
* [in/out] timer_limits: The session's timer limits. These will be updated. * [in/out] timer_limits: The session's timer limits. These will be updated.
* [in/out] clock_values: The session's clock values. These will be updated. * [in/out] clock_values: The session's clock values. These will be updated.
* [in/out] nonce_values: The session's nonce values. These will be updated. * [in/out] nonce_values: The session's nonce values. These will be updated.
@@ -470,7 +504,7 @@ OEMCryptoResult ODK_RefreshV15Values(const ODK_TimerLimits* timer_limits,
* *
* Returns: * Returns:
* OEMCrypto_SUCCESS * OEMCrypto_SUCCESS
* ODK_ERROR_CORE_MESSAGE if the message did not parse correctly, or there * ODK_ERROR_CORE_MESSAGE: if the message did not parse correctly, or there
* were other incorrect values. An error should be returned to the CDM * were other incorrect values. An error should be returned to the CDM
* layer. * layer.
* ODK_UNSUPPORTED_API * ODK_UNSUPPORTED_API
@@ -490,9 +524,12 @@ OEMCryptoResult ODK_ParseLicense(
* ODK_ParseRenewal * ODK_ParseRenewal
* *
* Description: * Description:
* The function ODK_ParseRenewal will parse the message and verify that the * The function ODK_ParseRenewal will parse the message and verify its
* nonce values match those in the license. If the message does not parse * contents. If the message does not parse correctly, an error of
* correctly, an error of ODK_ERROR_CORE_MESSAGE is returned. * ODK_ERROR_CORE_MESSAGE is returned.
*
* ODK_ParseRenewal shall verify that all fields in nonce_values match those
* in the license. Otherwise it shall return OEMCrypto_ERROR_INVALID_NONCE.
* *
* After parsing the message, this function updates the clock_values based on * After parsing the message, this function updates the clock_values based on
* the timer_limits and the current system time. If playback may not * the timer_limits and the current system time. If playback may not
@@ -504,8 +541,8 @@ OEMCryptoResult ODK_ParseLicense(
* ODK_DISABLE_TIMER, then playback time is not limited. * ODK_DISABLE_TIMER, then playback time is not limited.
* *
* If OEMCrypto uses a hardware timer, and this function returns * If OEMCrypto uses a hardware timer, and this function returns
* ODK_SET_TIMER, then the timer should be set to the value pointed to by * ODK_SET_TIMER, then OEMCrypto shall set the timer to the value pointed to
* timer_value. * by timer_value.
* *
* Parameters: * Parameters:
* [in] message: pointer to the message buffer. * [in] message: pointer to the message buffer.
@@ -522,15 +559,16 @@ OEMCryptoResult ODK_ParseLicense(
* hardware timer. * hardware timer.
* *
* Returns: * Returns:
* ODK_ERROR_CORE_MESSAGE if the message did not parse correctly, or there * ODK_ERROR_CORE_MESSAGE: the message did not parse correctly, or there were
* were other incorrect values. An error should be returned to the CDM * other incorrect values. An error should be returned to the CDM layer.
* layer.
* ODK_SET_TIMER: Success. The timer should be reset to the specified timer * ODK_SET_TIMER: Success. The timer should be reset to the specified timer
* value. * value.
* ODK_DISABLE_TIMER: Success, but disable timer. Unlimited playback is * ODK_DISABLE_TIMER: Success, but disable timer. Unlimited playback is
* allowed. * allowed.
* ODK_TIMER_EXPIRED: Set timer as disabled. Playback is not allowed. * ODK_TIMER_EXPIRED: Set timer as disabled. Playback is not allowed.
* ODK_UNSUPPORTED_API * ODK_UNSUPPORTED_API
* ODK_STALE_RENEWAL: This renewal is not the most recently signed. It is
* rejected.
* OEMCrypto_ERROR_INVALID_NONCE * OEMCrypto_ERROR_INVALID_NONCE
* *
* Version: * Version:
@@ -551,14 +589,21 @@ OEMCryptoResult ODK_ParseRenewal(const uint8_t* message, size_t message_length,
* The function ODK_ParseProvisioning will parse the message and verify the * The function ODK_ParseProvisioning will parse the message and verify the
* nonce values match those in the license. * nonce values match those in the license.
* *
* The function ODK_ParseProvisioning will parse the message and set each
* substring pointer to point to a location in the message body. The message
* body is the buffer starting at message + core_message_length with size
* message_length - core_message_length.
*
* If the message does not parse correctly, ODK_ParseProvisioning will return * If the message does not parse correctly, ODK_ParseProvisioning will return
* an error that OEMCrypto should return to the CDM layer above. * an error that OEMCrypto should return to the CDM layer above.
* *
* If the API in the message is larger than 16, then ODK_UNSUPPORTED_API is
* returned.
*
* ODK_ParseProvisioning shall verify that nonce_values->nonce and
* nonce_values->session_id are the same as those in the message. Otherwise
* it shall return OEMCrypto_ERROR_INVALID_NONCE.
*
* The function ODK_ParseProvisioning will verify that each substring points
* to a location in the message body. The message body is the buffer starting
* at message + core_message_length with size message_length -
* core_message_length.
*
* Parameters: * Parameters:
* [in] message: pointer to the message buffer. * [in] message: pointer to the message buffer.
* [in] message_length: length of the entire message buffer. * [in] message_length: length of the entire message buffer.
@@ -572,9 +617,8 @@ OEMCryptoResult ODK_ParseRenewal(const uint8_t* message, size_t message_length,
* *
* Returns: * Returns:
* OEMCrypto_SUCCESS * OEMCrypto_SUCCESS
* ODK_ERROR_CORE_MESSAGE if the message did not parse correctly, or there * ODK_ERROR_CORE_MESSAGE: the message did not parse correctly, or there were
* were other incorrect values. An error should be returned to the CDM * other incorrect values. An error should be returned to the CDM layer.
* layer.
* ODK_UNSUPPORTED_API * ODK_UNSUPPORTED_API
* OEMCrypto_ERROR_INVALID_NONCE * OEMCrypto_ERROR_INVALID_NONCE
* *

View File

@@ -1,27 +0,0 @@
/*
* Copyright 2019 Google LLC. All Rights Reserved. This file and proprietary
* source code may only be used and distributed under the Widevine Master
* License Agreement.
*/
#ifndef ODK_ASSERT_H_
#define ODK_ASSERT_H_
#ifdef __cplusplus
extern "C" {
#endif
#if (__STDC_VERSION__ >= 201112L)
# include <assert.h>
# define odk_static_assert static_assert
#else
# define odk_static_assert(msg, e) \
enum { odk_static_assert = 1 / (!!((msg) && (e))) };
#endif
#ifdef __cplusplus
}
#endif
#endif /* ODK_ASSERT_H_ */

View File

@@ -1,33 +0,0 @@
/*
* Copyright 2019 Google LLC. All Rights Reserved. This file and proprietary
* source code may only be used and distributed under the Widevine Master
* License Agreement.
*/
#ifndef ODK_OVERFLOW_H_
#define ODK_OVERFLOW_H_
#ifdef __cplusplus
extern "C" {
#endif
#ifndef __has_builtin
# define __has_builtin(x) 0
#endif
#if (defined(__GNUC__) && __GNUC__ >= 5) || \
__has_builtin(__builtin_add_overflow)
# define odk_sub_overflow_u64 __builtin_sub_overflow
# define odk_add_overflow_u64 __builtin_add_overflow
# define odk_add_overflow_ux __builtin_add_overflow
#else
int odk_sub_overflow_u64(uint64_t a, uint64_t b, uint64_t* c);
int odk_add_overflow_u64(uint64_t a, uint64_t b, uint64_t* c);
int odk_add_overflow_ux(size_t a, size_t b, size_t* c);
#endif
#ifdef __cplusplus
}
#endif
#endif /* ODK_OVERFLOW_H_ */

View File

@@ -1,44 +0,0 @@
/*
* Copyright 2019 Google LLC. All Rights Reserved. This file and proprietary
* source code may only be used and distributed under the Widevine Master
* License Agreement.
*/
/*
* This code is auto-generated, do not edit
*/
#ifndef ODKITEE_SERIALIZER_H_
#define ODKITEE_SERIALIZER_H_
#include "odk_structs_priv.h"
#include "serialization_base.h"
#ifdef __cplusplus
extern "C" {
#endif
/* odk pack */
void Pack_ODK_PreparedLicense(Message* msg, ODK_PreparedLicense const* obj);
void Pack_ODK_RenewalMessage(Message* msg, ODK_RenewalMessage const* obj);
void Pack_ODK_ProvisioningMessage(Message* msg,
ODK_ProvisioningMessage const* obj);
/* odk unpack */
void Unpack_ODK_LicenseResponse(Message* msg, ODK_LicenseResponse* obj);
void Unpack_ODK_RenewalMessage(Message* msg, ODK_RenewalMessage* obj);
void Unpack_ODK_ProvisioningResponse(Message* msg,
ODK_ProvisioningResponse* obj);
/* kdo pack */
void Pack_ODK_LicenseResponse(Message* msg, ODK_LicenseResponse const* obj);
void Pack_ODK_ProvisioningResponse(Message* msg,
ODK_ProvisioningResponse const* obj);
/* kdo unpack */
void Unpack_ODK_PreparedLicense(Message* msg, ODK_PreparedLicense* obj);
void Unpack_ODK_ProvisioningMessage(Message* msg, ODK_ProvisioningMessage* obj);
#ifdef __cplusplus
} // extern "C"
#endif
#endif /* ODKITEE_SERIALIZER_H_ */

View File

@@ -8,63 +8,103 @@
#include <stdint.h> #include <stdint.h>
#include "OEMCryptoCENCCommon.h" #include "OEMCryptoCENCCommon.h"
#include "odk_target.h"
#define ODK_MAX_NUM_KEYS 32 /* The version of this library. */
#define ODK_MAJOR_VERSION 16
#define ODK_MINOR_VERSION 2
/* Some useful constants. */
#define ODK_DEVICE_ID_LEN_MAX 64 #define ODK_DEVICE_ID_LEN_MAX 64
#define ODK_SHA256_HASH_SIZE 32 #define ODK_SHA256_HASH_SIZE 32
/* /*
* ODK_TimerLimits is filled out by the function ODK_ParseLicense. * ODK_TimerLimits Structure
* *
* The fields in this structure are defined in the core license response * Description:
* message. This structure should be kept as part of the session and used * Timer limits are specified in a license and are used to determine when
* when calling the ODK timer functions described in the document "License * playback is allowed. See the document "License Duration and Renewal" for a
* Duration and Renewal" distributed as part of the OEMCrypto v16 design. * discussion on the time restrictions that may be placed on a license. The
* fields in this structure are directly related to the fields in the core
* license message. The fields are set when OEMCrypto calls the function
* ODK_ParseLicense or ODK_InitializeV15Values.
*
* Fields:
* soft_enforce_rental_duration: A boolean controlling the soft or hard
* enforcement of rental duration.
* soft_enforce_playback_duration: A boolean controlling the soft or hard
* enforcement of playback duration.
* earliest_playback_start_seconds: The earliest time that the first playback
* is allowed. Measured in seconds since the license request was signed. For
* most use cases, this is zero.
* rental_duration_seconds: Window of time for the allowed first playback.
* Measured in seconds since the earliest playback start. If
* soft_enforce_rental_duration is true, this applies only to the first
* playback. If soft_enforce_rental_duration is false, then this restricts
* any playback. A value of zero means no limit.
* total_playback_duration_seconds: Window of time for allowed playback.
* Measured in seconds since the first playback start. If
* soft_enforce_playback_duration is true, this applies only to the start of
* playback for any session. If soft_enforce_playback_duration is false, then
* this restricts any playback. A value of zero means no limit.
* initial_renewal_duration_seconds: Window of time for allowed playback.
* Measured in seconds since the first playback start. This value is only
* used to start the renewal timer. After a renewal message is loaded, the
* timer will be reset. A value of zero means no limit.
*
* Version:
* This struct changed in API version 16.2.
*/ */
typedef struct { typedef struct {
uint32_t /*boolean*/ soft_expiry; bool soft_enforce_rental_duration;
uint64_t earliest_playback_start_seconds; /* since license signed. */ bool soft_enforce_playback_duration;
uint64_t latest_playback_start_seconds; /* since license signed. */ uint64_t earliest_playback_start_seconds;
uint64_t initial_playback_duration_seconds; /* since playback start. */ uint64_t rental_duration_seconds;
uint64_t renewal_playback_duration_seconds; /* since renewal signed. */ uint64_t total_playback_duration_seconds;
uint64_t license_duration_seconds; /* since license signed. */ uint64_t initial_renewal_duration_seconds;
} ODK_TimerLimits; } ODK_TimerLimits;
/* /*
* ODK_ParsedLicense holds fields from the core license response. * ODK_ClockValues Structure
*/
typedef struct {
OEMCrypto_Substring enc_mac_keys_iv;
OEMCrypto_Substring enc_mac_keys;
OEMCrypto_Substring pst;
OEMCrypto_Substring srm_restriction_data;
uint32_t /* OEMCrypto_LicenseType */ license_type;
uint32_t nonce_required;
ODK_TimerLimits timer_limits;
uint8_t request_hash[ODK_SHA256_HASH_SIZE];
uint32_t key_array_length; /* num_keys */
OEMCrypto_KeyObject key_array[ODK_MAX_NUM_KEYS];
} ODK_ParsedLicense;
/*
* ODK_ParsedProvisioning holds fields from the core provisioning response.
*/
typedef struct {
uint32_t key_type;
OEMCrypto_Substring enc_private_key;
OEMCrypto_Substring enc_private_key_iv;
OEMCrypto_Substring encrypted_message_key; /* Used for Prov 3.0 */
} ODK_ParsedProvisioning;
/*
* ODK_ClockValues keeps information about a session's current clock values
* and timers.
* *
* Most of the fields in this structure are saved in the usage entry for each * Description:
* session. This structure should be initialized when a usage entry is * Clock values are modified when decryption occurs or when a renewal is
* created or loaded, and should be used to save a usage entry. It is * processed. They are used to track the current status of the license --
* updated using ODK functions listed in the document "License Duration and * i.e. has playback started? When does the timer expire? See the section
* Renewal". The time values are based on OEMCryptos system clock. * "Complete ODK API" of the document "Widevine Core Message Serialization"
* for a complete list of all fields in this structure. Most of these values
* shall be saved with the usage entry.
*
* All times are in seconds. Most of the fields in this structure are saved
* in the usage entry. This structure should be initialized when a usage
* entry is created or loaded, and should be used to save a usage entry. It
* is updated using the ODK functions listed below. The time values are based
* on OEMCrypto's system clock, as described in the document "License
* Duration and Renewal".
*
* Fields:
* time_of_license_signed: Time that the license request was signed, based on
* OEMCrypto's system clock. This value shall be stored and reloaded with
* usage entry as time_of_license_received.
* time_of_first_decrypt: Time of the first decrypt or call select key, based
* on OEMCrypto's system clock. This is 0 if the license has not been used to
* decrypt any data. This value shall be stored and reloaded with usage entry.
* time_of_last_decrypt: Time of the most recent decrypt call, based on
* OEMCrypto's system clock. This value shall be stored and reloaded with
* usage entry.
* time_of_renewal_request: Time of the most recent renewal request, based on
* OEMCrypto's system clock. This is used to verify that a renewal is not
* stale.
* time_when_timer_expires: Time that the current timer expires, based on
* OEMCrypto's system clock. If the timer is active, this is used by the ODK
* library to determine if it has expired.
* timer_status: Used internally by the ODK library to indicate the current
* timer status.
* status: The license or usage entry status. This value shall be stored and
* reloaded with usage entry.
*
* Version:
* This struct changed in API version 16.2.
*/ */
typedef struct { typedef struct {
uint64_t time_of_license_signed; uint64_t time_of_license_signed;
@@ -77,20 +117,99 @@ typedef struct {
} ODK_ClockValues; } ODK_ClockValues;
/* /*
* ODK_NonceValues are used to match a license or provisioning request to a * ODK_NonceValues Structure
* license or provisioning response. For this reason, the api_version might be *
* lower than that supported by OEMCrypto. The api_version matches the version * Description:
* of the license. Similarly the nonce and session_id match the session that * Nonce values are used to match a license or provisioning request to a
* generated the license request. For an offline license, these might not match * license or provisioning response. They are also used to match a renewal
* the session that is loading the license. We use the nonce to prevent a * request and response to a license. For this reason, the api_version might
* license from being replayed. By also including a session_id in the license * be lower than that supported by OEMCrypto. The api_version matches the
* request and license response, we prevent an attack using the birthday paradox * version of the license. Similarly the nonce and session_id match the
* to generate nonce collisions on a single device. * session that generated the license request. For an offline license, these
* might not match the session that is loading the license. We use the nonce
* to prevent a license from being replayed. By also including a session_id
* in the license request and license response, we prevent an attack using
* the birthday paradox to generate nonce collisions on a single device.
*
* Fields:
* api_major_version: the API version of the license. This is initialized to
* the API version of the ODK library, but may be lower.
* api_minor_version: the minor version of the ODK library. This is used by
* the server to verify that device is not using an obsolete version of the
* ODK library.
* nonce: a randomly generated number used to prevent replay attacks.
* session_id: the session id of the session which signed the license or
* provisioning request. It is used to prevent replay attacks from one
* session to another.
*
* Version:
* This struct changed in API version 16.2.
*/ */
typedef struct { typedef struct {
uint32_t api_version; uint16_t api_minor_version;
uint16_t api_major_version;
uint32_t nonce; uint32_t nonce;
uint32_t session_id; uint32_t session_id;
} ODK_NonceValues; } ODK_NonceValues;
/*
* ODK_ParsedLicense Structure
*
* Description:
* The parsed license structure contains information from the license
* message. The function ODK_ParseLicense will fill in the fields of this
* message. All substrings are contained within the message body.
*
* Fields:
* enc_mac_keys_iv: IV for decrypting new mac_key. Size is 128 bits.
* enc_mac_keys: encrypted mac_keys for generating new mac_keys. Size is 512
* bits.
* pst: the Provider Session Token.
* srm_restriction_data: optional data specifying the minimum SRM version.
* license_type: specifies if the license contains content keys or
* entitlement keys.
* nonce_required: indicates if the license requires a nonce.
* timer_limits: time limits of the for the license.
* key_array_length: number of keys present.
* key_array: set of keys to be installed.
*
* Version:
* This struct changed in API version 16.2.
*/
typedef struct {
OEMCrypto_Substring enc_mac_keys_iv;
OEMCrypto_Substring enc_mac_keys;
OEMCrypto_Substring pst;
OEMCrypto_Substring srm_restriction_data;
OEMCrypto_LicenseType license_type;
bool nonce_required;
ODK_TimerLimits timer_limits;
uint32_t key_array_length;
OEMCrypto_KeyObject key_array[ODK_MAX_NUM_KEYS];
} ODK_ParsedLicense;
/*
* ODK_ParsedProvisioning Structure
*
* Description:
* The parsed provisioning structure contains information from the license
* message. The function ODK_ParseProvisioning will fill in the fields of
* this message. All substrings are contained within the message body.
*
* Fields:
* key_type: indicates if this key is an RSA or ECC private key.
* enc_private_key: encrypted private key for the DRM certificate.
* enc_private_key_iv: IV for decrypting new private key. Size is 128 bits.
* encrypted_message_key: used for provisioning 3.0 to derive keys.
*
* Version:
* This struct changed in API version 16.2.
*/
typedef struct {
OEMCrypto_PrivateKeyType key_type;
OEMCrypto_Substring enc_private_key;
OEMCrypto_Substring enc_private_key_iv;
OEMCrypto_Substring encrypted_message_key; /* Used for Prov 3.0 */
} ODK_ParsedProvisioning;
#endif /* WIDEVINE_ODK_INCLUDE_ODK_STRUCTS_H_ */ #endif /* WIDEVINE_ODK_INCLUDE_ODK_STRUCTS_H_ */

View File

@@ -1,54 +0,0 @@
/*
* Copyright 2019 Google LLC. All Rights Reserved. This file and proprietary
* source code may only be used and distributed under the Widevine Master
* License Agreement.
*/
#ifndef ODK_STRUCTS_PRIV_H_
#define ODK_STRUCTS_PRIV_H_
#include <stdint.h>
#include "OEMCryptoCENCCommon.h"
#include "odk_structs.h"
typedef enum {
ODK_License_Request_Type = 1,
ODK_License_Response_Type = 2,
ODK_Renewal_Request_Type = 3,
ODK_Renewal_Response_Type = 4,
ODK_Provisioning_Request_Type = 5,
ODK_Provisioning_Response_Type = 6,
} ODK_MessageType;
typedef struct {
uint32_t message_type;
uint32_t message_length;
ODK_NonceValues nonce_values;
} ODK_CoreMessage;
typedef struct {
ODK_CoreMessage core_message;
} ODK_PreparedLicense;
typedef struct {
ODK_CoreMessage core_message;
uint64_t playback_time;
} ODK_RenewalMessage;
typedef struct {
ODK_CoreMessage core_message;
uint32_t device_id_length;
uint8_t device_id[ODK_DEVICE_ID_LEN_MAX];
} ODK_ProvisioningMessage;
typedef struct {
ODK_CoreMessage core_message;
ODK_ParsedLicense* parsed_license;
} ODK_LicenseResponse;
typedef struct {
ODK_ProvisioningMessage core_provisioning;
ODK_ParsedProvisioning* parsed_provisioning;
} ODK_ProvisioningResponse;
#endif // ODK_STRUCTS_PRIV_H_

View File

@@ -0,0 +1,13 @@
/* Copyright 2019 Google LLC. All rights reserved. This file is distributed */
/* under the Widevine Master License Agreement. */
/* Partners are expected to edit this file to support target specific code */
/* and limits. */
#ifndef WIDEVINE_ODK_INCLUDE_ODK_TARGET_H_
#define WIDEVINE_ODK_INCLUDE_ODK_TARGET_H_
/* Maximum number of keys can be modified to suit target's resource tier. */
#define ODK_MAX_NUM_KEYS 32
#endif /* WIDEVINE_ODK_INCLUDE_ODK_TARGET_H_ */

View File

@@ -1,91 +0,0 @@
/*
* Copyright 2019 Google LLC. All Rights Reserved. This file and proprietary
* source code may only be used and distributed under the Widevine Master
* License Agreement.
*/
#ifndef ODKITEE_SERIALIZATION_BASE_H_
#define ODKITEE_SERIALIZATION_BASE_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stddef.h>
#include <stdint.h>
#include "OEMCryptoCENCCommon.h"
#define SIZE_OF_MESSAGE_STRUCT 64
/*
* Description:
* Point |msg| to stack-array |blk|.
* |blk| is guaranteed large enough to hold a |Message| struct.
* |blk| cannot be used in the same scope as a variable name.
* |msg| points to valid memory in the same scope |AllocateMessage| is used.
* Parameters:
* msg: pointer to pointer to |Message| struct
* blk: variable name for stack-array
*/
#define AllocateMessage(msg, blk) \
uint8_t blk[SIZE_OF_MESSAGE_STRUCT]; \
*(msg) = (Message*)(blk);
typedef struct _Message Message;
bool ValidMessage(Message* message);
void Pack_uint32_t(Message* message, const uint32_t* value);
void Pack_uint64_t(Message* message, const uint64_t* value);
void PackArray(Message* message, const uint8_t* base, size_t size);
void Pack_OEMCrypto_Substring(Message* msg, const OEMCrypto_Substring* obj);
void Unpack_uint32_t(Message* message, uint32_t* value);
void Unpack_uint64_t(Message* message, uint64_t* value);
void UnpackArray(Message* message, uint8_t* base, size_t size); /* copy out */
void Unpack_OEMCrypto_Substring(Message* msg, OEMCrypto_Substring* obj);
typedef enum {
MESSAGE_STATUS_OK,
MESSAGE_STATUS_UNKNOWN_ERROR,
MESSAGE_STATUS_OVERFLOW_ERROR,
MESSAGE_STATUS_UNDERFLOW_ERROR,
MESSAGE_STATUS_PARSE_ERROR,
MESSAGE_STATUS_NULL_POINTER_ERROR,
MESSAGE_STATUS_API_VALUE_ERROR
} MessageStatus;
/*
* Create a message from a buffer. The message structure consumes the first
* sizeof(Message) bytes of the buffer. The caller is responsible for ensuring
* that the buffer remains allocated for the lifetime of the message.
*/
Message* CreateMessage(uint8_t* buffer, size_t buffer_size);
/*
* Initialize a message structure to reference a separate buffer. The caller
* is responsible for ensuring that the buffer remains allocated for the
* lifetime of the message.
*/
void InitMessage(Message* message, uint8_t* buffer, size_t capacity);
/*
* Reset an existing the message to an empty state
*/
void ResetMessage(Message* message);
uint8_t* GetBase(Message* message);
size_t GetCapacity(Message* message);
size_t GetSize(Message* message);
void SetSize(Message* message, size_t size);
MessageStatus GetStatus(Message* message);
void SetStatus(Message* message, MessageStatus status);
size_t GetOffset(Message* message);
size_t SizeOfMessageStruct();
#ifdef __cplusplus
} // extern "C"
#endif
#endif // ODKITEE_SERIALIZATION_BASE_H_

View File

@@ -1,172 +0,0 @@
/*
* Copyright 2019 Google LLC. All Rights Reserved. This file and proprietary
* source code may only be used and distributed under the Widevine Master
* License Agreement.
*/
// clang-format off
/*********************************************************************
* oec_util.h
*
* OEMCrypto v16 Core Message Serialization library counterpart (a.k.a. KDO)
*
* For Widevine Modular DRM, there are six message types between a server and
* a client device: license request and response, provisioning request and
* response, and renewal request and response.
*
* In OEMCrypto v15 and earlier, messages from the server were parsed by the
* CDM layer above OEMCrypto; the CDM in turn gave OEMCrypto a collection of
* pointers to protected data within the message. However, the pointers
* themselves were not signed by the server.
*
* Starting from OEMCrypto v16, all fields used by OEMCrypto in each of these
* messages have been identified in the document "Widevine Core Message
* Serialization". These fields are called the core of the message. Core
* message fields are (de)serialized using the ODK, a C library provided by
* Widevine. OEMCrypto will parse and verify the core of the message with
* help from the ODK.
*
* The KDO library is the counterpart of ODK used in the CDM & Widevine
* servers. For each message type generated by the ODK, KDO provides a
* corresponding parser. For each message type to be parsed by the ODK,
* KDO provides a corresponding writer.
*
* Table: ODK vs KDO (s: serialize; d: deserialize)
* +----------------------------------------+------------------------------------+
* | ODK | KDO |
* +---+------------------------------------+---+--------------------------------+
* | s | ODK_PrepareCoreLicenseRequest | d | ParseLicenseRequest |
* | +------------------------------------+ +--------------------------------+
* | | ODK_PrepareCoreRenewalRequest | | ParseRenewalRequest |
* | +------------------------------------+ +--------------------------------+
* | | ODK_PrepareCoreProvisioningRequest | | ParseProvisioningRequest |
* +---+------------------------------------+---+--------------------------------+
* | d | ODK_ParseLicense | s | CreateCoreLicenseResponse |
* | +------------------------------------+ +--------------------------------+
* | | ODK_ParseRenewal | | CreateCoreRenewalResponse |
* | +------------------------------------+ +--------------------------------+
* | | ODK_ParseProvisioning | | CreateCoreProvisioningResponse |
* +---+------------------------------------+---+--------------------------------+
*
*********************************************************************/
// clang-format on
#ifndef OEC_UTIL_H_
#define OEC_UTIL_H_
#include <cstdint>
#include <string>
#include "odk_structs.h"
using namespace std;
namespace oec_util {
// @ input/output structs
/**
* Output structure for ParseLicenseRequest
* Input structure for CreateCoreLicenseResponse
*/
struct ODK_LicenseRequest {
uint32_t api_version;
uint32_t nonce;
uint32_t session_id;
};
/**
* Output structure for ParseRenewalRequest
* Input structure for CreateCoreRenewalResponse
*/
struct ODK_RenewalRequest {
uint32_t api_version;
uint32_t nonce;
uint32_t session_id;
uint64_t playback_time;
};
/**
* Output structure for ParseProvisioningRequest
* Input structure for CreateCoreProvisioningResponse
*/
struct ODK_ProvisioningRequest {
uint32_t api_version;
uint32_t nonce;
uint32_t session_id;
string device_id;
};
// @ public parse request (deserializer) functions
/**
* Counterpart (deserializer) of ODK_PrepareCoreLicenseRequest (serializer)
*
* Parameters:
* [in] oemcrypto_core_message
* [out] core_license_request
*/
bool ParseLicenseRequest(const string& oemcrypto_core_message,
ODK_LicenseRequest* core_license_request);
/**
* Counterpart (deserializer) of ODK_PrepareCoreRenewalRequest (serializer)
*
* Parameters:
* [in] oemcrypto_core_message
* [out] core_renewal_request
*/
bool ParseRenewalRequest(const string& oemcrypto_core_message,
ODK_RenewalRequest* core_renewal_request);
/**
* Counterpart (deserializer) of ODK_PrepareCoreProvisioningRequest (serializer)
*
* Parameters:
* [in] oemcrypto_core_message
* [out] core_provisioning_request
*/
bool ParseProvisioningRequest(
const string& oemcrypto_core_message,
ODK_ProvisioningRequest* core_provisioning_request);
// @ public create response (serializer) functions
/**
* Counterpart (serializer) of ODK_ParseLicense (deserializer)
* struct-input variant
*
* Parameters:
* [in] parsed_lic
* [in] core_request
* [out] oemcrypto_core_message
*/
bool CreateCoreLicenseResponse(const ODK_ParsedLicense& parsed_lic,
const ODK_LicenseRequest& core_request,
string* oemcrypto_core_message);
/**
* Counterpart (serializer) of ODK_ParseRenewal (deserializer)
*
* Parameters:
* [in] core_request
* [out] oemcrypto_core_message
*/
bool CreateCoreRenewalResponse(const ODK_RenewalRequest& core_request,
string* oemcrypto_core_message);
/**
* Counterpart (serializer) of ODK_ParseProvisioning (deserializer)
* struct-input variant
*
* Parameters:
* [in] parsed_prov
* [in] core_request
* [out] oemcrypto_core_message
*/
bool CreateCoreProvisioningResponse(const ODK_ParsedProvisioning& parsed_prov,
const ODK_ProvisioningRequest& core_request,
string* oemcrypto_core_message);
} // namespace oec_util
#endif // OEC_UTIL_H_

View File

@@ -1,59 +0,0 @@
/*
* Copyright 2019 Google LLC. All Rights Reserved. This file and proprietary
* source code may only be used and distributed under the Widevine Master
* License Agreement.
*/
/*********************************************************************
* oec_util_proto.h
*
* These functions are an extension of those found in oec_util.h. The
* difference is that these use the license and provisioning messages
* in protobuf format to create the core message.
*********************************************************************/
#ifndef OEC_UTIL_PROTO_H_
#define OEC_UTIL_PROTO_H_
#include <cstdint>
#include <string>
#include "license_protocol.pb.h"
#include "oec_util.h"
using namespace std;
using video_widevine::License;
using video_widevine::License_KeyContainer;
namespace oec_util {
// @ public create response (serializer) functions
/**
* Counterpart (serializer) of ODK_ParseLicense (deserializer)
*
* Parameters:
* [in] license
* [in] core_request
* [out] oemcrypto_core_message
*/
bool CreateCoreLicenseResponse(const video_widevine::License& license,
const ODK_LicenseRequest& core_request,
string* oemcrypto_core_message);
/**
* Counterpart (serializer) of ODK_ParseProvisioning (deserializer)
*
* Parameters:
* [in] provisioning_response
* [in] core_request
* [out] oemcrypto_core_message
*/
bool CreateCoreProvisioningResponse(
const video_widevine::ProvisioningResponse& provisioning_response,
const ODK_ProvisioningRequest& core_request,
string* oemcrypto_core_message);
} // namespace oec_util
#endif // OEC_UTIL_PROTO_H_

View File

@@ -19,8 +19,8 @@ namespace oemcrypto_core_message {
namespace deserialize { namespace deserialize {
namespace { namespace {
const int EARLIEST_OEMCRYPTO_VERSION_WITH_ODK = 16; constexpr int EARLIEST_OEMCRYPTO_VERSION_WITH_ODK = 16;
const int LATEST_OEMCRYPTO_VERSION = 16; constexpr int LATEST_OEMCRYPTO_VERSION = 16;
/** /**
* Template for parsing requests * Template for parsing requests
@@ -53,29 +53,43 @@ bool ParseRequest(uint32_t message_type,
} }
const auto& core_message = prepared->core_message; const auto& core_message = prepared->core_message;
core_request->api_version = core_message.nonce_values.api_version; core_request->api_major_version = core_message.nonce_values.api_major_version;
core_request->api_minor_version = core_message.nonce_values.api_minor_version;
core_request->nonce = core_message.nonce_values.nonce; core_request->nonce = core_message.nonce_values.nonce;
core_request->session_id = core_message.nonce_values.session_id; core_request->session_id = core_message.nonce_values.session_id;
// Verify that the minor version matches the released version for the given
// major version.
if ((core_request->api_major_version < EARLIEST_OEMCRYPTO_VERSION_WITH_ODK) ||
(core_request->api_major_version > LATEST_OEMCRYPTO_VERSION)) {
// Non existing and future versions are not supported.
return false;
} else if (core_request->api_major_version == 16) {
// For version 16, we demand a minor version of at least 2.
if (core_request->api_major_version < 2) return false;
} else {
// Other versions do not (yet) have a restriction on minor number.
}
return core_message.message_type == message_type && return core_message.message_type == message_type &&
core_message.message_length == GetOffset(msg) && core_message.message_length == GetOffset(msg) &&
core_request->api_version >= EARLIEST_OEMCRYPTO_VERSION_WITH_ODK && core_request->api_major_version >=
core_request->api_version <= LATEST_OEMCRYPTO_VERSION; EARLIEST_OEMCRYPTO_VERSION_WITH_ODK &&
core_request->api_major_version <= LATEST_OEMCRYPTO_VERSION;
} }
} // namespace } // namespace
bool CoreLicenseRequestFromMessage(const std::string& oemcrypto_core_message, bool CoreLicenseRequestFromMessage(const std::string& oemcrypto_core_message,
ODK_LicenseRequest* core_license_request) { ODK_LicenseRequest* core_license_request) {
const auto unpacker = Unpack_ODK_PreparedLicense; const auto unpacker = Unpack_ODK_PreparedLicenseRequest;
ODK_PreparedLicense prepared_license = {}; ODK_PreparedLicenseRequest prepared_license = {};
return ParseRequest(ODK_License_Request_Type, oemcrypto_core_message, return ParseRequest(ODK_License_Request_Type, oemcrypto_core_message,
core_license_request, &prepared_license, unpacker); core_license_request, &prepared_license, unpacker);
} }
bool CoreRenewalRequestFromMessage(const std::string& oemcrypto_core_message, bool CoreRenewalRequestFromMessage(const std::string& oemcrypto_core_message,
ODK_RenewalRequest* core_renewal_request) { ODK_RenewalRequest* core_renewal_request) {
const auto unpacker = Unpack_ODK_RenewalMessage; const auto unpacker = Unpack_ODK_PreparedRenewalRequest;
ODK_RenewalMessage prepared_renewal = {}; ODK_PreparedRenewalRequest prepared_renewal = {};
if (!ParseRequest(ODK_Renewal_Request_Type, oemcrypto_core_message, if (!ParseRequest(ODK_Renewal_Request_Type, oemcrypto_core_message,
core_renewal_request, &prepared_renewal, unpacker)) { core_renewal_request, &prepared_renewal, unpacker)) {
return false; return false;
@@ -87,8 +101,8 @@ bool CoreRenewalRequestFromMessage(const std::string& oemcrypto_core_message,
bool CoreProvisioningRequestFromMessage( bool CoreProvisioningRequestFromMessage(
const std::string& oemcrypto_core_message, const std::string& oemcrypto_core_message,
ODK_ProvisioningRequest* core_provisioning_request) { ODK_ProvisioningRequest* core_provisioning_request) {
const auto unpacker = Unpack_ODK_ProvisioningMessage; const auto unpacker = Unpack_ODK_PreparedProvisioningRequest;
ODK_ProvisioningMessage prepared_provision = {}; ODK_PreparedProvisioningRequest prepared_provision = {};
if (!ParseRequest(ODK_Provisioning_Request_Type, oemcrypto_core_message, if (!ParseRequest(ODK_Provisioning_Request_Type, oemcrypto_core_message,
core_provisioning_request, &prepared_provision, unpacker)) { core_provisioning_request, &prepared_provision, unpacker)) {
return false; return false;

View File

@@ -4,7 +4,6 @@
#include "core_message_serialize.h" #include "core_message_serialize.h"
#include <algorithm>
#include <cstddef> #include <cstddef>
#include <cstdint> #include <cstdint>
#include <cstring> #include <cstring>
@@ -38,11 +37,12 @@ bool CreateResponse(uint32_t message_type, const S& core_request,
auto* header = reinterpret_cast<ODK_CoreMessage*>(&response); auto* header = reinterpret_cast<ODK_CoreMessage*>(&response);
header->message_type = message_type; header->message_type = message_type;
header->nonce_values.api_version = core_request.api_version; header->nonce_values.api_major_version = core_request.api_major_version;
header->nonce_values.api_minor_version = core_request.api_minor_version;
header->nonce_values.nonce = core_request.nonce; header->nonce_values.nonce = core_request.nonce;
header->nonce_values.session_id = core_request.session_id; header->nonce_values.session_id = core_request.session_id;
const size_t BUF_CAPACITY = 2048; static constexpr size_t BUF_CAPACITY = 2048;
std::vector<uint8_t> buf(BUF_CAPACITY, 0); std::vector<uint8_t> buf(BUF_CAPACITY, 0);
Message* msg = nullptr; Message* msg = nullptr;
AllocateMessage(&msg, message_block); AllocateMessage(&msg, message_block);
@@ -63,16 +63,14 @@ bool CreateResponse(uint32_t message_type, const S& core_request,
bool CopyDeviceId(const ODK_ProvisioningRequest& src, bool CopyDeviceId(const ODK_ProvisioningRequest& src,
ODK_ProvisioningResponse* dest) { ODK_ProvisioningResponse* dest) {
auto& core_provisioning = dest->core_provisioning; auto& request = dest->request;
const std::string& device_id = src.device_id; const std::string& device_id = src.device_id;
core_provisioning.device_id_length = device_id.size(); if (request.device_id_length > sizeof(request.device_id)) {
if (core_provisioning.device_id_length >
sizeof(core_provisioning.device_id)) {
return false; return false;
} }
memset(core_provisioning.device_id, 0, sizeof(core_provisioning.device_id)); request.device_id_length = device_id.size();
memcpy(core_provisioning.device_id, device_id.data(), memset(request.device_id, 0, sizeof(request.device_id));
core_provisioning.device_id_length); memcpy(request.device_id, device_id.data(), request.device_id_length);
return true; return true;
} }
@@ -80,21 +78,28 @@ bool CopyDeviceId(const ODK_ProvisioningRequest& src,
bool CreateCoreLicenseResponse(const ODK_ParsedLicense& parsed_lic, bool CreateCoreLicenseResponse(const ODK_ParsedLicense& parsed_lic,
const ODK_LicenseRequest& core_request, const ODK_LicenseRequest& core_request,
const std::string& core_request_sha256,
std::string* oemcrypto_core_message) { std::string* oemcrypto_core_message) {
ODK_LicenseResponse license_response{ ODK_LicenseResponse license_response{
{}, const_cast<ODK_ParsedLicense*>(&parsed_lic)}; {}, const_cast<ODK_ParsedLicense*>(&parsed_lic), {0}};
if (core_request_sha256.size() != sizeof(license_response.request_hash))
return false;
memcpy(license_response.request_hash, core_request_sha256.data(),
sizeof(license_response.request_hash));
return CreateResponse(ODK_License_Response_Type, core_request, return CreateResponse(ODK_License_Response_Type, core_request,
oemcrypto_core_message, license_response, oemcrypto_core_message, license_response,
Pack_ODK_LicenseResponse); Pack_ODK_LicenseResponse);
} }
bool CreateCoreRenewalResponse(const ODK_RenewalRequest& core_request, bool CreateCoreRenewalResponse(const ODK_RenewalRequest& core_request,
uint64_t renewal_duration_seconds,
std::string* oemcrypto_core_message) { std::string* oemcrypto_core_message) {
ODK_RenewalMessage renewal{{}, core_request.playback_time_seconds}; ODK_RenewalResponse renewal_response{{}, core_request.playback_time_seconds};
renewal.playback_time = core_request.playback_time_seconds; renewal_response.request.playback_time = core_request.playback_time_seconds;
renewal_response.renewal_duration_seconds = renewal_duration_seconds;
return CreateResponse(ODK_Renewal_Response_Type, core_request, return CreateResponse(ODK_Renewal_Response_Type, core_request,
oemcrypto_core_message, renewal, oemcrypto_core_message, renewal_response,
Pack_ODK_RenewalMessage); Pack_ODK_RenewalResponse);
} }
bool CreateCoreProvisioningResponse(const ODK_ParsedProvisioning& parsed_prov, bool CreateCoreProvisioningResponse(const ODK_ParsedProvisioning& parsed_prov,

View File

@@ -67,6 +67,7 @@ OEMCrypto_KeyObject KeyContainerToOecKey(
bool CreateCoreLicenseResponseFromProto(const std::string& serialized_license, bool CreateCoreLicenseResponseFromProto(const std::string& serialized_license,
const ODK_LicenseRequest& core_request, const ODK_LicenseRequest& core_request,
const std::string& core_request_sha256, const std::string& core_request_sha256,
const bool nonce_required,
std::string* oemcrypto_core_message) { std::string* oemcrypto_core_message) {
video_widevine::License lic; video_widevine::License lic;
if (!lic.ParseFromString(serialized_license)) { if (!lic.ParseFromString(serialized_license)) {
@@ -74,25 +75,33 @@ bool CreateCoreLicenseResponseFromProto(const std::string& serialized_license,
} }
ODK_ParsedLicense parsed_lic{}; ODK_ParsedLicense parsed_lic{};
if (core_request_sha256.size() != ODK_SHA256_HASH_SIZE) { bool any_content = false;
return false; bool any_entitlement = false;
}
std::memcpy(parsed_lic.request_hash, core_request_sha256.data(),
ODK_SHA256_HASH_SIZE);
for (int i = 0; i < lic.key_size(); ++i) { for (int i = 0; i < lic.key_size(); ++i) {
const auto& k = lic.key(i); const auto& k = lic.key(i);
switch (k.type()) { switch (k.type()) {
case video_widevine::License_KeyContainer::SIGNING: { case video_widevine::License_KeyContainer::SIGNING: {
if (!k.has_key()) {
continue;
}
parsed_lic.enc_mac_keys_iv = parsed_lic.enc_mac_keys_iv =
GetOecSubstring(serialized_license, k.iv()); GetOecSubstring(serialized_license, k.iv());
// Strip off PKCS#5 padding std::string mac_keys(k.key(), k.key().size());
const size_t MAC_KEY_SIZE = 32;
std::string mac_keys(k.key(), 2 * MAC_KEY_SIZE);
parsed_lic.enc_mac_keys = GetOecSubstring(serialized_license, mac_keys); parsed_lic.enc_mac_keys = GetOecSubstring(serialized_license, mac_keys);
break; break;
} }
case video_widevine::License_KeyContainer::CONTENT: { case video_widevine::License_KeyContainer::CONTENT: {
any_content = true;
if (parsed_lic.key_array_length >= ODK_MAX_NUM_KEYS) {
return false;
}
uint32_t& n = parsed_lic.key_array_length;
parsed_lic.key_array[n++] = KeyContainerToOecKey(serialized_license, k);
break;
}
case video_widevine::License_KeyContainer::ENTITLEMENT: {
any_entitlement = true;
if (parsed_lic.key_array_length >= ODK_MAX_NUM_KEYS) { if (parsed_lic.key_array_length >= ODK_MAX_NUM_KEYS) {
return false; return false;
} }
@@ -105,7 +114,16 @@ bool CreateCoreLicenseResponseFromProto(const std::string& serialized_license,
} }
} }
} }
if (any_content && any_entitlement) {
// TODO(b/147513335): this should be logged -- both type of keys.
return false;
}
if (!any_content && !any_entitlement) {
// TODO(b/147513335): this should be logged -- no keys?
return false;
}
parsed_lic.license_type =
any_content ? OEMCrypto_ContentLicense : OEMCrypto_EntitlementLicense;
const auto& lid = lic.id(); const auto& lid = lic.id();
if (lid.has_provider_session_token()) { if (lid.has_provider_session_token()) {
parsed_lic.pst = parsed_lic.pst =
@@ -117,22 +135,25 @@ bool CreateCoreLicenseResponseFromProto(const std::string& serialized_license,
GetOecSubstring(serialized_license, lic.srm_requirement()); GetOecSubstring(serialized_license, lic.srm_requirement());
} }
parsed_lic.license_type = lid.type(); parsed_lic.nonce_required = nonce_required;
// todo(robertshih): nonce_required
const auto& policy = lic.policy(); const auto& policy = lic.policy();
ODK_TimerLimits& timer_limits = parsed_lic.timer_limits; ODK_TimerLimits& timer_limits = parsed_lic.timer_limits;
timer_limits.soft_expiry = policy.soft_enforce_playback_duration(); // TODO(b/148241181): add field to protobuf.
// timer_limits.soft_enforce_rental_duration =
// policy.soft_enforce_rental_duration();
timer_limits.soft_enforce_rental_duration = true;
timer_limits.soft_enforce_playback_duration =
policy.soft_enforce_playback_duration();
timer_limits.earliest_playback_start_seconds = 0; timer_limits.earliest_playback_start_seconds = 0;
timer_limits.latest_playback_start_seconds = timer_limits.rental_duration_seconds = policy.rental_duration_seconds();
policy.license_duration_seconds(); timer_limits.total_playback_duration_seconds =
timer_limits.initial_playback_duration_seconds =
policy.playback_duration_seconds(); policy.playback_duration_seconds();
timer_limits.renewal_playback_duration_seconds = timer_limits.initial_renewal_duration_seconds =
policy.playback_duration_seconds(); policy.renewal_delay_seconds() +
timer_limits.license_duration_seconds = policy.license_duration_seconds(); policy.renewal_recovery_duration_seconds();
return CreateCoreLicenseResponse(parsed_lic, core_request, return CreateCoreLicenseResponse(parsed_lic, core_request,
oemcrypto_core_message); core_request_sha256, oemcrypto_core_message);
} }
bool CreateCoreProvisioningResponseFromProto( bool CreateCoreProvisioningResponseFromProto(
@@ -145,7 +166,8 @@ bool CreateCoreProvisioningResponseFromProto(
return false; return false;
} }
parsed_prov.key_type = 0; // todo(robertshih): ECC or RSA parsed_prov.key_type =
OEMCrypto_RSA_Private_Key; // TODO(b/148404408): ECC or RSA
if (prov.has_device_rsa_key()) { if (prov.has_device_rsa_key()) {
parsed_prov.enc_private_key = parsed_prov.enc_private_key =
GetOecSubstring(serialized_provisioning_resp, prov.device_rsa_key()); GetOecSubstring(serialized_provisioning_resp, prov.device_rsa_key());

View File

@@ -12,12 +12,12 @@
#include "odk_serialize.h" #include "odk_serialize.h"
#include "odk_structs.h" #include "odk_structs.h"
#include "odk_structs_priv.h" #include "odk_structs_priv.h"
#include "odk_util.h"
#include "serialization_base.h" #include "serialization_base.h"
#define ODK_LICENSE_REQUEST_SIZE 20 #define ODK_LICENSE_REQUEST_SIZE 20
#define ODK_RENEWAL_REQUEST_SIZE 28 #define ODK_RENEWAL_REQUEST_SIZE 28
#define ODK_PROVISIONING_REQUEST_SIZE 88 #define ODK_PROVISIONING_REQUEST_SIZE 88
#define OEC_API_VERSION 16
/* @ private odk functions */ /* @ private odk functions */
@@ -26,8 +26,8 @@ static OEMCryptoResult ODK_PrepareRequest(uint8_t* buffer, size_t buffer_length,
uint32_t message_type, uint32_t message_type,
const ODK_NonceValues* nonce_values, const ODK_NonceValues* nonce_values,
ODK_CoreMessage* core_message) { ODK_CoreMessage* core_message) {
if (!nonce_values || !core_message_length || !core_message || if (nonce_values == NULL || core_message_length == NULL ||
*core_message_length > buffer_length) { core_message == NULL || *core_message_length > buffer_length) {
return ODK_ERROR_CORE_MESSAGE; return ODK_ERROR_CORE_MESSAGE;
} }
@@ -43,17 +43,20 @@ static OEMCryptoResult ODK_PrepareRequest(uint8_t* buffer, size_t buffer_length,
switch (message_type) { switch (message_type) {
case ODK_License_Request_Type: { case ODK_License_Request_Type: {
core_message->message_length = ODK_LICENSE_REQUEST_SIZE; core_message->message_length = ODK_LICENSE_REQUEST_SIZE;
Pack_ODK_PreparedLicense(msg, (ODK_PreparedLicense*)core_message); Pack_ODK_PreparedLicenseRequest(
msg, (ODK_PreparedLicenseRequest*)core_message);
break; break;
} }
case ODK_Renewal_Request_Type: { case ODK_Renewal_Request_Type: {
core_message->message_length = ODK_RENEWAL_REQUEST_SIZE; core_message->message_length = ODK_RENEWAL_REQUEST_SIZE;
Pack_ODK_RenewalMessage(msg, (ODK_RenewalMessage*)core_message); Pack_ODK_PreparedRenewalRequest(
msg, (ODK_PreparedRenewalRequest*)core_message);
break; break;
} }
case ODK_Provisioning_Request_Type: { case ODK_Provisioning_Request_Type: {
core_message->message_length = ODK_PROVISIONING_REQUEST_SIZE; core_message->message_length = ODK_PROVISIONING_REQUEST_SIZE;
Pack_ODK_ProvisioningMessage(msg, (ODK_ProvisioningMessage*)core_message); Pack_ODK_PreparedProvisioningRequest(
msg, (ODK_PreparedProvisioningRequest*)core_message);
break; break;
} }
default: { default: {
@@ -93,7 +96,7 @@ static OEMCryptoResult ODK_ParseResponse(const uint8_t* buf,
break; break;
} }
case ODK_Renewal_Response_Type: { case ODK_Renewal_Response_Type: {
Unpack_ODK_RenewalMessage(msg, (ODK_RenewalMessage*)core_message); Unpack_ODK_RenewalResponse(msg, (ODK_RenewalResponse*)core_message);
break; break;
} }
case ODK_Provisioning_Response_Type: { case ODK_Provisioning_Response_Type: {
@@ -114,7 +117,10 @@ static OEMCryptoResult ODK_ParseResponse(const uint8_t* buf,
if (nonce_values) { if (nonce_values) {
/* always verify nonce_values for Renewal and Provisioning responses */ /* always verify nonce_values for Renewal and Provisioning responses */
if (nonce_values->api_version != core_message->nonce_values.api_version || if (nonce_values->api_major_version !=
core_message->nonce_values.api_major_version ||
nonce_values->api_minor_version !=
core_message->nonce_values.api_minor_version ||
nonce_values->nonce != core_message->nonce_values.nonce || nonce_values->nonce != core_message->nonce_values.nonce ||
nonce_values->session_id != core_message->nonce_values.session_id) { nonce_values->session_id != core_message->nonce_values.session_id) {
return OEMCrypto_ERROR_INVALID_NONCE; return OEMCrypto_ERROR_INVALID_NONCE;
@@ -131,7 +137,7 @@ static OEMCryptoResult ODK_ParseResponse(const uint8_t* buf,
OEMCryptoResult ODK_PrepareCoreLicenseRequest( OEMCryptoResult ODK_PrepareCoreLicenseRequest(
uint8_t* message, size_t message_length, size_t* core_message_length, uint8_t* message, size_t message_length, size_t* core_message_length,
const ODK_NonceValues* nonce_values) { const ODK_NonceValues* nonce_values) {
ODK_PreparedLicense license_request = { ODK_PreparedLicenseRequest license_request = {
{0}, {0},
}; };
return ODK_PrepareRequest(message, message_length, core_message_length, return ODK_PrepareRequest(message, message_length, core_message_length,
@@ -139,13 +145,17 @@ OEMCryptoResult ODK_PrepareCoreLicenseRequest(
&license_request.core_message); &license_request.core_message);
} }
OEMCryptoResult ODK_PrepareCoreRenewalRequest( OEMCryptoResult ODK_PrepareCoreRenewalRequest(uint8_t* message,
uint8_t* message, size_t message_length, size_t* core_message_size, size_t message_length,
const ODK_NonceValues* nonce_values, ODK_ClockValues* clock_values, size_t* core_message_size,
uint64_t system_time_seconds) { ODK_NonceValues* nonce_values,
ODK_RenewalMessage renewal_request = { ODK_ClockValues* clock_values,
{0}, uint64_t system_time_seconds) {
}; if (nonce_values == NULL || clock_values == NULL)
return ODK_ERROR_CORE_MESSAGE;
ODK_PreparedRenewalRequest renewal_request = {{0}, 0};
/* First, we compute the time this request was made relative to the playback
* clock. */
if (clock_values->time_of_first_decrypt == 0) { if (clock_values->time_of_first_decrypt == 0) {
/* It is OK to preemptively request a renewal before playback starts. /* It is OK to preemptively request a renewal before playback starts.
* We'll treat this as asking for a renewal at playback time 0. */ * We'll treat this as asking for a renewal at playback time 0. */
@@ -158,7 +168,9 @@ OEMCryptoResult ODK_PrepareCoreRenewalRequest(
return ODK_ERROR_CORE_MESSAGE; return ODK_ERROR_CORE_MESSAGE;
} }
} }
/* Save time for this request so that we can verify the response. */ /* Save time for this request so that we can verify the response. This makes
* all earlier requests invalid. If preparing this request fails, then all
* requests will be bad. */
clock_values->time_of_renewal_request = renewal_request.playback_time; clock_values->time_of_renewal_request = renewal_request.playback_time;
return ODK_PrepareRequest(message, message_length, core_message_size, return ODK_PrepareRequest(message, message_length, core_message_size,
ODK_Renewal_Request_Type, nonce_values, ODK_Renewal_Request_Type, nonce_values,
@@ -169,7 +181,9 @@ OEMCryptoResult ODK_PrepareCoreProvisioningRequest(
uint8_t* message, size_t message_length, size_t* core_message_length, uint8_t* message, size_t message_length, size_t* core_message_length,
const ODK_NonceValues* nonce_values, const uint8_t* device_id, const ODK_NonceValues* nonce_values, const uint8_t* device_id,
size_t device_id_length) { size_t device_id_length) {
ODK_ProvisioningMessage provisioning_request = { ODK_PreparedProvisioningRequest provisioning_request = {
{0},
0,
{0}, {0},
}; };
if (device_id_length > sizeof(provisioning_request.device_id)) { if (device_id_length > sizeof(provisioning_request.device_id)) {
@@ -192,55 +206,61 @@ OEMCryptoResult ODK_ParseLicense(
const uint8_t* request_hash, ODK_TimerLimits* timer_limits, const uint8_t* request_hash, ODK_TimerLimits* timer_limits,
ODK_ClockValues* clock_values, ODK_NonceValues* nonce_values, ODK_ClockValues* clock_values, ODK_NonceValues* nonce_values,
ODK_ParsedLicense* parsed_license) { ODK_ParsedLicense* parsed_license) {
if (!message || !request_hash || !timer_limits || !clock_values || if (message == NULL || request_hash == NULL || timer_limits == NULL ||
!nonce_values || !parsed_license) { clock_values == NULL || nonce_values == NULL || parsed_license == NULL) {
return ODK_ERROR_CORE_MESSAGE; return ODK_ERROR_CORE_MESSAGE;
} }
ODK_LicenseResponse license_response = {{0}, parsed_license}; ODK_LicenseResponse license_response = {{{0}}, parsed_license, {0}};
OEMCryptoResult err = ODK_ParseResponse( const OEMCryptoResult err = ODK_ParseResponse(
message, message_length, core_message_length, ODK_License_Response_Type, message, message_length, core_message_length, ODK_License_Response_Type,
NULL, &license_response.core_message); NULL, &license_response.request.core_message);
if (err != OEMCrypto_SUCCESS) { if (err != OEMCrypto_SUCCESS) {
return err; return err;
} }
/* This function should not be used for legacy licenses. */ /* This function should not be used for legacy licenses. */
if (license_response.core_message.nonce_values.api_version != if (license_response.request.core_message.nonce_values.api_major_version !=
OEC_API_VERSION) { ODK_MAJOR_VERSION) {
return ODK_UNSUPPORTED_API; return ODK_UNSUPPORTED_API;
} }
/* If the license has a provider session token (pst), then OEMCrypto should
* have a usage entry loaded. The opposite is also an error. */
if ((usage_entry_present && parsed_license->pst.length == 0) ||
(!usage_entry_present && parsed_license->pst.length > 0)) {
return ODK_ERROR_CORE_MESSAGE;
}
if (parsed_license->nonce_required) { if (parsed_license->nonce_required) {
if (initial_license_load) { if (initial_license_load) {
if (nonce_values->nonce != if (nonce_values->nonce !=
license_response.core_message.nonce_values.nonce || license_response.request.core_message.nonce_values.nonce ||
nonce_values->session_id != nonce_values->session_id !=
license_response.core_message.nonce_values.session_id) { license_response.request.core_message.nonce_values.session_id) {
return OEMCrypto_ERROR_INVALID_NONCE; return OEMCrypto_ERROR_INVALID_NONCE;
} }
} else { /* !initial_license_load */ } else { /* !initial_license_load */
nonce_values->nonce = license_response.core_message.nonce_values.nonce; nonce_values->nonce =
license_response.request.core_message.nonce_values.nonce;
nonce_values->session_id = nonce_values->session_id =
license_response.core_message.nonce_values.session_id; license_response.request.core_message.nonce_values.session_id;
} }
} }
/* For v16, in order to be backwards compatible with a v15 license server, /* For v16, in order to be backwards compatible with a v15 license server,
* OEMCrypto stores a hash of the core license request and only signs the * OEMCrypto stores a hash of the core license request and only signs the
* message body. Here, when we process the license response, we verify that * message body. Here, when we process the license response, we verify that
* the server has the same hash of the core request. */ * the server has the same hash of the core request. */
if (initial_license_load && memcmp(request_hash, parsed_license->request_hash, if (initial_license_load &&
ODK_SHA256_HASH_SIZE)) { crypto_memcmp(request_hash, license_response.request_hash,
return ODK_ERROR_CORE_MESSAGE; ODK_SHA256_HASH_SIZE)) {
}
/* If the license has a provider session token (pst), then OEMCrypto should
* have a usage entry loaded. */
if (usage_entry_present && parsed_license->pst.length == 0) {
return ODK_ERROR_CORE_MESSAGE; return ODK_ERROR_CORE_MESSAGE;
} }
*timer_limits = parsed_license->timer_limits; *timer_limits = parsed_license->timer_limits;
return err; /* And update the clock values state. */
clock_values->timer_status = ODK_CLOCK_TIMER_STATUS_LICENSE_LOADED;
return OEMCrypto_SUCCESS;
} }
OEMCryptoResult ODK_ParseRenewal(const uint8_t* message, size_t message_length, OEMCryptoResult ODK_ParseRenewal(const uint8_t* message, size_t message_length,
@@ -250,16 +270,18 @@ OEMCryptoResult ODK_ParseRenewal(const uint8_t* message, size_t message_length,
const ODK_TimerLimits* timer_limits, const ODK_TimerLimits* timer_limits,
ODK_ClockValues* clock_values, ODK_ClockValues* clock_values,
uint64_t* timer_value) { uint64_t* timer_value) {
if (!message || !nonce_values || !timer_limits || !clock_values) { if (message == NULL || nonce_values == NULL || timer_limits == NULL ||
clock_values == NULL) {
return ODK_ERROR_CORE_MESSAGE; return ODK_ERROR_CORE_MESSAGE;
} }
ODK_RenewalMessage renewal_response = { ODK_RenewalResponse renewal_response = {
{0}, {{0}, 0},
0,
}; };
OEMCryptoResult err = ODK_ParseResponse( OEMCryptoResult err = ODK_ParseResponse(
message, message_length, core_message_length, ODK_Renewal_Response_Type, message, message_length, core_message_length, ODK_Renewal_Response_Type,
nonce_values, &renewal_response.core_message); nonce_values, &renewal_response.request.core_message);
if (err) { if (err) {
return err; return err;
@@ -269,69 +291,51 @@ OEMCryptoResult ODK_ParseRenewal(const uint8_t* message, size_t message_length,
* Doc: License Duration and Renewal (Changes for OEMCrypto v16) * Doc: License Duration and Renewal (Changes for OEMCrypto v16)
* Section: Renewal Message * Section: Renewal Message
*/ */
/* If a renewal request is lost in transit, we should throw it out and create /* If a renewal request is lost in transit, we should throw it out and create
* a new one. We use the timestamp to make sure we have the latest request. * a new one. We use the timestamp to make sure we have the latest request.
*/ */
if (clock_values->time_of_renewal_request < renewal_response.playback_time) { if (clock_values->time_of_renewal_request <
renewal_response.request.playback_time) {
return ODK_STALE_RENEWAL; return ODK_STALE_RENEWAL;
} }
return ODK_ComputeRenewalDuration(timer_limits, clock_values, system_time,
/* The timer value should be set to the renewal duration. */ renewal_response.renewal_duration_seconds,
if (timer_value) { timer_value);
*timer_value = timer_limits->renewal_playback_duration_seconds;
}
if (timer_limits->renewal_playback_duration_seconds == 0) {
clock_values->time_when_timer_expires = 0;
clock_values->timer_status = ODK_DISABLE_TIMER;
return ODK_DISABLE_TIMER;
}
if (odk_add_overflow_u64(system_time,
timer_limits->renewal_playback_duration_seconds,
&clock_values->time_when_timer_expires)) {
return ODK_ERROR_CORE_MESSAGE;
}
clock_values->timer_status = ODK_SET_TIMER;
return ODK_SET_TIMER;
} }
OEMCryptoResult ODK_ParseProvisioning( OEMCryptoResult ODK_ParseProvisioning(
const uint8_t* message, size_t message_length, size_t core_message_length, const uint8_t* message, size_t message_length, size_t core_message_length,
const ODK_NonceValues* nonce_values, const uint8_t* device_id, const ODK_NonceValues* nonce_values, const uint8_t* device_id,
size_t device_id_length, ODK_ParsedProvisioning* parsed_response) { size_t device_id_length, ODK_ParsedProvisioning* parsed_response) {
if (!message || !nonce_values || !device_id || !parsed_response) { if (message == NULL || nonce_values == NULL || device_id == NULL ||
parsed_response == NULL) {
return ODK_ERROR_CORE_MESSAGE; return ODK_ERROR_CORE_MESSAGE;
} }
ODK_ProvisioningResponse provisioning_response = {{ ODK_ProvisioningResponse provisioning_response = {{{0}, 0, {0}},
{0},
},
parsed_response}; parsed_response};
if (device_id_length > ODK_DEVICE_ID_LEN_MAX) { if (device_id_length > ODK_DEVICE_ID_LEN_MAX) {
return ODK_ERROR_CORE_MESSAGE; return ODK_ERROR_CORE_MESSAGE;
} }
const OEMCryptoResult err = OEMCryptoResult err =
ODK_ParseResponse(message, message_length, core_message_length, ODK_ParseResponse(message, message_length, core_message_length,
ODK_Provisioning_Response_Type, nonce_values, ODK_Provisioning_Response_Type, nonce_values,
&provisioning_response.core_provisioning.core_message); &provisioning_response.request.core_message);
if (err) { if (err) {
return err; return err;
} }
if (memcmp(device_id, provisioning_response.core_provisioning.device_id, if (memcmp(device_id, provisioning_response.request.device_id,
device_id_length)) { device_id_length)) {
return ODK_ERROR_CORE_MESSAGE; return ODK_ERROR_CORE_MESSAGE;
} }
uint8_t zero[ODK_DEVICE_ID_LEN_MAX] = {0}; uint8_t zero[ODK_DEVICE_ID_LEN_MAX] = {0};
/* check bytes beyond device_id_length are 0 */ /* check bytes beyond device_id_length are 0 */
if (memcmp( if (memcmp(zero, provisioning_response.request.device_id + device_id_length,
zero, ODK_DEVICE_ID_LEN_MAX - device_id_length)) {
provisioning_response.core_provisioning.device_id + device_id_length,
ODK_DEVICE_ID_LEN_MAX - device_id_length)) {
return ODK_ERROR_CORE_MESSAGE; return ODK_ERROR_CORE_MESSAGE;
} }

View File

@@ -3,13 +3,14 @@
# Agreement. # Agreement.
# These files are built into the ODK library on the device. They are also used # These files are built into the ODK library on the device. They are also used
# by the server and by test cocde. These files should compile on C98 compilers. # by the server and by test cocde. These files should compile on C99 compilers.
{ {
'sources': [ 'sources': [
'odk.c', 'odk.c',
'odk_overflow.c', 'odk_overflow.c',
'odk_serialize.c', 'odk_serialize.c',
'odk_timer.c', 'odk_timer.c',
'odk_util.c',
'serialization_base.c', 'serialization_base.c',
], ],
} }

View File

@@ -5,6 +5,9 @@
#ifndef WIDEVINE_ODK_SRC_ODK_OVERFLOW_H_ #ifndef WIDEVINE_ODK_SRC_ODK_OVERFLOW_H_
#define WIDEVINE_ODK_SRC_ODK_OVERFLOW_H_ #define WIDEVINE_ODK_SRC_ODK_OVERFLOW_H_
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif

View File

@@ -14,7 +14,8 @@
/* @@ private serialize */ /* @@ private serialize */
static void Pack_ODK_NonceValues(Message* msg, ODK_NonceValues const* obj) { static void Pack_ODK_NonceValues(Message* msg, ODK_NonceValues const* obj) {
Pack_uint32_t(msg, &obj->api_version); Pack_uint16_t(msg, &obj->api_minor_version);
Pack_uint16_t(msg, &obj->api_major_version);
Pack_uint32_t(msg, &obj->nonce); Pack_uint32_t(msg, &obj->nonce);
Pack_uint32_t(msg, &obj->session_id); Pack_uint32_t(msg, &obj->session_id);
} }
@@ -35,12 +36,12 @@ static void Pack_OEMCrypto_KeyObject(Message* msg,
} }
static void Pack_ODK_TimerLimits(Message* msg, ODK_TimerLimits const* obj) { static void Pack_ODK_TimerLimits(Message* msg, ODK_TimerLimits const* obj) {
Pack_uint32_t(msg, &obj->soft_expiry); Pack_bool(msg, &obj->soft_enforce_rental_duration);
Pack_bool(msg, &obj->soft_enforce_playback_duration);
Pack_uint64_t(msg, &obj->earliest_playback_start_seconds); Pack_uint64_t(msg, &obj->earliest_playback_start_seconds);
Pack_uint64_t(msg, &obj->latest_playback_start_seconds); Pack_uint64_t(msg, &obj->rental_duration_seconds);
Pack_uint64_t(msg, &obj->initial_playback_duration_seconds); Pack_uint64_t(msg, &obj->total_playback_duration_seconds);
Pack_uint64_t(msg, &obj->renewal_playback_duration_seconds); Pack_uint64_t(msg, &obj->initial_renewal_duration_seconds);
Pack_uint64_t(msg, &obj->license_duration_seconds);
} }
static void Pack_ODK_ParsedLicense(Message* msg, ODK_ParsedLicense const* obj) { static void Pack_ODK_ParsedLicense(Message* msg, ODK_ParsedLicense const* obj) {
@@ -53,10 +54,9 @@ static void Pack_ODK_ParsedLicense(Message* msg, ODK_ParsedLicense const* obj) {
Pack_OEMCrypto_Substring(msg, &obj->enc_mac_keys); Pack_OEMCrypto_Substring(msg, &obj->enc_mac_keys);
Pack_OEMCrypto_Substring(msg, &obj->pst); Pack_OEMCrypto_Substring(msg, &obj->pst);
Pack_OEMCrypto_Substring(msg, &obj->srm_restriction_data); Pack_OEMCrypto_Substring(msg, &obj->srm_restriction_data);
Pack_uint32_t(msg, &obj->license_type); Pack_enum(msg, obj->license_type);
Pack_uint32_t(msg, &obj->nonce_required); Pack_bool(msg, &obj->nonce_required);
Pack_ODK_TimerLimits(msg, &obj->timer_limits); Pack_ODK_TimerLimits(msg, &obj->timer_limits);
PackArray(msg, &obj->request_hash[0], sizeof(obj->request_hash));
Pack_uint32_t(msg, &obj->key_array_length); Pack_uint32_t(msg, &obj->key_array_length);
size_t i; size_t i;
for (i = 0; i < (size_t)obj->key_array_length; i++) { for (i = 0; i < (size_t)obj->key_array_length; i++) {
@@ -66,7 +66,7 @@ static void Pack_ODK_ParsedLicense(Message* msg, ODK_ParsedLicense const* obj) {
static void Pack_ODK_ParsedProvisioning(Message* msg, static void Pack_ODK_ParsedProvisioning(Message* msg,
ODK_ParsedProvisioning const* obj) { ODK_ParsedProvisioning const* obj) {
Pack_uint32_t(msg, &obj->key_type); Pack_enum(msg, obj->key_type);
Pack_OEMCrypto_Substring(msg, &obj->enc_private_key); Pack_OEMCrypto_Substring(msg, &obj->enc_private_key);
Pack_OEMCrypto_Substring(msg, &obj->enc_private_key_iv); Pack_OEMCrypto_Substring(msg, &obj->enc_private_key_iv);
Pack_OEMCrypto_Substring(msg, &obj->encrypted_message_key); Pack_OEMCrypto_Substring(msg, &obj->encrypted_message_key);
@@ -74,17 +74,19 @@ static void Pack_ODK_ParsedProvisioning(Message* msg,
/* @@ odk serialize */ /* @@ odk serialize */
void Pack_ODK_PreparedLicense(Message* msg, ODK_PreparedLicense const* obj) { void Pack_ODK_PreparedLicenseRequest(Message* msg,
ODK_PreparedLicenseRequest const* obj) {
Pack_ODK_CoreMessage(msg, &obj->core_message); Pack_ODK_CoreMessage(msg, &obj->core_message);
} }
void Pack_ODK_RenewalMessage(Message* msg, ODK_RenewalMessage const* obj) { void Pack_ODK_PreparedRenewalRequest(Message* msg,
ODK_PreparedRenewalRequest const* obj) {
Pack_ODK_CoreMessage(msg, &obj->core_message); Pack_ODK_CoreMessage(msg, &obj->core_message);
Pack_uint64_t(msg, &obj->playback_time); Pack_uint64_t(msg, &obj->playback_time);
} }
void Pack_ODK_ProvisioningMessage(Message* msg, void Pack_ODK_PreparedProvisioningRequest(
ODK_ProvisioningMessage const* obj) { Message* msg, ODK_PreparedProvisioningRequest const* obj) {
Pack_ODK_CoreMessage(msg, &obj->core_message); Pack_ODK_CoreMessage(msg, &obj->core_message);
Pack_uint32_t(msg, &obj->device_id_length); Pack_uint32_t(msg, &obj->device_id_length);
PackArray(msg, &obj->device_id[0], sizeof(obj->device_id)); PackArray(msg, &obj->device_id[0], sizeof(obj->device_id));
@@ -93,13 +95,19 @@ void Pack_ODK_ProvisioningMessage(Message* msg,
/* @@ kdo serialize */ /* @@ kdo serialize */
void Pack_ODK_LicenseResponse(Message* msg, ODK_LicenseResponse const* obj) { void Pack_ODK_LicenseResponse(Message* msg, ODK_LicenseResponse const* obj) {
Pack_ODK_CoreMessage(msg, &obj->core_message); Pack_ODK_PreparedLicenseRequest(msg, &obj->request);
Pack_ODK_ParsedLicense(msg, (const ODK_ParsedLicense*)obj->parsed_license); Pack_ODK_ParsedLicense(msg, (const ODK_ParsedLicense*)obj->parsed_license);
PackArray(msg, &obj->request_hash[0], sizeof(obj->request_hash));
}
void Pack_ODK_RenewalResponse(Message* msg, ODK_RenewalResponse const* obj) {
Pack_ODK_PreparedRenewalRequest(msg, &obj->request);
Pack_uint64_t(msg, &obj->renewal_duration_seconds);
} }
void Pack_ODK_ProvisioningResponse(Message* msg, void Pack_ODK_ProvisioningResponse(Message* msg,
ODK_ProvisioningResponse const* obj) { ODK_ProvisioningResponse const* obj) {
Pack_ODK_ProvisioningMessage(msg, &obj->core_provisioning); Pack_ODK_PreparedProvisioningRequest(msg, &obj->request);
Pack_ODK_ParsedProvisioning( Pack_ODK_ParsedProvisioning(
msg, (const ODK_ParsedProvisioning*)obj->parsed_provisioning); msg, (const ODK_ParsedProvisioning*)obj->parsed_provisioning);
} }
@@ -109,7 +117,8 @@ void Pack_ODK_ProvisioningResponse(Message* msg,
/* @@ private deserialize */ /* @@ private deserialize */
static void Unpack_ODK_NonceValues(Message* msg, ODK_NonceValues* obj) { static void Unpack_ODK_NonceValues(Message* msg, ODK_NonceValues* obj) {
Unpack_uint32_t(msg, &obj->api_version); Unpack_uint16_t(msg, &obj->api_minor_version);
Unpack_uint16_t(msg, &obj->api_major_version);
Unpack_uint32_t(msg, &obj->nonce); Unpack_uint32_t(msg, &obj->nonce);
Unpack_uint32_t(msg, &obj->session_id); Unpack_uint32_t(msg, &obj->session_id);
} }
@@ -129,12 +138,12 @@ static void Unpack_OEMCrypto_KeyObject(Message* msg, OEMCrypto_KeyObject* obj) {
} }
static void Unpack_ODK_TimerLimits(Message* msg, ODK_TimerLimits* obj) { static void Unpack_ODK_TimerLimits(Message* msg, ODK_TimerLimits* obj) {
Unpack_uint32_t(msg, &obj->soft_expiry); Unpack_bool(msg, &obj->soft_enforce_rental_duration);
Unpack_bool(msg, &obj->soft_enforce_playback_duration);
Unpack_uint64_t(msg, &obj->earliest_playback_start_seconds); Unpack_uint64_t(msg, &obj->earliest_playback_start_seconds);
Unpack_uint64_t(msg, &obj->latest_playback_start_seconds); Unpack_uint64_t(msg, &obj->rental_duration_seconds);
Unpack_uint64_t(msg, &obj->initial_playback_duration_seconds); Unpack_uint64_t(msg, &obj->total_playback_duration_seconds);
Unpack_uint64_t(msg, &obj->renewal_playback_duration_seconds); Unpack_uint64_t(msg, &obj->initial_renewal_duration_seconds);
Unpack_uint64_t(msg, &obj->license_duration_seconds);
} }
static void Unpack_ODK_ParsedLicense(Message* msg, ODK_ParsedLicense* obj) { static void Unpack_ODK_ParsedLicense(Message* msg, ODK_ParsedLicense* obj) {
@@ -142,10 +151,9 @@ static void Unpack_ODK_ParsedLicense(Message* msg, ODK_ParsedLicense* obj) {
Unpack_OEMCrypto_Substring(msg, &obj->enc_mac_keys); Unpack_OEMCrypto_Substring(msg, &obj->enc_mac_keys);
Unpack_OEMCrypto_Substring(msg, &obj->pst); Unpack_OEMCrypto_Substring(msg, &obj->pst);
Unpack_OEMCrypto_Substring(msg, &obj->srm_restriction_data); Unpack_OEMCrypto_Substring(msg, &obj->srm_restriction_data);
Unpack_uint32_t(msg, &obj->license_type); obj->license_type = Unpack_enum(msg);
Unpack_uint32_t(msg, &obj->nonce_required); Unpack_bool(msg, &obj->nonce_required);
Unpack_ODK_TimerLimits(msg, &obj->timer_limits); Unpack_ODK_TimerLimits(msg, &obj->timer_limits);
UnpackArray(msg, &obj->request_hash[0], sizeof(obj->request_hash));
Unpack_uint32_t(msg, &obj->key_array_length); Unpack_uint32_t(msg, &obj->key_array_length);
if (obj->key_array_length > ODK_MAX_NUM_KEYS) { if (obj->key_array_length > ODK_MAX_NUM_KEYS) {
SetStatus(msg, MESSAGE_STATUS_OVERFLOW_ERROR); SetStatus(msg, MESSAGE_STATUS_OVERFLOW_ERROR);
@@ -159,7 +167,7 @@ static void Unpack_ODK_ParsedLicense(Message* msg, ODK_ParsedLicense* obj) {
static void Unpack_ODK_ParsedProvisioning(Message* msg, static void Unpack_ODK_ParsedProvisioning(Message* msg,
ODK_ParsedProvisioning* obj) { ODK_ParsedProvisioning* obj) {
Unpack_uint32_t(msg, &obj->key_type); obj->key_type = Unpack_enum(msg);
Unpack_OEMCrypto_Substring(msg, &obj->enc_private_key); Unpack_OEMCrypto_Substring(msg, &obj->enc_private_key);
Unpack_OEMCrypto_Substring(msg, &obj->enc_private_key_iv); Unpack_OEMCrypto_Substring(msg, &obj->enc_private_key_iv);
Unpack_OEMCrypto_Substring(msg, &obj->encrypted_message_key); Unpack_OEMCrypto_Substring(msg, &obj->encrypted_message_key);
@@ -167,12 +175,19 @@ static void Unpack_ODK_ParsedProvisioning(Message* msg,
/* @ kdo deserialize */ /* @ kdo deserialize */
void Unpack_ODK_PreparedLicense(Message* msg, ODK_PreparedLicense* obj) { void Unpack_ODK_PreparedLicenseRequest(Message* msg,
ODK_PreparedLicenseRequest* obj) {
Unpack_ODK_CoreMessage(msg, &obj->core_message); Unpack_ODK_CoreMessage(msg, &obj->core_message);
} }
void Unpack_ODK_ProvisioningMessage(Message* msg, void Unpack_ODK_PreparedRenewalRequest(Message* msg,
ODK_ProvisioningMessage* obj) { ODK_PreparedRenewalRequest* obj) {
Unpack_ODK_CoreMessage(msg, &obj->core_message);
Unpack_uint64_t(msg, &obj->playback_time);
}
void Unpack_ODK_PreparedProvisioningRequest(
Message* msg, ODK_PreparedProvisioningRequest* obj) {
Unpack_ODK_CoreMessage(msg, &obj->core_message); Unpack_ODK_CoreMessage(msg, &obj->core_message);
Unpack_uint32_t(msg, &obj->device_id_length); Unpack_uint32_t(msg, &obj->device_id_length);
UnpackArray(msg, &obj->device_id[0], sizeof(obj->device_id)); UnpackArray(msg, &obj->device_id[0], sizeof(obj->device_id));
@@ -181,17 +196,18 @@ void Unpack_ODK_ProvisioningMessage(Message* msg,
/* @@ odk deserialize */ /* @@ odk deserialize */
void Unpack_ODK_LicenseResponse(Message* msg, ODK_LicenseResponse* obj) { void Unpack_ODK_LicenseResponse(Message* msg, ODK_LicenseResponse* obj) {
Unpack_ODK_CoreMessage(msg, &obj->core_message); Unpack_ODK_PreparedLicenseRequest(msg, &obj->request);
Unpack_ODK_ParsedLicense(msg, obj->parsed_license); Unpack_ODK_ParsedLicense(msg, obj->parsed_license);
UnpackArray(msg, &obj->request_hash[0], sizeof(obj->request_hash));
} }
void Unpack_ODK_RenewalMessage(Message* msg, ODK_RenewalMessage* obj) { void Unpack_ODK_RenewalResponse(Message* msg, ODK_RenewalResponse* obj) {
Unpack_ODK_CoreMessage(msg, &obj->core_message); Unpack_ODK_PreparedRenewalRequest(msg, &obj->request);
Unpack_uint64_t(msg, &obj->playback_time); Unpack_uint64_t(msg, &obj->renewal_duration_seconds);
} }
void Unpack_ODK_ProvisioningResponse(Message* msg, void Unpack_ODK_ProvisioningResponse(Message* msg,
ODK_ProvisioningResponse* obj) { ODK_ProvisioningResponse* obj) {
Unpack_ODK_ProvisioningMessage(msg, &obj->core_provisioning); Unpack_ODK_PreparedProvisioningRequest(msg, &obj->request);
Unpack_ODK_ParsedProvisioning(msg, obj->parsed_provisioning); Unpack_ODK_ParsedProvisioning(msg, obj->parsed_provisioning);
} }

View File

@@ -16,25 +16,32 @@ extern "C" {
#endif #endif
/* odk pack */ /* odk pack */
void Pack_ODK_PreparedLicense(Message* msg, ODK_PreparedLicense const* obj); void Pack_ODK_PreparedLicenseRequest(Message* msg,
void Pack_ODK_RenewalMessage(Message* msg, ODK_RenewalMessage const* obj); const ODK_PreparedLicenseRequest* obj);
void Pack_ODK_ProvisioningMessage(Message* msg, void Pack_ODK_PreparedRenewalRequest(Message* msg,
ODK_ProvisioningMessage const* obj); const ODK_PreparedRenewalRequest* obj);
void Pack_ODK_PreparedProvisioningRequest(
Message* msg, const ODK_PreparedProvisioningRequest* obj);
/* odk unpack */ /* odk unpack */
void Unpack_ODK_LicenseResponse(Message* msg, ODK_LicenseResponse* obj); void Unpack_ODK_LicenseResponse(Message* msg, ODK_LicenseResponse* obj);
void Unpack_ODK_RenewalMessage(Message* msg, ODK_RenewalMessage* obj); void Unpack_ODK_RenewalResponse(Message* msg, ODK_RenewalResponse* obj);
void Unpack_ODK_ProvisioningResponse(Message* msg, void Unpack_ODK_ProvisioningResponse(Message* msg,
ODK_ProvisioningResponse* obj); ODK_ProvisioningResponse* obj);
/* kdo pack */ /* kdo pack */
void Pack_ODK_LicenseResponse(Message* msg, ODK_LicenseResponse const* obj); void Pack_ODK_LicenseResponse(Message* msg, const ODK_LicenseResponse* obj);
void Pack_ODK_RenewalResponse(Message* msg, const ODK_RenewalResponse* obj);
void Pack_ODK_ProvisioningResponse(Message* msg, void Pack_ODK_ProvisioningResponse(Message* msg,
ODK_ProvisioningResponse const* obj); const ODK_ProvisioningResponse* obj);
/* kdo unpack */ /* kdo unpack */
void Unpack_ODK_PreparedLicense(Message* msg, ODK_PreparedLicense* obj); void Unpack_ODK_PreparedLicenseRequest(Message* msg,
void Unpack_ODK_ProvisioningMessage(Message* msg, ODK_ProvisioningMessage* obj); ODK_PreparedLicenseRequest* obj);
void Unpack_ODK_PreparedRenewalRequest(Message* msg,
ODK_PreparedRenewalRequest* obj);
void Unpack_ODK_PreparedProvisioningRequest(
Message* msg, ODK_PreparedProvisioningRequest* obj);
#ifdef __cplusplus #ifdef __cplusplus
} /* extern "C" */ } /* extern "C" */

View File

@@ -10,6 +10,10 @@
#include "OEMCryptoCENCCommon.h" #include "OEMCryptoCENCCommon.h"
#include "odk_structs.h" #include "odk_structs.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef enum { typedef enum {
ODK_License_Request_Type = 1, ODK_License_Request_Type = 1,
ODK_License_Response_Type = 2, ODK_License_Response_Type = 2,
@@ -27,27 +31,61 @@ typedef struct {
typedef struct { typedef struct {
ODK_CoreMessage core_message; ODK_CoreMessage core_message;
} ODK_PreparedLicense; } ODK_PreparedLicenseRequest;
typedef struct { typedef struct {
ODK_CoreMessage core_message; ODK_CoreMessage core_message;
uint64_t playback_time; uint64_t playback_time;
} ODK_RenewalMessage; } ODK_PreparedRenewalRequest;
typedef struct { typedef struct {
ODK_CoreMessage core_message; ODK_CoreMessage core_message;
uint32_t device_id_length; uint32_t device_id_length;
uint8_t device_id[ODK_DEVICE_ID_LEN_MAX]; uint8_t device_id[ODK_DEVICE_ID_LEN_MAX];
} ODK_ProvisioningMessage; } ODK_PreparedProvisioningRequest;
typedef struct { typedef struct {
ODK_CoreMessage core_message; ODK_PreparedLicenseRequest request;
ODK_ParsedLicense* parsed_license; ODK_ParsedLicense* parsed_license;
uint8_t request_hash[ODK_SHA256_HASH_SIZE];
} ODK_LicenseResponse; } ODK_LicenseResponse;
typedef struct { typedef struct {
ODK_ProvisioningMessage core_provisioning; ODK_PreparedRenewalRequest request;
uint64_t renewal_duration_seconds;
} ODK_RenewalResponse;
typedef struct {
ODK_PreparedProvisioningRequest request;
ODK_ParsedProvisioning* parsed_provisioning; ODK_ParsedProvisioning* parsed_provisioning;
} ODK_ProvisioningResponse; } ODK_ProvisioningResponse;
/* These are the possible timer status values. */
#define ODK_CLOCK_TIMER_STATUS_UNDEFINED 0 /* Should not happen. */
/* When the structure has been initialized, but no license is loaded. */
#define ODK_CLOCK_TIMER_STATUS_LICENSE_NOT_LOADED 1
/* After the license is loaded, before a successful decrypt. */
#define ODK_CLOCK_TIMER_STATUS_LICENSE_LOADED 2
/* After the license is loaded, if a renewal has also been loaded. */
#define ODK_CLOCK_TIMER_STATUS_RENEWAL_LOADED 3
/* The first decrypt has occurred and the timer is active. */
#define ODK_CLOCK_TIMER_STATUS_ACTIVE 4
/* The first decrypt has occurred and the timer is unlimited. */
#define ODK_CLOCK_TIMER_STATUS_UNLIMITED 5
/* The timer has transitioned from active to expired. */
#define ODK_CLOCK_TIMER_STATUS_EXPIRED 6
/* The license has been marked as inactive. */
#define ODK_CLOCK_TIMER_STATUS_LICENSE_INACTIVE 7
/* A helper function for computing timer limits when a renewal is loaded. */
OEMCryptoResult ODK_ComputeRenewalDuration(const ODK_TimerLimits* timer_limits,
ODK_ClockValues* clock_values,
uint64_t system_time_seconds,
uint64_t new_renewal_duration,
uint64_t* timer_value);
#ifdef __cplusplus
}
#endif
#endif /* WIDEVINE_ODK_SRC_ODK_STRUCTS_PRIV_H_ */ #endif /* WIDEVINE_ODK_SRC_ODK_STRUCTS_PRIV_H_ */

View File

@@ -6,41 +6,279 @@
#include <string.h> #include <string.h>
#include "odk.h" #include "odk.h"
#include "odk_overflow.h"
#include "odk_structs_priv.h"
/* Private function. Checks to see if the license is active. Returns
* ODK_TIMER_EXPIRED if the license is valid but inactive. Returns
* OEMCrypto_SUCCESS if the license is active. Returns
* OEMCrypto_ERROR_UNKNOWN_FAILURE on other errors. This also updates the
* timer_status if appropriate. */
static OEMCryptoResult ODK_LicenseActive(const ODK_TimerLimits* timer_limits,
ODK_ClockValues* clock_values) {
/* Check some basic errors. */
if (clock_values == NULL || timer_limits == NULL) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
/* Check if the license has not been loaded yet. */
if (clock_values->timer_status == ODK_CLOCK_TIMER_STATUS_UNDEFINED ||
clock_values->timer_status == ODK_CLOCK_TIMER_STATUS_LICENSE_NOT_LOADED) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (clock_values->status > kActive) {
clock_values->timer_status = ODK_CLOCK_TIMER_STATUS_LICENSE_INACTIVE;
return ODK_TIMER_EXPIRED;
}
return OEMCrypto_SUCCESS;
}
/* Private function. Sets the timer_value to be the min(timer_value, new_value),
* with the convention that 0 means infinite. The convention that 0 means
* infinite is used for all Widevine license and duration values. */
static void ComputeMinimum(uint64_t* timer_value, uint64_t new_value) {
if (timer_value == NULL) return;
if (new_value > 0) {
if (*timer_value == 0 || *timer_value > new_value) {
*timer_value = new_value;
}
}
}
/* Private function. Check to see if the rental window restricts playback. If
* the rental enforcement is hard, or if this is the first playback, then we
* verify that system_time_seconds is within the rental window. If the
* enforcement is soft and we have already started playback, then there is no
* restriction.
* Return ODK_TIMER_EXPIRED if out of the window.
* Return ODK_TIMER_ACTIVE if within the window, and there is a hard limit.
* Return ODK_DISABLE_TIMER if no there should be no limit.
* Return other error on error.
* Also, if this function does compute a limit, the timer_value is reduced to
* obey that limit. If the limit is less restrictive than the current
* timer_value, then timer_value is not changed. */
static OEMCryptoResult ODK_CheckRentalWindow(
const ODK_TimerLimits* timer_limits, ODK_ClockValues* clock_values,
uint64_t system_time_seconds, uint64_t* timer_value) {
if (clock_values == NULL || timer_limits == NULL || timer_value == NULL) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
/* If playback has already started, and rental duration enforcement is soft,
* then there is no restriction. */
if (clock_values->time_of_first_decrypt > 0 &&
timer_limits->soft_enforce_rental_duration) {
return ODK_DISABLE_TIMER;
}
/* rental_clock = time since license signed. */
uint64_t rental_clock = 0;
if (odk_sub_overflow_u64(system_time_seconds,
clock_values->time_of_license_signed,
&rental_clock)) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
/* Check if it is before license is valid. This is an unusual case. First
* playback may still work if it occurs after the rental window opens. */
if (rental_clock < timer_limits->earliest_playback_start_seconds) {
return ODK_TIMER_EXPIRED;
}
/* If the rental duration is 0, there is no limit. */
if (timer_limits->rental_duration_seconds == 0) {
return ODK_DISABLE_TIMER;
}
/* End of rental window, based on rental clock (not system time). */
uint64_t end_of_rental_window = 0;
if (odk_add_overflow_u64(timer_limits->earliest_playback_start_seconds,
timer_limits->rental_duration_seconds,
&end_of_rental_window)) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
if (end_of_rental_window <= rental_clock) {
return ODK_TIMER_EXPIRED;
}
/* At this point system_time is within the rental window. */
if (timer_limits->soft_enforce_rental_duration) {
/* For soft enforcement, we allow playback, and do not adjust the timer. */
return ODK_DISABLE_TIMER;
}
uint64_t time_left = 0;
if (odk_sub_overflow_u64(end_of_rental_window, rental_clock, &time_left)) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
ComputeMinimum(timer_value, time_left);
return ODK_SET_TIMER;
}
/* Private function. Check to see if the playback window restricts
* playback. This should only be called if playback has started, so that
* clock_values->time_of_first_decrypt is nonzero.
* Return ODK_TIMER_EXPIRED if out of the window.
* Return ODK_SET_TIMER if within the window, and there is a hard limit.
* Return ODK_DISABLE_TIMER if no limit.
* Return other error on error.
* Also, if this function does compute a limit, the timer_value is reduced to
* obey that limit. If the limit is less restrictive than the current
* timer_value, then timer_value is not changed. */
static OEMCryptoResult ODK_CheckPlaybackWindow(
const ODK_TimerLimits* timer_limits, ODK_ClockValues* clock_values,
uint64_t system_time_seconds, uint64_t* timer_value) {
if (clock_values == NULL || timer_limits == NULL || timer_value == NULL) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
/* if the playback duration is 0, there is no limit. */
if (timer_limits->total_playback_duration_seconds == 0) {
return ODK_DISABLE_TIMER;
}
uint64_t end_of_playback_window = 0;
if (odk_add_overflow_u64(timer_limits->total_playback_duration_seconds,
clock_values->time_of_first_decrypt,
&end_of_playback_window)) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
if (end_of_playback_window <= system_time_seconds) {
return ODK_TIMER_EXPIRED;
}
/* At this point, system_time is within the total playback window. */
if (timer_limits->soft_enforce_playback_duration) {
/* For soft enforcement, we allow playback, and do not adjust the timer. */
return ODK_DISABLE_TIMER;
}
uint64_t time_left = 0;
if (odk_sub_overflow_u64(end_of_playback_window, system_time_seconds,
&time_left)) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
ComputeMinimum(timer_value, time_left);
return ODK_SET_TIMER;
}
/* Update the timer status. If playback has already started, we use the given
* status. However, if playback has not yet started, then we expect a call to
* ODK_AttemptFirstPlayback in the future, and we need to signal to it that we
* have already computed the timer limit. */
static void ODK_UpdateTimerStatusForRenewal(ODK_ClockValues* clock_values,
uint32_t new_status) {
if (clock_values == NULL) return; /* should not happen. */
if (clock_values->timer_status == ODK_CLOCK_TIMER_STATUS_LICENSE_LOADED) {
/* Signal that the timer is already set. */
clock_values->timer_status = ODK_CLOCK_TIMER_STATUS_RENEWAL_LOADED;
} else {
clock_values->timer_status = new_status;
}
}
/* Private function, but accessed from odk.c so cannot be static. This checks to
* see if a renewal message should restart the playback timer and sets the value
* appropriately. */
OEMCryptoResult ODK_ComputeRenewalDuration(const ODK_TimerLimits* timer_limits,
ODK_ClockValues* clock_values,
uint64_t system_time_seconds,
uint64_t new_renewal_duration,
uint64_t* timer_value) {
if (timer_limits == NULL || clock_values == NULL)
return OEMCrypto_ERROR_INVALID_CONTEXT; /* should not happen. */
/* If this is before the license was signed, something is odd. Return an
* error. */
if (system_time_seconds < clock_values->time_of_license_signed)
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
const OEMCryptoResult license_status =
ODK_LicenseActive(timer_limits, clock_values);
/* If the license is not active, then we cannot renew the license. */
if (license_status != OEMCrypto_SUCCESS) return license_status;
/* We start with the new renewal duration as the new timer limit. */
uint64_t new_timer_value = new_renewal_duration;
/* Then we factor in the rental window restrictions. This might decrease
* new_timer_value. */
const OEMCryptoResult rental_status = ODK_CheckRentalWindow(
timer_limits, clock_values, system_time_seconds, &new_timer_value);
/* If the rental status forbids playback, then we're done. */
if ((rental_status != ODK_DISABLE_TIMER) && (rental_status != ODK_SET_TIMER))
return rental_status;
/* If playback has already started and it has hard enforcement, then check
* total playback window. */
if (clock_values->time_of_first_decrypt > 0 &&
!timer_limits->soft_enforce_playback_duration) {
/* This might decrease new_timer_value. */
const OEMCryptoResult playback_status = ODK_CheckPlaybackWindow(
timer_limits, clock_values, system_time_seconds, &new_timer_value);
/* If the timer limits forbid playback in the playback window, then we're
* done. */
if ((playback_status != ODK_DISABLE_TIMER) &&
(playback_status != ODK_SET_TIMER))
return playback_status;
}
/* If new_timer_value is infinite (represented by 0), then there are no
* limits, so we can return now. */
if (new_timer_value == 0) {
clock_values->time_when_timer_expires = 0;
ODK_UpdateTimerStatusForRenewal(clock_values,
ODK_CLOCK_TIMER_STATUS_UNLIMITED);
return ODK_DISABLE_TIMER;
}
/* If the caller gave us a pointer to store the new timer value. Fill it. */
if (timer_value != NULL) {
*timer_value = new_timer_value;
}
if (odk_add_overflow_u64(system_time_seconds, new_timer_value,
&clock_values->time_when_timer_expires)) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
ODK_UpdateTimerStatusForRenewal(clock_values, ODK_CLOCK_TIMER_STATUS_ACTIVE);
return ODK_SET_TIMER;
}
/************************************************************************/
/************************************************************************/
/* Public functions, declared in odk.h. */
/* This is called when OEMCrypto opens a new session. */
OEMCryptoResult ODK_InitializeSessionValues(ODK_TimerLimits* timer_limits, OEMCryptoResult ODK_InitializeSessionValues(ODK_TimerLimits* timer_limits,
ODK_ClockValues* clock_values, ODK_ClockValues* clock_values,
ODK_NonceValues* nonce_values, ODK_NonceValues* nonce_values,
uint32_t api_version, uint32_t api_major_version,
uint32_t session_id) { uint32_t session_id) {
if (clock_values == NULL || clock_values == NULL || nonce_values == NULL) if (clock_values == NULL || clock_values == NULL || nonce_values == NULL)
return OEMCrypto_ERROR_INVALID_CONTEXT; return OEMCrypto_ERROR_INVALID_CONTEXT;
timer_limits->soft_expiry = false; /* Check that the API version passed in from OEMCrypto matches the version of
* this ODK library. */
if (api_major_version != ODK_MAJOR_VERSION) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
timer_limits->soft_enforce_rental_duration = false;
timer_limits->soft_enforce_playback_duration = false;
timer_limits->earliest_playback_start_seconds = 0; timer_limits->earliest_playback_start_seconds = 0;
timer_limits->latest_playback_start_seconds = 0; timer_limits->rental_duration_seconds = 0;
timer_limits->initial_playback_duration_seconds = 0; timer_limits->total_playback_duration_seconds = 0;
timer_limits->renewal_playback_duration_seconds = 0; timer_limits->initial_renewal_duration_seconds = 0;
timer_limits->license_duration_seconds = 0;
clock_values->time_of_license_signed = 0; ODK_InitializeClockValues(clock_values, 0);
clock_values->time_of_first_decrypt = 0;
clock_values->time_of_last_decrypt = 0;
clock_values->time_when_timer_expires = 0;
clock_values->timer_status = 0;
clock_values->status = kUnused;
nonce_values->api_version = api_version; nonce_values->api_major_version = ODK_MAJOR_VERSION;
nonce_values->api_minor_version = ODK_MINOR_VERSION;
nonce_values->nonce = 0; nonce_values->nonce = 0;
nonce_values->session_id = session_id; nonce_values->session_id = session_id;
return OEMCrypto_SUCCESS; return OEMCrypto_SUCCESS;
} }
/* This is called when OEMCrypto generates a new nonce in
* OEMCrypto_GenerateNonce. */
OEMCryptoResult ODK_SetNonceValues(ODK_NonceValues* nonce_values, OEMCryptoResult ODK_SetNonceValues(ODK_NonceValues* nonce_values,
uint32_t nonce) { uint32_t nonce) {
if (nonce_values == NULL) return OEMCrypto_ERROR_INVALID_CONTEXT;
/* Setting the nonce should only happen once per session. */
if (nonce_values->nonce != 0) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
nonce_values->nonce = nonce; nonce_values->nonce = nonce;
return OEMCrypto_SUCCESS; return OEMCrypto_SUCCESS;
} }
/* This is called when OEMCrypto signs a license. */
OEMCryptoResult ODK_InitializeClockValues(ODK_ClockValues* clock_values, OEMCryptoResult ODK_InitializeClockValues(ODK_ClockValues* clock_values,
uint64_t system_time_seconds) { uint64_t system_time_seconds) {
if (clock_values == NULL) return OEMCrypto_ERROR_INVALID_CONTEXT; if (clock_values == NULL) return OEMCrypto_ERROR_INVALID_CONTEXT;
@@ -48,12 +286,12 @@ OEMCryptoResult ODK_InitializeClockValues(ODK_ClockValues* clock_values,
clock_values->time_of_first_decrypt = 0; clock_values->time_of_first_decrypt = 0;
clock_values->time_of_last_decrypt = 0; clock_values->time_of_last_decrypt = 0;
clock_values->time_when_timer_expires = 0; clock_values->time_when_timer_expires = 0;
/* TODO(b/142415188): document this. */ clock_values->timer_status = ODK_CLOCK_TIMER_STATUS_LICENSE_NOT_LOADED;
clock_values->timer_status = 0;
clock_values->status = kUnused; clock_values->status = kUnused;
return OEMCrypto_SUCCESS; return OEMCrypto_SUCCESS;
} }
/* This is called when OEMCrypto reloads a usage entry. */
OEMCryptoResult ODK_ReloadClockValues(ODK_ClockValues* clock_values, OEMCryptoResult ODK_ReloadClockValues(ODK_ClockValues* clock_values,
uint64_t time_of_license_signed, uint64_t time_of_license_signed,
uint64_t time_of_first_decrypt, uint64_t time_of_first_decrypt,
@@ -65,114 +303,84 @@ OEMCryptoResult ODK_ReloadClockValues(ODK_ClockValues* clock_values,
clock_values->time_of_first_decrypt = time_of_first_decrypt; clock_values->time_of_first_decrypt = time_of_first_decrypt;
clock_values->time_of_last_decrypt = time_of_last_decrypt; clock_values->time_of_last_decrypt = time_of_last_decrypt;
clock_values->time_when_timer_expires = 0; clock_values->time_when_timer_expires = 0;
clock_values->timer_status = 0; clock_values->timer_status = ODK_CLOCK_TIMER_STATUS_LICENSE_NOT_LOADED;
clock_values->status = status; clock_values->status = status;
return OEMCrypto_SUCCESS; return OEMCrypto_SUCCESS;
} }
/* This is called on the first playback for a session. */ /* This is called on the first playback for a session. */
uint32_t ODK_AttemptFirstPlayback(uint64_t system_time_seconds, OEMCryptoResult ODK_AttemptFirstPlayback(uint64_t system_time_seconds,
const ODK_TimerLimits* timer_limits, const ODK_TimerLimits* timer_limits,
ODK_ClockValues* clock_values, ODK_ClockValues* clock_values,
uint64_t* timer_value) { uint64_t* timer_value) {
if (clock_values == NULL || timer_limits == NULL) if (clock_values == NULL || timer_limits == NULL)
return OEMCrypto_ERROR_UNKNOWN_FAILURE; return OEMCrypto_ERROR_UNKNOWN_FAILURE;
/* All times are relative to when the license was signed. */ /* All times are relative to when the license was signed. */
const uint64_t rental_time = uint64_t rental_time = 0;
system_time_seconds - clock_values->time_of_license_signed; if (odk_sub_overflow_u64(system_time_seconds,
clock_values->time_of_license_signed,
&rental_time)) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
if (rental_time < timer_limits->earliest_playback_start_seconds) { if (rental_time < timer_limits->earliest_playback_start_seconds) {
clock_values->timer_status = ODK_TIMER_EXPIRED; clock_values->timer_status = ODK_TIMER_EXPIRED;
return ODK_TIMER_EXPIRED; return ODK_TIMER_EXPIRED;
} }
/* If the clock status is already marked as inactive, then playback is /* If the license is inactive or not loaded, then playback is not allowed. */
* not allowed. */ OEMCryptoResult status = ODK_LicenseActive(timer_limits, clock_values);
/* TODO(b/142415188): add helper function. */ if (status != OEMCrypto_SUCCESS) return status;
if (clock_values->status > kActive) {
clock_values->timer_status = ODK_TIMER_EXPIRED; /* We start with the initial renewal duration as the timer limit. */
return ODK_TIMER_EXPIRED; uint64_t new_timer_value = timer_limits->initial_renewal_duration_seconds;
} /* However, if a renewal was loaded before this first playback, use the
/* If this license is still inactive (never used) then we just look at the * previously computed limit. */
* rental window. This is the first playback for the license, not just this if (clock_values->timer_status == ODK_CLOCK_TIMER_STATUS_RENEWAL_LOADED) {
* session. */ if (clock_values->time_when_timer_expires <= system_time_seconds) {
if (clock_values->status == kUnused) {
/* If the rental clock has expired, the license has expired. */
if (rental_time > timer_limits->latest_playback_start_seconds &&
timer_limits->latest_playback_start_seconds > 0) {
clock_values->timer_status = ODK_TIMER_EXPIRED;
return ODK_TIMER_EXPIRED; return ODK_TIMER_EXPIRED;
} }
/* The timer should be limited by the playback duration. */ if (odk_sub_overflow_u64(clock_values->time_when_timer_expires,
uint64_t time_left = timer_limits->initial_playback_duration_seconds; system_time_seconds, &new_timer_value)) {
/* If there is a license duration, it also limits the timer. Remeber, a return OEMCrypto_ERROR_INVALID_CONTEXT;
* limit of 0 means no limit, or infinite. */
if (timer_limits->license_duration_seconds > 0) {
if (timer_limits->license_duration_seconds < rental_time) {
/* If the license duration has expired. This is unusual, because this
* can only happen if the license duration is less than the rental
* window. */
clock_values->timer_status = ODK_TIMER_EXPIRED;
return ODK_TIMER_EXPIRED;
}
if (timer_limits->license_duration_seconds - rental_time < time_left ||
time_left == 0) {
time_left = timer_limits->license_duration_seconds - rental_time;
}
} }
/* This is a new license, and we can start playback. */ }
clock_values->status = kActive;
/* Then we factor in the rental window restrictions. This might decrease
* new_timer_value. */
status = ODK_CheckRentalWindow(timer_limits, clock_values,
system_time_seconds, &new_timer_value);
if ((status != ODK_DISABLE_TIMER) && (status != ODK_SET_TIMER)) return status;
/* If playback has not already started, then this is the first playback. */
if (clock_values->time_of_first_decrypt == 0) {
clock_values->time_of_first_decrypt = system_time_seconds; clock_values->time_of_first_decrypt = system_time_seconds;
clock_values->time_of_last_decrypt = system_time_seconds; clock_values->status = kActive;
if (time_left == 0 || timer_limits->soft_expiry) { /* Unlimited. */
clock_values->time_when_timer_expires = 0;
clock_values->timer_status = ODK_DISABLE_TIMER;
return ODK_DISABLE_TIMER;
}
/* Set timer to limit playback. */
if (timer_value) *timer_value = time_left;
clock_values->time_when_timer_expires = system_time_seconds + time_left;
clock_values->timer_status = ODK_SET_TIMER;
return ODK_SET_TIMER;
} }
/* Otherwise, this is the second loading of a persistent license. In this
* case, we ignore the rental window. */ /* Similar to the rental window, we check the playback window
const uint64_t time_since_first_decrypt = * restrictions. This might decrease new_timer_value. */
system_time_seconds - clock_values->time_of_first_decrypt; status = ODK_CheckPlaybackWindow(timer_limits, clock_values,
uint64_t time_left = 0; system_time_seconds, &new_timer_value);
/* If there is an initial playback duration, the we use that as a limit. if ((status != ODK_DISABLE_TIMER) && (status != ODK_SET_TIMER)) return status;
* This ignores any license renewals. If renewals are allowed, then the last
* one can be reloaded to reset the timer. */ /* We know we are allowed to decrypt. The rest computes the timer duration. */
if (timer_limits->initial_playback_duration_seconds > 0) {
if (timer_limits->initial_playback_duration_seconds <=
time_since_first_decrypt) {
clock_values->timer_status = ODK_TIMER_EXPIRED;
return ODK_TIMER_EXPIRED;
}
time_left = timer_limits->initial_playback_duration_seconds -
time_since_first_decrypt;
}
/* If there is a license duration, it also limits the timer. */
if (timer_limits->license_duration_seconds > 0) {
if (timer_limits->license_duration_seconds < rental_time) {
/* The license duration has expired. */
clock_values->timer_status = ODK_TIMER_EXPIRED;
return ODK_TIMER_EXPIRED;
}
if (timer_limits->license_duration_seconds - rental_time < time_left ||
time_left == 0) {
time_left = timer_limits->license_duration_seconds - rental_time;
}
}
/* We can restart playback for this license. Update last playback time. */
clock_values->time_of_last_decrypt = system_time_seconds; clock_values->time_of_last_decrypt = system_time_seconds;
if (time_left == 0 || timer_limits->soft_expiry) { /* Unlimited. */
/* If new_timer_value is infinite (represented by 0), then there are no
* limits, so we can return now. */
if (new_timer_value == 0) {
clock_values->time_when_timer_expires = 0; clock_values->time_when_timer_expires = 0;
clock_values->timer_status = ODK_DISABLE_TIMER; clock_values->timer_status = ODK_CLOCK_TIMER_STATUS_UNLIMITED;
return ODK_DISABLE_TIMER; return ODK_DISABLE_TIMER;
} }
/* Set timer. */ /* If the caller gave us a pointer to store the new timer value. Fill it. */
if (timer_value) *timer_value = time_left; if (timer_value) {
clock_values->time_when_timer_expires = system_time_seconds + time_left; *timer_value = new_timer_value;
clock_values->timer_status = ODK_SET_TIMER; }
if (odk_add_overflow_u64(system_time_seconds, new_timer_value,
&clock_values->time_when_timer_expires)) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
clock_values->timer_status = ODK_CLOCK_TIMER_STATUS_ACTIVE;
return ODK_SET_TIMER; return ODK_SET_TIMER;
} }
@@ -181,27 +389,29 @@ uint32_t ODK_AttemptFirstPlayback(uint64_t system_time_seconds,
OEMCryptoResult ODK_UpdateLastPlaybackTime(uint64_t system_time_seconds, OEMCryptoResult ODK_UpdateLastPlaybackTime(uint64_t system_time_seconds,
const ODK_TimerLimits* timer_limits, const ODK_TimerLimits* timer_limits,
ODK_ClockValues* clock_values) { ODK_ClockValues* clock_values) {
if (clock_values == NULL || timer_limits == NULL) OEMCryptoResult status = ODK_LicenseActive(timer_limits, clock_values);
return OEMCrypto_ERROR_UNKNOWN_FAILURE; if (status != OEMCrypto_SUCCESS) return status;
if (clock_values->timer_status == ODK_TIMER_EXPIRED) { switch (clock_values->timer_status) {
return ODK_TIMER_EXPIRED; case ODK_CLOCK_TIMER_STATUS_UNLIMITED:
} break;
/* If the clock status is already marked as inactive, then playback is case ODK_CLOCK_TIMER_STATUS_ACTIVE:
* not allowed. */ /* Note: we allow playback at the time when the timer expires, but not
/* TODO(b/142415188): add helper function. */ * after. This is not important for business cases, but it makes it
if (clock_values->status > kActive) { * easier to write tests. */
clock_values->timer_status = ODK_TIMER_EXPIRED; if (clock_values->time_when_timer_expires > 0 &&
return ODK_TIMER_EXPIRED; system_time_seconds > clock_values->time_when_timer_expires) {
} clock_values->timer_status = ODK_CLOCK_TIMER_STATUS_EXPIRED;
if (clock_values->time_when_timer_expires > 0 && return ODK_TIMER_EXPIRED;
system_time_seconds > clock_values->time_when_timer_expires) { }
clock_values->timer_status = ODK_TIMER_EXPIRED; break;
return ODK_TIMER_EXPIRED; default: /* Expired, error state, or never started. */
return ODK_TIMER_EXPIRED;
} }
clock_values->time_of_last_decrypt = system_time_seconds; clock_values->time_of_last_decrypt = system_time_seconds;
return OEMCrypto_SUCCESS; return OEMCrypto_SUCCESS;
} }
/* This is called from OEMCrypto_DeactivateUsageEntry. */
OEMCryptoResult ODK_DeactivateUsageEntry(ODK_ClockValues* clock_values) { OEMCryptoResult ODK_DeactivateUsageEntry(ODK_ClockValues* clock_values) {
if (clock_values == NULL) return OEMCrypto_ERROR_UNKNOWN_FAILURE; if (clock_values == NULL) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
if (clock_values->status == kUnused) { if (clock_values->status == kUnused) {
@@ -209,9 +419,12 @@ OEMCryptoResult ODK_DeactivateUsageEntry(ODK_ClockValues* clock_values) {
} else if (clock_values->status == kActive) { } else if (clock_values->status == kActive) {
clock_values->status = kInactiveUsed; clock_values->status = kInactiveUsed;
} }
clock_values->timer_status = ODK_CLOCK_TIMER_STATUS_LICENSE_INACTIVE;
return OEMCrypto_SUCCESS; return OEMCrypto_SUCCESS;
} }
/* This is called when OEMCrypto loads a legacy v15 license, from
* OEMCrypto_LoadKeys. */
OEMCryptoResult ODK_InitializeV15Values(ODK_TimerLimits* timer_limits, OEMCryptoResult ODK_InitializeV15Values(ODK_TimerLimits* timer_limits,
ODK_ClockValues* clock_values, ODK_ClockValues* clock_values,
ODK_NonceValues* nonce_values, ODK_NonceValues* nonce_values,
@@ -219,67 +432,41 @@ OEMCryptoResult ODK_InitializeV15Values(ODK_TimerLimits* timer_limits,
uint64_t system_time_seconds) { uint64_t system_time_seconds) {
if (clock_values == NULL || clock_values == NULL || nonce_values == NULL) if (clock_values == NULL || clock_values == NULL || nonce_values == NULL)
return OEMCrypto_ERROR_INVALID_CONTEXT; return OEMCrypto_ERROR_INVALID_CONTEXT;
timer_limits->soft_expiry = false; timer_limits->soft_enforce_playback_duration = false;
timer_limits->soft_enforce_rental_duration = false;
timer_limits->earliest_playback_start_seconds = 0; timer_limits->earliest_playback_start_seconds = 0;
timer_limits->latest_playback_start_seconds = 0; timer_limits->rental_duration_seconds = 0;
timer_limits->initial_playback_duration_seconds = key_duration; timer_limits->total_playback_duration_seconds = 0;
timer_limits->renewal_playback_duration_seconds = key_duration; timer_limits->initial_renewal_duration_seconds = key_duration;
timer_limits->license_duration_seconds = 0;
nonce_values->api_version = 15; nonce_values->api_major_version = 15;
nonce_values->api_minor_version = 0;
if (key_duration > 0) { if (key_duration > 0) {
clock_values->time_when_timer_expires = system_time_seconds + key_duration; clock_values->time_when_timer_expires = system_time_seconds + key_duration;
} else { } else {
clock_values->time_when_timer_expires = 0; clock_values->time_when_timer_expires = 0;
} }
clock_values->timer_status = ODK_CLOCK_TIMER_STATUS_LICENSE_LOADED;
return OEMCrypto_SUCCESS; return OEMCrypto_SUCCESS;
} }
/* This is called when OEMCrypto loads a legacy license renewal in
* OEMCrypto_RefreshKeys. */
OEMCryptoResult ODK_RefreshV15Values(const ODK_TimerLimits* timer_limits, OEMCryptoResult ODK_RefreshV15Values(const ODK_TimerLimits* timer_limits,
ODK_ClockValues* clock_values, ODK_ClockValues* clock_values,
const ODK_NonceValues* nonce_values, const ODK_NonceValues* nonce_values,
uint64_t system_time_seconds, uint64_t system_time_seconds,
uint32_t new_key_duration,
uint64_t* timer_value) { uint64_t* timer_value) {
if (timer_limits == NULL || clock_values == NULL || nonce_values == NULL) if (timer_limits == NULL || clock_values == NULL || nonce_values == NULL)
return OEMCrypto_ERROR_INVALID_CONTEXT; return OEMCrypto_ERROR_INVALID_CONTEXT;
if (nonce_values->api_version != 15) return OEMCrypto_ERROR_INVALID_NONCE; if (nonce_values->api_major_version != 15)
return OEMCrypto_ERROR_INVALID_NONCE;
if (clock_values->status > kActive) { if (clock_values->status > kActive) {
clock_values->timer_status = ODK_TIMER_EXPIRED; clock_values->timer_status = ODK_TIMER_EXPIRED;
return ODK_TIMER_EXPIRED; return ODK_TIMER_EXPIRED;
} }
/* If this is before the license was signed, something is odd. Return an return ODK_ComputeRenewalDuration(timer_limits, clock_values,
* error. */ system_time_seconds, new_key_duration,
if (system_time_seconds < clock_values->time_of_license_signed) timer_value);
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
/* All times are relative to when the license was signed. */
const uint64_t rental_time =
system_time_seconds - clock_values->time_of_license_signed;
/* The timer should be limited by the renewal playback duration. This is
* similar to code in AttemptFirstPlayback, above, except we use the
* renewal_playback_duration here, and we do not change clock_values->status.
*/
uint64_t time_left = timer_limits->renewal_playback_duration_seconds;
/* If there is a license duration, it also limits the timer. Remember, a
* limit of 0 means no limit, or infinite. */
if (timer_limits->license_duration_seconds > 0) {
if (timer_limits->license_duration_seconds < rental_time) {
clock_values->timer_status = ODK_TIMER_EXPIRED;
return ODK_TIMER_EXPIRED;
}
if (timer_limits->license_duration_seconds - rental_time < time_left ||
time_left == 0) {
time_left = timer_limits->license_duration_seconds - rental_time;
}
}
if (time_left == 0 || timer_limits->soft_expiry) { /* Unlimited. */
clock_values->time_when_timer_expires = 0;
clock_values->timer_status = ODK_DISABLE_TIMER;
return ODK_DISABLE_TIMER;
}
/* Set timer to limit playback. */
if (timer_value) *timer_value = time_left;
clock_values->time_when_timer_expires = system_time_seconds + time_left;
clock_values->timer_status = ODK_SET_TIMER;
return ODK_SET_TIMER;
} }

View File

@@ -0,0 +1,25 @@
/* Copyright 2019 Google LLC. All rights reserved. This file and proprietary */
/* source code may only be used and distributed under the Widevine Master */
/* License Agreement. */
#include "odk_util.h"
int crypto_memcmp(const void* in_a, const void* in_b, size_t len) {
if (len == 0) {
return 0;
}
/* Only valid pointers are allowed. */
if (in_a == NULL || in_b == NULL) {
return -1;
}
const uint8_t* a = in_a;
const uint8_t* b = in_b;
uint8_t x = 0;
for (size_t i = 0; i < len; i++) {
x |= a[i] ^ b[i];
}
return x;
}

View File

@@ -0,0 +1,24 @@
/* Copyright 2019 Google LLC. All rights reserved. This file and proprietary */
/* source code may only be used and distributed under the Widevine Master */
/* License Agreement. */
#ifndef WIDEVINE_ODK_SRC_ODK_UTIL_H_
#define WIDEVINE_ODK_SRC_ODK_UTIL_H_
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/* crypto_memcmp returns zero iff the |len| bytes at |a| and |b| are equal. It
* takes an amount of time dependent on |len|, but independent of the contents
* of |a| and |b|. Unlike memcmp, it cannot be used to order elements as the
* return value when a != b is undefined, other than being non-zero. */
int crypto_memcmp(const void* a, const void* b, size_t len);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* WIDEVINE_ODK_SRC_ODK_UTIL_H_ */

View File

@@ -51,6 +51,26 @@ static void PackBytes(Message* message, const uint8_t* ptr, size_t count) {
} }
} }
void Pack_enum(Message* message, int value) {
uint32_t v32 = value;
Pack_uint32_t(message, &v32);
}
void Pack_bool(Message* message, const bool* value) {
if (!ValidMessage(message)) return;
uint8_t data[4] = {0};
data[3] = *value ? 1 : 0;
PackBytes(message, data, sizeof(data));
}
void Pack_uint16_t(Message* message, const uint16_t* value) {
if (!ValidMessage(message)) return;
uint8_t data[2] = {0};
data[0] = *value >> 8;
data[1] = *value >> 0;
PackBytes(message, data, sizeof(data));
}
void Pack_uint32_t(Message* message, const uint32_t* value) { void Pack_uint32_t(Message* message, const uint32_t* value) {
if (!ValidMessage(message)) return; if (!ValidMessage(message)) return;
uint8_t data[4] = {0}; uint8_t data[4] = {0};
@@ -90,6 +110,27 @@ static void UnpackBytes(Message* message, uint8_t* ptr, size_t count) {
} }
} }
int Unpack_enum(Message* message) {
uint32_t v32;
Unpack_uint32_t(message, &v32);
return v32;
}
void Unpack_bool(Message* message, bool* value) {
if (!ValidMessage(message)) return;
uint8_t data[4] = {0};
UnpackBytes(message, data, sizeof(data));
*value = (0 != data[3]);
}
void Unpack_uint16_t(Message* message, uint16_t* value) {
if (!ValidMessage(message)) return;
uint8_t data[2] = {0};
UnpackBytes(message, data, sizeof(data));
*value = data[0];
*value = *value << 8 | data[1];
}
void Unpack_uint32_t(Message* message, uint32_t* value) { void Unpack_uint32_t(Message* message, uint32_t* value) {
if (!ValidMessage(message)) return; if (!ValidMessage(message)) return;
uint8_t data[4] = {0}; uint8_t data[4] = {0};
@@ -195,7 +236,8 @@ size_t GetSize(Message* message) {
void SetSize(Message* message, size_t size) { void SetSize(Message* message, size_t size) {
if (message == NULL) return; if (message == NULL) return;
if (size > message->capacity) message->status = MESSAGE_STATUS_OVERFLOW_ERROR; if (size > message->capacity) message->status = MESSAGE_STATUS_OVERFLOW_ERROR;
message->size = size; else
message->size = size;
} }
MessageStatus GetStatus(Message* message) { return message->status; } MessageStatus GetStatus(Message* message) { return message->status; }

View File

@@ -34,11 +34,17 @@ typedef struct _Message Message;
bool ValidMessage(Message* message); bool ValidMessage(Message* message);
void Pack_enum(Message* message, int value);
void Pack_bool(Message* message, const bool* value);
void Pack_uint16_t(Message* message, const uint16_t* value);
void Pack_uint32_t(Message* message, const uint32_t* value); void Pack_uint32_t(Message* message, const uint32_t* value);
void Pack_uint64_t(Message* message, const uint64_t* value); void Pack_uint64_t(Message* message, const uint64_t* value);
void PackArray(Message* message, const uint8_t* base, size_t size); void PackArray(Message* message, const uint8_t* base, size_t size);
void Pack_OEMCrypto_Substring(Message* msg, const OEMCrypto_Substring* obj); void Pack_OEMCrypto_Substring(Message* msg, const OEMCrypto_Substring* obj);
int Unpack_enum(Message* message);
void Unpack_bool(Message* message, bool* value);
void Unpack_uint16_t(Message* message, uint16_t* value);
void Unpack_uint32_t(Message* message, uint32_t* value); void Unpack_uint32_t(Message* message, uint32_t* value);
void Unpack_uint64_t(Message* message, uint64_t* value); void Unpack_uint64_t(Message* message, uint64_t* value);
void UnpackArray(Message* message, uint8_t* address, void UnpackArray(Message* message, uint8_t* address,

View File

@@ -115,7 +115,7 @@ static bool kdo_fun_LicenseResponse(const ODK_ParseLicense_Args* args,
static OEMCryptoResult odk_fun_RenewalResponse( static OEMCryptoResult odk_fun_RenewalResponse(
const uint8_t* buf, size_t len, uint32_t api_version, uint32_t nonce, const uint8_t* buf, size_t len, uint32_t api_version, uint32_t nonce,
uint32_t session_id, ODK_ParseRenewal_Args* a, uint32_t session_id, ODK_ParseRenewal_Args* a,
ODK_RenewalMessage& renewal_msg) { ODK_PreparedRenewalRequest& renewal_msg) {
uint64_t timer_value = 0; uint64_t timer_value = 0;
OEMCryptoResult err = OEMCryptoResult err =
ODK_ParseRenewal(buf, len, api_version, nonce, session_id, a->system_time, ODK_ParseRenewal(buf, len, api_version, nonce, session_id, a->system_time,
@@ -125,15 +125,16 @@ static OEMCryptoResult odk_fun_RenewalResponse(
AllocateMessage(&msg, message_block); AllocateMessage(&msg, message_block);
InitMessage(msg, const_cast<uint8_t*>(buf), len); InitMessage(msg, const_cast<uint8_t*>(buf), len);
SetSize(msg, len); SetSize(msg, len);
Unpack_ODK_RenewalMessage(msg, &renewal_msg); Unpack_ODK_PreparedRenewalRequest(msg, &renewal_msg);
assert(ValidMessage(msg)); assert(ValidMessage(msg));
} }
return err; return err;
} }
static bool kdo_fun_RenewalResponse(const ODK_ParseRenewal_Args* args, static bool kdo_fun_RenewalResponse(
const ODK_RenewalMessage& renewal_msg, const ODK_ParseRenewal_Args* args,
std::string* oemcrypto_core_message) { const ODK_PreparedRenewalRequest& renewal_msg,
std::string* oemcrypto_core_message) {
const auto& common = args->common; const auto& common = args->common;
ODK_RenewalRequest core_request{common.api_version, common.nonce, ODK_RenewalRequest core_request{common.api_version, common.nonce,
common.session_id, renewal_msg.playback_time}; common.session_id, renewal_msg.playback_time};
@@ -217,7 +218,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
odk_kdo<ODK_ParseLicense_Args, ODK_ParsedLicense>( odk_kdo<ODK_ParseLicense_Args, ODK_ParsedLicense>(
odk_fun_LicenseResponse, kdo_fun_LicenseResponse)); odk_fun_LicenseResponse, kdo_fun_LicenseResponse));
verify_roundtrip(data, size, verify_roundtrip(data, size,
odk_kdo<ODK_ParseRenewal_Args, ODK_RenewalMessage>( odk_kdo<ODK_ParseRenewal_Args, ODK_PreparedRenewalRequest>(
odk_fun_RenewalResponse, kdo_fun_RenewalResponse)); odk_fun_RenewalResponse, kdo_fun_RenewalResponse));
verify_roundtrip( verify_roundtrip(
data, size, data, size,

View File

@@ -4,6 +4,8 @@
#include "odk.h" #include "odk.h"
#include <endian.h> // TODO(b/147944591): use this one? Or odk_endian.h?
#include <cstdint> #include <cstdint>
#include <cstdio> #include <cstdio>
#include <cstdlib> #include <cstdlib>
@@ -37,6 +39,7 @@ using oemcrypto_core_message::serialize::CreateCoreProvisioningResponse;
using oemcrypto_core_message::serialize::CreateCoreRenewalResponse; using oemcrypto_core_message::serialize::CreateCoreRenewalResponse;
enum ODK_FieldType { enum ODK_FieldType {
ODK_UINT16,
ODK_UINT32, ODK_UINT32,
ODK_UINT64, ODK_UINT64,
ODK_SUBSTRING, ODK_SUBSTRING,
@@ -59,6 +62,8 @@ struct ODK_Field {
size_t ODK_FieldLength(ODK_FieldType type) { size_t ODK_FieldLength(ODK_FieldType type) {
switch (type) { switch (type) {
case ODK_UINT16:
return sizeof(uint16_t);
case ODK_UINT32: case ODK_UINT32:
return sizeof(uint32_t); return sizeof(uint32_t);
case ODK_UINT64: case ODK_UINT64:
@@ -87,6 +92,11 @@ OEMCryptoResult ODK_WriteSingleField(uint8_t* const buf,
return ODK_ERROR_CORE_MESSAGE; return ODK_ERROR_CORE_MESSAGE;
} }
switch (field->type) { switch (field->type) {
case ODK_UINT16: {
uint16_t u16 = htobe16(*static_cast<uint16_t*>(field->value));
memcpy(buf, &u16, sizeof(u16));
break;
}
case ODK_UINT32: { case ODK_UINT32: {
uint32_t u32 = htobe32(*static_cast<uint32_t*>(field->value)); uint32_t u32 = htobe32(*static_cast<uint32_t*>(field->value));
memcpy(buf, &u32, sizeof(u32)); memcpy(buf, &u32, sizeof(u32));
@@ -125,6 +135,12 @@ OEMCryptoResult ODK_ReadSingleField(const uint8_t* const buf,
return ODK_ERROR_CORE_MESSAGE; return ODK_ERROR_CORE_MESSAGE;
} }
switch (field->type) { switch (field->type) {
case ODK_UINT16: {
memcpy(field->value, buf, sizeof(uint16_t));
uint16_t* u16p = static_cast<uint16_t*>(field->value);
*u16p = be16toh(*u16p);
break;
}
case ODK_UINT32: { case ODK_UINT32: {
memcpy(field->value, buf, sizeof(uint32_t)); memcpy(field->value, buf, sizeof(uint32_t));
uint32_t* u32p = static_cast<uint32_t*>(field->value); uint32_t* u32p = static_cast<uint32_t*>(field->value);
@@ -166,6 +182,14 @@ OEMCryptoResult ODK_DumpSingleField(const uint8_t* const buf,
return ODK_ERROR_CORE_MESSAGE; return ODK_ERROR_CORE_MESSAGE;
} }
switch (field->type) { switch (field->type) {
case ODK_UINT16: {
uint16_t val;
memcpy(&val, buf, sizeof(uint16_t));
val = be16toh(val);
std::cerr << field->name << ": " << val << " = 0x" << std::hex << val
<< "\n";
break;
}
case ODK_UINT32: { case ODK_UINT32: {
uint32_t val; uint32_t val;
memcpy(&val, buf, sizeof(uint32_t)); memcpy(&val, buf, sizeof(uint32_t));
@@ -230,14 +254,18 @@ OEMCryptoResult ODK_IterFields(ODK_FieldMode mode, uint8_t* const buf,
return ODK_ERROR_CORE_MESSAGE; return ODK_ERROR_CORE_MESSAGE;
} }
uint8_t* const buf_off = buf + off; uint8_t* const buf_off = buf + off;
if (mode == ODK_WRITE) { switch (mode) {
ODK_WriteSingleField(buf_off, &fields[i]); case ODK_WRITE:
} else if (mode == ODK_READ) { ODK_WriteSingleField(buf_off, &fields[i]);
ODK_ReadSingleField(buf_off, &fields[i]); break;
} else if (mode == ODK_DUMP) { case ODK_READ:
ODK_DumpSingleField(buf_off, &fields[i]); ODK_ReadSingleField(buf_off, &fields[i]);
} else { break;
return ODK_ERROR_CORE_MESSAGE; case ODK_DUMP:
ODK_DumpSingleField(buf_off, &fields[i]);
break;
default:
return ODK_ERROR_CORE_MESSAGE;
} }
off = off2; off = off2;
} }
@@ -280,14 +308,17 @@ void ValidateRequest(uint32_t message_type,
const std::vector<ODK_Field>& extra_fields, const std::vector<ODK_Field>& extra_fields,
const F& odk_prepare_func, const G& kdo_parse_func) { const F& odk_prepare_func, const G& kdo_parse_func) {
uint32_t message_size = 0; uint32_t message_size = 0;
uint32_t api_version = 16; uint16_t api_major_version = ODK_MAJOR_VERSION;
uint16_t api_minor_version = ODK_MINOR_VERSION;
uint32_t nonce = 0xdeadbeef; uint32_t nonce = 0xdeadbeef;
uint32_t session_id = 0xcafebabe; uint32_t session_id = 0xcafebabe;
ODK_NonceValues nonce_values{api_version, nonce, session_id}; ODK_NonceValues nonce_values{api_minor_version, api_major_version, nonce,
session_id};
std::vector<ODK_Field> total_fields = { std::vector<ODK_Field> total_fields = {
{ODK_UINT32, &message_type, "message_type"}, {ODK_UINT32, &message_type, "message_type"},
{ODK_UINT32, &message_size, "message_size"}, {ODK_UINT32, &message_size, "message_size"},
{ODK_UINT32, &api_version, "api_version"}, {ODK_UINT16, &api_minor_version, "api_minor_version"},
{ODK_UINT16, &api_major_version, "api_major_version"},
{ODK_UINT32, &nonce, "nonce"}, {ODK_UINT32, &nonce, "nonce"},
{ODK_UINT32, &session_id, "session_id"}, {ODK_UINT32, &session_id, "session_id"},
}; };
@@ -317,7 +348,8 @@ void ValidateRequest(uint32_t message_type,
std::string oemcrypto_core_message(reinterpret_cast<char*>(buf), std::string oemcrypto_core_message(reinterpret_cast<char*>(buf),
message_size); message_size);
EXPECT_TRUE(kdo_parse_func(oemcrypto_core_message, &t)); EXPECT_TRUE(kdo_parse_func(oemcrypto_core_message, &t));
nonce_values.api_version = t.api_version; nonce_values.api_minor_version = t.api_minor_version;
nonce_values.api_major_version = t.api_major_version;
nonce_values.nonce = t.nonce; nonce_values.nonce = t.nonce;
nonce_values.session_id = t.session_id; nonce_values.session_id = t.session_id;
EXPECT_EQ(OEMCrypto_SUCCESS, EXPECT_EQ(OEMCrypto_SUCCESS,
@@ -340,13 +372,15 @@ void ValidateResponse(uint32_t message_type,
const std::vector<ODK_Field>& extra_fields, const std::vector<ODK_Field>& extra_fields,
const F& odk_parse_func, const G& kdo_prepare_func) { const F& odk_parse_func, const G& kdo_prepare_func) {
uint32_t message_size = 0; uint32_t message_size = 0;
uint32_t api_version = 16; uint16_t api_minor_version = ODK_MINOR_VERSION;
uint16_t api_major_version = ODK_MAJOR_VERSION;
uint32_t nonce = 0xdeadbeef; uint32_t nonce = 0xdeadbeef;
uint32_t session_id = 0xcafebabe; uint32_t session_id = 0xcafebabe;
std::vector<ODK_Field> total_fields = { std::vector<ODK_Field> total_fields = {
{ODK_UINT32, &message_type, "message_type"}, {ODK_UINT32, &message_type, "message_type"},
{ODK_UINT32, &message_size, "message_size"}, {ODK_UINT32, &message_size, "message_size"},
{ODK_UINT32, &api_version, "api_version"}, {ODK_UINT16, &api_minor_version, "api_minor_version"},
{ODK_UINT16, &api_major_version, "api_major_version"},
{ODK_UINT32, &nonce, "nonce"}, {ODK_UINT32, &nonce, "nonce"},
{ODK_UINT32, &session_id, "session_id"}, {ODK_UINT32, &session_id, "session_id"},
}; };
@@ -367,7 +401,8 @@ void ValidateResponse(uint32_t message_type,
size_t bytes_read = 0, bytes_written = 0; size_t bytes_read = 0, bytes_written = 0;
T t = {}; T t = {};
t.api_version = api_version; t.api_minor_version = api_minor_version;
t.api_major_version = api_major_version;
t.nonce = nonce; t.nonce = nonce;
t.session_id = session_id; t.session_id = session_id;
@@ -383,7 +418,8 @@ void ValidateResponse(uint32_t message_type,
bytes_written - bytes_read == header_size); bytes_written - bytes_read == header_size);
// parse buf with odk // parse buf with odk
ODK_NonceValues nonce_values{api_version, nonce, session_id}; ODK_NonceValues nonce_values{ODK_MINOR_VERSION, api_major_version, nonce,
session_id};
EXPECT_EQ(OEMCrypto_SUCCESS, EXPECT_EQ(OEMCrypto_SUCCESS,
odk_parse_func(buf, bytes_written, &nonce_values)); odk_parse_func(buf, bytes_written, &nonce_values));
@@ -465,16 +501,16 @@ TEST(OdkTest, LicenseRequest) {
} }
TEST(OdkTest, RenewalRequest) { TEST(OdkTest, RenewalRequest) {
const uint64_t system_time_seconds = 0xBADDCAFE000FF1CE; constexpr uint64_t system_time_seconds = 0xBADDCAFE000FF1CE;
uint64_t playback_time = 0xCAFE00000000; uint64_t playback_time = 0xCAFE00000000;
const uint64_t playback_start = system_time_seconds - playback_time; const uint64_t playback_start = system_time_seconds - playback_time;
std::vector<ODK_Field> extra_fields = { const std::vector<ODK_Field> extra_fields = {
{ODK_UINT64, &playback_time, "playback_time"}, {ODK_UINT64, &playback_time, "playback_time"},
}; };
ODK_ClockValues clock_values = {0}; ODK_ClockValues clock_values = {0};
clock_values.time_of_first_decrypt = playback_start; clock_values.time_of_first_decrypt = playback_start;
auto odk_prepare_func = [&](uint8_t* const buf, size_t* size, auto odk_prepare_func = [&](uint8_t* const buf, size_t* size,
const ODK_NonceValues* nonce_values) { ODK_NonceValues* nonce_values) {
return ODK_PrepareCoreRenewalRequest(buf, SIZE_MAX, size, nonce_values, return ODK_PrepareCoreRenewalRequest(buf, SIZE_MAX, size, nonce_values,
&clock_values, system_time_seconds); &clock_values, system_time_seconds);
}; };
@@ -528,20 +564,17 @@ TEST(OdkTest, LicenseResponse) {
.enc_mac_keys = {.offset = 2, .length = 3}, .enc_mac_keys = {.offset = 2, .length = 3},
.pst = {.offset = 4, .length = 5}, .pst = {.offset = 4, .length = 5},
.srm_restriction_data = {.offset = 6, .length = 7}, .srm_restriction_data = {.offset = 6, .length = 7},
.license_type = 8, .license_type = OEMCrypto_EntitlementLicense,
.nonce_required = 0xDEADC0DE, .nonce_required = true,
.timer_limits = .timer_limits =
{ {
.soft_expiry = 9, .soft_enforce_rental_duration = true,
.soft_enforce_playback_duration = false,
.earliest_playback_start_seconds = 10, .earliest_playback_start_seconds = 10,
.latest_playback_start_seconds = 11, .rental_duration_seconds = 11,
.initial_playback_duration_seconds = 12, .total_playback_duration_seconds = 12,
.renewal_playback_duration_seconds = 13, .initial_renewal_duration_seconds = 13,
.license_duration_seconds = 14,
}, },
.request_hash = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
22, 23, 24, 25, 26, 27, 28, 29, 30, 31},
.key_array_length = 3, .key_array_length = 3,
.key_array = .key_array =
{ {
@@ -569,6 +602,11 @@ TEST(OdkTest, LicenseResponse) {
}, },
}; };
const uint8_t request_hash[ODK_SHA256_HASH_SIZE] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
uint8_t request_hash_read[ODK_SHA256_HASH_SIZE];
memcpy(request_hash_read, request_hash, sizeof(request_hash));
std::vector<ODK_Field> extra_fields = { std::vector<ODK_Field> extra_fields = {
{ODK_SUBSTRING, &parsed_license.enc_mac_keys_iv, ".enc_mac_keys_iv"}, {ODK_SUBSTRING, &parsed_license.enc_mac_keys_iv, ".enc_mac_keys_iv"},
{ODK_SUBSTRING, &parsed_license.enc_mac_keys, ".enc_mac_keys"}, {ODK_SUBSTRING, &parsed_license.enc_mac_keys, ".enc_mac_keys"},
@@ -577,20 +615,19 @@ TEST(OdkTest, LicenseResponse) {
".srm_restriction_data"}, ".srm_restriction_data"},
{ODK_UINT32, &parsed_license.license_type, ".license_type"}, {ODK_UINT32, &parsed_license.license_type, ".license_type"},
{ODK_UINT32, &parsed_license.nonce_required, ".nonce_required"}, {ODK_UINT32, &parsed_license.nonce_required, ".nonce_required"},
{ODK_UINT32, &parsed_license.timer_limits.soft_expiry, ".soft_expiry"}, {ODK_UINT32, &parsed_license.timer_limits.soft_enforce_rental_duration,
".soft_enforce_rental_duration"},
{ODK_UINT32, &parsed_license.timer_limits.soft_enforce_playback_duration,
".soft_enforce_playback_duration"},
{ODK_UINT64, &parsed_license.timer_limits.earliest_playback_start_seconds, {ODK_UINT64, &parsed_license.timer_limits.earliest_playback_start_seconds,
".earliest_playback_start_seconds"}, ".earliest_playback_start_seconds"},
{ODK_UINT64, &parsed_license.timer_limits.latest_playback_start_seconds, {ODK_UINT64, &parsed_license.timer_limits.rental_duration_seconds,
".latest_playback_start_seconds"}, ".rental_duration_seconds"},
{ODK_UINT64, &parsed_license.timer_limits.total_playback_duration_seconds,
".total_playback_duration_seconds"},
{ODK_UINT64, {ODK_UINT64,
&parsed_license.timer_limits.initial_playback_duration_seconds, &parsed_license.timer_limits.initial_renewal_duration_seconds,
".initial_playback_duration_seconds"}, ".initial_renewal_duration_seconds"},
{ODK_UINT64,
&parsed_license.timer_limits.renewal_playback_duration_seconds,
".renewal_playback_duration_seconds"},
{ODK_UINT64, &parsed_license.timer_limits.license_duration_seconds,
".license_duration_seconds"},
{ODK_HASH, &parsed_license.request_hash, ".request_hash"},
{ODK_UINT32, &parsed_license.key_array_length, ".key_array_length"}, {ODK_UINT32, &parsed_license.key_array_length, ".key_array_length"},
{ODK_SUBSTRING, &parsed_license.key_array[0].key_id, ".key_id"}, {ODK_SUBSTRING, &parsed_license.key_array[0].key_id, ".key_id"},
{ODK_SUBSTRING, &parsed_license.key_array[0].key_data_iv, ".key_data_iv"}, {ODK_SUBSTRING, &parsed_license.key_array[0].key_data_iv, ".key_data_iv"},
@@ -610,21 +647,24 @@ TEST(OdkTest, LicenseResponse) {
{ODK_SUBSTRING, &parsed_license.key_array[2].key_control_iv, {ODK_SUBSTRING, &parsed_license.key_array[2].key_control_iv,
".key_control_iv"}, ".key_control_iv"},
{ODK_SUBSTRING, &parsed_license.key_array[2].key_control, ".key_control"}, {ODK_SUBSTRING, &parsed_license.key_array[2].key_control, ".key_control"},
{ODK_HASH, request_hash_read, ".request_hash"},
}; };
const std::string request_hash_string(
uint8_t request_hash[ODK_SHA256_HASH_SIZE] = {}; reinterpret_cast<const char*>(request_hash), sizeof(request_hash));
memcpy(request_hash, parsed_license.request_hash, ODK_SHA256_HASH_SIZE);
auto odk_parse_func = [&](const uint8_t* buf, size_t size, auto odk_parse_func = [&](const uint8_t* buf, size_t size,
ODK_NonceValues* nonce_values) { ODK_NonceValues* nonce_values) {
ODK_TimerLimits timer_limits; ODK_TimerLimits timer_limits;
ODK_ClockValues clock_values; ODK_ClockValues clock_values;
return ODK_ParseLicense(buf, size + 128, size, true, false, request_hash, constexpr bool initial_license_load = true;
&timer_limits, &clock_values, nonce_values, constexpr bool usage_entry_present = true;
&parsed_license); return ODK_ParseLicense(buf, size + 128, size, initial_license_load,
usage_entry_present, request_hash, &timer_limits,
&clock_values, nonce_values, &parsed_license);
}; };
auto kdo_prepare_func = [&](const ODK_LicenseRequest& core_request, auto kdo_prepare_func = [&](const ODK_LicenseRequest& core_request,
std::string* oemcrypto_core_message) { std::string* oemcrypto_core_message) {
return CreateCoreLicenseResponse(parsed_license, core_request, return CreateCoreLicenseResponse(parsed_license, core_request,
request_hash_string,
oemcrypto_core_message); oemcrypto_core_message);
}; };
ValidateResponse<ODK_LicenseRequest>(ODK_License_Response_Type, extra_fields, ValidateResponse<ODK_LicenseRequest>(ODK_License_Response_Type, extra_fields,
@@ -636,26 +676,29 @@ TEST(OdkTest, RenewalResponse) {
uint64_t playback_clock = 11; uint64_t playback_clock = 11;
uint64_t playback_timer = 12; uint64_t playback_timer = 12;
uint64_t message_playback_clock = 10; uint64_t message_playback_clock = 10;
constexpr uint64_t renewal_duration = 130;
uint64_t var_renewal_duration = renewal_duration;
std::vector<ODK_Field> extra_fields = { std::vector<ODK_Field> extra_fields = {
{ODK_UINT64, &message_playback_clock, "message_playback_clock"}, {ODK_UINT64, &message_playback_clock, "message_playback_clock"},
{ODK_UINT64, &var_renewal_duration, "renewal_duration"},
}; };
ODK_TimerLimits timer_limits = { ODK_TimerLimits timer_limits = {
.soft_expiry = 0, .soft_enforce_rental_duration = false,
.soft_enforce_playback_duration = false,
.earliest_playback_start_seconds = 0, .earliest_playback_start_seconds = 0,
.latest_playback_start_seconds = 100, .rental_duration_seconds = 1000,
.initial_playback_duration_seconds = 10, .total_playback_duration_seconds = 2000,
.renewal_playback_duration_seconds = 20, .initial_renewal_duration_seconds = 30,
.license_duration_seconds = 100,
}; };
ODK_ClockValues clock_values = { ODK_ClockValues clock_values = {
.time_of_license_signed = 0, .time_of_license_signed = system_time - playback_clock - 42,
.time_of_first_decrypt = system_time - playback_clock, .time_of_first_decrypt = system_time - playback_clock,
.time_of_last_decrypt = 0, .time_of_last_decrypt = 0,
.time_of_renewal_request = message_playback_clock, .time_of_renewal_request = message_playback_clock,
.time_when_timer_expires = system_time + playback_timer, .time_when_timer_expires = system_time + playback_timer,
.timer_status = 0, .timer_status = ODK_CLOCK_TIMER_STATUS_LICENSE_LOADED,
.status = kUnused, .status = kUnused,
}; };
@@ -666,7 +709,7 @@ TEST(OdkTest, RenewalResponse) {
&timer_limits, &clock_values, &playback_timer); &timer_limits, &clock_values, &playback_timer);
EXPECT_EQ(ODK_SET_TIMER, err); EXPECT_EQ(ODK_SET_TIMER, err);
EXPECT_EQ(timer_limits.renewal_playback_duration_seconds, playback_timer); EXPECT_EQ(renewal_duration, playback_timer);
EXPECT_EQ(clock_values.time_when_timer_expires, EXPECT_EQ(clock_values.time_when_timer_expires,
system_time + playback_timer); system_time + playback_timer);
@@ -678,7 +721,8 @@ TEST(OdkTest, RenewalResponse) {
auto kdo_prepare_func = [&](ODK_RenewalRequest& core_request, auto kdo_prepare_func = [&](ODK_RenewalRequest& core_request,
std::string* oemcrypto_core_message) { std::string* oemcrypto_core_message) {
core_request.playback_time_seconds = message_playback_clock; core_request.playback_time_seconds = message_playback_clock;
return CreateCoreRenewalResponse(core_request, oemcrypto_core_message); return CreateCoreRenewalResponse(core_request, renewal_duration,
oemcrypto_core_message);
}; };
ValidateResponse<ODK_RenewalRequest>(ODK_Renewal_Response_Type, extra_fields, ValidateResponse<ODK_RenewalRequest>(ODK_Renewal_Response_Type, extra_fields,
odk_parse_func, kdo_prepare_func); odk_parse_func, kdo_prepare_func);
@@ -732,10 +776,12 @@ TEST(OdkSizeTest, LicenseRequest) {
uint8_t* message = nullptr; uint8_t* message = nullptr;
size_t message_length = 0; size_t message_length = 0;
size_t core_message_length = 0; size_t core_message_length = 0;
uint32_t api_version = 0; uint16_t api_minor_version = ODK_MINOR_VERSION;
uint16_t api_major_version = 0;
uint32_t nonce = 0; uint32_t nonce = 0;
uint32_t session_id = 0; uint32_t session_id = 0;
ODK_NonceValues nonce_values{api_version, nonce, session_id}; ODK_NonceValues nonce_values{api_minor_version, api_major_version, nonce,
session_id};
EXPECT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, EXPECT_EQ(OEMCrypto_ERROR_SHORT_BUFFER,
ODK_PrepareCoreLicenseRequest(message, message_length, ODK_PrepareCoreLicenseRequest(message, message_length,
&core_message_length, &nonce_values)); &core_message_length, &nonce_values));
@@ -748,13 +794,15 @@ TEST(OdkSizeTest, RenewalRequest) {
uint8_t* message = nullptr; uint8_t* message = nullptr;
size_t message_length = 0; size_t message_length = 0;
size_t core_message_length = 0; size_t core_message_length = 0;
uint32_t api_version = 0; uint16_t api_minor_version = ODK_MINOR_VERSION;
uint16_t api_major_version = 0;
uint32_t nonce = 0; uint32_t nonce = 0;
uint32_t session_id = 0; uint32_t session_id = 0;
ODK_ClockValues clock_values = {}; ODK_ClockValues clock_values = {};
clock_values.time_of_first_decrypt = 10; clock_values.time_of_first_decrypt = 10;
uint64_t system_time_seconds = 15; uint64_t system_time_seconds = 15;
ODK_NonceValues nonce_values{api_version, nonce, session_id}; ODK_NonceValues nonce_values{api_minor_version, api_major_version, nonce,
session_id};
EXPECT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, EXPECT_EQ(OEMCrypto_ERROR_SHORT_BUFFER,
ODK_PrepareCoreRenewalRequest(message, message_length, ODK_PrepareCoreRenewalRequest(message, message_length,
&core_message_length, &nonce_values, &core_message_length, &nonce_values,
@@ -768,11 +816,13 @@ TEST(OdkSizeTest, ProvisioningRequest) {
uint8_t* message = nullptr; uint8_t* message = nullptr;
size_t message_length = 0; size_t message_length = 0;
size_t core_message_length = 0; size_t core_message_length = 0;
uint32_t api_version = 0; uint16_t api_minor_version = ODK_MINOR_VERSION;
uint16_t api_major_version = 0;
uint32_t nonce = 0; uint32_t nonce = 0;
uint32_t session_id = 0; uint32_t session_id = 0;
uint32_t device_id_length = 0; uint32_t device_id_length = 0;
ODK_NonceValues nonce_values{api_version, nonce, session_id}; ODK_NonceValues nonce_values{api_minor_version, api_major_version, nonce,
session_id};
EXPECT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, EXPECT_EQ(OEMCrypto_ERROR_SHORT_BUFFER,
ODK_PrepareCoreProvisioningRequest( ODK_PrepareCoreProvisioningRequest(
message, message_length, &core_message_length, &nonce_values, message, message_length, &core_message_length, &nonce_values,

View File

@@ -1,64 +0,0 @@
/*
* Copyright 2019 Google LLC. All Rights Reserved. This file and proprietary
* source code may only be used and distributed under the Widevine Master
* License Agreement.
*/
#ifndef ODK_TEST_H_
#define ODK_TEST_H_
#include "OEMCryptoCENCCommon.h"
typedef enum {
ODK_License_Request_Type = 1,
ODK_License_Response_Type = 2,
ODK_Renewal_Request_Type = 3,
ODK_Renewal_Response_Type = 4,
ODK_Provisioning_Request_Type = 5,
ODK_Provisioning_Response_Type = 6,
} ODK_MessageType;
typedef enum {
ODK_UINT32,
ODK_UINT64,
ODK_SUBSTRING,
ODK_DEVICEID,
ODK_HASH,
ODK_NUMTYPES,
} ODK_FieldType;
typedef enum {
ODK_READ,
ODK_WRITE,
} ODK_FieldMode;
typedef struct {
ODK_FieldType type;
void* value;
} ODK_Field;
#define DEVICE_ID_MAX (64)
#ifdef __cplusplus
extern "C" {
#endif
size_t ODK_FieldLength(ODK_FieldType type);
OEMCryptoResult ODK_WriteSingleField(uint8_t* const buf,
const ODK_Field* const field);
OEMCryptoResult ODK_ReadSingleField(const uint8_t* const buf,
const ODK_Field* const field);
OEMCryptoResult ODK_ReadFields(const uint8_t* const buf, const size_t size_in,
size_t* size_out, const size_t n,
const ODK_Field* const fields);
OEMCryptoResult ODK_WriteFields(uint8_t* const buf, const size_t size_in,
size_t* size_out, const size_t n,
const ODK_Field* const fields);
#ifdef __cplusplus
}
#endif
#endif // ODK_TEST_H_

File diff suppressed because it is too large Load Diff

View File

@@ -42,11 +42,15 @@ CryptoEngine::~CryptoEngine() {
} }
bool CryptoEngine::Initialize() { bool CryptoEngine::Initialize() {
std::string file_path = GetUsageTimeFileFullPath();
LoadOfflineTimeInfo(file_path);
usage_table_.reset(MakeUsageTable()); usage_table_.reset(MakeUsageTable());
return true; return true;
} }
void CryptoEngine::Terminate() { void CryptoEngine::Terminate() {
std::string file_path = GetUsageTimeFileFullPath();
SaveOfflineTimeInfo(file_path);
std::unique_lock<std::mutex> lock(session_table_lock_); std::unique_lock<std::mutex> lock(session_table_lock_);
ActiveSessions::iterator it; ActiveSessions::iterator it;
for (it = sessions_.begin(); it != sessions_.end(); ++it) { for (it = sessions_.begin(); it != sessions_.end(); ++it) {
@@ -102,44 +106,53 @@ int64_t CryptoEngine::OnlineTime() {
} }
int64_t CryptoEngine::RollbackCorrectedOfflineTime() { int64_t CryptoEngine::RollbackCorrectedOfflineTime() {
struct TimeInfo { // Add any time offsets in the past to the current time.
// The max time recorded through this function call. int64_t current_time = OnlineTime() + offline_time_info_.rollback_offset;
int64_t previous_time; // Write time info to disk if kTimeInfoUpdateWindowInSeconds has elapsed since
// If the wall time is rollbacked to before the previous_time, this member // last write.
// is updated to reflect the offset. if (current_time - offline_time_info_.previous_time >
int64_t rollback_offset; kTimeInfoUpdateWindowInSeconds) {
// Pad the struct so that TimeInfo is a multiple of 16. std::string file_path = GetUsageTimeFileFullPath();
uint8_t padding[16 - (2 * sizeof(time_t)) % 16]; SaveOfflineTimeInfo(file_path);
}; }
return current_time;
}
std::vector<uint8_t> encrypted_buffer(sizeof(TimeInfo)); std::string CryptoEngine::GetUsageTimeFileFullPath() const {
std::vector<uint8_t> clear_buffer(sizeof(TimeInfo)); std::string file_path;
TimeInfo time_info; // Note: file path is OK for a real implementation, but using security
memset(&time_info, 0, sizeof(time_info)); // level 1 would be better.
// Use the device key for encrypt/decrypt.
const std::vector<uint8_t>& key = DeviceRootKey();
std::unique_ptr<wvcdm::File> file;
std::string path;
// Note: this path is OK for a real implementation, but using security level 1
// would be better.
// TODO(fredgc, jfore): Address how this property is presented to the ref. // TODO(fredgc, jfore): Address how this property is presented to the ref.
// For now, the path is empty. // For now, the file path is empty.
/*if (!wvcdm::Properties::GetDeviceFilesBasePath(wvcdm::kSecurityLevelL3, /*if (!wvcdm::Properties::GetDeviceFilesBasePath(wvcdm::kSecurityLevelL3,
&path)) { &file_path)) {
LOGE("RollbackCorrectedOfflineTime: Unable to get base path"); LOGE("RollbackCorrectedOfflineTime: Unable to get base path");
}*/ }*/
std::string filename = path + "StoredUsageTime.dat"; return file_path + kStoredUsageTimeFileName;
}
bool CryptoEngine::LoadOfflineTimeInfo(const std::string& file_path) {
memset(&offline_time_info_, 0, sizeof(TimeInfo));
wvcdm::FileSystem* file_system = file_system_.get(); wvcdm::FileSystem* file_system = file_system_.get();
if (file_system->Exists(filename)) { if (file_system->Exists(file_path)) {
// Load time info from previous call to this function. std::vector<uint8_t> encrypted_buffer(sizeof(TimeInfo));
file = file_system->Open(filename, wvcdm::FileSystem::kReadOnly); std::vector<uint8_t> clear_buffer(sizeof(TimeInfo));
KeyboxError error_code = ValidateKeybox();
if (error_code != NO_ERROR) {
LOGE("Keybox is invalid: %d", error_code);
return false;
}
// Use the device key for encrypt/decrypt.
const std::vector<uint8_t>& key = DeviceRootKey();
std::unique_ptr<wvcdm::File> file =
file_system->Open(file_path, wvcdm::FileSystem::kReadOnly);
if (!file) { if (!file) {
LOGE("RollbackCorrectedOfflineTime: File open failed: %s", LOGE("RollbackCorrectedOfflineTime: File open failed: %s",
filename.c_str()); file_path.c_str());
return OnlineTime(); return false;
} }
// Load time info from previous call.
file->Read(reinterpret_cast<char*>(&encrypted_buffer[0]), sizeof(TimeInfo)); file->Read(reinterpret_cast<char*>(&encrypted_buffer[0]), sizeof(TimeInfo));
// Decrypt the encrypted TimeInfo buffer. // Decrypt the encrypted TimeInfo buffer.
AES_KEY aes_key; AES_KEY aes_key;
@@ -147,42 +160,65 @@ int64_t CryptoEngine::RollbackCorrectedOfflineTime() {
std::vector<uint8_t> iv(wvoec::KEY_IV_SIZE, 0); std::vector<uint8_t> iv(wvoec::KEY_IV_SIZE, 0);
AES_cbc_encrypt(&encrypted_buffer[0], &clear_buffer[0], sizeof(TimeInfo), AES_cbc_encrypt(&encrypted_buffer[0], &clear_buffer[0], sizeof(TimeInfo),
&aes_key, iv.data(), AES_DECRYPT); &aes_key, iv.data(), AES_DECRYPT);
memcpy(&time_info, &clear_buffer[0], sizeof(TimeInfo)); memcpy(&offline_time_info_, &clear_buffer[0], sizeof(TimeInfo));
}
int64_t current_time; // Detect offline time rollback after loading from disk.
// Add any time offsets in the past to the current time. // Add any time offsets in the past to the current time.
current_time = OnlineTime() + time_info.rollback_offset; int64_t current_time = OnlineTime() + offline_time_info_.rollback_offset;
if (time_info.previous_time > current_time) { if (offline_time_info_.previous_time > current_time) {
// Time has been rolled back. // Current time is earlier than the previously saved time. Time has been
// Update the rollback offset. // rolled back. Update the rollback offset.
time_info.rollback_offset += time_info.previous_time - current_time; offline_time_info_.rollback_offset +=
// Keep current time at previous recorded time. offline_time_info_.previous_time - current_time;
current_time = time_info.previous_time; // Keep current time at previous recorded time.
current_time = offline_time_info_.previous_time;
}
// The new previous_time will either stay the same or move forward.
offline_time_info_.previous_time = current_time;
} }
return true;
}
bool CryptoEngine::SaveOfflineTimeInfo(const std::string& file_path) {
// Add any time offsets in the past to the current time. If there was an
// earlier offline rollback, the rollback offset will be updated in
// LoadOfflineTimeInfo(). It guarantees that the current time to be saved
// will never go back.
int64_t current_time = OnlineTime() + offline_time_info_.rollback_offset;
// The new previous_time will either stay the same or move forward. // The new previous_time will either stay the same or move forward.
time_info.previous_time = current_time; if (current_time > offline_time_info_.previous_time)
offline_time_info_.previous_time = current_time;
KeyboxError error_code = ValidateKeybox();
if (error_code != NO_ERROR) {
LOGE("Keybox is invalid: %d", error_code);
return false;
}
// Use the device key for encrypt/decrypt.
const std::vector<uint8_t>& key = DeviceRootKey();
std::vector<uint8_t> encrypted_buffer(sizeof(TimeInfo));
std::vector<uint8_t> clear_buffer(sizeof(TimeInfo));
// Copy updated data and encrypt the buffer. // Copy updated data and encrypt the buffer.
memcpy(&clear_buffer[0], &time_info, sizeof(TimeInfo)); memcpy(&clear_buffer[0], &offline_time_info_, sizeof(TimeInfo));
AES_KEY aes_key; AES_KEY aes_key;
AES_set_encrypt_key(&key[0], 128, &aes_key); AES_set_encrypt_key(&key[0], 128, &aes_key);
std::vector<uint8_t> iv(wvoec::KEY_IV_SIZE, 0); std::vector<uint8_t> iv(wvoec::KEY_IV_SIZE, 0);
AES_cbc_encrypt(&clear_buffer[0], &encrypted_buffer[0], sizeof(TimeInfo), AES_cbc_encrypt(&clear_buffer[0], &encrypted_buffer[0], sizeof(TimeInfo),
&aes_key, iv.data(), AES_ENCRYPT); &aes_key, iv.data(), AES_ENCRYPT);
std::unique_ptr<wvcdm::File> file;
wvcdm::FileSystem* file_system = file_system_.get();
// Write the encrypted buffer to disk. // Write the encrypted buffer to disk.
file = file_system->Open( file = file_system->Open(
filename, wvcdm::FileSystem::kCreate | wvcdm::FileSystem::kTruncate); file_path, wvcdm::FileSystem::kCreate | wvcdm::FileSystem::kTruncate);
if (!file) { if (!file) {
LOGE("RollbackCorrectedOfflineTime: File open failed: %s", LOGE("RollbackCorrectedOfflineTime: File open failed: %s",
filename.c_str()); file_path.c_str());
return OnlineTime(); return false;
} }
file->Write(reinterpret_cast<char*>(&encrypted_buffer[0]), sizeof(TimeInfo)); file->Write(reinterpret_cast<char*>(&encrypted_buffer[0]), sizeof(TimeInfo));
return true;
// Return time with offset.
return current_time;
} }
bool CryptoEngine::NonceCollision(uint32_t nonce) { bool CryptoEngine::NonceCollision(uint32_t nonce) {

View File

@@ -29,10 +29,23 @@ namespace wvoec_ref {
typedef std::map<SessionId, SessionContext*> ActiveSessions; typedef std::map<SessionId, SessionContext*> ActiveSessions;
static const std::string kStoredUsageTimeFileName = "StoredUsageTime.dat";
typedef struct {
// The max time recorded
int64_t previous_time;
// If the wall time is rollbacked to before the previous_time, this member
// is updated to reflect the offset.
int64_t rollback_offset;
// Pad the struct so that TimeInfo is a multiple of 16.
uint8_t padding[16 - (2 * sizeof(time_t)) % 16];
} TimeInfo;
class CryptoEngine { class CryptoEngine {
public: public:
static const uint32_t kApiVersion = 16; static const uint32_t kApiVersion = 16;
static const uint32_t kMinorApiVersion = 0; static const uint32_t kMinorApiVersion = 0;
static const int64_t kTimeInfoUpdateWindowInSeconds = 300;
// This is like a factory method, except we choose which version to use at // This is like a factory method, except we choose which version to use at
// compile time. It is defined in several source files. The build system // compile time. It is defined in several source files. The build system
@@ -217,6 +230,10 @@ class CryptoEngine {
// System clock with antirollback protection, measuring time in seconds. // System clock with antirollback protection, measuring time in seconds.
int64_t RollbackCorrectedOfflineTime(); int64_t RollbackCorrectedOfflineTime();
bool LoadOfflineTimeInfo(const std::string& file_path);
bool SaveOfflineTimeInfo(const std::string& file_path);
std::string GetUsageTimeFileFullPath() const;
explicit CryptoEngine(std::unique_ptr<wvcdm::FileSystem>&& file_system); explicit CryptoEngine(std::unique_ptr<wvcdm::FileSystem>&& file_system);
virtual SessionContext* MakeSession(SessionId sid); virtual SessionContext* MakeSession(SessionId sid);
virtual UsageTable* MakeUsageTable(); virtual UsageTable* MakeUsageTable();
@@ -226,6 +243,7 @@ class CryptoEngine {
std::mutex session_table_lock_; std::mutex session_table_lock_;
std::unique_ptr<wvcdm::FileSystem> file_system_; std::unique_ptr<wvcdm::FileSystem> file_system_;
std::unique_ptr<UsageTable> usage_table_; std::unique_ptr<UsageTable> usage_table_;
TimeInfo offline_time_info_;
CORE_DISALLOW_COPY_AND_ASSIGN(CryptoEngine); CORE_DISALLOW_COPY_AND_ASSIGN(CryptoEngine);
}; };

View File

@@ -467,14 +467,17 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_RefreshKeys(
return OEMCrypto_ERROR_INVALID_CONTEXT; return OEMCrypto_ERROR_INVALID_CONTEXT;
} }
// Range check // We only use the first key object to update the entire license. Since we
for (size_t i = 0; i < num_keys; i++) { // know num_keys > 0 after the last if statement, we can assume index is not
if (!RangeCheck(message_length, key_array[i].key_id, true) || // out of bounds.
!RangeCheck(message_length, key_array[i].key_control, false) || constexpr size_t kIndex = 0;
!RangeCheck(message_length, key_array[i].key_control_iv, true)) {
LOGE("Range Check %zu", i); // Range check.
return OEMCrypto_ERROR_INVALID_CONTEXT; if (!RangeCheck(message_length, key_array[kIndex].key_id, true) ||
} !RangeCheck(message_length, key_array[kIndex].key_control, false) ||
!RangeCheck(message_length, key_array[kIndex].key_control_iv, true)) {
LOGE("Range Check %zu", kIndex);
return OEMCrypto_ERROR_INVALID_CONTEXT;
} }
// Validate message signature // Validate message signature
@@ -489,37 +492,31 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_RefreshKeys(
std::vector<uint8_t> key_id; std::vector<uint8_t> key_id;
std::vector<uint8_t> key_control; std::vector<uint8_t> key_control;
std::vector<uint8_t> key_control_iv; std::vector<uint8_t> key_control_iv;
for (size_t i = 0; i < num_keys; i++) { if (key_array[kIndex].key_id.length != 0) {
if (key_array[i].key_id.length != 0) { key_id.assign(message + key_array[kIndex].key_id.offset,
key_id.assign( message + key_array[kIndex].key_id.offset +
message + key_array[i].key_id.offset, key_array[kIndex].key_id.length);
message + key_array[i].key_id.offset + key_array[i].key_id.length); key_control.assign(message + key_array[kIndex].key_control.offset,
key_control.assign( message + key_array[kIndex].key_control.offset +
message + key_array[i].key_control.offset, wvoec::KEY_CONTROL_SIZE);
message + key_array[i].key_control.offset + wvoec::KEY_CONTROL_SIZE); if (key_array[kIndex].key_control_iv.length == 0) {
if (key_array[i].key_control_iv.length == 0) {
key_control_iv.clear();
} else {
key_control_iv.assign(
message + key_array[i].key_control_iv.offset,
message + key_array[i].key_control_iv.offset + wvoec::KEY_IV_SIZE);
}
} else {
// key_id could be null if special control key type
// key_control is not encrypted in this case
key_id.clear();
key_control_iv.clear(); key_control_iv.clear();
key_control.assign( } else {
message + key_array[i].key_control.offset, key_control_iv.assign(message + key_array[kIndex].key_control_iv.offset,
message + key_array[i].key_control.offset + wvoec::KEY_CONTROL_SIZE); message + key_array[kIndex].key_control_iv.offset +
} wvoec::KEY_IV_SIZE);
status = session_ctx->RefreshKey(key_id, key_control, key_control_iv);
if (status != OEMCrypto_SUCCESS) {
LOGE("error %d in key %zu", status, i);
break;
} }
} else {
// key_id could be null if special control key type
// key_control is not encrypted in this case
key_id.clear();
key_control_iv.clear();
key_control.assign(message + key_array[kIndex].key_control.offset,
message + key_array[kIndex].key_control.offset +
wvoec::KEY_CONTROL_SIZE);
} }
status = session_ctx->RefreshKey(key_id, key_control, key_control_iv);
if (status != OEMCrypto_SUCCESS) { if (status != OEMCrypto_SUCCESS) {
return status; return status;
} }

View File

@@ -430,7 +430,7 @@ OEMCryptoResult SessionContext::PrepAndSignRenewalRequest(
} }
// If we are talking to an old license server, then we only sign the message // If we are talking to an old license server, then we only sign the message
// body. // body.
if (nonce_values_.api_version < 16) { if (nonce_values_.api_major_version < 16) {
const uint8_t* message_body = message + *core_message_length; const uint8_t* message_body = message + *core_message_length;
const size_t message_body_length = message_length - *core_message_length; const size_t message_body_length = message_length - *core_message_length;
return GenerateSignature(message_body, message_body_length, signature, return GenerateSignature(message_body, message_body_length, signature,
@@ -1100,10 +1100,40 @@ OEMCryptoResult SessionContext::RefreshKey(
if (session_keys_ == nullptr) { if (session_keys_ == nullptr) {
return OEMCrypto_ERROR_INVALID_CONTEXT; return OEMCrypto_ERROR_INVALID_CONTEXT;
} }
if (key_control.empty()) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
std::vector<uint8_t> decrypted_key_control;
if (key_id.empty()) {
// Key control is not encrypted if key id is NULL
decrypted_key_control = key_control;
} else {
Key* content_key = session_keys_->Find(key_id);
if (nullptr == content_key) {
LOGE("Key ID not found.");
return OEMCrypto_ERROR_NO_CONTENT_KEY;
}
const std::vector<uint8_t> content_key_value = content_key->value();
// Decrypt encrypted key control block
if (key_control_iv.empty()) {
decrypted_key_control = key_control;
} else {
if (!DecryptMessage(content_key_value, key_control_iv, key_control,
&decrypted_key_control, 128 /* key size */)) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
}
}
KeyControlBlock key_control_block(decrypted_key_control);
if (!key_control_block.valid()) {
LOGE("Parse key control error.");
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
uint32_t new_key_duration = key_control_block.duration();
uint64_t* timer_value = nullptr; uint64_t* timer_value = nullptr;
const OEMCryptoResult result = const OEMCryptoResult result =
ODK_RefreshV15Values(&timer_limits_, &clock_values_, &nonce_values_, ODK_RefreshV15Values(&timer_limits_, &clock_values_, &nonce_values_,
ce_->SystemTime(), timer_value); ce_->SystemTime(), new_key_duration, timer_value);
if (result == ODK_SET_TIMER || result == ODK_DISABLE_TIMER) if (result == ODK_SET_TIMER || result == ODK_DISABLE_TIMER)
return OEMCrypto_SUCCESS; return OEMCrypto_SUCCESS;
if (result == ODK_TIMER_EXPIRED) return OEMCrypto_ERROR_KEY_EXPIRED; if (result == ODK_TIMER_EXPIRED) return OEMCrypto_ERROR_KEY_EXPIRED;

View File

@@ -56,7 +56,7 @@ class UsageTableEntry {
virtual OEMCryptoResult ReportUsage(const std::vector<uint8_t>& pst, virtual OEMCryptoResult ReportUsage(const std::vector<uint8_t>& pst,
uint8_t* buffer, size_t* buffer_length); uint8_t* buffer, size_t* buffer_length);
virtual void UpdateAndIncrement(ODK_ClockValues* clock_values); virtual void UpdateAndIncrement(ODK_ClockValues* clock_values);
// Save all data to the give buffer. This should be called after updating the // Save all data to the given buffer. This should be called after updating the
// data. // data.
OEMCryptoResult SaveData(CryptoEngine* ce, SessionContext* session, OEMCryptoResult SaveData(CryptoEngine* ce, SessionContext* session,
uint8_t* signed_buffer, size_t buffer_size); uint8_t* signed_buffer, size_t buffer_size);
@@ -72,8 +72,8 @@ class UsageTableEntry {
recent_decrypt_ = recent_decrypt; recent_decrypt_ = recent_decrypt;
} }
static size_t SignedEntrySize(); static size_t SignedEntrySize();
const uint8_t* mac_key_server() { return data_.mac_key_server; } const uint8_t* mac_key_server() const { return data_.mac_key_server; }
const uint8_t* mac_key_client() { return data_.mac_key_client; } const uint8_t* mac_key_client() const { return data_.mac_key_client; }
protected: protected:
UsageTable* usage_table_; // Owner of this object. UsageTable* usage_table_; // Owner of this object.

View File

@@ -63,7 +63,6 @@ bool CanChangeTime() {
void DeviceFeatures::Initialize() { void DeviceFeatures::Initialize() {
if (initialized_) return; if (initialized_) return;
uses_keybox = false; uses_keybox = false;
uses_certificate = false;
loads_certificate = false; loads_certificate = false;
generic_crypto = false; generic_crypto = false;
usage_table = false; usage_table = false;
@@ -100,10 +99,6 @@ void DeviceFeatures::Initialize() {
loads_certificate = false; loads_certificate = false;
} }
printf("loads_certificate = %s.\n", loads_certificate ? "true" : "false"); printf("loads_certificate = %s.\n", loads_certificate ? "true" : "false");
uses_certificate = (OEMCrypto_ERROR_NOT_IMPLEMENTED !=
OEMCrypto_GenerateRSASignature(session, buffer, 0, buffer,
&size, kSign_RSASSA_PSS));
printf("uses_certificate = %s.\n", uses_certificate ? "true" : "false");
generic_crypto = generic_crypto =
(OEMCrypto_ERROR_NOT_IMPLEMENTED != (OEMCrypto_ERROR_NOT_IMPLEMENTED !=
OEMCrypto_Generic_Encrypt(session, buffer, 0, buffer, OEMCrypto_Generic_Encrypt(session, buffer, 0, buffer,
@@ -145,7 +140,6 @@ void DeviceFeatures::Initialize() {
printf("NO_METHOD: Cannot derive known session keys.\n"); printf("NO_METHOD: Cannot derive known session keys.\n");
// Note: cast_receiver left unchanged because set by user on command line. // Note: cast_receiver left unchanged because set by user on command line.
uses_keybox = false; uses_keybox = false;
uses_certificate = false;
loads_certificate = false; loads_certificate = false;
generic_crypto = false; generic_crypto = false;
usage_table = false; usage_table = false;
@@ -174,7 +168,6 @@ std::string DeviceFeatures::RestrictFilter(const std::string& initial_filter) {
std::string filter = initial_filter; std::string filter = initial_filter;
// clang-format off // clang-format off
if (!uses_keybox) FilterOut(&filter, "*KeyboxTest*"); if (!uses_keybox) FilterOut(&filter, "*KeyboxTest*");
if (!uses_certificate) FilterOut(&filter, "OEMCrypto*Cert*");
if (!loads_certificate) FilterOut(&filter, "OEMCryptoLoadsCert*"); if (!loads_certificate) FilterOut(&filter, "OEMCryptoLoadsCert*");
if (!generic_crypto) FilterOut(&filter, "*GenericCrypto*"); if (!generic_crypto) FilterOut(&filter, "*GenericCrypto*");
if (!cast_receiver) FilterOut(&filter, "*CastReceiver*"); if (!cast_receiver) FilterOut(&filter, "*CastReceiver*");

View File

@@ -38,7 +38,6 @@ class DeviceFeatures {
enum DeriveMethod derive_key_method; enum DeriveMethod derive_key_method;
bool uses_keybox; // Device uses a keybox to derive session keys. bool uses_keybox; // Device uses a keybox to derive session keys.
bool uses_certificate; // Device uses a certificate to derive session keys.
bool loads_certificate; // Device can load a certificate from the server. bool loads_certificate; // Device can load a certificate from the server.
bool generic_crypto; // Device supports generic crypto. bool generic_crypto; // Device supports generic crypto.
bool cast_receiver; // Device supports alternate rsa signature padding. bool cast_receiver; // Device supports alternate rsa signature padding.

View File

@@ -19,6 +19,7 @@
#include <stdint.h> #include <stdint.h>
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <algorithm>
#include <iostream> #include <iostream>
#include <memory> #include <memory>
#include <string> #include <string>
@@ -137,26 +138,6 @@ class boringssl_ptr {
CORE_DISALLOW_COPY_AND_ASSIGN(boringssl_ptr); CORE_DISALLOW_COPY_AND_ASSIGN(boringssl_ptr);
}; };
OEMCrypto_Substring GetSubstring(const std::string& message,
const std::string& field, bool set_zero) {
OEMCrypto_Substring substring;
if (set_zero || field.empty() || message.empty()) {
substring.offset = 0;
substring.length = 0;
} else {
size_t pos = message.find(field);
if (pos == std::string::npos) {
LOGW("GetSubstring : Cannot find offset for %s", field.c_str());
substring.offset = 0;
substring.length = 0;
} else {
substring.offset = pos;
substring.length = field.length();
}
}
return substring;
}
Test_PST_Report::Test_PST_Report(const std::string& pst_in, Test_PST_Report::Test_PST_Report(const std::string& pst_in,
OEMCrypto_Usage_Entry_Status status_in) OEMCrypto_Usage_Entry_Status status_in)
: status(status_in), pst(pst_in) { : status(status_in), pst(pst_in) {
@@ -169,23 +150,29 @@ void RoundTrip<CoreRequest, PrepAndSignRequest, CoreResponse,
ResponseData>::SignAndVerifyRequest() { ResponseData>::SignAndVerifyRequest() {
// In the real world, a message should be signed by the client and // In the real world, a message should be signed by the client and
// verified by the server. This simulates that. // verified by the server. This simulates that.
vector<uint8_t> data(message_size_);
for (size_t i = 0; i < data.size(); i++) data[i] = i & 0xFF;
OEMCryptoResult sts;
size_t gen_signature_length = 0; size_t gen_signature_length = 0;
size_t core_message_length = 0; size_t core_message_length = 0;
sts = constexpr size_t small_size = 42; // arbitrary.
size_t message_size =
std::max(required_message_size_, core_message_length + small_size);
vector<uint8_t> data(message_size, 0);
for (size_t i = 0; i < data.size(); i++) data[i] = i & 0xFF;
ASSERT_EQ(
PrepAndSignRequest(session()->session_id(), data.data(), data.size(), PrepAndSignRequest(session()->session_id(), data.data(), data.size(),
&core_message_length, nullptr, &gen_signature_length); &core_message_length, nullptr, &gen_signature_length),
ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts); OEMCrypto_ERROR_SHORT_BUFFER);
// If this fails, either the test needs to be modified, or the core message is // Make the message buffer a little bigger than the core message, or the
// very very large. // required size, whichever is larger.
ASSERT_LT(core_message_length, message_size_); message_size =
std::max(required_message_size_, core_message_length + small_size);
data.resize(message_size);
for (size_t i = 0; i < data.size(); i++) data[i] = i & 0xFF;
vector<uint8_t> gen_signature(gen_signature_length); vector<uint8_t> gen_signature(gen_signature_length);
sts = PrepAndSignRequest(session()->session_id(), data.data(), data.size(), ASSERT_EQ(PrepAndSignRequest(session()->session_id(), data.data(),
&core_message_length, gen_signature.data(), data.size(), &core_message_length,
&gen_signature_length); gen_signature.data(), &gen_signature_length),
ASSERT_EQ(OEMCrypto_SUCCESS, sts); OEMCrypto_SUCCESS);
if (global_features.api_version >= kCoreMessagesAPI) { if (global_features.api_version >= kCoreMessagesAPI) {
ASSERT_GT(data.size(), core_message_length); ASSERT_GT(data.size(), core_message_length);
std::string core_message(reinterpret_cast<char*>(data.data()), std::string core_message(reinterpret_cast<char*>(data.data()),
@@ -247,7 +234,7 @@ void ProvisioningRoundTrip::FillAndVerifyCoreRequest(
EXPECT_TRUE( EXPECT_TRUE(
oemcrypto_core_message::deserialize::CoreProvisioningRequestFromMessage( oemcrypto_core_message::deserialize::CoreProvisioningRequestFromMessage(
core_message_string, &core_request_)); core_message_string, &core_request_));
EXPECT_EQ(global_features.api_version, core_request_.api_version); EXPECT_EQ(global_features.api_version, core_request_.api_major_version);
EXPECT_EQ(session()->nonce(), core_request_.nonce); EXPECT_EQ(session()->nonce(), core_request_.nonce);
EXPECT_EQ(session()->session_id(), core_request_.session_id); EXPECT_EQ(session()->session_id(), core_request_.session_id);
size_t device_id_length = core_request_.device_id.size(); size_t device_id_length = core_request_.device_id.size();
@@ -282,7 +269,7 @@ void ProvisioningRoundTrip::CreateDefaultResponse() {
} else { } else {
response_data_.enc_message_key_length = 0; response_data_.enc_message_key_length = 0;
} }
core_response_.key_type = OEMCrypto_Supports_RSA_2048bit; core_response_.key_type = OEMCrypto_RSA_Private_Key;
core_response_.enc_private_key = core_response_.enc_private_key =
FindSubstring(response_data_.rsa_key, response_data_.rsa_key_length); FindSubstring(response_data_.rsa_key, response_data_.rsa_key_length);
core_response_.enc_private_key_iv = FindSubstring( core_response_.enc_private_key_iv = FindSubstring(
@@ -296,12 +283,20 @@ void ProvisioningRoundTrip::EncryptAndSignResponse() {
&encrypted_response_data_); &encrypted_response_data_);
core_response_.enc_private_key.length = core_response_.enc_private_key.length =
encrypted_response_data_.rsa_key_length; encrypted_response_data_.rsa_key_length;
ASSERT_TRUE(oemcrypto_core_message::serialize::CreateCoreProvisioningResponse( if (global_features.api_version >= kCoreMessagesAPI) {
core_response_, core_request_, &serialized_core_message_)); ASSERT_TRUE(
oemcrypto_core_message::serialize::CreateCoreProvisioningResponse(
core_response_, core_request_, &serialized_core_message_));
}
// Make the message buffer a just big enough, or the
// required size, whichever is larger.
const size_t message_size =
std::max(required_message_size_, serialized_core_message_.size() +
sizeof(encrypted_response_data_));
// Stripe the encrypted message. // Stripe the encrypted message.
encrypted_response_.resize(message_size_); encrypted_response_.resize(message_size);
for (size_t i = 0; i < encrypted_response_.size(); i++) { for (size_t i = 0; i < encrypted_response_.size(); i++) {
encrypted_response_[i] = i % 0x100; encrypted_response_[i] = i & 0xFF;
} }
ASSERT_GE(encrypted_response_.size(), serialized_core_message_.size()); ASSERT_GE(encrypted_response_.size(), serialized_core_message_.size());
memcpy(encrypted_response_.data(), serialized_core_message_.data(), memcpy(encrypted_response_.data(), serialized_core_message_.data(),
@@ -322,19 +317,83 @@ void ProvisioningRoundTrip::EncryptAndSignResponse() {
OEMCryptoResult ProvisioningRoundTrip::LoadResponse(Session* session) { OEMCryptoResult ProvisioningRoundTrip::LoadResponse(Session* session) {
EXPECT_NE(session, nullptr); EXPECT_NE(session, nullptr);
size_t wrapped_key_length = 0; size_t wrapped_key_length = 0;
const OEMCryptoResult sts = OEMCrypto_LoadProvisioning( const OEMCryptoResult sts = LoadResponseNoRetry(session, &wrapped_key_length);
session->session_id(), encrypted_response_.data(),
encrypted_response_.size(), serialized_core_message_.size(),
response_signature_.data(), response_signature_.size(), nullptr,
&wrapped_key_length);
if (sts != OEMCrypto_ERROR_SHORT_BUFFER) return sts; if (sts != OEMCrypto_ERROR_SHORT_BUFFER) return sts;
wrapped_rsa_key_.clear(); wrapped_rsa_key_.clear();
wrapped_rsa_key_.assign(wrapped_key_length, 0); wrapped_rsa_key_.assign(wrapped_key_length, 0);
return OEMCrypto_LoadProvisioning( return LoadResponseNoRetry(session, &wrapped_key_length);
session->session_id(), encrypted_response_.data(), }
encrypted_response_.size(), serialized_core_message_.size(),
response_signature_.data(), response_signature_.size(), #ifdef TEST_OEMCRYPTO_V15
wrapped_rsa_key_.data(), &wrapped_key_length); // If this platform supports v15 functions, then will test with them:
# define OEMCrypto_RewrapDeviceRSAKey_V15 OEMCrypto_RewrapDeviceRSAKey
# define OEMCrypto_RewrapDeviceRSAKey30_V15 OEMCrypto_RewrapDeviceRSAKey30
#else
// If this platform does not support v15 functions, we just need to stub these
// out so that the tests compile.
OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey30_V15(
OEMCrypto_SESSION session, const uint32_t* unaligned_nonce,
const uint8_t* encrypted_message_key, size_t encrypted_message_key_length,
const uint8_t* enc_rsa_key, size_t enc_rsa_key_length,
const uint8_t* enc_rsa_key_iv, uint8_t* wrapped_rsa_key,
size_t* wrapped_rsa_key_length) {
LOGE("Support for v15 functions not included. Define TEST_OEMCRYPTO_V15.");
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey_V15(
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
const uint8_t* signature, size_t signature_length,
const uint32_t* unaligned_nonce, const uint8_t* enc_rsa_key,
size_t enc_rsa_key_length, const uint8_t* enc_rsa_key_iv,
uint8_t* wrapped_rsa_key, size_t* wrapped_rsa_key_length) {
LOGE("Support for v15 functions not included. Define TEST_OEMCRYPTO_V15.");
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
#endif
template <typename T>
const T* ProvisioningRoundTrip::RemapPointer(const T* response_pointer) const {
const uint8_t* original_pointer =
reinterpret_cast<const uint8_t*>(response_pointer);
size_t delta =
original_pointer - reinterpret_cast<const uint8_t*>(&response_data_);
// Base offset should be 0 if this is a v15 message, which is the only time
// this function is called.
size_t base_offset = serialized_core_message_.size();
const uint8_t* new_pointer = encrypted_response_.data() + delta + base_offset;
return reinterpret_cast<const T*>(new_pointer);
}
OEMCryptoResult ProvisioningRoundTrip::LoadResponseNoRetry(
Session* session, size_t* wrapped_key_length) {
EXPECT_NE(session, nullptr);
if (global_features.api_version >= kCoreMessagesAPI) {
return OEMCrypto_LoadProvisioning(
session->session_id(), encrypted_response_.data(),
encrypted_response_.size(), serialized_core_message_.size(),
response_signature_.data(), response_signature_.size(),
wrapped_rsa_key_.data(), wrapped_key_length);
} else if (global_features.provisioning_method == OEMCrypto_Keybox) {
return OEMCrypto_RewrapDeviceRSAKey_V15(
session->session_id(), encrypted_response_.data(),
encrypted_response_.size(), response_signature_.data(),
response_signature_.size(), RemapPointer(&response_data_.nonce),
RemapPointer(response_data_.rsa_key),
encrypted_response_data_.rsa_key_length,
RemapPointer(response_data_.rsa_key_iv), wrapped_rsa_key_.data(),
wrapped_key_length);
} else {
return OEMCrypto_RewrapDeviceRSAKey30_V15(
session->session_id(), &encrypted_response_data_.nonce,
RemapPointer(response_data_.enc_message_key),
response_data_.enc_message_key_length,
RemapPointer(response_data_.rsa_key),
encrypted_response_data_.rsa_key_length,
RemapPointer(response_data_.rsa_key_iv), wrapped_rsa_key_.data(),
wrapped_key_length);
}
} }
void ProvisioningRoundTrip::VerifyLoadFailed() { void ProvisioningRoundTrip::VerifyLoadFailed() {
@@ -346,10 +405,17 @@ void ProvisioningRoundTrip::VerifyLoadFailed() {
void LicenseRoundTrip::VerifyRequestSignature( void LicenseRoundTrip::VerifyRequestSignature(
const vector<uint8_t>& data, const vector<uint8_t>& generated_signature, const vector<uint8_t>& data, const vector<uint8_t>& generated_signature,
size_t core_message_length) { size_t core_message_length) {
std::vector<uint8_t> subdata(data.begin() + core_message_length, data.end()); const std::vector<uint8_t> subdata(data.begin() + core_message_length,
data.end());
session()->VerifyRSASignature(subdata, generated_signature.data(), session()->VerifyRSASignature(subdata, generated_signature.data(),
generated_signature.size(), kSign_RSASSA_PSS); generated_signature.size(), kSign_RSASSA_PSS);
SHA256(data.data(), core_message_length, core_response_.request_hash); SHA256(data.data(), core_message_length, request_hash_);
// If the api version was not set by the test, then we record the api version
// from the request. Also, if the api was set to be higher than oemcrypto
// supports, then we lower it. This version will be used in the response.
if (api_version_ == 0) api_version_ = core_request_.api_major_version;
if (api_version_ > global_features.api_version)
api_version_ = global_features.api_version;
} }
void LicenseRoundTrip::FillAndVerifyCoreRequest( void LicenseRoundTrip::FillAndVerifyCoreRequest(
@@ -357,12 +423,16 @@ void LicenseRoundTrip::FillAndVerifyCoreRequest(
EXPECT_TRUE( EXPECT_TRUE(
oemcrypto_core_message::deserialize::CoreLicenseRequestFromMessage( oemcrypto_core_message::deserialize::CoreLicenseRequestFromMessage(
core_message_string, &core_request_)); core_message_string, &core_request_));
EXPECT_EQ(global_features.api_version, core_request_.api_version); EXPECT_EQ(global_features.api_version, core_request_.api_major_version);
// If we are testing the latest OEMCrypto version, make sure it is built with
// the latest ODK version, too:
if (global_features.api_version == ODK_MAJOR_VERSION) {
EXPECT_EQ(ODK_MINOR_VERSION, core_request_.api_minor_version);
}
if (expect_request_has_correct_nonce_) { if (expect_request_has_correct_nonce_) {
EXPECT_EQ(session()->nonce(), core_request_.nonce); EXPECT_EQ(session()->nonce(), core_request_.nonce);
} }
EXPECT_EQ(session()->session_id(), core_request_.session_id); EXPECT_EQ(session()->session_id(), core_request_.session_id);
if (api_version_ == 0) api_version_ = core_request_.api_version;
} }
void LicenseRoundTrip::CreateDefaultResponse() { void LicenseRoundTrip::CreateDefaultResponse() {
@@ -371,10 +441,12 @@ void LicenseRoundTrip::CreateDefaultResponse() {
memset(response_data_.padding, 0, sizeof(response_data_.padding)); memset(response_data_.padding, 0, sizeof(response_data_.padding));
EXPECT_EQ(1, GetRandBytes(response_data_.mac_keys, EXPECT_EQ(1, GetRandBytes(response_data_.mac_keys,
sizeof(response_data_.mac_keys))); sizeof(response_data_.mac_keys)));
// For backwards compatibility, we use the license duration for each key's // For backwards compatibility, we use the largest limit in timer_limits for
// duration. // each key's duration.
uint32_t duration = static_cast<uint32_t>( uint32_t key_duration = static_cast<uint32_t>(
core_response_.timer_limits.license_duration_seconds); std::max({core_response_.timer_limits.rental_duration_seconds,
core_response_.timer_limits.total_playback_duration_seconds,
core_response_.timer_limits.initial_renewal_duration_seconds}));
// The key data for an entitlement license is an AES-256 key, otherwise the // The key data for an entitlement license is an AES-256 key, otherwise the
// default is an AES_128 key. // default is an AES_128 key.
uint32_t default_key_size = uint32_t default_key_size =
@@ -393,7 +465,7 @@ void LicenseRoundTrip::CreateDefaultResponse() {
sizeof(response_data_.keys[i].control_iv))); sizeof(response_data_.keys[i].control_iv)));
std::string kcVersion = "kc" + std::to_string(api_version_); std::string kcVersion = "kc" + std::to_string(api_version_);
memcpy(response_data_.keys[i].control.verification, kcVersion.c_str(), 4); memcpy(response_data_.keys[i].control.verification, kcVersion.c_str(), 4);
response_data_.keys[i].control.duration = htonl(duration); response_data_.keys[i].control.duration = htonl(key_duration);
response_data_.keys[i].control.nonce = htonl(session_->nonce()); response_data_.keys[i].control.nonce = htonl(session_->nonce());
response_data_.keys[i].control.control_bits = htonl(control_); response_data_.keys[i].control.control_bits = htonl(control_);
response_data_.keys[i].cipher_mode = OEMCrypto_CipherMode_CTR; response_data_.keys[i].cipher_mode = OEMCrypto_CipherMode_CTR;
@@ -493,12 +565,20 @@ void LicenseRoundTrip::EncryptAndSignResponse() {
if (api_version_ < kCoreMessagesAPI) { if (api_version_ < kCoreMessagesAPI) {
serialized_core_message_.resize(0); serialized_core_message_.resize(0);
} else { } else {
std::string request_hash_string(
reinterpret_cast<const char*>(request_hash_), sizeof(request_hash_));
ASSERT_TRUE(oemcrypto_core_message::serialize::CreateCoreLicenseResponse( ASSERT_TRUE(oemcrypto_core_message::serialize::CreateCoreLicenseResponse(
core_response_, core_request_, &serialized_core_message_)); core_response_, core_request_, request_hash_string,
&serialized_core_message_));
} }
// Make the message buffer a just big enough, or the
// required size, whichever is larger.
const size_t message_size =
std::max(required_message_size_, serialized_core_message_.size() +
sizeof(encrypted_response_data_));
// Stripe the encrypted message. // Stripe the encrypted message.
encrypted_response_.resize(message_size_); encrypted_response_.resize(message_size);
for (size_t i = 0; i < encrypted_response_.size(); i++) { for (size_t i = 0; i < encrypted_response_.size(); i++) {
encrypted_response_[i] = i % 0x100; encrypted_response_[i] = i % 0x100;
} }
@@ -522,15 +602,19 @@ void LicenseRoundTrip::EncryptAndSignResponse() {
OEMCryptoResult LicenseRoundTrip::LoadResponse(Session* session) { OEMCryptoResult LicenseRoundTrip::LoadResponse(Session* session) {
EXPECT_NE(session, nullptr); EXPECT_NE(session, nullptr);
// Some tests adjust the offset to be beyond the length of the message. Here, // Some tests adjust the offset to be beyond the length of the message. Here,
// we create a duplicate of the message buffer so that these offsets do not // we create a duplicate of the main message buffer so that these offsets do
// point to garbage data. The goal is to make sure OEMCrypto is verifying that // not point to garbage data. The goal is to make sure OEMCrypto is verifying
// the offset points outside of the message -- we don't want OEMCrypto to look // that the offset points outside of the message -- we don't want OEMCrypto to
// at what offset points to and return an error if the data is garbage. Since // look at what offset points to and return an error if the data is
// the memory after the message buffer is an exact copy of the message, we can // garbage. Since the memory after the message buffer is an exact copy of the
// increment the offset by the message size and get valid data. // message, we can increment the offset by the message size and get valid
// data.
std::vector<uint8_t> double_message = encrypted_response_; std::vector<uint8_t> double_message = encrypted_response_;
double_message.insert(double_message.end(), encrypted_response_.begin(), double_message.insert(
encrypted_response_.end()); double_message.end(),
reinterpret_cast<const uint8_t*>(&encrypted_response_data_),
reinterpret_cast<const uint8_t*>(&encrypted_response_data_) +
sizeof(encrypted_response_data_));
OEMCryptoResult result; OEMCryptoResult result;
if (api_version_ < kCoreMessagesAPI) { if (api_version_ < kCoreMessagesAPI) {
result = OEMCrypto_LoadKeys( result = OEMCrypto_LoadKeys(
@@ -584,11 +668,9 @@ void LicenseRoundTrip::VerifyTestKeys() {
if (sts != OEMCrypto_ERROR_NOT_IMPLEMENTED) { if (sts != OEMCrypto_ERROR_NOT_IMPLEMENTED) {
ASSERT_EQ(OEMCrypto_SUCCESS, sts); ASSERT_EQ(OEMCrypto_SUCCESS, sts);
ASSERT_EQ(sizeof(block), size); ASSERT_EQ(sizeof(block), size);
// control duration and bits stored in network byte order. For printing // Note: we do not assume that duration is stored with each key after v16.
// control bits stored in network byte order. For printing
// we change to host byte order. // we change to host byte order.
ASSERT_EQ(htonl_fnc(response_data_.keys[i].control.duration),
htonl_fnc(block.duration))
<< "For key " << i;
ASSERT_EQ(htonl_fnc(response_data_.keys[i].control.control_bits), ASSERT_EQ(htonl_fnc(response_data_.keys[i].control.control_bits),
htonl_fnc(block.control_bits)) htonl_fnc(block.control_bits))
<< "For key " << i; << "For key " << i;
@@ -758,8 +840,8 @@ void RenewalRoundTrip::FillAndVerifyCoreRequest(
EXPECT_TRUE( EXPECT_TRUE(
oemcrypto_core_message::deserialize::CoreRenewalRequestFromMessage( oemcrypto_core_message::deserialize::CoreRenewalRequestFromMessage(
core_message_string, &core_request_)); core_message_string, &core_request_));
EXPECT_EQ(license_messages_->core_request().api_version, EXPECT_EQ(license_messages_->core_request().api_major_version,
core_request_.api_version); core_request_.api_major_version);
if (!is_release_) { if (!is_release_) {
// For a license release, we do not expect the nonce to be correct. That // For a license release, we do not expect the nonce to be correct. That
// is because a release might be sent without loading the license first. // is because a release might be sent without loading the license first.
@@ -786,12 +868,17 @@ void RenewalRoundTrip::CreateDefaultResponse() {
constexpr size_t index = 0; constexpr size_t index = 0;
response_data_.keys[index].key_id_length = 0; response_data_.keys[index].key_id_length = 0;
response_data_.keys[index].key_id[0] = '\0'; response_data_.keys[index].key_id[0] = '\0';
std::string kcVersion = "kc" + std::to_string(core_request_.api_version); std::string kcVersion =
"kc" + std::to_string(core_request_.api_major_version);
if (global_features.api_version < kCoreMessagesAPI) {
// For v15 or earlier devices, we use the api of the device.
kcVersion = "kc" + std::to_string(global_features.api_version);
}
memcpy(response_data_.keys[index].control.verification, kcVersion.c_str(), memcpy(response_data_.keys[index].control.verification, kcVersion.c_str(),
4); 4);
const uint32_t duration = static_cast<uint32_t>( const uint32_t duration = static_cast<uint32_t>(
license_messages_->core_response() license_messages_->core_response()
.timer_limits.renewal_playback_duration_seconds); .timer_limits.initial_renewal_duration_seconds);
response_data_.keys[index].control.duration = htonl(duration); response_data_.keys[index].control.duration = htonl(duration);
response_data_.keys[index].control.nonce = htonl(nonce); response_data_.keys[index].control.nonce = htonl(nonce);
response_data_.keys[index].control.control_bits = htonl(control); response_data_.keys[index].control.control_bits = htonl(control);
@@ -801,12 +888,6 @@ void RenewalRoundTrip::CreateDefaultResponse() {
void RenewalRoundTrip::EncryptAndSignResponse() { void RenewalRoundTrip::EncryptAndSignResponse() {
// Renewal messages are not encrypted. // Renewal messages are not encrypted.
encrypted_response_data_ = response_data_; encrypted_response_data_ = response_data_;
// Stripe the encrypted message.
encrypted_response_.resize(message_size_);
for (size_t i = 0; i < encrypted_response_.size(); i++) {
encrypted_response_[i] = i % 0x100;
}
// Either create a KeyRefreshObject for a call to RefreshKeys or a core // Either create a KeyRefreshObject for a call to RefreshKeys or a core
// response for a call to LoadRenewal. // response for a call to LoadRenewal.
if (license_messages_->api_version() < kCoreMessagesAPI) { if (license_messages_->api_version() < kCoreMessagesAPI) {
@@ -818,7 +899,17 @@ void RenewalRoundTrip::EncryptAndSignResponse() {
serialized_core_message_.resize(0); serialized_core_message_.resize(0);
} else { } else {
ASSERT_TRUE(oemcrypto_core_message::serialize::CreateCoreRenewalResponse( ASSERT_TRUE(oemcrypto_core_message::serialize::CreateCoreRenewalResponse(
core_request_, &serialized_core_message_)); core_request_, renewal_duration_seconds_, &serialized_core_message_));
}
// Make the message buffer a just big enough, or the
// required size, whichever is larger.
const size_t message_size =
std::max(required_message_size_, serialized_core_message_.size() +
sizeof(encrypted_response_data_));
// Stripe the encrypted message.
encrypted_response_.resize(message_size);
for (size_t i = 0; i < encrypted_response_.size(); i++) {
encrypted_response_[i] = i % 0x100;
} }
// Concatenate the core message and the response. // Concatenate the core message and the response.
ASSERT_GE(kMaxCoreMessage, serialized_core_message_.size()); ASSERT_GE(kMaxCoreMessage, serialized_core_message_.size());

View File

@@ -133,13 +133,6 @@ uint32_t htonl_fnc(uint32_t x);
// Prints error string from BoringSSL // Prints error string from BoringSSL
void dump_boringssl_error(); void dump_boringssl_error();
// Given a message and field, returns an OEMCrypto_Substring with the field's
// offset into the message and its length. If |set_zero| is true, both the
// offset and length will be zero.
OEMCrypto_Substring GetSubstring(const std::string& message = "",
const std::string& field = "",
bool set_zero = false);
class Session; class Session;
// The prototype of the OEMCrypto function to prepare and sign a request. // The prototype of the OEMCrypto function to prepare and sign a request.
typedef OEMCryptoResult (*PrepAndSignRequest_t)( typedef OEMCryptoResult (*PrepAndSignRequest_t)(
@@ -159,7 +152,7 @@ class RoundTrip {
core_response_(), core_response_(),
response_data_(), response_data_(),
encrypted_response_data_(), encrypted_response_data_(),
message_size_(sizeof(ResponseData) + kMaxCoreMessage){}; required_message_size_(0) {}
virtual ~RoundTrip() {} virtual ~RoundTrip() {}
// Have OEMCrypto sign a request message and then verify the signature and the // Have OEMCrypto sign a request message and then verify the signature and the
@@ -190,9 +183,7 @@ class RoundTrip {
} }
// Set the size of the buffer used the encrypted license. // Set the size of the buffer used the encrypted license.
void set_message_size(size_t size) { message_size_ = size; } void set_message_size(size_t size) { required_message_size_ = size; }
// The size of the encrypted message.
size_t message_size() { return message_size_; }
std::vector<uint8_t>& response_signature() { return response_signature_; } std::vector<uint8_t>& response_signature() { return response_signature_; }
const std::string& serialized_core_message() const { const std::string& serialized_core_message() const {
return serialized_core_message_; return serialized_core_message_;
@@ -218,7 +209,9 @@ class RoundTrip {
CoreRequest core_request_; CoreRequest core_request_;
CoreResponse core_response_; CoreResponse core_response_;
ResponseData response_data_, encrypted_response_data_; ResponseData response_data_, encrypted_response_data_;
size_t message_size_; // How much of the padded message to use. // Message buffers will be at least this big. Tests for loading and signing
// messages will increase all buffers to this size.
size_t required_message_size_;
std::vector<uint8_t> response_signature_; std::vector<uint8_t> response_signature_;
std::string serialized_core_message_; std::string serialized_core_message_;
std::vector<uint8_t> encrypted_response_; std::vector<uint8_t> encrypted_response_;
@@ -257,6 +250,15 @@ class ProvisioningRoundTrip
// Verify the values of the core response. // Verify the values of the core response.
virtual void FillAndVerifyCoreRequest( virtual void FillAndVerifyCoreRequest(
const std::string& core_message_string) override; const std::string& core_message_string) override;
// Load the response, without the retry. Called by LoadResponse.
OEMCryptoResult LoadResponseNoRetry(Session* session,
size_t* wrapped_key_length);
// This takes a pointer in the response_data_ and remaps it to the same
// pointer within the encrypted message. This is used for backwards
// compatibliity testing, so that a v15 oemcrypto will accept range checks.
template <typename T>
const T* RemapPointer(const T* response_pointer) const;
uint32_t allowed_schemes_; uint32_t allowed_schemes_;
Encryptor encryptor_; Encryptor encryptor_;
// The message key used for Prov 3.0. // The message key used for Prov 3.0.
@@ -317,7 +319,7 @@ class LicenseRoundTrip
} }
// Change the hash of the core request. This should cause the response to be // Change the hash of the core request. This should cause the response to be
// rejected. // rejected.
void BreakRequestHash() { core_response_.request_hash[3] ^= 42; } void BreakRequestHash() { request_hash_[3] ^= 42; }
// Set the API version for the license itself. This will be used in // Set the API version for the license itself. This will be used in
// CreateDefaultResponse. // CreateDefaultResponse.
void set_api_version(uint32_t api_version) { api_version_ = api_version; } void set_api_version(uint32_t api_version) { api_version_ = api_version; }
@@ -363,6 +365,7 @@ class LicenseRoundTrip
// Whether this is a content license or an entitlement license. Used in // Whether this is a content license or an entitlement license. Used in
// CreateDefaultResponse. // CreateDefaultResponse.
OEMCrypto_LicenseType license_type_; OEMCrypto_LicenseType license_type_;
uint8_t request_hash_[ODK_SHA256_HASH_SIZE];
}; };
class RenewalRoundTrip class RenewalRoundTrip
@@ -377,11 +380,20 @@ class RenewalRoundTrip
: RoundTrip(license_messages->session()), : RoundTrip(license_messages->session()),
license_messages_(license_messages), license_messages_(license_messages),
refresh_object_(), refresh_object_(),
renewal_duration_seconds_(
license_messages->core_response()
.timer_limits.initial_renewal_duration_seconds),
is_release_(false) {} is_release_(false) {}
void CreateDefaultResponse() override; void CreateDefaultResponse() override;
void EncryptAndSignResponse() override; void EncryptAndSignResponse() override;
OEMCryptoResult LoadResponse() override { return LoadResponse(session_); } OEMCryptoResult LoadResponse() override { return LoadResponse(session_); }
OEMCryptoResult LoadResponse(Session* session) override; OEMCryptoResult LoadResponse(Session* session) override;
uint64_t renewal_duration_seconds() const {
return renewal_duration_seconds_;
}
void set_renewal_duration_seconds(uint64_t renewal_duration_seconds) {
renewal_duration_seconds_ = renewal_duration_seconds;
}
void set_is_release(bool is_release) { is_release_ = is_release; } void set_is_release(bool is_release) { is_release_ = is_release; }
protected: protected:
@@ -393,6 +405,7 @@ class RenewalRoundTrip
const std::string& core_message_string) override; const std::string& core_message_string) override;
LicenseRoundTrip* license_messages_; LicenseRoundTrip* license_messages_;
OEMCrypto_KeyRefreshObject refresh_object_; OEMCrypto_KeyRefreshObject refresh_object_;
uint64_t renewal_duration_seconds_;
bool is_release_; // If this is a license release, and not a real renewal. bool is_release_; // If this is a license release, and not a real renewal.
}; };

View File

@@ -71,7 +71,6 @@ void SessionUtil::EnsureTestKeys() {
// This makes sure that the derived keys (encryption key and two mac keys) // This makes sure that the derived keys (encryption key and two mac keys)
// are installed in OEMCrypto and in the test session. // are installed in OEMCrypto and in the test session.
void SessionUtil::InstallTestRSAKey(Session* s) { void SessionUtil::InstallTestRSAKey(Session* s) {
ASSERT_TRUE(global_features.uses_certificate);
if (global_features.loads_certificate) { if (global_features.loads_certificate) {
if (wrapped_rsa_key_.size() == 0) { if (wrapped_rsa_key_.size() == 0) {
// If we don't have a wrapped key yet, create one. // If we don't have a wrapped key yet, create one.

View File

@@ -214,7 +214,7 @@ TEST_F(OEMCryptoClientTest, VersionNumber) {
} }
// The resource rating is a number from 1 to 4. The first three levels were // The resource rating is a number from 1 to 4. The first three levels were
// initiallly defined in API 15 and they were expaneded in API 16. // initially defined in API 15 and they were expanded in API 16.
TEST_F(OEMCryptoClientTest, ResourceRatingAPI15) { TEST_F(OEMCryptoClientTest, ResourceRatingAPI15) {
ASSERT_GE(OEMCrypto_ResourceRatingTier(), 1u); ASSERT_GE(OEMCrypto_ResourceRatingTier(), 1u);
ASSERT_LE(OEMCrypto_ResourceRatingTier(), 4u); ASSERT_LE(OEMCrypto_ResourceRatingTier(), 4u);
@@ -298,7 +298,7 @@ TEST_F(OEMCryptoClientTest, CheckMaxNumberOfSessionsAPI10) {
TEST_F(OEMCryptoClientTest, CheckUsageTableSizeAPI16) { TEST_F(OEMCryptoClientTest, CheckUsageTableSizeAPI16) {
const size_t maximum = OEMCrypto_MaximumUsageTableHeaderSize(); const size_t maximum = OEMCrypto_MaximumUsageTableHeaderSize();
printf(" Max Usage Table Size: %zu.\n", maximum); printf(" Max Usage Table Size: %zu.\n", maximum);
// A maximum of 0 means the table is constrained my dynamic memory allocation. // A maximum of 0 means the table is constrained by dynamic memory allocation.
if (maximum > 0) ASSERT_GE(maximum, RequiredUsageSize()); if (maximum > 0) ASSERT_GE(maximum, RequiredUsageSize());
} }
@@ -875,7 +875,7 @@ TEST_P(OEMCryptoLicenseTest, LoadKeyWithNonce) {
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
} }
// Verify that a second license may be not be loaded in a session. // Verify that a second license may not be loaded in a session.
TEST_P(OEMCryptoLicenseTest, LoadKeyNoNonceTwiceAPI16) { TEST_P(OEMCryptoLicenseTest, LoadKeyNoNonceTwiceAPI16) {
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
license_messages_.set_control(0); license_messages_.set_control(0);
@@ -886,8 +886,8 @@ TEST_P(OEMCryptoLicenseTest, LoadKeyNoNonceTwiceAPI16) {
ASSERT_EQ(OEMCrypto_ERROR_LICENSE_RELOAD, license_messages_.LoadResponse()); ASSERT_EQ(OEMCrypto_ERROR_LICENSE_RELOAD, license_messages_.LoadResponse());
} }
// Verify that a second license may be not be loaded in a session. // Verify that a second license may not be loaded in a session.
TEST_P(OEMCryptoLicenseTest, LoadKeyWithNonceTwice) { TEST_P(OEMCryptoLicenseTest, LoadKeyWithNonceTwiceAPI16) {
ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce());
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
@@ -985,7 +985,7 @@ TEST_P(OEMCryptoLicenseTest, LoadKeyWithBadRange_enc_mac_keys) {
// See the comment in LicenseRoundTrip::LoadResponse for why we increment by // See the comment in LicenseRoundTrip::LoadResponse for why we increment by
// the message size. // the message size.
license_messages_.core_response().enc_mac_keys.offset += license_messages_.core_response().enc_mac_keys.offset +=
license_messages_.message_size(); sizeof(license_messages_.response_data());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
} }
@@ -997,7 +997,7 @@ TEST_P(OEMCryptoLicenseTest, LoadKeyWithBadRange_enc_mac_keys_iv) {
// See the comment in LicenseRoundTrip::LoadResponse for why we increment by // See the comment in LicenseRoundTrip::LoadResponse for why we increment by
// the message size. // the message size.
license_messages_.core_response().enc_mac_keys_iv.offset += license_messages_.core_response().enc_mac_keys_iv.offset +=
license_messages_.message_size(); sizeof(license_messages_.response_data());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
} }
@@ -1009,7 +1009,7 @@ TEST_P(OEMCryptoLicenseTest, LoadKeyWithBadRange_key_id) {
// See the comment in LicenseRoundTrip::LoadResponse for why we increment by // See the comment in LicenseRoundTrip::LoadResponse for why we increment by
// the message size. // the message size.
license_messages_.core_response().key_array[0].key_id.offset += license_messages_.core_response().key_array[0].key_id.offset +=
license_messages_.message_size(); sizeof(license_messages_.response_data());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
} }
@@ -1021,7 +1021,7 @@ TEST_P(OEMCryptoLicenseTest, LoadKeyWithBadRange_key_data) {
// See the comment in LicenseRoundTrip::LoadResponse for why we increment by // See the comment in LicenseRoundTrip::LoadResponse for why we increment by
// the message size. // the message size.
license_messages_.core_response().key_array[1].key_data.offset += license_messages_.core_response().key_array[1].key_data.offset +=
license_messages_.message_size(); sizeof(license_messages_.response_data());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
} }
@@ -1033,7 +1033,7 @@ TEST_P(OEMCryptoLicenseTest, LoadKeyWithBadRange_key_data_iv) {
// See the comment in LicenseRoundTrip::LoadResponse for why we increment by // See the comment in LicenseRoundTrip::LoadResponse for why we increment by
// the message size. // the message size.
license_messages_.core_response().key_array[1].key_data_iv.offset += license_messages_.core_response().key_array[1].key_data_iv.offset +=
license_messages_.message_size(); sizeof(license_messages_.response_data());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
} }
@@ -1045,7 +1045,7 @@ TEST_P(OEMCryptoLicenseTest, LoadKeyWithBadRange_key_control) {
// See the comment in LicenseRoundTrip::LoadResponse for why we increment by // See the comment in LicenseRoundTrip::LoadResponse for why we increment by
// the message size. // the message size.
license_messages_.core_response().key_array[2].key_control.offset += license_messages_.core_response().key_array[2].key_control.offset +=
license_messages_.message_size(); sizeof(license_messages_.response_data());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
} }
@@ -1057,7 +1057,7 @@ TEST_P(OEMCryptoLicenseTest, LoadKeyWithBadRange_key_control_iv) {
// See the comment in LicenseRoundTrip::LoadResponse for why we increment by // See the comment in LicenseRoundTrip::LoadResponse for why we increment by
// the message size. // the message size.
license_messages_.core_response().key_array[2].key_control_iv.offset += license_messages_.core_response().key_array[2].key_control_iv.offset +=
license_messages_.message_size(); sizeof(license_messages_.response_data());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
} }
@@ -1071,7 +1071,7 @@ TEST_P(OEMCryptoLicenseTest, LoadKeyWithBadRange_pst) {
// See the comment in LicenseRoundTrip::LoadResponse for why we increment by // See the comment in LicenseRoundTrip::LoadResponse for why we increment by
// the message size. // the message size.
license_messages_.core_response().pst.offset += license_messages_.core_response().pst.offset +=
license_messages_.message_size(); sizeof(license_messages_.response_data());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
// If we have a pst, then we need a usage entry. // If we have a pst, then we need a usage entry.
ASSERT_NO_FATAL_FAILURE(session_.CreateNewUsageEntry()); ASSERT_NO_FATAL_FAILURE(session_.CreateNewUsageEntry());
@@ -1081,7 +1081,8 @@ TEST_P(OEMCryptoLicenseTest, LoadKeyWithBadRange_pst) {
//---------------------------------------------------------------------------// //---------------------------------------------------------------------------//
// The IV should not be identical to the data right before the encrypted mac // The IV should not be identical to the data right before the encrypted mac
// keys. // keys. This requirement was added in 15.2, so it frequently fails on
// production devices.
TEST_F(OEMCryptoLicenseTestAPI15, LoadKeyWithSuspiciousIV) { TEST_F(OEMCryptoLicenseTestAPI15, LoadKeyWithSuspiciousIV) {
ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce());
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
@@ -1272,7 +1273,7 @@ TEST_P(OEMCryptoLicenseTest, LoadKeyUnalignedMessageAPI16) {
ASSERT_EQ(OEMCrypto_SUCCESS, ASSERT_EQ(OEMCrypto_SUCCESS,
OEMCrypto_LoadLicense( OEMCrypto_LoadLicense(
session_.session_id(), unaligned_message, session_.session_id(), unaligned_message,
license_messages_.message_size(), license_messages_.encrypted_response_buffer().size(),
license_messages_.serialized_core_message().size(), license_messages_.serialized_core_message().size(),
license_messages_.response_signature().data(), license_messages_.response_signature().data(),
license_messages_.response_signature().size())); license_messages_.response_signature().size()));
@@ -1293,6 +1294,10 @@ TEST_P(OEMCryptoLicenseTest, LoadLicenseAgainFailureAPI16) {
TEST_P(OEMCryptoLicenseTestRangeAPI, LoadKeys) { TEST_P(OEMCryptoLicenseTestRangeAPI, LoadKeys) {
ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce());
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
// Re-set the API version. The function VerifyRequestSignature sets the api to
// be a sane value. But in this test, we want to verify an unsupported version
// is rejected.
license_messages_.set_api_version(license_api_version_);
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
@@ -1626,6 +1631,7 @@ TEST_F(OEMCryptoSessionTests, CheckMinimumPatchLevel) {
ASSERT_NO_FATAL_FAILURE(license_messages.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages.EncryptAndSignResponse()); ASSERT_NO_FATAL_FAILURE(license_messages.EncryptAndSignResponse());
EXPECT_EQ(global_features.api_version, license_messages.api_version());
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages.LoadResponse()); ASSERT_EQ(OEMCrypto_SUCCESS, license_messages.LoadResponse());
} }
// Reject any future patch levels. // Reject any future patch levels.
@@ -1716,22 +1722,16 @@ class OEMCryptoRefreshTest : public OEMCryptoLicenseTest {
// playback right away. All times are in seconds since the license was // playback right away. All times are in seconds since the license was
// signed. // signed.
// Soft expiry false means timers are strictly enforce. // Soft expiry false means timers are strictly enforce.
timer_limits_.soft_expiry = false; timer_limits_.soft_enforce_rental_duration = true;
timer_limits_.soft_enforce_playback_duration = false;
// Playback may begin immediately. // Playback may begin immediately.
timer_limits_.earliest_playback_start_seconds = 0; timer_limits_.earliest_playback_start_seconds = 0;
// First playback may be within the first two seconds. // First playback may be within the first two seconds.
timer_limits_.latest_playback_start_seconds = kDuration; timer_limits_.rental_duration_seconds = kDuration;
// Once started, playback may last two seconds without a renewal. // Once started, playback may last two seconds without a renewal.
timer_limits_.initial_playback_duration_seconds = kDuration; timer_limits_.initial_renewal_duration_seconds = kDuration;
// Playback may continue for four seconds after a renewal is loaded. // Total playback is not limited.
timer_limits_.renewal_playback_duration_seconds = 2 * kDuration; timer_limits_.total_playback_duration_seconds = 0;
if (license_api_version_ < kCoreMessagesAPI) {
// For legacy licenses, only license duration is enforced.
timer_limits_.license_duration_seconds = kDuration;
} else {
// Total playback is not limited.
timer_limits_.license_duration_seconds = 0;
}
} }
void LoadLicense() { void LoadLicense() {
@@ -1902,8 +1902,8 @@ TEST_P(OEMCryptoLicenseTest, HashForbiddenAPI15) {
TEST_P(OEMCryptoLicenseTest, Decrypt) { TEST_P(OEMCryptoLicenseTest, Decrypt) {
ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce());
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
license_messages_.core_response().timer_limits.license_duration_seconds = license_messages_.core_response()
kDuration; .timer_limits.total_playback_duration_seconds = kDuration;
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
@@ -1914,7 +1914,8 @@ TEST_P(OEMCryptoLicenseTest, Decrypt) {
TEST_P(OEMCryptoLicenseTest, DecryptZeroDuration) { TEST_P(OEMCryptoLicenseTest, DecryptZeroDuration) {
ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce());
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
license_messages_.core_response().timer_limits.license_duration_seconds = 0; license_messages_.core_response()
.timer_limits.total_playback_duration_seconds = 0;
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
@@ -2179,8 +2180,8 @@ class OEMCryptoSessionTestsDecryptTests
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
license_messages_.core_response().timer_limits.license_duration_seconds = license_messages_.core_response()
kDuration; .timer_limits.initial_renewal_duration_seconds = kDuration;
memcpy(license_messages_.response_data().keys[0].key_data, key_, memcpy(license_messages_.response_data().keys[0].key_data, key_,
sizeof(key_)); sizeof(key_));
license_messages_.response_data().keys[0].cipher_mode = cipher_mode_; license_messages_.response_data().keys[0].cipher_mode = cipher_mode_;
@@ -2424,7 +2425,7 @@ TEST_P(OEMCryptoSessionTestsDecryptTests, PartialBlock) {
// //
// 1) The maximum total sample size // 1) The maximum total sample size
// 2) The maximum number of subsamples multiplied by the maximum subsample size // 2) The maximum number of subsamples multiplied by the maximum subsample size
TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptMaxSample) { TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptMaxSampleAPI16) {
const size_t max_sample_size = GetResourceValue(kMaxSampleSize); const size_t max_sample_size = GetResourceValue(kMaxSampleSize);
const size_t max_subsample_size = GetResourceValue(kMaxSubsampleSize); const size_t max_subsample_size = GetResourceValue(kMaxSubsampleSize);
const size_t max_num_subsamples = GetResourceValue(kMaxNumberSubsamples); const size_t max_num_subsamples = GetResourceValue(kMaxNumberSubsamples);
@@ -2589,8 +2590,8 @@ TEST_P(OEMCryptoLicenseTest, DecryptNoAnalogToClearAPI13) {
TEST_P(OEMCryptoLicenseTest, KeyDuration) { TEST_P(OEMCryptoLicenseTest, KeyDuration) {
ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce());
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
license_messages_.core_response().timer_limits.license_duration_seconds = license_messages_.core_response()
kDuration; .timer_limits.total_playback_duration_seconds = kDuration;
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
@@ -2669,14 +2670,14 @@ TEST_F(OEMCryptoLoadsCertificate, CertificateProvision) {
// Verify that RewrapDeviceRSAKey checks pointers are within the provisioning // Verify that RewrapDeviceRSAKey checks pointers are within the provisioning
// message. // message.
TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange1) { TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange1_API16) {
Session s; Session s;
ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_); ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_);
provisioning_messages.PrepareSession(keybox_); provisioning_messages.PrepareSession(keybox_);
ASSERT_NO_FATAL_FAILURE(provisioning_messages.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(provisioning_messages.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(provisioning_messages.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(provisioning_messages.CreateDefaultResponse());
provisioning_messages.core_response().enc_private_key.offset = provisioning_messages.core_response().enc_private_key.offset =
provisioning_messages.message_size() + 1; provisioning_messages.encrypted_response_buffer().size() + 1;
ASSERT_NO_FATAL_FAILURE(provisioning_messages.EncryptAndSignResponse()); ASSERT_NO_FATAL_FAILURE(provisioning_messages.EncryptAndSignResponse());
ASSERT_NE(OEMCrypto_SUCCESS, provisioning_messages.LoadResponse()); ASSERT_NE(OEMCrypto_SUCCESS, provisioning_messages.LoadResponse());
provisioning_messages.VerifyLoadFailed(); provisioning_messages.VerifyLoadFailed();
@@ -2684,14 +2685,14 @@ TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange1) {
// Verify that RewrapDeviceRSAKey checks pointers are within the provisioning // Verify that RewrapDeviceRSAKey checks pointers are within the provisioning
// message. // message.
TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange2) { TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange2_API16) {
Session s; Session s;
ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_); ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_);
provisioning_messages.PrepareSession(keybox_); provisioning_messages.PrepareSession(keybox_);
ASSERT_NO_FATAL_FAILURE(provisioning_messages.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(provisioning_messages.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(provisioning_messages.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(provisioning_messages.CreateDefaultResponse());
provisioning_messages.core_response().enc_private_key_iv.offset = provisioning_messages.core_response().enc_private_key_iv.offset =
provisioning_messages.message_size() + 1; provisioning_messages.encrypted_response_buffer().size() + 1;
ASSERT_NO_FATAL_FAILURE(provisioning_messages.EncryptAndSignResponse()); ASSERT_NO_FATAL_FAILURE(provisioning_messages.EncryptAndSignResponse());
ASSERT_NE(OEMCrypto_SUCCESS, provisioning_messages.LoadResponse()); ASSERT_NE(OEMCrypto_SUCCESS, provisioning_messages.LoadResponse());
provisioning_messages.VerifyLoadFailed(); provisioning_messages.VerifyLoadFailed();
@@ -2699,7 +2700,7 @@ TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange2) {
// Verify that RewrapDeviceRSAKey checks pointers are within the provisioning // Verify that RewrapDeviceRSAKey checks pointers are within the provisioning
// message. // message.
TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange3) { TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange3_API16) {
Session s; Session s;
ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_); ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_);
provisioning_messages.PrepareSession(keybox_); provisioning_messages.PrepareSession(keybox_);
@@ -2708,7 +2709,7 @@ TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange3) {
// If the offset is before the end, but the offset+length is bigger, then // If the offset is before the end, but the offset+length is bigger, then
// the message should be rejected. // the message should be rejected.
provisioning_messages.core_response().enc_private_key.offset = provisioning_messages.core_response().enc_private_key.offset =
provisioning_messages.message_size() - 5; provisioning_messages.encrypted_response_buffer().size() - 5;
ASSERT_NO_FATAL_FAILURE(provisioning_messages.EncryptAndSignResponse()); ASSERT_NO_FATAL_FAILURE(provisioning_messages.EncryptAndSignResponse());
ASSERT_NE(OEMCrypto_SUCCESS, provisioning_messages.LoadResponse()); ASSERT_NE(OEMCrypto_SUCCESS, provisioning_messages.LoadResponse());
provisioning_messages.VerifyLoadFailed(); provisioning_messages.VerifyLoadFailed();
@@ -2716,7 +2717,7 @@ TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange3) {
// Verify that RewrapDeviceRSAKey checks pointers are within the provisioning // Verify that RewrapDeviceRSAKey checks pointers are within the provisioning
// message. // message.
TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange4) { TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange4_API16) {
Session s; Session s;
ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_); ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_);
provisioning_messages.PrepareSession(keybox_); provisioning_messages.PrepareSession(keybox_);
@@ -2725,7 +2726,7 @@ TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange4) {
// If the offset is before the end, but the offset+length is bigger, then // If the offset is before the end, but the offset+length is bigger, then
// the message should be rejected. // the message should be rejected.
provisioning_messages.core_response().enc_private_key_iv.offset = provisioning_messages.core_response().enc_private_key_iv.offset =
provisioning_messages.message_size() - 5; provisioning_messages.encrypted_response_buffer().size() - 5;
ASSERT_NO_FATAL_FAILURE(provisioning_messages.EncryptAndSignResponse()); ASSERT_NO_FATAL_FAILURE(provisioning_messages.EncryptAndSignResponse());
ASSERT_NE(OEMCrypto_SUCCESS, provisioning_messages.LoadResponse()); ASSERT_NE(OEMCrypto_SUCCESS, provisioning_messages.LoadResponse());
provisioning_messages.VerifyLoadFailed(); provisioning_messages.VerifyLoadFailed();
@@ -2733,7 +2734,7 @@ TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange4) {
// Verify that RewrapDeviceRSAKey checks pointers are within the provisioning // Verify that RewrapDeviceRSAKey checks pointers are within the provisioning
// message. // message.
TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange5Prov30) { TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange5Prov30_API16) {
Session s; Session s;
ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_); ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_);
provisioning_messages.PrepareSession(keybox_); provisioning_messages.PrepareSession(keybox_);
@@ -2742,7 +2743,7 @@ TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange5Prov30) {
// If the offset is before the end, but the offset+length is bigger, then // If the offset is before the end, but the offset+length is bigger, then
// the message should be rejected. // the message should be rejected.
provisioning_messages.core_response().encrypted_message_key.offset = provisioning_messages.core_response().encrypted_message_key.offset =
provisioning_messages.message_size() + 1; provisioning_messages.encrypted_response_buffer().size() + 1;
ASSERT_NO_FATAL_FAILURE(provisioning_messages.EncryptAndSignResponse()); ASSERT_NO_FATAL_FAILURE(provisioning_messages.EncryptAndSignResponse());
ASSERT_NE(OEMCrypto_SUCCESS, provisioning_messages.LoadResponse()); ASSERT_NE(OEMCrypto_SUCCESS, provisioning_messages.LoadResponse());
provisioning_messages.VerifyLoadFailed(); provisioning_messages.VerifyLoadFailed();
@@ -2764,7 +2765,7 @@ TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadSignatureKeyboxTest) {
} }
// Test that RewrapDeviceRSAKey verifies the nonce is current. // Test that RewrapDeviceRSAKey verifies the nonce is current.
TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadNonce) { TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadNonce_API16) {
Session s; Session s;
ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_); ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_);
provisioning_messages.PrepareSession(keybox_); provisioning_messages.PrepareSession(keybox_);
@@ -3199,7 +3200,7 @@ TEST_F(OEMCryptoLoadsCertificateAlternates, DisallowForbiddenPaddingAPI09) {
// The alternate padding is only required for cast receivers, but if a device // The alternate padding is only required for cast receivers, but if a device
// does load an alternate certificate, it should NOT use it for generating // does load an alternate certificate, it should NOT use it for generating
// a license request signature. // a license request signature.
TEST_F(OEMCryptoLoadsCertificateAlternates, TestSignaturePKCS1) { TEST_F(OEMCryptoLoadsCertificateAlternates, TestSignaturePKCS1_API16) {
// Try to load an RSA key with alternative padding schemes. This signing // Try to load an RSA key with alternative padding schemes. This signing
// scheme is used by cast receivers. // scheme is used by cast receivers.
LoadWithAllowedSchemes(kSign_PKCS1_Block1, false); LoadWithAllowedSchemes(kSign_PKCS1_Block1, false);
@@ -4587,8 +4588,8 @@ TEST_P(OEMCryptoGenericCryptoTest, GenericKeyVerifyLargeBuffer) {
// Test Generic_Encrypt when the key duration has expired. // Test Generic_Encrypt when the key duration has expired.
TEST_P(OEMCryptoGenericCryptoTest, KeyDurationEncrypt) { TEST_P(OEMCryptoGenericCryptoTest, KeyDurationEncrypt) {
license_messages_.core_response().timer_limits.license_duration_seconds = license_messages_.core_response()
kDuration; .timer_limits.total_playback_duration_seconds = kDuration;
license_messages_.CreateResponseWithGenericCryptoKeys(); license_messages_.CreateResponseWithGenericCryptoKeys();
EncryptAndLoadKeys(); EncryptAndLoadKeys();
vector<uint8_t> expected_encrypted; vector<uint8_t> expected_encrypted;
@@ -4622,8 +4623,8 @@ TEST_P(OEMCryptoGenericCryptoTest, KeyDurationEncrypt) {
// Test Generic_Decrypt when the key duration has expired. // Test Generic_Decrypt when the key duration has expired.
TEST_P(OEMCryptoGenericCryptoTest, KeyDurationDecrypt) { TEST_P(OEMCryptoGenericCryptoTest, KeyDurationDecrypt) {
license_messages_.core_response().timer_limits.license_duration_seconds = license_messages_.core_response()
kDuration; .timer_limits.total_playback_duration_seconds = kDuration;
license_messages_.CreateResponseWithGenericCryptoKeys(); license_messages_.CreateResponseWithGenericCryptoKeys();
EncryptAndLoadKeys(); EncryptAndLoadKeys();
@@ -4656,8 +4657,8 @@ TEST_P(OEMCryptoGenericCryptoTest, KeyDurationDecrypt) {
// Test Generic_Sign when the key duration has expired. // Test Generic_Sign when the key duration has expired.
TEST_P(OEMCryptoGenericCryptoTest, KeyDurationSign) { TEST_P(OEMCryptoGenericCryptoTest, KeyDurationSign) {
license_messages_.core_response().timer_limits.license_duration_seconds = license_messages_.core_response()
kDuration; .timer_limits.total_playback_duration_seconds = kDuration;
license_messages_.CreateResponseWithGenericCryptoKeys(); license_messages_.CreateResponseWithGenericCryptoKeys();
EncryptAndLoadKeys(); EncryptAndLoadKeys();
@@ -4692,8 +4693,8 @@ TEST_P(OEMCryptoGenericCryptoTest, KeyDurationSign) {
// Test Generic_Verify when the key duration has expired. // Test Generic_Verify when the key duration has expired.
TEST_P(OEMCryptoGenericCryptoTest, KeyDurationVerify) { TEST_P(OEMCryptoGenericCryptoTest, KeyDurationVerify) {
license_messages_.core_response().timer_limits.license_duration_seconds = license_messages_.core_response()
kDuration; .timer_limits.total_playback_duration_seconds = kDuration;
license_messages_.CreateResponseWithGenericCryptoKeys(); license_messages_.CreateResponseWithGenericCryptoKeys();
EncryptAndLoadKeys(); EncryptAndLoadKeys();
@@ -4732,8 +4733,8 @@ class OEMCryptoGenericCryptoKeyIdLengthTest
OEMCryptoGenericCryptoTest::SetUp(); OEMCryptoGenericCryptoTest::SetUp();
license_messages_.set_num_keys(5); license_messages_.set_num_keys(5);
license_messages_.set_control(wvoec::kControlAllowDecrypt); license_messages_.set_control(wvoec::kControlAllowDecrypt);
license_messages_.core_response().timer_limits.license_duration_seconds = license_messages_.core_response()
kDuration; .timer_limits.total_playback_duration_seconds = kDuration;
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
SetUniformKeyIdLength(16); // Start with all key ids being 16 bytes. SetUniformKeyIdLength(16); // Start with all key ids being 16 bytes.
// But, we are testing that the key ids do not have to have the same length. // But, we are testing that the key ids do not have to have the same length.
@@ -5127,7 +5128,7 @@ TEST_P(OEMCryptoUsageTableTest, OnlineMissingEntry) {
// Sessions should have at most one entry at a time. This tests different // Sessions should have at most one entry at a time. This tests different
// orderings of CreateNewUsageEntry and LoadUsageEntry calls. // orderings of CreateNewUsageEntry and LoadUsageEntry calls.
TEST_P(OEMCryptoUsageTableTest, CreateAndLoadMultipleEntries) { TEST_P(OEMCryptoUsageTableTest, CreateAndLoadMultipleEntriesAPI16) {
// Entry Count: we start each test with an empty header. // Entry Count: we start each test with an empty header.
uint32_t usage_entry_number; uint32_t usage_entry_number;
LicenseWithUsageEntry entry; LicenseWithUsageEntry entry;

View File

@@ -1,4 +1,4 @@
// Copyright 2013 Google Inc. All Rights Reserved. // Copyright 2019 Google Inc. All Rights Reserved.
// //
// Clock - A fake clock just for running tests. // Clock - A fake clock just for running tests.

View File

@@ -1,4 +1,4 @@
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary // Copyright 2019 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master // source code may only be used and distributed under the Widevine Master
// License Agreement. // License Agreement.
// //