OEMCrypto v16.2 unit tests and reference code
This commit is contained in:
Binary file not shown.
@@ -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
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -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_ */
|
|
||||||
@@ -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_ */
|
|
||||||
@@ -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_ */
|
|
||||||
@@ -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 OEMCrypto’s 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_ */
|
||||||
|
|||||||
@@ -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_
|
|
||||||
13
oemcrypto/odk/include/odk_target.h
Normal file
13
oemcrypto/odk/include/odk_target.h
Normal 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_ */
|
||||||
@@ -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_
|
|
||||||
@@ -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_
|
|
||||||
@@ -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_
|
|
||||||
@@ -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;
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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());
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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',
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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" */
|
||||||
|
|||||||
@@ -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_ */
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
}
|
||||||
|
|||||||
25
oemcrypto/odk/src/odk_util.c
Normal file
25
oemcrypto/odk/src/odk_util.c
Normal 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;
|
||||||
|
}
|
||||||
24
oemcrypto/odk/src/odk_util.h
Normal file
24
oemcrypto/odk/src/odk_util.h
Normal 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_ */
|
||||||
@@ -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; }
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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
@@ -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) {
|
||||||
|
|||||||
@@ -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);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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*");
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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());
|
||||||
|
|||||||
@@ -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.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
||||||
//
|
//
|
||||||
|
|||||||
Reference in New Issue
Block a user