Initial v16 ODK Library

This commit has the initial ODK library.  Partners may use this code
to begin integrating the ODK library into their platform.  The
functionality is not complete, but this should help partners get an
early start playing with build files.
This commit is contained in:
Fred Gylys-Colwell
2019-10-04 14:10:55 -07:00
parent ded4417dd4
commit 4de11d11e8
65 changed files with 4371 additions and 2004 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -29,6 +29,7 @@ extern "C" {
typedef uint32_t OEMCrypto_SESSION;
// clang-format off
typedef enum OEMCryptoResult {
OEMCrypto_SUCCESS = 0,
OEMCrypto_ERROR_INIT_FAILED = 1,
@@ -87,7 +88,14 @@ typedef enum OEMCryptoResult {
OEMCrypto_ERROR_OUTPUT_TOO_LARGE = 54,
OEMCrypto_ERROR_SESSION_LOST_STATE = 55,
OEMCrypto_ERROR_SYSTEM_INVALIDATED = 56,
/* ODK return values */
ODK_ERROR_BASE = 1000,
ODK_ERROR_CORE_MESSAGE = ODK_ERROR_BASE,
ODK_SET_TIMER = ODK_ERROR_BASE + 1,
ODK_DISABLE_TIMER = ODK_ERROR_BASE + 2,
ODK_TIMER_EXPIRED = ODK_ERROR_BASE + 3,
} OEMCryptoResult;
// clang-format on
/*
* OEMCrypto_DestBufferDesc
@@ -393,6 +401,7 @@ typedef enum OEMCrypto_ProvisioningMethod {
/*
* Obfuscation Renames.
*/
// clang-format off
#define OEMCrypto_Initialize _oecc01
#define OEMCrypto_Terminate _oecc02
#define OEMCrypto_InstallKeybox _oecc03
@@ -413,7 +422,7 @@ typedef enum OEMCrypto_ProvisioningMethod {
#define OEMCrypto_OpenSession _oecc09
#define OEMCrypto_CloseSession _oecc10
#define OEMCrypto_DecryptCTR_V10 _oecc11
#define OEMCrypto_GenerateDerivedKeys _oecc12
#define OEMCrypto_GenerateDerivedKeys_V15 _oecc12
#define OEMCrypto_GenerateSignature _oecc13
#define OEMCrypto_GenerateNonce _oecc14
#define OEMCrypto_LoadKeys_V8 _oecc15
@@ -451,7 +460,7 @@ typedef enum OEMCrypto_ProvisioningMethod {
#define OEMCrypto_LoadKeys_V11_or_V12 _oecc47
#define OEMCrypto_DecryptCENC _oecc48
#define OEMCrypto_GetProvisioningMethod _oecc49
#define OEMCrypto_GetOEMPublicCertificate _oecc50
#define OEMCrypto_GetOEMPublicCertificate_V15 _oecc50
#define OEMCrypto_RewrapDeviceRSAKey30 _oecc51
#define OEMCrypto_SupportedCertificates _oecc52
#define OEMCrypto_IsSRMUpdateSupported _oecc53
@@ -483,8 +492,22 @@ typedef enum OEMCrypto_ProvisioningMethod {
#define OEMCrypto_GetHashErrorCode _oecc89
#define OEMCrypto_BuildInformation _oecc90
#define OEMCrypto_RefreshKeys _oecc91
#define OEMCrypto_LoadEntitledContentKeys _oecc92
#define OEMCrypto_LoadEntitledContentKeys_V15 _oecc92
#define OEMCrypto_CopyBuffer _oecc93
#define OEMCrypto_MaximumUsageTableHeaderSize _oecc94
#define OEMCrypto_GenerateDerivedKeys _oecc95
#define OEMCrypto_SignLicenseRequest _oecc96
#define OEMCrypto_SignRenewalRequest _oecc97
#define OEMCrypto_SignProvisioningRequest _oecc98
#define OEMCrypto_LoadLicense _oecc99
#define OEMCrypto_ReloadLicense _oecc100
#define OEMCrypto_LoadRenewal _oecc101
#define OEMCrypto_LoadProvisioning _oecc102
#define OEMCrypto_LoadOEMPrivateKey _oecc103
#define OEMCrypto_GetOEMPublicCertificate _oecc104
#define OEMCrypto_DecryptCENC_TODO _oecc105
#define OEMCrypto_LoadEntitledContentKeys _oecc106
// clang-format on
/*
* OEMCrypto_SetSandbox
@@ -700,9 +723,9 @@ OEMCryptoResult OEMCrypto_CloseSession(OEMCrypto_SESSION session);
*/
OEMCryptoResult OEMCrypto_GenerateDerivedKeys(OEMCrypto_SESSION session,
const uint8_t* mac_key_context,
uint32_t mac_key_context_length,
size_t mac_key_context_length,
const uint8_t* enc_key_context,
uint32_t enc_key_context_length);
size_t enc_key_context_length);
/*
* OEMCrypto_DeriveKeysFromSessionKey
@@ -893,11 +916,6 @@ OEMCryptoResult OEMCrypto_GenerateNonce(OEMCrypto_SESSION session,
* Version:
* This method changed in API version 12.
*/
OEMCryptoResult OEMCrypto_GenerateSignature(OEMCrypto_SESSION session,
const uint8_t* message,
size_t message_length,
uint8_t* signature,
size_t* signature_length);
/*
* OEMCrypto_LoadSRM
@@ -1178,13 +1196,6 @@ OEMCryptoResult OEMCrypto_LoadSRM(const uint8_t* buffer, size_t buffer_length);
* Version:
* This method changed in API version 14.
*/
OEMCryptoResult OEMCrypto_LoadKeys(
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
const uint8_t* signature, size_t signature_length,
OEMCrypto_Substring enc_mac_keys_iv, OEMCrypto_Substring enc_mac_keys,
size_t num_keys, const OEMCrypto_KeyObject* key_array,
OEMCrypto_Substring pst, OEMCrypto_Substring srm_restriction_data,
OEMCrypto_LicenseType license_type);
/*
* OEMCrypto_LoadEntitledContentKeys
@@ -1247,7 +1258,8 @@ OEMCryptoResult OEMCrypto_LoadKeys(
*/
OEMCryptoResult OEMCrypto_LoadEntitledContentKeys(
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
size_t num_keys, const OEMCrypto_EntitledContentKeyObject* key_array);
const OEMCrypto_EntitledContentKeyObject* key_array,
size_t key_array_length);
/*
* OEMCrypto_RefreshKeys
@@ -1361,10 +1373,6 @@ OEMCryptoResult OEMCrypto_LoadEntitledContentKeys(
* Version:
* This method changed in API version 12.
*/
OEMCryptoResult OEMCrypto_RefreshKeys(
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
const uint8_t* signature, size_t signature_length, size_t num_keys,
const OEMCrypto_KeyRefreshObject* key_array);
/*
* OEMCrypto_QueryKeyControl
@@ -2391,9 +2399,6 @@ OEMCryptoResult OEMCrypto_LoadTestKeybox(const uint8_t* buffer, size_t length);
* Version:
* This method is new API version 12.
*/
OEMCryptoResult OEMCrypto_GetOEMPublicCertificate(OEMCrypto_SESSION session,
uint8_t* public_cert,
size_t* public_cert_length);
/*
* OEMCrypto_GetRandom
@@ -2429,7 +2434,6 @@ OEMCryptoResult OEMCrypto_GetOEMPublicCertificate(OEMCrypto_SESSION session,
* Version:
* This method is supported in all API versions.
*/
OEMCryptoResult OEMCrypto_GetRandom(uint8_t* randomData, size_t dataLength);
/*
* OEMCrypto_APIVersion
@@ -3094,12 +3098,6 @@ uint32_t OEMCrypto_ResourceRatingTier(void);
* Version:
* This method changed in API version 12.
*/
OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey30(
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);
/*
* OEMCrypto_RewrapDeviceRSAKey
@@ -3229,12 +3227,6 @@ OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey30(
* Version:
* This method changed in API version 12.
*/
OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(
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);
/*
* OEMCrypto_LoadDeviceRSAKey
@@ -3943,9 +3935,6 @@ OEMCryptoResult OEMCrypto_ShrinkUsageTableHeader(uint32_t new_entry_count,
* Version:
* This method is new in API version 13.
*/
OEMCryptoResult OEMCrypto_CopyOldUsageEntry(OEMCrypto_SESSION session,
const uint8_t* pst,
size_t pst_length);
/*
* OEMCrypto_DeleteOldUsageTable
@@ -3977,7 +3966,6 @@ OEMCryptoResult OEMCrypto_CopyOldUsageEntry(OEMCrypto_SESSION session,
* Version:
* This method is new in API version 13.
*/
OEMCryptoResult OEMCrypto_DeleteOldUsageTable(void);
/*
* OEMCrypto_RemoveSRM
@@ -4032,14 +4020,6 @@ OEMCryptoResult OEMCrypto_RemoveSRM(void);
* Version:
* This method is new in API version 13.
*/
OEMCryptoResult OEMCrypto_CreateOldUsageEntry(uint64_t time_since_license_received,
uint64_t time_since_first_decrypt,
uint64_t time_since_last_decrypt,
OEMCrypto_Usage_Entry_Status status,
uint8_t *server_mac_key,
uint8_t *client_mac_key,
const uint8_t* pst,
size_t pst_length);
/*
* OEMCrypto_SupportsDecryptHash
@@ -4190,6 +4170,158 @@ OEMCryptoResult OEMCrypto_SetDecryptHash(OEMCrypto_SESSION session,
OEMCryptoResult OEMCrypto_GetHashErrorCode(OEMCrypto_SESSION session,
uint32_t* failed_frame_number);
/****************************************************************************/
/****************************************************************************/
/* The following functions are deprecated. They are not required for the
* current version of OEMCrypto. They are being declared here to help with
* backwards compatibility.
*/
OEMCryptoResult OEMCrypto_GenerateSignature(OEMCrypto_SESSION session,
const uint8_t* message,
size_t message_length,
uint8_t* signature,
size_t* signature_length);
OEMCryptoResult OEMCrypto_LoadKeys(
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
const uint8_t* signature, size_t signature_length,
OEMCrypto_Substring enc_mac_keys_iv, OEMCrypto_Substring enc_mac_keys,
size_t num_keys, const OEMCrypto_KeyObject* key_array,
OEMCrypto_Substring pst, OEMCrypto_Substring srm_restriction_data,
OEMCrypto_LicenseType license_type);
OEMCryptoResult OEMCrypto_RefreshKeys(
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
const uint8_t* signature, size_t signature_length, size_t num_keys,
const OEMCrypto_KeyRefreshObject* key_array);
OEMCryptoResult OEMCrypto_GetRandom(uint8_t* randomData, size_t dataLength);
OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey30(
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);
OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(
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);
OEMCryptoResult OEMCrypto_UpdateUsageTable();
OEMCryptoResult OEMCrypto_DeleteUsageEntry(OEMCrypto_SESSION, const uint8_t*,
size_t, const uint8_t*, size_t,
const uint8_t*, size_t);
OEMCryptoResult OEMCrypto_ForceDeleteUsageEntry(const uint8_t*, size_t);
OEMCryptoResult OEMCrypto_CopyOldUsageEntry(OEMCrypto_SESSION session,
const uint8_t* pst,
size_t pst_length);
OEMCryptoResult OEMCrypto_DeleteOldUsageTable(void);
OEMCryptoResult OEMCrypto_CreateOldUsageEntry(
uint64_t time_since_license_received, uint64_t time_since_first_decrypt,
uint64_t time_since_last_decrypt, OEMCrypto_Usage_Entry_Status status,
uint8_t* server_mac_key, uint8_t* client_mac_key, const uint8_t* pst,
size_t pst_length);
OEMCryptoResult OEMCrypto_GenerateDerivedKeys_V15(
OEMCrypto_SESSION session, const uint8_t* mac_key_context,
uint32_t mac_key_context_length, const uint8_t* enc_key_context,
uint32_t enc_key_context_length);
OEMCryptoResult OEMCrypto_LoadEntitledContentKeys_V15(
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
size_t num_keys, const OEMCrypto_EntitledContentKeyObject* key_array);
// TODO: functions below will be updated in future CL.
OEMCryptoResult OEMCrypto_DecryptCENC_V15(
OEMCrypto_SESSION session, const uint8_t* data_addr, size_t data_length,
bool is_encrypted, const uint8_t* iv,
size_t block_offset, // used for CTR "cenc" mode only.
OEMCrypto_DestBufferDesc* out_buffer,
const OEMCrypto_CENCEncryptPatternDesc* pattern, uint8_t subsample_flags);
OEMCryptoResult OEMCrypto_GetOEMPublicCertificate_V15(
OEMCrypto_SESSION session, uint8_t* public_cert,
size_t* public_cert_length);
/****************************************************************************/
/****************************************************************************/
/* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */
// Hand added functions. This section will be replaced after docs have been
// updated.
size_t OEMCrypto_MaximumUsageTableHeaderSize();
OEMCryptoResult OEMCrypto_SignLicenseRequest(
OEMCrypto_SESSION session, const uint8_t* protobuf_message,
size_t protobuf_message_length, uint8_t* core_message,
size_t* core_message_length, uint8_t* signature, size_t* signature_length);
OEMCryptoResult OEMCrypto_SignRenewalRequest(
OEMCrypto_SESSION session, const uint8_t* protobuf_message,
size_t protobuf_message_length, uint8_t* core_message,
size_t* core_message_length, uint8_t* signature, size_t* signature_length);
OEMCryptoResult OEMCrypto_SignProvisioningRequest(
OEMCrypto_SESSION session, const uint8_t* protobuf_message,
size_t protobuf_message_length, uint8_t* core_message,
size_t* core_message_length, uint8_t* signature, size_t* signature_length);
// TODO: functions below have not yet added.
OEMCryptoResult OEMCrypto_LoadLicense(OEMCrypto_SESSION session,
const uint8_t* protobuf_message,
size_t protobuf_message_length,
const uint8_t* core_message,
size_t core_message_length,
const uint8_t* signature,
size_t signature_length);
OEMCryptoResult OEMCrypto_ReloadLicense(
OEMCrypto_SESSION session, const uint8_t* protobuf_message,
size_t protobuf_message_length, const uint8_t* core_message,
size_t core_message_length, const uint8_t* signature,
size_t signature_length, bool v15_license);
OEMCryptoResult OEMCrypto_LoadRenewal(OEMCrypto_SESSION session,
const uint8_t* protobuf_message,
size_t protobuf_message_length,
const uint8_t* core_message,
size_t core_message_length,
const uint8_t* signature,
size_t signature_length);
OEMCryptoResult OEMCrypto_LoadProvisioning(
OEMCrypto_SESSION session, const uint8_t* protobuf_message,
size_t protobuf_message_length, const uint8_t* core_message,
size_t core_message_length, const uint8_t* signature,
size_t signature_length, const uint8_t* wrapped_private_key,
size_t* wrapped_private_key_length);
OEMCryptoResult OEMCrypto_LoadOEMPrivateKey(OEMCrypto_SESSION session);
OEMCryptoResult OEMCrypto_GetOEMPublicCertificate(uint8_t* public_cert,
size_t* public_cert_length);
// TODO: move up to top and add comments.
typedef struct {
const uint8_t* input_data; // source for encrypted data.
size_t input_data_length; // length of encrypted data.
OEMCrypto_DestBufferDesc output; // destination for clear data.
} InputOutputPair;
typedef struct {
size_t num_bytes_clear;
size_t num_bytes_encrypted;
} SubSampleDescription;
typedef struct {
InputOutputPair buffers; // The source and destination for this sample.
const uint8_t iv[16]; // The IV for the initial subsample.
const SubSampleDescription* subsamples; // an array of subsamples.
size_t subsamples_length; // the number of elements in subsample.
} SampleDescription;
OEMCryptoResult OEMCrypto_DecryptCENC_TODO(
OEMCrypto_SESSION session,
const SampleDescription* samples, // an array of samples.
size_t sample_length, // the number of samples.
const OEMCrypto_CENCEncryptPatternDesc* pattern);
// End of hand added functions.
/* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */
#ifdef __cplusplus
}
#endif

310
oemcrypto/odk/include/odk.h Normal file
View File

@@ -0,0 +1,310 @@
/*
* 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.
*/
/*********************************************************************
* odk.h
*
* OEMCrypto v16 Core Message Serialization library
*
* 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 ODK functions that parse code will fill out structs that have similar
* formats to the function parameters of the OEMCrypto v15 functions being
* replaced. The ODK will be provided in source code and it is Widevine's
* intention that partners can build and link ODK with their implementation
* of OEMCrypto with no or few code changes.
*
* OEMCrypto implementers shall build the ODK library as part of the Trusted
* Application (TA) running in the TEE. All memory and buffers used by the
* ODK library shall be sanitized by the OEMCrypto implementer to prevent
* modification by any process running the REE.
*
* See the document "Widevine Core Message Serialization" for a detailed
* description of the ODK API. You can find this document in the widevine
* repository as docs/Widevine_Core_Message_Serialization.pdf
*
*********************************************************************/
#ifndef ODK_H_
#define ODK_H_
#include <stdint.h>
#include "OEMCryptoCENC.h"
#include "odk_structs.h"
#ifdef __cplusplus
extern "C" {
#endif
/*
* ODK_PrepareCoreLicenseRequest
*
* Description
* OEMCrypto will use ODK_PrepareCoreLicenseRequest to prepare the core
* license request message.
*
* If ODK_PrepareCoreLicenseRequest returns OEMCrypto_SUCCESS, then
* OEMCrypto shall sign the concatenation of the core message and a non-core
* CDM protobuf message using the DRM certificate's private key. If it
* returns an error, the error should be returned by OEMCrypto to the CDM
* layer.
*
* We use nonce to prevent replay attacks.
*
* We use session_id and nonce in license request/response to prevent
* birthday attacks that attemp to trigger nonce collision.
*
* Parameters:
* [in/out] message:
* Pointer to memory for the entire message.
* Modified by the ODK library.
* [in] message_length: length of the entire message buffer.
* [in/out] core_message_length:
* length of the core message at the beginning of the message.
* (in) size of buffer reserved for the core message, in bytes.
* (out) actual length of the core message, in bytes.
* [in] api_version: should be the same as OEMCrypto_APIVersion
* [in] nonce: the nonce generated by OEMCrypto_GenerateNonce
* [in] session_id: the current session id.
*
* Returns:
* OEMCrypto_SUCCESS success
* OEMCrypto_ERROR_SHORT_BUFFER if buffer is too short
*/
OEMCryptoResult ODK_PrepareCoreLicenseRequest(
uint8_t* message, size_t message_length, size_t* core_message_length,
uint32_t api_version, uint32_t nonce, uint32_t session_id);
/*
* ODK_PrepareCoreRenewalRequest
*
* Description:
* OEMCrypto will use ODK_PrepareCoreRenewalRequest to prepare the core
* license renewal message.
*
* If ODK_PrepareCoreRenewalRequest returns OEMCrypto_SUCCESS, then OEMCrypto
* signs the concatenation of the core message and a non-core CDM
* protobuf message using the session's client renewal mac key. If it returns
* an error, the error should be returned by OEMCrypto to the CDM layer. This
* renewal mac key will have been delivered in the license via LoadLicense.
*
* The proper value of timers and clocks are discussed in the document "Timer
* and License Renewal Updates". It is important to notice that the nonce
* passed into the renewal message is from the original message loaded via
* LoadLicense. A new nonce is not used for each renewal.
*
* Parameters:
* [in/out] message:
* Pointer to memory for the entire message.
* Modified by the ODK library.
* [in] message_length: length of the entire message buffer.
* [in/out] core_message_length:
* length of the core message at the beginning of the message.
* (in) size of buffer reserved for the core message, in bytes.
* (out) actual length of the core message, in bytes.
* [in] api_version:should be the same as OEMCrypto_APIVersion
* [in] license_nonce: the nonce from the original license.
* [in] session_id: the current session id.
* [in] clock_values: the sessions clock values.
* [in] system_time_seconds: the current time on OEMCrypto's clock.
*
* Returns:
* OEMCrypto_SUCCESS success
* OEMCrypto_ERROR_SHORT_BUFFER if buffer is too short
*/
OEMCryptoResult ODK_PrepareCoreRenewalRequest(
uint8_t* message, size_t message_length, size_t* core_message_length,
uint32_t api_version, uint32_t license_nonce, uint32_t session_id,
const ODK_ClockValues* clock_values, uint64_t system_time_seconds);
/*
* ODK_PrepareCoreProvisioningRequest
*
* Description:
* OEMCrypto will use ODK_PrepareCoreProvisioningRequest to prepare the core
* provisioning message.
*
* If ODK_PrepareCoreProvisioningRequest returns OEMCrypto_SUCCESS, then
* OEMCrypto shall sign the concatenation of the core message and a non-core
* CDM protobuf message. If it returns an error, the error should be returned
* by OEMCrypto to the CDM layer.
*
* For a device that has a keybox, i.e. Provisioning 2.0, OEMCrypto will sign
* the response with the sessions derived client mac key.
*
* For a device that has an OEM Certificate, i.e. Provisioning 3.0, OEMCrypto
* will sign the response with the private key associated with the OEM
* Certificate.
*
* Parameters:
* [in/out] message:
* Pointer to memory for the entire message.
* Modified by the ODK library.
* [in] message_length: length of the entire message buffer.
* [in/out] core_message_length:
* length of the core message at the beginning of the message.
* (in) size of buffer reserved for the core message, in bytes.
* (out) actual length of the core message, in bytes.
* [in] api_version: should be the same as OEMCrypto_APIVersion
* [in] nonce: the nonce generated by OEMCrypto_GenerateNonce
* [in] session_id: the current session id.
* [in] device_id:
* For devices with a keybox, this is the device id from the keybox.
* For devices with an OEM Certificate, this is a device unique id string.
* [in] device_id_length: length of device_id, at most 64 bytes.
*
* Returns:
* OEMCrypto_SUCCESS success
* OEMCrypto_ERROR_SHORT_BUFFER if buffer is too short
*/
OEMCryptoResult ODK_PrepareCoreProvisioningRequest(
uint8_t* message, size_t message_length, size_t* core_message_length,
uint32_t api_version, uint32_t nonce, uint32_t session_id,
const uint8_t* const device_id, uint32_t device_id_length);
/*
* ODK_ParseLicense
*
* Description:
* OEMCrypto will use ODK_ParseLicense to parse and verify the license
* response.
*
* ODK_ParseLicense will parse the license response and verify:
* 1. Either the nonce matches the one passed in or
* the license does not require a nonce.
* 2. The API version of the license response matches.
* 3. The session id of the license response matches.
*
* ODK_ParseLicense will parse the message and set each OEMCrypto_Substring
* output field to a location in the license response. If the license
* response does not parse correctly, ODK_ParseLicense will return an error
* that OEMCrypto should return to the CDM.
*
* Parameters:
* [in] message: pointer to license response message
* [in] message_length: length of the license response
* [in] api_version: should be the same as OEMCrypto_APIVersion
* [in] nonce: the last nonce generated by OEMCrypto_GenerateNonce
* [in] session_id: the current session id.
* [in] initial_license_load:
* true when called for OEMCrypto_LoadLicense
* false when called for OEMCrypto_ReloadLicense
* [in] usage_entry_present:
* whether the session has a new usage entry associated with it created via
* OEMCrypto_CreateNewUsageEntry
* [in] max_num_keys:
* the maximum size of the array key_array.
* For many implementations, this is a compile time constant
* [out] parsed_license: destination struct for parsed output
*
* Returns:
* OEMCrypto_SUCCESS success
* ODK_ERROR_CORE_MESSAGE
* if the license response did not parse correctly,
* or there were other incorrect values.
*/
OEMCryptoResult ODK_ParseLicense(const uint8_t* message, size_t message_length,
uint32_t api_version, uint32_t nonce,
uint32_t session_id, bool initial_license_load,
bool usage_entry_present, size_t max_num_keys,
ODK_ParsedLicense* parsed_license);
/*
* ODK_ParseRenewal
*
* Description:
* OEMCrypto will use ODK_ParseRenewal to parse and verify the renewal
* response.
*
* If ODK_ParseRenewal returns success, then the session's timers and clocks
* will be updated as described in the document "Timer and License Renewal
* Updates" and in "Widevine Modular DRM Version 16 Delta". If
* ODK_ParseRenewal returns an error, OEMCrypto returns the error to the CDM
* layer.
*
* Parameters:
* [in] message: pointer to renewal response message
* [in] message_length: length of the renewal response
* [in] api_version: should be the same as OEMCrypto_APIVersion
* [in] license_nonce: the nonce from the original license.
* [in] session_id: the current session id.
* [in] system_time: the current time on OEMCrypto's clock.
* [in] timer_limits: timer limits specified in the license.
* [in/out] clock_values: the sessions clock values.
* [out] timer_value:
* set to the new timer value.
* Only used if the return value is ODK_SET_TIMER.
*
* Returns:
* ODK_ERROR_CORE_MESSAGE
* if the renewal response did not parse correctly,
* or there were other incorrect values.
* ODK_SET_TIMER Success, reset timer to the specified timer value.
* ODK_DISABLE_TIMER Success, but disable timer. Allow Unlimited playback.
* ODK_TIMER_EXPIRED Disable timer. Playback is not allowed.
*/
OEMCryptoResult ODK_ParseRenewal(const uint8_t* message, size_t message_length,
uint32_t api_version, uint32_t license_nonce,
uint32_t session_id, uint64_t system_time,
const ODK_TimerLimits* timer_limits,
ODK_ClockValues* clock_values,
uint64_t* timer_value);
/*
* ODK_ParseProvisioning
*
* Description:
* OEMCrypto will use ODK_ParseProvisioning to parse and verify the
* provisioning response.
*
* After the provisioning response has been parsed, OEMCrypto does the same
* verification and data flow as the v15 functions
* OEMCrypto_RewrapDeviceRSAKey or OEMCrypto_RewrapDeviceRSAKey30 depending
* on if the device has a keybox (Provisioning 2.0) or has an OEM Certificate
* (Provisioning 3.0).
*
* Parameters:
* [in] message: pointer to renewal response message
* [in] message_length: length of the renewal response
* [in] api_version: should be the same as OEMCrypto_APIVersion
* [in] nonce: the last nonce generated by OEMCrypto_GenerateNonce
* [in] session_id: the current session id.
* [in] device_id:
* For devices with a keybox, this is the device id from the keybox.
* For devices with an OEM Certificate, this is a device unique id string.
* [in] device_id_length: length of device_id, at most 64 bytes.
* [out] parsed_response: destination struct for parsed output
*
* Returns:
* OEMCrypto_SUCCESS success
* ODK_ERROR_CORE_MESSAGE
* if the provisioning response did not parse correctly,
* or there were other incorrect values.
*/
OEMCryptoResult ODK_ParseProvisioning(
const uint8_t* message, size_t message_length, uint32_t api_version,
uint32_t nonce, uint32_t session_id, const uint8_t* device_id,
size_t device_id_length, ODK_ParsedProvisioning* parsed_response);
#ifdef __cplusplus
}
#endif
#endif // ODK_H_

View File

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

View File

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

View File

@@ -0,0 +1,76 @@
/*
* 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_H_
#define ODK_STRUCTS_H_
#include <stdint.h>
#include "OEMCryptoCENC.h"
#define ODK_MAX_NUM_KEYS 32
/*
* ODK_TimerLimits is filled out by the function ODK_ParseLicense.
*
* The fields in this structure are defined in the core license response
* message. This structure should be kept as part of the session and used
* when calling the ODK timer functions described in the document "License
* Duration and Renewal" distributed as part of the OEMCrypto v16 design.
*/
typedef struct {
uint32_t /*boolean*/ soft_expiry;
uint64_t earliest_playback_start_seconds;
uint64_t latest_playback_start_seconds;
uint64_t initial_playback_duration_seconds;
uint64_t renewal_playback_duration_seconds;
uint64_t license_duration_seconds;
} ODK_TimerLimits;
/*
* ODK_ParsedLicense holds fields from the core license response.
*/
typedef struct {
OEMCrypto_Substring enc_mac_keys_iv;
OEMCrypto_Substring enc_mac_keys;
OEMCrypto_Substring pst;
OEMCrypto_Substring srm_restriction_data;
uint32_t license_type;
uint32_t nonce_required;
ODK_TimerLimits timer_limits;
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
* session. 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 ODK functions listed in the document "License Duration and
* Renewal". The time values are based on OEMCryptos system clock.
*/
typedef struct {
uint64_t time_of_license_signed;
uint64_t time_of_first_decrypt;
uint64_t time_of_last_decrypt;
uint64_t time_when_timer_expires;
uint32_t timer_status;
enum OEMCrypto_Usage_Entry_Status status;
} ODK_ClockValues;
#endif // ODK_STRUCTS_H_

View File

@@ -0,0 +1,59 @@
/*
* 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.
*/
/*********************************************************************
* odk_timer.h
*
* OEMCrypto v16 Timer and Renewal Functions
*
*********************************************************************/
#ifndef ODK_TIMER_H_
#define ODK_TIMER_H_
#include <stdint.h>
#include "OEMCryptoCENC.h"
#include "odk_structs.h"
#ifdef __cplusplus
extern "C" {
#endif
/*
Documentation to be added later. Hopefully automaically from doc.
*/
void ODK_InitializeClockValues(ODK_ClockValues* clock_values,
uint64_t system_time_seconds);
/*
Documentation to be added later. Hopefully automaically from doc.
*/
void ODK_ReloadClockValues(ODK_ClockValues* clock_values,
uint64_t time_of_license_signed,
uint64_t time_of_first_decrypt,
uint64_t time_of_last_decrypt,
enum OEMCrypto_Usage_Entry_Status status,
uint64_t system_time_seconds);
/*
Documentation to be added later. Hopefully automaically from doc.
*/
uint32_t ODK_AttemptFirstPlayback(uint64_t system_time_seconds,
const ODK_TimerLimits* timer_limits,
ODK_ClockValues* clock_values,
uint64_t* timer_value);
/*
Documentation to be added later. Hopefully automaically from doc.
*/
OEMCryptoResult ODK_UpdateLastPlaybackTime(const ODK_TimerLimits* timer_limits,
uint64_t system_time_seconds,
ODK_ClockValues* clock_values);
#ifdef __cplusplus
}
#endif
#endif /* ODK_TIMER_H_ */

258
oemcrypto/odk/src/odk.c Normal file
View File

@@ -0,0 +1,258 @@
/*
* 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 <stddef.h>
#include <stdint.h>
#include <string.h>
#include "odk.h"
#include "odk_overflow.h"
#include "odk_serialize.h"
#include "odk_structs.h"
#include "odk_structs_priv.h"
#include "serialization_base.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;
#define ODK_LICENSE_REQUEST_SIZE 20
#define ODK_RENEWAL_REQUEST_SIZE 28
#define ODK_PROVISIONING_REQUEST_SIZE 88
/* @ private odk functions */
OEMCryptoResult ODK_PrepareRequest(uint8_t* buffer, size_t buffer_length,
size_t* core_message_length,
uint32_t message_type, uint32_t api_version,
uint32_t nonce, uint32_t session_id,
ODK_CoreMessage* core_message) {
if (!core_message_length || !core_message ||
*core_message_length > buffer_length) {
return ODK_ERROR_CORE_MESSAGE;
}
Message* msg = NULL;
AllocateMessage(&msg, message_block);
InitMessage(msg, buffer, *core_message_length);
*core_message = (ODK_CoreMessage){
message_type, 0, api_version, nonce, session_id,
};
switch (message_type) {
case ODK_License_Request_Type: {
core_message->message_length = ODK_LICENSE_REQUEST_SIZE;
Pack_ODK_PreparedLicense(msg, (ODK_PreparedLicense*)core_message);
break;
}
case ODK_Renewal_Request_Type: {
core_message->message_length = ODK_RENEWAL_REQUEST_SIZE;
Pack_ODK_RenewalMessage(msg, (ODK_RenewalMessage*)core_message);
break;
}
case ODK_Provisioning_Request_Type: {
core_message->message_length = ODK_PROVISIONING_REQUEST_SIZE;
Pack_ODK_ProvisioningMessage(msg, (ODK_ProvisioningMessage*)core_message);
break;
}
default: {
return ODK_ERROR_CORE_MESSAGE;
}
}
*core_message_length = core_message->message_length;
if (GetStatus(msg) != MESSAGE_STATUS_OK ||
GetSize(msg) != *core_message_length) {
return OEMCrypto_ERROR_SHORT_BUFFER;
}
return OEMCrypto_SUCCESS;
}
OEMCryptoResult ODK_ParseResponse(const uint8_t* buf, size_t message_length,
uint32_t message_type, uint32_t api_version,
uint32_t nonce, uint32_t session_id,
ODK_CoreMessage* const core_message) {
Message* msg = NULL;
AllocateMessage(&msg, message_block);
InitMessage(msg, (uint8_t*)buf, message_length);
SetSize(msg, message_length);
switch (message_type) {
case ODK_License_Response_Type: {
Unpack_ODK_LicenseResponse(msg, (ODK_LicenseResponse*)core_message);
break;
}
case ODK_Renewal_Response_Type: {
Unpack_ODK_RenewalMessage(msg, (ODK_RenewalMessage*)core_message);
break;
}
case ODK_Provisioning_Response_Type: {
Unpack_ODK_ProvisioningResponse(msg,
(ODK_ProvisioningResponse*)core_message);
break;
}
default: {
return ODK_ERROR_CORE_MESSAGE;
}
}
if (GetStatus(msg) != MESSAGE_STATUS_OK ||
message_type != core_message->message_type ||
GetOffset(msg) != core_message->message_length ||
api_version != core_message->api_version ||
nonce != core_message->nonce || session_id != core_message->session_id) {
return ODK_ERROR_CORE_MESSAGE;
}
return OEMCrypto_SUCCESS;
}
/* @ public odk functions */
/* @@ prepare request functions */
OEMCryptoResult ODK_PrepareCoreLicenseRequest(
uint8_t* message, size_t message_length, size_t* core_message_length,
uint32_t api_version, uint32_t nonce, uint32_t session_id) {
ODK_PreparedLicense license_request = {0};
return ODK_PrepareRequest(message, message_length, core_message_length,
ODK_License_Request_Type, api_version, nonce,
session_id, &license_request.core_message);
}
OEMCryptoResult ODK_PrepareCoreRenewalRequest(
uint8_t* message, size_t message_length, size_t* core_message_length,
uint32_t api_version, uint32_t license_nonce, uint32_t session_id,
const ODK_ClockValues* clock_values, uint64_t system_time_seconds) {
ODK_RenewalMessage renewal_request = {0};
if (odk_sub_overflow_u64(system_time_seconds,
clock_values->time_of_first_decrypt,
&renewal_request.playback_time)) {
return ODK_ERROR_CORE_MESSAGE;
}
return ODK_PrepareRequest(
message, message_length, core_message_length, ODK_Renewal_Request_Type,
api_version, license_nonce, session_id, &renewal_request.core_message);
}
OEMCryptoResult ODK_PrepareCoreProvisioningRequest(
uint8_t* message, size_t message_length, size_t* core_message_length,
uint32_t api_version, uint32_t nonce, uint32_t session_id,
const uint8_t* device_id, uint32_t device_id_length) {
ODK_ProvisioningMessage provisioning_request = {0};
if (device_id_length > sizeof(provisioning_request.device_id)) {
return ODK_ERROR_CORE_MESSAGE;
}
provisioning_request.device_id_length = device_id_length;
if (device_id) {
memcpy(provisioning_request.device_id, device_id, device_id_length);
}
return ODK_PrepareRequest(message, message_length, core_message_length,
ODK_Provisioning_Request_Type, api_version, nonce,
session_id, &provisioning_request.core_message);
}
/* @@ parse request functions */
OEMCryptoResult ODK_ParseLicense(const uint8_t* message, size_t message_length,
uint32_t api_version, uint32_t nonce,
uint32_t session_id, bool initial_license_load,
bool usage_entry_present, size_t max_num_keys,
ODK_ParsedLicense* parsed_license) {
/* todo: check initial_license_load, usage_entry_present, and nonce_reqiured
*/
if (!parsed_license) {
return ODK_ERROR_CORE_MESSAGE;
}
ODK_LicenseResponse license_response = {{0}, parsed_license, max_num_keys};
OEMCryptoResult err = ODK_ParseResponse(
message, message_length, ODK_License_Response_Type, api_version, nonce,
session_id, &license_response.core_message);
return err;
}
OEMCryptoResult ODK_ParseRenewal(const uint8_t* message, size_t message_length,
uint32_t api_version, uint32_t license_nonce,
uint32_t session_id, uint64_t system_time,
const ODK_TimerLimits* timer_limits,
ODK_ClockValues* clock_values,
uint64_t* timer_value) {
if (!timer_limits || !clock_values || !timer_value) {
return ODK_ERROR_CORE_MESSAGE;
}
ODK_RenewalMessage renewal_response = {0};
OEMCryptoResult err = ODK_ParseResponse(
message, message_length, ODK_Renewal_Response_Type, api_version,
license_nonce, session_id, &renewal_response.core_message);
if (err) {
return err;
}
/* Reference:
* Doc: License Duration and Renewal (Changes for OEMCrypto v16)
* Section: Renewal Message
*/
uint64_t playback_timer = 0;
if (odk_sub_overflow_u64(clock_values->time_when_timer_expires, system_time,
&playback_timer)) {
return ODK_TIMER_EXPIRED;
}
uint64_t time_since_playback_began = 0;
uint64_t time_since_reset = 0;
uint64_t time_since_message_signed = 0;
/* ... or use clock_values->time_of_license_signed ? */
if (odk_sub_overflow_u64(system_time, clock_values->time_of_first_decrypt,
&time_since_playback_began) ||
odk_sub_overflow_u64(timer_limits->renewal_playback_duration_seconds,
playback_timer, &time_since_reset) ||
odk_sub_overflow_u64(time_since_playback_began,
renewal_response.playback_time,
&time_since_message_signed) ||
time_since_message_signed >= time_since_reset ||
odk_add_overflow_u64(system_time,
timer_limits->renewal_playback_duration_seconds,
&clock_values->time_when_timer_expires)) {
return ODK_ERROR_CORE_MESSAGE;
}
/* todo: when to return ODK_DISABLE_TIMER */
*timer_value = timer_limits->renewal_playback_duration_seconds;
return ODK_SET_TIMER;
}
OEMCryptoResult ODK_ParseProvisioning(
const uint8_t* message, size_t message_length, uint32_t api_version,
uint32_t nonce, uint32_t session_id, const uint8_t* device_id,
size_t device_id_length, ODK_ParsedProvisioning* parsed_response) {
if (!device_id || !parsed_response) {
return ODK_ERROR_CORE_MESSAGE;
}
ODK_ProvisioningResponse provisioning_response = {{0}, parsed_response};
OEMCryptoResult err = ODK_ParseResponse(
message, message_length, ODK_Provisioning_Response_Type, api_version,
nonce, session_id, &provisioning_response.core_provisioning.core_message);
if (err ||
memcmp(device_id, provisioning_response.core_provisioning.device_id,
device_id_length)) {
return err;
}
return OEMCrypto_SUCCESS;
}

24
oemcrypto/odk/src/odk.gyp Normal file
View File

@@ -0,0 +1,24 @@
# Copyright 2019 Google LLC. All Rights Reserved. This file and proprietary
# source code may only be used and distributed under the Widevine Master License
# Agreement.
{
'targets': [
{
'target_name': 'odk',
'type': 'static_library',
'include_dirs': [
'../include',
'../../include',
],
'includes' : [
'odk.gypi',
],
'direct_dependent_settings': {
'include_dirs': [
'../include',
],
}
},
],
}

View File

@@ -0,0 +1,14 @@
# 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.
{
'sources': [
'odk.c',
'odk_overflow.c',
'odk_serialize.c',
'odk_timer.c',
'serialization_base.c',
],
}

View File

@@ -0,0 +1,38 @@
/*
* 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 <stddef.h>
#include <stdint.h>
int odk_sub_overflow_u64(uint64_t a, uint64_t b, uint64_t* c) {
if (a >= b) {
if (c) {
*c = a - b;
}
return 0;
}
return 1;
}
int odk_add_overflow_u64(uint64_t a, uint64_t b, uint64_t* c) {
if (UINT64_MAX - a >= b) {
if (c) {
*c = a + b;
}
return 0;
}
return 1;
}
int odk_add_overflow_ux(size_t a, size_t b, size_t* c) {
if (SIZE_MAX - a >= b) {
if (c) {
*c = a + b;
}
return 0;
}
return 1;
}

View File

@@ -0,0 +1,115 @@
/*
* 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
*/
#include "odk_structs_priv.h"
#include "serialization_base.h"
void Pack_ODK_CoreMessage(Message* msg, ODK_CoreMessage const* obj) {
Pack_uint32_t(msg, (const uint32_t*)&obj->message_type);
Pack_uint32_t(msg, (const uint32_t*)&obj->message_length);
Pack_uint32_t(msg, (const uint32_t*)&obj->api_version);
Pack_uint32_t(msg, (const uint32_t*)&obj->nonce);
Pack_uint32_t(msg, (const uint32_t*)&obj->session_id);
}
void Pack_ODK_PreparedLicense(Message* msg, ODK_PreparedLicense const* obj) {
Pack_ODK_CoreMessage(msg, (const ODK_CoreMessage*)&obj->core_message);
}
void Pack_ODK_RenewalMessage(Message* msg, ODK_RenewalMessage const* obj) {
Pack_ODK_CoreMessage(msg, (const ODK_CoreMessage*)&obj->core_message);
Pack_uint64_t(msg, (const uint64_t*)&obj->playback_time);
}
void Pack_ODK_ProvisioningMessage(Message* msg,
ODK_ProvisioningMessage const* obj) {
Pack_ODK_CoreMessage(msg, (const ODK_CoreMessage*)&obj->core_message);
Pack_uint32_t(msg, (const uint32_t*)&obj->device_id_length);
PackArray(msg, (const uint8_t*)&obj->device_id[0], 64);
}
void Unpack_ODK_CoreMessage(Message* msg, ODK_CoreMessage* obj) {
Unpack_uint32_t(msg, (uint32_t*)&obj->message_type);
Unpack_uint32_t(msg, (uint32_t*)&obj->message_length);
Unpack_uint32_t(msg, (uint32_t*)&obj->api_version);
Unpack_uint32_t(msg, (uint32_t*)&obj->nonce);
Unpack_uint32_t(msg, (uint32_t*)&obj->session_id);
if (!ValidMessage(msg)) return;
}
void Unpack_ODK_RenewalMessage(Message* msg, ODK_RenewalMessage* obj) {
Unpack_ODK_CoreMessage(msg, (ODK_CoreMessage*)&obj->core_message);
Unpack_uint64_t(msg, (uint64_t*)&obj->playback_time);
}
void Unpack_ODK_ProvisioningMessage(Message* msg,
ODK_ProvisioningMessage* obj) {
Unpack_ODK_CoreMessage(msg, (ODK_CoreMessage*)&obj->core_message);
Unpack_uint32_t(msg, (uint32_t*)&obj->device_id_length);
UnpackArray(msg, (uint8_t*)&obj->device_id[0], 64);
}
void Unpack_OEMCrypto_KeyObject(Message* msg, OEMCrypto_KeyObject* obj) {
Unpack_OEMCrypto_Substring(msg, (OEMCrypto_Substring*)&obj->key_id);
Unpack_OEMCrypto_Substring(msg, (OEMCrypto_Substring*)&obj->key_data_iv);
Unpack_OEMCrypto_Substring(msg, (OEMCrypto_Substring*)&obj->key_data);
Unpack_OEMCrypto_Substring(msg, (OEMCrypto_Substring*)&obj->key_control_iv);
Unpack_OEMCrypto_Substring(msg, (OEMCrypto_Substring*)&obj->key_control);
}
void Unpack_ODK_TimerLimits(Message* msg, ODK_TimerLimits* obj) {
Unpack_uint32_t(msg, (uint32_t*)&obj->soft_expiry);
Unpack_uint64_t(msg, (uint64_t*)&obj->earliest_playback_start_seconds);
Unpack_uint64_t(msg, (uint64_t*)&obj->latest_playback_start_seconds);
Unpack_uint64_t(msg, (uint64_t*)&obj->initial_playback_duration_seconds);
Unpack_uint64_t(msg, (uint64_t*)&obj->renewal_playback_duration_seconds);
Unpack_uint64_t(msg, (uint64_t*)&obj->license_duration_seconds);
}
void Unpack_ODK_ParsedLicense(Message* msg, ODK_ParsedLicense* obj) {
Unpack_OEMCrypto_Substring(msg, (OEMCrypto_Substring*)&obj->enc_mac_keys_iv);
Unpack_OEMCrypto_Substring(msg, (OEMCrypto_Substring*)&obj->enc_mac_keys);
Unpack_OEMCrypto_Substring(msg, (OEMCrypto_Substring*)&obj->pst);
Unpack_OEMCrypto_Substring(msg,
(OEMCrypto_Substring*)&obj->srm_restriction_data);
Unpack_uint32_t(msg, (uint32_t*)&obj->license_type);
Unpack_uint32_t(msg, (uint32_t*)&obj->nonce_required);
Unpack_ODK_TimerLimits(msg, (ODK_TimerLimits*)&obj->timer_limits);
Unpack_uint32_t(msg, (uint32_t*)&obj->key_array_length);
if (obj->key_array_length > ODK_MAX_NUM_KEYS) {
SetStatus(msg, MESSAGE_STATUS_OVERFLOW_ERROR);
return;
}
for (size_t i = 0; i < (size_t)obj->key_array_length; i++) {
Unpack_OEMCrypto_KeyObject(msg, &obj->key_array[i]);
}
}
void Unpack_ODK_ParsedProvisioning(Message* msg, ODK_ParsedProvisioning* obj) {
Unpack_uint32_t(msg, (uint32_t*)&obj->key_type);
Unpack_OEMCrypto_Substring(msg, (OEMCrypto_Substring*)&obj->enc_private_key);
Unpack_OEMCrypto_Substring(msg,
(OEMCrypto_Substring*)&obj->enc_private_key_iv);
Unpack_OEMCrypto_Substring(msg,
(OEMCrypto_Substring*)&obj->encrypted_message_key);
}
void Unpack_ODK_LicenseResponse(Message* msg, ODK_LicenseResponse* obj) {
Unpack_ODK_CoreMessage(msg, (ODK_CoreMessage*)&obj->core_message);
Unpack_ODK_ParsedLicense(msg, (ODK_ParsedLicense*)obj->parsed_license);
}
void Unpack_ODK_ProvisioningResponse(Message* msg,
ODK_ProvisioningResponse* obj) {
Unpack_ODK_ProvisioningMessage(
msg, (ODK_ProvisioningMessage*)&obj->core_provisioning);
Unpack_ODK_ParsedProvisioning(
msg, (ODK_ParsedProvisioning*)obj->parsed_provisioning);
}

View File

@@ -0,0 +1,44 @@
/*
* 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
void Pack_ODK_CoreMessage(Message* msg, ODK_CoreMessage const* obj);
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);
void Unpack_ODK_CoreMessage(Message* msg, ODK_CoreMessage* obj);
void Unpack_ODK_RenewalMessage(Message* msg, ODK_RenewalMessage* obj);
void Unpack_ODK_ProvisioningMessage(Message* msg, ODK_ProvisioningMessage* obj);
void Unpack_OEMCrypto_Substring(Message* msg, OEMCrypto_Substring* obj);
void Unpack_OEMCrypto_KeyObject(Message* msg, OEMCrypto_KeyObject* obj);
void Unpack_ODK_TimerLimits(Message* msg, ODK_TimerLimits* obj);
void Unpack_ODK_ParsedLicense(Message* msg, ODK_ParsedLicense* obj);
void Unpack_ODK_ParsedProvisioning(Message* msg, ODK_ParsedProvisioning* obj);
void Unpack_ODK_LicenseResponse(Message* msg, ODK_LicenseResponse* obj);
void Unpack_ODK_ProvisioningResponse(Message* msg,
ODK_ProvisioningResponse* obj);
#ifdef __cplusplus
} // extern "C"
#endif
#endif /* ODKITEE_SERIALIZER_H_ */

View File

@@ -0,0 +1,48 @@
/*
* 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 "OEMCryptoCENC.h"
#include "odk_structs.h"
typedef struct {
uint32_t message_type;
uint32_t message_length;
uint32_t api_version;
uint32_t nonce;
uint32_t session_id;
} 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[64];
} ODK_ProvisioningMessage;
typedef struct {
ODK_CoreMessage core_message;
ODK_ParsedLicense* parsed_license;
size_t max_num_keys;
} ODK_LicenseResponse;
typedef struct {
ODK_ProvisioningMessage core_provisioning;
ODK_ParsedProvisioning* parsed_provisioning;
} ODK_ProvisioningResponse;
#endif // ODK_STRUCTS_PRIV_H_

View File

@@ -0,0 +1,39 @@
/*
* 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 <stdint.h>
#include <string.h>
#include "odk.h"
#include "odk_timer.h"
void ODK_InitializeClockValues(ODK_ClockValues* clock_values,
uint64_t system_time_seconds) {
if (clock_values == NULL) return;
clock_values->time_of_license_signed = system_time_seconds;
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;
}
/* Stub functions. */
void ODK_ReloadClockValues(ODK_ClockValues* clock_values,
uint64_t time_of_license_signed,
uint64_t time_of_first_decrypt,
uint64_t time_of_last_decrypt,
enum OEMCrypto_Usage_Entry_Status status,
uint64_t system_time_seconds) {}
uint32_t ODK_AttemptFirstPlayback(uint64_t system_time_seconds,
const ODK_TimerLimits* timer_limits,
ODK_ClockValues* clock_values,
uint64_t* timer_value) {}
OEMCryptoResult ODK_UpdateLastPlaybackTime(const ODK_TimerLimits* timer_limits,
uint64_t system_time_seconds,
ODK_ClockValues* clock_values) {}

View File

@@ -0,0 +1,199 @@
/*
* 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 "serialization_base.h"
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include "OEMCryptoCENC.h"
#include "odk_assert.h"
#include "odk_overflow.h"
struct _Message {
uint8_t* base;
size_t capacity;
size_t size; /* bytes written */
size_t read_offset; /* bytes read */
MessageStatus status;
};
odk_static_assert(SIZE_OF_MESSAGE_STRUCT >= sizeof(Message),
"SIZE_OF_MESSAGE_STRUCT too small");
bool ValidMessage(Message* message) {
if (message == NULL) {
return false;
}
if (message->status != MESSAGE_STATUS_OK) {
return false;
}
if (message->base == NULL) {
message->status = MESSAGE_STATUS_NULL_POINTER_ERROR;
return false;
}
if (message->size > message->capacity ||
message->read_offset > message->size) {
message->status = MESSAGE_STATUS_OVERFLOW_ERROR;
return false;
}
return true;
}
static void PackBytes(Message* message, const uint8_t* ptr, size_t count) {
if (count <= message->capacity - message->size) {
memcpy((void*)(message->base + message->size), (void*)ptr, count);
message->size += count;
} else {
message->status = MESSAGE_STATUS_OVERFLOW_ERROR;
}
}
void Pack_uint32_t(Message* message, const uint32_t* value) {
if (!ValidMessage(message)) return;
uint8_t data[4] = {0};
data[0] = *value >> 24;
data[1] = *value >> 16;
data[2] = *value >> 8;
data[3] = *value >> 0;
PackBytes(message, data, sizeof(data));
}
void Pack_uint64_t(Message* message, const uint64_t* value) {
if (!ValidMessage(message)) return;
uint32_t hi = *value >> 32;
uint32_t lo = *value;
Pack_uint32_t(message, &hi);
Pack_uint32_t(message, &lo);
}
void PackArray(Message* message, const uint8_t* base, size_t size) {
if (!ValidMessage(message)) return;
PackBytes(message, base, size);
}
static void UnpackBytes(Message* message, uint8_t* ptr, size_t count) {
if (count <= message->size - message->read_offset) {
memcpy((void*)ptr, (void*)(message->base + message->read_offset), count);
message->read_offset += count;
} else {
message->status = MESSAGE_STATUS_UNDERFLOW_ERROR;
}
}
void Unpack_uint32_t(Message* message, uint32_t* value) {
if (!ValidMessage(message)) return;
uint8_t data[4] = {0};
UnpackBytes(message, data, sizeof(data));
*value = data[0];
*value = *value << 8 | data[1];
*value = *value << 8 | data[2];
*value = *value << 8 | data[3];
}
void Unpack_uint64_t(Message* message, uint64_t* value) {
if (!ValidMessage(message)) return;
uint32_t hi = 0;
uint32_t lo = 0;
Unpack_uint32_t(message, &hi);
Unpack_uint32_t(message, &lo);
*value = hi;
*value = *value << 32 | lo;
}
void Unpack_OEMCrypto_Substring(Message* msg, OEMCrypto_Substring* obj) {
uint32_t offset = 0, length = 0;
Unpack_uint32_t(msg, &offset);
Unpack_uint32_t(msg, &length);
if (!ValidMessage(msg)) return;
size_t end = 0;
if (obj->offset > msg->capacity ||
odk_add_overflow_ux(obj->offset, obj->length, &end) ||
end > msg->capacity) {
msg->status = MESSAGE_STATUS_OVERFLOW_ERROR;
return;
}
obj->offset = offset;
obj->length = length;
}
/* copy out */
void UnpackArray(Message* message, uint8_t* address, size_t size) {
if (!ValidMessage(message)) return;
UnpackBytes(message, address, size);
}
/*
* The message structure, which is separate from the buffer,
* is initialized to reference the buffer
*/
void InitMessage(Message* message, uint8_t* buffer, size_t capacity) {
if (message == NULL) return;
memset(message, 0, sizeof(Message));
message->base = buffer;
message->capacity = capacity;
message->size = 0;
message->read_offset = 0;
message->status = MESSAGE_STATUS_OK;
}
/*
* The message structure is in the first sizeof(Memory) bytes
* of the buffer
*/
Message* CreateMessage(uint8_t* buffer, size_t buffer_size) {
if (buffer == NULL || buffer_size < sizeof(Message)) return NULL;
Message* message = (Message*)buffer;
message->base = buffer + sizeof(Message);
message->capacity = buffer_size - sizeof(Message);
message->size = 0;
message->read_offset = 0;
message->status = MESSAGE_STATUS_OK;
return message;
}
/*
* Set the message to an empty state
*/
void ResetMessage(Message* message) {
message->size = 0;
message->read_offset = 0;
message->status = MESSAGE_STATUS_OK;
}
uint8_t* GetBase(Message* message) {
if (message == NULL) return NULL;
return message->base;
}
size_t GetCapacity(Message* message) {
if (message == NULL) return 0;
return message->capacity;
}
size_t GetSize(Message* message) {
if (message == NULL) return 0;
return message->size;
}
void SetSize(Message* message, size_t size) {
if (message == NULL) return;
message->size = size;
}
MessageStatus GetStatus(Message* message) { return message->status; }
void SetStatus(Message* message, MessageStatus status) {
message->status = status;
}
size_t GetOffset(Message* message) {
if (message == NULL) return 0;
return message->read_offset;
}
size_t SizeOfMessageStruct() { return sizeof(Message); }

View File

@@ -0,0 +1,90 @@
/*
* 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 "OEMCryptoCENC.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*)(message_block);
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 Unpack_uint32_t(Message* message, uint32_t* value);
void Unpack_uint64_t(Message* message, uint64_t* value);
void UnpackArray(Message* message, uint8_t* base, size_t size); /* copy out */
void Unpack_OEMCrypto_Substring(Message* msg, OEMCrypto_Substring* obj);
typedef enum {
MESSAGE_STATUS_OK,
MESSAGE_STATUS_UNKNOWN_ERROR,
MESSAGE_STATUS_OVERFLOW_ERROR,
MESSAGE_STATUS_UNDERFLOW_ERROR,
MESSAGE_STATUS_PARSE_ERROR,
MESSAGE_STATUS_NULL_POINTER_ERROR,
MESSAGE_STATUS_API_VALUE_ERROR
} MessageStatus;
/*
* Create a message from a buffer. The message structure consumes the first
* sizeof(Message) bytes of the buffer. The caller is responsible for ensuring
* that the buffer remains allocated for the lifetime of the message.
*/
Message* CreateMessage(uint8_t* buffer, size_t buffer_size);
/*
* Initialize a message structure to reference a separate buffer. The caller
* is responsible for ensuring that the buffer remains allocated for the
* lifetime of the message.
*/
void InitMessage(Message* message, uint8_t* buffer, size_t capacity);
/*
* Reset an existing the message to an empty state
*/
void ResetMessage(Message* message);
uint8_t* GetBase(Message* message);
size_t GetCapacity(Message* message);
size_t GetSize(Message* message);
void SetSize(Message* message, size_t size);
MessageStatus GetStatus(Message* message);
void SetStatus(Message* message, MessageStatus status);
size_t GetOffset(Message* message);
size_t SizeOfMessageStruct();
#ifdef __cplusplus
} // extern "C"
#endif
#endif // ODKITEE_SERIALIZATION_BASE_H_

View File

@@ -0,0 +1,635 @@
/*
* 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 <algorithm>
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <ctime>
#include <fstream>
#include <functional>
#include <iostream>
#include <string>
#include <vector>
#include <bits/stdint-uintn.h>
#include <string.h>
#include <unistd.h>
#include <gtest/gtest.h>
#include "odk.h"
#include "odk_test.h"
size_t ODK_FieldLength(ODK_FieldType type) {
switch (type) {
case ODK_UINT32:
return sizeof(uint32_t);
case ODK_UINT64:
return sizeof(uint64_t);
case ODK_SUBSTRING:
return sizeof(uint32_t) + sizeof(uint32_t);
case ODK_DEVICEID:
return DEVICE_ID_MAX;
default:
return SIZE_MAX;
}
}
size_t ODK_AllocSize(ODK_FieldType type) {
if (type == ODK_SUBSTRING) {
return sizeof(OEMCrypto_Substring);
}
return ODK_FieldLength(type);
}
OEMCryptoResult ODK_WriteSingleField(uint8_t* const buf,
const ODK_Field* const field) {
if (!field || !field->value) {
return ODK_ERROR_CORE_MESSAGE;
}
switch (field->type) {
case ODK_UINT32: {
uint32_t u32 = htobe32(*static_cast<uint32_t*>(field->value));
memcpy(buf, &u32, sizeof(u32));
break;
}
case ODK_UINT64: {
uint64_t u64 = htobe64(*static_cast<uint64_t*>(field->value));
memcpy(buf, &u64, sizeof(u64));
break;
}
case ODK_SUBSTRING: {
OEMCrypto_Substring* s = static_cast<OEMCrypto_Substring*>(field->value);
uint32_t off = htobe32(s->offset);
uint32_t len = htobe32(s->length);
memcpy(buf, &off, sizeof(off));
memcpy(buf + sizeof(off), &len, sizeof(len));
break;
}
case ODK_DEVICEID: {
const uint8_t* const id = static_cast<uint8_t*>(field->value);
memcpy(buf, id, DEVICE_ID_MAX);
break;
}
default:
return ODK_ERROR_CORE_MESSAGE;
}
return OEMCrypto_SUCCESS;
}
OEMCryptoResult ODK_ReadSingleField(const uint8_t* const buf,
const ODK_Field* const field) {
if (!field || !field->value) {
return ODK_ERROR_CORE_MESSAGE;
}
switch (field->type) {
case ODK_UINT32: {
memcpy(field->value, buf, sizeof(uint32_t));
uint32_t* u32p = static_cast<uint32_t*>(field->value);
*u32p = be32toh(*u32p);
break;
}
case ODK_UINT64: {
memcpy(field->value, buf, sizeof(uint64_t));
uint64_t* u64p = static_cast<uint64_t*>(field->value);
*u64p = be64toh(*u64p);
break;
}
case ODK_SUBSTRING: {
OEMCrypto_Substring* s = static_cast<OEMCrypto_Substring*>(field->value);
uint32_t off = 0;
uint32_t len = 0;
memcpy(&off, buf, sizeof(off));
memcpy(&len, buf + sizeof(off), sizeof(len));
s->offset = be32toh(off);
s->length = be32toh(len);
break;
}
case ODK_DEVICEID: {
uint8_t* const id = static_cast<uint8_t*>(field->value);
memcpy(id, buf, DEVICE_ID_MAX);
break;
}
default:
return ODK_ERROR_CORE_MESSAGE;
}
return OEMCrypto_SUCCESS;
}
/*
* Parameters:
* [in] size_in: buffer size
* [out] size_out: bytes processed
*/
OEMCryptoResult ODK_IterFields(ODK_FieldMode mode, uint8_t* const buf,
const size_t size_in, size_t* size_out,
std::vector<ODK_Field>& fields) {
if (!buf || !size_out) {
return ODK_ERROR_CORE_MESSAGE;
}
size_t off = 0, off2 = 0;
for (size_t i = 0; i < fields.size(); i++) {
if (__builtin_add_overflow(off, ODK_FieldLength(fields[i].type), &off2) ||
off2 > size_in) {
return ODK_ERROR_CORE_MESSAGE;
}
uintptr_t base = reinterpret_cast<uintptr_t>(buf);
if (__builtin_add_overflow(base, off, &base)) {
return ODK_ERROR_CORE_MESSAGE;
}
uint8_t* const buf_off = buf + off;
if (mode == ODK_WRITE) {
ODK_WriteSingleField(buf_off, &fields[i]);
} else if (mode == ODK_READ) {
ODK_ReadSingleField(buf_off, &fields[i]);
} else {
return ODK_ERROR_CORE_MESSAGE;
}
off = off2;
}
*size_out = off;
if (*size_out > size_in) {
return ODK_ERROR_CORE_MESSAGE;
}
return OEMCrypto_SUCCESS;
}
OEMCryptoResult ODK_ReadFields(const uint8_t* const buf, const size_t size_in,
size_t* size_out,
std::vector<ODK_Field>& fields) {
return ODK_IterFields(ODK_READ, const_cast<uint8_t*>(buf), size_in, size_out,
fields);
}
OEMCryptoResult ODK_WriteFields(uint8_t* const buf, const size_t size_in,
size_t* size_out,
std::vector<ODK_Field>& fields) {
return ODK_IterFields(ODK_WRITE, buf, size_in, size_out, fields);
}
OEMCryptoResult ODK_ValidateSubstrings(const size_t size, const size_t n,
const ODK_Field* const fields) {
if (!fields) {
return ODK_ERROR_CORE_MESSAGE;
}
size_t off = 0;
for (size_t i = 0; i < n; i++) {
if (fields[i].type != ODK_SUBSTRING) {
continue;
}
if (!fields[i].value) {
return ODK_ERROR_CORE_MESSAGE;
}
size_t end = 0;
OEMCrypto_Substring* s = static_cast<OEMCrypto_Substring*>(fields[i].value);
if (s->offset > size ||
__builtin_add_overflow(s->offset, s->length, &end) || end > size) {
return ODK_ERROR_CORE_MESSAGE;
}
}
return OEMCrypto_SUCCESS;
}
void expect_eq_buf(const void* s1, const void* s2, size_t n) {
if (memcmp(s1, s2, n)) {
const void* buffers[] = {s1, s2};
for (int i = 0; i < 2; i++) {
char _tmp[] = "/tmp/fileXXXXXX";
mkstemp(_tmp);
std::string tmp(_tmp);
std::fstream out(tmp, std::ios::out | std::ios::binary);
out.write((char*)buffers[i], n);
out.close();
std:
std::cerr << "buffer " << i << " dumped to " << tmp << std::endl;
}
FAIL();
}
}
template <typename F>
void ValidateRequest(uint32_t message_type,
std::vector<ODK_Field>& extra_fields,
const F& odk_prepare_func) {
uint32_t message_size = 0;
uint32_t api_version = 16;
uint32_t nonce = 0xdeadbeef;
uint32_t session_id = 0xcafebabe;
std::vector<ODK_Field> total_fields = {
{ODK_UINT32, &message_type}, {ODK_UINT32, &message_size},
{ODK_UINT32, &api_version}, {ODK_UINT32, &nonce},
{ODK_UINT32, &session_id},
};
total_fields.insert(total_fields.end(), extra_fields.begin(),
extra_fields.end());
for (auto& field : total_fields) {
message_size += ODK_FieldLength(field.type);
}
uint8_t* buf = new uint8_t[message_size]();
uint8_t* buf2 = new uint8_t[message_size]();
size_t bytes_written = message_size;
EXPECT_EQ(
OEMCrypto_SUCCESS,
odk_prepare_func(buf, &bytes_written, api_version, nonce, session_id));
EXPECT_EQ(bytes_written, message_size);
EXPECT_EQ(OEMCrypto_SUCCESS, ODK_IterFields(ODK_WRITE, buf2, SIZE_MAX,
&bytes_written, total_fields));
EXPECT_EQ(bytes_written, message_size);
expect_eq_buf(buf, buf2, message_size);
delete[] buf;
delete[] buf2;
}
template <typename F>
void ValidateResponse(uint32_t message_type,
std::vector<ODK_Field>& extra_fields,
const F& odk_parse_func) {
uint32_t message_size = 0;
uint32_t api_version = 16;
uint32_t nonce = 0xdeadbeef;
uint32_t session_id = 0xcafebabe;
std::vector<ODK_Field> total_fields = {
{ODK_UINT32, &message_type}, {ODK_UINT32, &message_size},
{ODK_UINT32, &api_version}, {ODK_UINT32, &nonce},
{ODK_UINT32, &session_id},
};
uint32_t header_size = 0;
for (auto& field : total_fields) {
header_size += ODK_FieldLength(field.type);
}
total_fields.insert(total_fields.end(), extra_fields.begin(),
extra_fields.end());
for (auto& field : total_fields) {
message_size += ODK_FieldLength(field.type);
}
uint8_t* buf = new uint8_t[message_size]();
uint8_t* buf2 = new uint8_t[message_size]();
uint8_t* zero = new uint8_t[message_size]();
size_t bytes_read = 0, bytes_written = 0;
// serialize input to buf
EXPECT_EQ(OEMCrypto_SUCCESS, ODK_IterFields(ODK_WRITE, buf, SIZE_MAX,
&bytes_written, total_fields));
EXPECT_EQ(bytes_written, message_size);
// zero-out input
EXPECT_EQ(OEMCrypto_SUCCESS, ODK_IterFields(ODK_READ, zero, bytes_written,
&bytes_read, extra_fields));
EXPECT_TRUE(bytes_written > bytes_read &&
bytes_written - bytes_read == header_size);
// parse buf with odk
EXPECT_EQ(OEMCrypto_SUCCESS,
odk_parse_func(buf, bytes_written, api_version, nonce, session_id));
// serialize odk output to buf2
EXPECT_EQ(OEMCrypto_SUCCESS, ODK_IterFields(ODK_WRITE, buf2, SIZE_MAX,
&bytes_written, total_fields));
EXPECT_EQ(bytes_written, message_size);
expect_eq_buf(buf, buf2, message_size);
delete[] buf;
delete[] buf2;
delete[] zero;
}
TEST(OdkTest, SerializeFields) {
uint32_t x[] = {0, 1, 2};
uint64_t y[] = {3ll << 32, 4ll << 32, 5ll << 32};
OEMCrypto_Substring s = {.offset = 6, .length = 7};
std::vector<ODK_Field> fields = {
{ODK_UINT32, &x[0]}, {ODK_UINT32, &x[1]}, {ODK_UINT32, &x[2]},
{ODK_UINT64, &y[0]}, {ODK_UINT64, &y[1]}, {ODK_UINT64, &y[2]},
{ODK_SUBSTRING, &s},
};
uint8_t buf[1024] = {0};
uint8_t buf2[1024] = {0};
size_t bytes_read = 0, bytes_written = 0;
ODK_IterFields(ODK_WRITE, buf, SIZE_MAX, &bytes_read, fields);
ODK_IterFields(ODK_READ, buf, bytes_read, &bytes_written, fields);
ODK_IterFields(ODK_WRITE, buf2, SIZE_MAX, &bytes_read, fields);
expect_eq_buf(buf, buf2, bytes_read);
}
TEST(OdkTest, SerializeFieldsStress) {
const int n = 1024;
std::vector<ODK_Field> fields(n);
std::srand(0);
size_t total_size = 0;
for (int i = 0; i < n; i++) {
fields[i].type = static_cast<ODK_FieldType>(std::rand() %
static_cast<int>(ODK_NUMTYPES));
size_t field_size = ODK_AllocSize(fields[i].type);
fields[i].value = malloc(ODK_AllocSize(fields[i].type));
total_size += ODK_FieldLength(fields[i].type);
}
uint8_t* buf = new uint8_t[total_size];
for (int i = 0; i < total_size; i++) {
buf[i] = std::rand() & 0xff;
}
size_t bytes_read = 0, bytes_written = 0;
uint8_t* buf2 = new uint8_t[total_size];
ODK_IterFields(ODK_READ, buf, total_size, &bytes_read, fields);
EXPECT_EQ(bytes_read, total_size);
ODK_IterFields(ODK_WRITE, buf2, total_size, &bytes_written, fields);
EXPECT_EQ(bytes_written, total_size);
expect_eq_buf(buf, buf2, total_size);
// cleanup
for (int i = 0; i < n; i++) {
free(fields[i].value);
}
delete[] buf;
delete[] buf2;
}
TEST(OdkTest, LicenseRequest) {
std::vector<ODK_Field> empty;
auto odk_prepare_func = [&](uint8_t* const buf, size_t* size,
uint32_t api_version, uint32_t nonce,
uint32_t session_id) {
return ODK_PrepareCoreLicenseRequest(buf, SIZE_MAX, size, api_version,
nonce, session_id);
};
ValidateRequest(ODK_License_Request_Type, empty, odk_prepare_func);
}
TEST(OdkTest, RenewalRequest) {
uint64_t system_time_seconds = 0xBADDCAFE000FF1CE;
std::vector<ODK_Field> extra_fields = {
{ODK_UINT64, &system_time_seconds},
};
ODK_ClockValues clock_values = {0};
auto odk_prepare_func = [&](uint8_t* const buf, size_t* size,
uint32_t api_version, uint32_t nonce,
uint32_t session_id) {
return ODK_PrepareCoreRenewalRequest(buf, SIZE_MAX, size, api_version,
nonce, session_id, &clock_values,
system_time_seconds);
};
ValidateRequest(ODK_Renewal_Request_Type, extra_fields, odk_prepare_func);
}
TEST(OdkTest, ProvisionRequest) {
uint32_t device_id_length = DEVICE_ID_MAX / 2;
uint8_t device_id[DEVICE_ID_MAX] = {0};
memset(device_id, 0xff, device_id_length);
std::vector<ODK_Field> extra_fields = {
{ODK_UINT32, &device_id_length},
{ODK_DEVICEID, device_id},
};
auto odk_prepare_func = [&](uint8_t* const buf, size_t* size,
uint32_t api_version, uint32_t nonce,
uint32_t session_id) {
return ODK_PrepareCoreProvisioningRequest(buf, SIZE_MAX, size, api_version,
nonce, session_id, device_id,
device_id_length);
};
ValidateRequest(ODK_Provisioning_Request_Type, extra_fields,
odk_prepare_func);
}
TEST(OdkTest, LicenseResponse) {
ODK_ParsedLicense parsed_license = {
.enc_mac_keys_iv = {.offset = 0, .length = 1},
.enc_mac_keys = {.offset = 2, .length = 3},
.pst = {.offset = 4, .length = 5},
.srm_restriction_data = {.offset = 6, .length = 7},
.license_type = 8,
.nonce_required = 0xDEADC0DE,
.timer_limits =
{
.soft_expiry = 9,
.earliest_playback_start_seconds = 10,
.latest_playback_start_seconds = 11,
.initial_playback_duration_seconds = 12,
.renewal_playback_duration_seconds = 13,
.license_duration_seconds = 14,
},
.key_array_length = 3,
.key_array =
{
{
.key_id = {.offset = 15, .length = 16},
.key_data_iv = {.offset = 17, .length = 18},
.key_data = {.offset = 19, .length = 20},
.key_control_iv = {.offset = 21, .length = 22},
.key_control = {.offset = 23, .length = 24},
},
{
.key_id = {.offset = 25, .length = 26},
.key_data_iv = {.offset = 27, .length = 28},
.key_data = {.offset = 29, .length = 30},
.key_control_iv = {.offset = 31, .length = 32},
.key_control = {.offset = 33, .length = 34},
},
{
.key_id = {.offset = 35, .length = 36},
.key_data_iv = {.offset = 37, .length = 38},
.key_data = {.offset = 39, .length = 40},
.key_control_iv = {.offset = 41, .length = 42},
.key_control = {.offset = 43, .length = 44},
},
},
};
uint32_t message_type = ODK_License_Response_Type;
std::vector<ODK_Field> extra_fields = {
{ODK_SUBSTRING, &parsed_license.enc_mac_keys_iv},
{ODK_SUBSTRING, &parsed_license.enc_mac_keys},
{ODK_SUBSTRING, &parsed_license.pst},
{ODK_SUBSTRING, &parsed_license.srm_restriction_data},
{ODK_UINT32, &parsed_license.license_type},
{ODK_UINT32, &parsed_license.nonce_required},
{ODK_UINT32, &parsed_license.timer_limits.soft_expiry},
{ODK_UINT64,
&parsed_license.timer_limits.earliest_playback_start_seconds},
{ODK_UINT64, &parsed_license.timer_limits.latest_playback_start_seconds},
{ODK_UINT64,
&parsed_license.timer_limits.initial_playback_duration_seconds},
{ODK_UINT64,
&parsed_license.timer_limits.renewal_playback_duration_seconds},
{ODK_UINT64, &parsed_license.timer_limits.license_duration_seconds},
{ODK_UINT32, &parsed_license.key_array_length},
{ODK_SUBSTRING, &parsed_license.key_array[0].key_id},
{ODK_SUBSTRING, &parsed_license.key_array[0].key_data_iv},
{ODK_SUBSTRING, &parsed_license.key_array[0].key_data},
{ODK_SUBSTRING, &parsed_license.key_array[0].key_control_iv},
{ODK_SUBSTRING, &parsed_license.key_array[0].key_control},
{ODK_SUBSTRING, &parsed_license.key_array[1].key_id},
{ODK_SUBSTRING, &parsed_license.key_array[1].key_data_iv},
{ODK_SUBSTRING, &parsed_license.key_array[1].key_data},
{ODK_SUBSTRING, &parsed_license.key_array[1].key_control_iv},
{ODK_SUBSTRING, &parsed_license.key_array[1].key_control},
{ODK_SUBSTRING, &parsed_license.key_array[2].key_id},
{ODK_SUBSTRING, &parsed_license.key_array[2].key_data_iv},
{ODK_SUBSTRING, &parsed_license.key_array[2].key_data},
{ODK_SUBSTRING, &parsed_license.key_array[2].key_control_iv},
{ODK_SUBSTRING, &parsed_license.key_array[2].key_control},
};
auto odk_parse_func = [&](const uint8_t* buf, size_t size,
uint32_t api_version, uint32_t nonce,
uint32_t session_id) {
return ODK_ParseLicense(buf, size + 128, api_version, nonce, session_id, 0,
0, 3, &parsed_license);
};
ValidateResponse(ODK_License_Response_Type, extra_fields, odk_parse_func);
}
TEST(OdkTest, RenewalResponse) {
uint64_t system_time = 0xfaceb00c;
uint64_t playback_clock = 11;
uint64_t playback_timer = 12;
uint64_t message_playback_clock = 10;
std::vector<ODK_Field> extra_fields = {
{ODK_UINT64, &message_playback_clock},
};
ODK_TimerLimits timer_limits = {
.soft_expiry = 0,
.earliest_playback_start_seconds = 0,
.latest_playback_start_seconds = 100,
.initial_playback_duration_seconds = 10,
.renewal_playback_duration_seconds = 20,
.license_duration_seconds = 100,
};
ODK_ClockValues clock_values = {
.time_of_license_signed = 0,
.time_of_first_decrypt = system_time - playback_clock,
.time_of_last_decrypt = 0,
.time_when_timer_expires = system_time + playback_timer,
.timer_status = 0,
.status = kUnused,
};
auto odk_parse_func = [&](const uint8_t* buf, size_t size,
uint32_t api_version, uint32_t nonce,
uint32_t session_id) {
OEMCryptoResult err =
ODK_ParseRenewal(buf, size, api_version, nonce, session_id, system_time,
&timer_limits, &clock_values, &playback_timer);
EXPECT_EQ(ODK_SET_TIMER, err);
EXPECT_EQ(timer_limits.renewal_playback_duration_seconds, playback_timer);
EXPECT_EQ(clock_values.time_when_timer_expires,
system_time + playback_timer);
// manually restore message_playback_clock since ODK_ParseRenewal doesn't
// generate output
message_playback_clock = 10;
return OEMCrypto_SUCCESS;
};
ValidateResponse(ODK_Renewal_Response_Type, extra_fields, odk_parse_func);
}
TEST(OdkTest, ProvisionResponse) {
uint32_t device_id_length = DEVICE_ID_MAX / 2;
uint8_t device_id[DEVICE_ID_MAX] = {0};
memset(device_id, 0xff, device_id_length);
ODK_ParsedProvisioning parsed_response = {
.enc_private_key = {.offset = 0, .length = 1},
.enc_private_key_iv = {.offset = 2, .length = 3},
.encrypted_message_key = {.offset = 4, .length = 5},
};
std::vector<ODK_Field> extra_fields = {
{ODK_UINT32, &device_id_length},
{ODK_DEVICEID, device_id},
{ODK_UINT32, &parsed_response.key_type},
{ODK_SUBSTRING, &parsed_response.enc_private_key},
{ODK_SUBSTRING, &parsed_response.enc_private_key_iv},
{ODK_SUBSTRING, &parsed_response.encrypted_message_key},
};
auto odk_parse_func = [&](const uint8_t* buf, size_t size,
uint32_t api_version, uint32_t nonce,
uint32_t session_id) {
OEMCryptoResult err =
ODK_ParseProvisioning(buf, size + 16, api_version, nonce, session_id,
device_id, device_id_length, &parsed_response);
// restore device id because it is not part of parsed_response
device_id_length = DEVICE_ID_MAX / 2;
memset(device_id, 0xff, device_id_length);
return err;
};
ValidateResponse(ODK_Provisioning_Response_Type, extra_fields,
odk_parse_func);
}
TEST(OdkSizeTest, LicenseRequest) {
uint8_t* message = nullptr;
size_t message_length = 0;
size_t core_message_length = 0;
uint32_t api_version = 0;
uint32_t nonce = 0;
uint32_t session_id = 0;
EXPECT_EQ(OEMCrypto_ERROR_SHORT_BUFFER,
ODK_PrepareCoreLicenseRequest(message, message_length,
&core_message_length, api_version,
nonce, session_id));
// All messages have at least a five 4-byte fields.
size_t minimum_message_size = 5 * 4;
EXPECT_GE(core_message_length, minimum_message_size);
}
TEST(OdkSizeTest, RenewalRequest) {
uint8_t* message = nullptr;
size_t message_length = 0;
size_t core_message_length = 0;
uint32_t api_version = 0;
uint32_t nonce = 0;
uint32_t session_id = 0;
ODK_ClockValues clock_values;
clock_values.time_of_first_decrypt = 10;
uint64_t system_time_seconds = 15;
EXPECT_EQ(OEMCrypto_ERROR_SHORT_BUFFER,
ODK_PrepareCoreRenewalRequest(
message, message_length, &core_message_length, api_version,
nonce, session_id, &clock_values, system_time_seconds));
// All messages have at least a five 4-byte fields.
size_t minimum_message_size = 5 * 4;
EXPECT_GE(core_message_length, minimum_message_size);
}
TEST(OdkSizeTest, ProvisioningRequest) {
uint8_t* message = nullptr;
size_t message_length = 0;
size_t core_message_length = 0;
uint32_t api_version = 0;
uint32_t nonce = 0;
uint32_t session_id = 0;
uint8_t* device_id = nullptr;
uint32_t device_id_length = 0;
EXPECT_EQ(OEMCrypto_ERROR_SHORT_BUFFER,
ODK_PrepareCoreProvisioningRequest(
message, message_length, &core_message_length, api_version,
nonce, session_id, nullptr, device_id_length));
// All messages have at least a five 4-byte fields.
size_t minimum_message_size = 5 * 4;
EXPECT_GE(core_message_length, minimum_message_size);
}
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

View File

@@ -0,0 +1,11 @@
# 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.
{
'sources': [
'odk_test.cpp',
'odk_timer_test.cpp',
],
}

View File

@@ -0,0 +1,63 @@
/*
* 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 "OEMCryptoCENC.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_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_

View File

@@ -0,0 +1,21 @@
/*
* 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 <gtest/gtest.h>
#include "odk_timer.h"
TEST(OdkTimerTest, Init) {
ODK_ClockValues clock_values;
uint64_t time = 42;
ODK_InitializeClockValues(&clock_values, time);
EXPECT_EQ(clock_values.time_of_license_signed, time);
EXPECT_EQ(clock_values.time_of_first_decrypt, 0);
EXPECT_EQ(clock_values.time_of_last_decrypt, 0);
EXPECT_EQ(clock_values.time_when_timer_expires, 0);
EXPECT_EQ(clock_values.timer_status, 0);
EXPECT_EQ(clock_values.status, kUnused);
}

View File

@@ -0,0 +1,56 @@
# This is a gyp file for building the OEMCrypto unit tests with the reference
# code from the stand-alone source code.
{
'variables': {
# Override the variables below for the location of various gyp files.
# Alternatively, set the environment variable PATH_TO_CDM_DIR to point to a
# recent version of the source CDM. This *must* be a relative path.
'boringssl_dependency%': '<!(echo $PATH_TO_CDM_DIR)/third_party/boringssl/boringssl.gyp:ssl',
'gtest_dependency%': '<!(echo $PATH_TO_CDM_DIR)/third_party/gmock.gyp:gtest',
'gmock_dependency%': '<!(echo $PATH_TO_CDM_DIR)/third_party/gmock.gyp:gmock',
'gmock_main_dependency%': '<!(echo $PATH_TO_CDM_DIR)/third_party/gmock.gyp:gmock_main',
'oemcrypto_dir%': '.',
'util_dir%': '../util',
'platform_specific_dir%': '<!(echo $PATH_TO_CDM_DIR)/linux/src',
},
'targets': [
{
'target_name': 'oemcrypto_unittests',
'type': 'executable',
'sources': [
'test/oemcrypto_test_main.cpp',
'<(platform_specific_dir)/file_store.cpp',
'<(platform_specific_dir)/log.cpp',
'<(util_dir)/src/platform.cpp',
'<(util_dir)/src/rw_lock.cpp',
'<(util_dir)/src/string_conversions.cpp',
],
'includes': [
'test/oemcrypto_unittests.gypi',
'ref/oec_ref.gypi',
],
'dependencies': [
'<(boringssl_dependency)',
'<(gtest_dependency)',
'<(gmock_dependency)',
],
},
{
'target_name': 'odk_tests',
'type': 'executable',
'includes': [
'odk/test/odk_test.gypi',
'odk/src/odk.gypi',
],
'include_dirs': [
'include',
'odk/include',
'odk/src',
],
'dependencies': [
'<(gtest_dependency)',
'<(gmock_main_dependency)',
],
},
],
}

View File

@@ -6,12 +6,14 @@
{
'include_dirs': [
'<(oemcrypto_dir)/include',
'<(util_dir)/include',
'<(oemcrypto_dir)/ref/src',
'<(oemcrypto_dir)/odk/include',
'<(util_dir)/include',
],
'direct_dependent_settings': {
'include_dirs': [
'<(oemcrypto_dir)/include',
'<(oemcrypto_dir)/odk/include',
'<(oemcrypto_dir)/../util/include',
],
},
@@ -22,8 +24,6 @@
'<(oemcrypto_dir)/ref/src/oemcrypto_key_ref.cpp',
'<(oemcrypto_dir)/ref/src/oemcrypto_keybox_ref.cpp',
'<(oemcrypto_dir)/ref/src/oemcrypto_ref.cpp',
'<(oemcrypto_dir)/ref/src/oemcrypto_nonce_table.cpp',
'<(oemcrypto_dir)/ref/src/oemcrypto_old_usage_table_ref.cpp',
'<(oemcrypto_dir)/ref/src/oemcrypto_rsa_key_shared.cpp',
'<(oemcrypto_dir)/ref/src/oemcrypto_session.cpp',
'<(oemcrypto_dir)/ref/src/oemcrypto_session_key_table.cpp',
@@ -36,4 +36,7 @@
'dependencies': [
'<(boringssl_dependency)',
],
'includes': [
'../odk/src/odk.gypi',
],
}

View File

@@ -53,7 +53,7 @@ class Prov30CryptoEngine : public CryptoEngine {
if (kOEMPublicCertSize == 0) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
if (public_cert_length == NULL) {
if (public_cert_length == nullptr) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (*public_cert_length < kOEMPublicCertSize) {
@@ -61,7 +61,7 @@ class Prov30CryptoEngine : public CryptoEngine {
return OEMCrypto_ERROR_SHORT_BUFFER;
}
*public_cert_length = kOEMPublicCertSize;
if (public_cert == NULL) {
if (public_cert == nullptr) {
return OEMCrypto_ERROR_SHORT_BUFFER;
}
memcpy(public_cert, kOEMPublicCert, kOEMPublicCertSize);

View File

@@ -88,7 +88,7 @@ SessionContext* CryptoEngine::FindSession(SessionId sid) {
if (it != sessions_.end()) {
return it->second;
}
return NULL;
return nullptr;
}
time_t CryptoEngine::OnlineTime() {
@@ -135,7 +135,7 @@ time_t CryptoEngine::RollbackCorrectedOfflineTime() {
if (!file) {
LOGE("RollbackCorrectedOfflineTime: File open failed: %s",
filename.c_str());
return time(NULL);
return time(nullptr);
}
file->Read(reinterpret_cast<char*>(&encrypted_buffer[0]), sizeof(TimeInfo));
// Decrypt the encrypted TimeInfo buffer.
@@ -149,7 +149,7 @@ time_t CryptoEngine::RollbackCorrectedOfflineTime() {
time_t current_time;
// Add any time offsets in the past to the current time.
current_time = time(NULL) + time_info.rollback_offset;
current_time = time(nullptr) + time_info.rollback_offset;
if (time_info.previous_time > current_time) {
// Time has been rolled back.
// Update the rollback offset.
@@ -174,7 +174,7 @@ time_t CryptoEngine::RollbackCorrectedOfflineTime() {
if (!file) {
LOGE("RollbackCorrectedOfflineTime: File open failed: %s",
filename.c_str());
return time(NULL);
return time(nullptr);
}
file->Write(reinterpret_cast<char*>(&encrypted_buffer[0]), sizeof(TimeInfo));
@@ -183,9 +183,9 @@ time_t CryptoEngine::RollbackCorrectedOfflineTime() {
}
bool CryptoEngine::NonceCollision(uint32_t nonce) {
for (const auto & session_pair : sessions_) {
for (const auto& session_pair : sessions_) {
const SessionContext* session = session_pair.second;
if (session->NonceCollision(nonce)) return true;
if (nonce == session->nonce()) return true;
}
return false;
}
@@ -218,7 +218,7 @@ OEMCryptoResult CryptoEngine::SetDestination(
// Direct buffer type is only used on some specialized devices where
// oemcrypto has a direct connection to the screen buffer. It is not,
// for example, supported on Android.
destination_ = NULL;
destination_ = nullptr;
break;
default:
return OEMCrypto_ERROR_INVALID_CONTEXT;
@@ -237,7 +237,7 @@ OEMCryptoResult CryptoEngine::SetDestination(
}
adjust_destination(out_description, data_length, subsample_flags);
if ((out_description->type != OEMCrypto_BufferType_Direct) &&
(destination_ == NULL)) {
(destination_ == nullptr)) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
return OEMCrypto_SUCCESS;

View File

@@ -91,8 +91,7 @@ class CryptoEngine {
time_t RollbackCorrectedOfflineTime();
// Verify that this nonce does not collide with another nonce in any session's
// nonce table.
// Verify that this nonce does not collide with another nonce in any session.
virtual bool NonceCollision(uint32_t nonce);
// Returns the HDCP version currently in use.
@@ -133,12 +132,15 @@ class CryptoEngine {
return OEMCrypto_Keybox;
}
virtual OEMCryptoResult get_oem_certificate(SessionContext* session,
uint8_t* public_cert,
virtual OEMCryptoResult get_oem_certificate(uint8_t* public_cert,
size_t* public_cert_length) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
virtual OEMCryptoResult load_oem_private_key(SessionContext* session) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
// Used for OEMCrypto_IsAntiRollbackHwPresent.
virtual bool config_is_anti_rollback_hw_present() { return false; }

View File

@@ -22,7 +22,8 @@ bool KeyControlBlock::Validate() {
memcmp(verification_, "kc12", 4) && // add in version 12 api
memcmp(verification_, "kc13", 4) && // add in version 13 api
memcmp(verification_, "kc14", 4) && // add in version 14 api
memcmp(verification_, "kc15", 4)) { // add in version 15 api
memcmp(verification_, "kc15", 4) && // add in version 15 api
memcmp(verification_, "kc16", 4)) { // add in version 16 api
LOGE("KCB: BAD verification string: %4.4s", verification_);
valid_ = false;
} else {

View File

@@ -1,76 +0,0 @@
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
//
// Reference implementation of OEMCrypto APIs
//
#include "oemcrypto_nonce_table.h"
namespace wvoec_ref {
void NonceTable::AddNonce(uint32_t nonce) {
int new_slot = -1;
int oldest_slot = -1;
// Flush any nonces that have been checked but not flushed.
// After flush, nonces will be either valid or invalid.
Flush();
for (int i = 0; i < kTableSize; ++i) {
// Increase age of all valid nonces.
if (kNTStateValid == state_[i]) {
++age_[i];
if (-1 == oldest_slot) {
oldest_slot = i;
} else {
if (age_[i] > age_[oldest_slot]) {
oldest_slot = i;
}
}
} else {
if (-1 == new_slot) {
age_[i] = 0;
nonces_[i] = nonce;
state_[i] = kNTStateValid;
new_slot = i;
}
}
}
if (-1 == new_slot) {
// reuse oldest
// assert (oldest_slot != -1)
int i = oldest_slot;
age_[i] = 0;
nonces_[i] = nonce;
state_[i] = kNTStateValid;
}
}
bool NonceTable::CheckNonce(uint32_t nonce) {
for (int i = 0; i < kTableSize; ++i) {
if (kNTStateInvalid != state_[i]) {
if (nonce == nonces_[i]) {
state_[i] = kNTStateFlushPending;
return true;
}
}
}
return false;
}
bool NonceTable::NonceCollision(uint32_t nonce) const {
for (int i = 0; i < kTableSize; ++i) {
if (nonce == nonces_[i]) return true;
}
return false;
}
void NonceTable::Flush() {
for (int i = 0; i < kTableSize; ++i) {
if (kNTStateFlushPending == state_[i]) {
state_[i] = kNTStateInvalid;
}
}
}
} // namespace wvoec_ref

View File

@@ -1,42 +0,0 @@
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
//
// Reference implementation of OEMCrypto APIs
//
#ifndef REF_OEMCRYPTO_NONCE_TABLE_H_
#define REF_OEMCRYPTO_NONCE_TABLE_H_
#include <stdint.h>
namespace wvoec_ref {
class NonceTable {
public:
static const int kTableSize = 4;
NonceTable() {
for (int i = 0; i < kTableSize; ++i) {
state_[i] = kNTStateInvalid;
}
}
~NonceTable() {}
void AddNonce(uint32_t nonce);
bool CheckNonce(uint32_t nonce);
// Verify that the nonce is not the same as any in this table.
bool NonceCollision(uint32_t nonce) const;
void Flush();
private:
enum NonceTableState {
kNTStateInvalid,
kNTStateValid,
kNTStateFlushPending
};
NonceTableState state_[kTableSize];
uint32_t age_[kTableSize];
uint32_t nonces_[kTableSize];
};
} // namespace wvoec_ref
#endif // REF_OEMCRYPTO_NONCE_TABLE_H_

View File

@@ -1,239 +0,0 @@
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
//
// Reference implementation of OEMCrypto APIs
//
// This is from the v12 version of oemcrypto usage tables. It is used for
// devices that upgrade from v12 to v13 in the field, and need to convert from
// the old type of usage table to the new.
#include "oemcrypto_old_usage_table_ref.h"
#include <string.h>
#include <time.h>
#include <string>
#include <vector>
#include <openssl/aes.h>
#include <openssl/hmac.h>
#include <openssl/rand.h>
#include <openssl/sha.h>
#include "file_store.h"
#include "log.h"
#include "oemcrypto_engine_ref.h"
// TODO(fredgc): Setting the device files base bath is currently broken as
// wvcdm::Properties is no longer used by the reference code.
//#include "properties.h"
#include "pst_report.h"
#include "string_conversions.h"
namespace wvoec_ref {
OldUsageTableEntry::OldUsageTableEntry(OldUsageTable *old_usage_table,
const std::vector<uint8_t> &pst_hash)
: pst_hash_(pst_hash),
old_usage_table_(old_usage_table),
time_of_license_received_(
old_usage_table_->ce_->RollbackCorrectedOfflineTime()),
time_of_first_decrypt_(0),
time_of_last_decrypt_(0),
status_(kUnused) {}
OldUsageTableEntry::~OldUsageTableEntry() {}
OldUsageTableEntry::OldUsageTableEntry(OldUsageTable *old_usage_table,
const OldStoredUsageEntry *buffer)
: old_usage_table_(old_usage_table) {
pst_hash_.assign(buffer->pst_hash, buffer->pst_hash + SHA256_DIGEST_LENGTH);
time_of_license_received_ = buffer->time_of_license_received;
time_of_first_decrypt_ = buffer->time_of_first_decrypt;
time_of_last_decrypt_ = buffer->time_of_last_decrypt;
status_ = buffer->status;
mac_key_server_.assign(buffer->mac_key_server,
buffer->mac_key_server + wvoec::MAC_KEY_SIZE);
mac_key_client_.assign(buffer->mac_key_client,
buffer->mac_key_client + wvoec::MAC_KEY_SIZE);
}
OldUsageTable::OldUsageTable(CryptoEngine *ce) {
ce_ = ce;
generation_ = 0;
table_.clear();
// Load saved table.
wvcdm::FileSystem *file_system = ce->file_system();
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.
// For now, the path is empty.
/*if (!Properties::GetDeviceFilesBasePath(kSecurityLevelL3, &path)) {
LOGE("OldUsageTable: Unable to get base path");
return;
}*/
std::string filename = path + "UsageTable.dat";
if (!file_system->Exists(filename)) {
return;
}
size_t file_size = file_system->FileSize(filename);
std::vector<uint8_t> encrypted_buffer(file_size);
std::vector<uint8_t> buffer(file_size);
OldStoredUsageTable *stored_table =
reinterpret_cast<OldStoredUsageTable *>(&buffer[0]);
OldStoredUsageTable *encrypted_table =
reinterpret_cast<OldStoredUsageTable *>(&encrypted_buffer[0]);
file = file_system->Open(filename, wvcdm::FileSystem::kReadOnly);
if (!file) {
LOGE("OldUsageTable: File open failed: %s", path.c_str());
return;
}
file->Read(reinterpret_cast<char *>(&encrypted_buffer[0]), file_size);
// Verify the signature of the usage table file.
// This should be encrypted and signed with a device specific key.
// For the reference implementation, I'm just going to use the keybox key.
const std::vector<uint8_t> &key = ce_->DeviceRootKey();
if (key.empty()) {
LOGE("OldUsageTable: DeviceRootKey is unexpectedly empty.");
table_.clear();
return;
}
uint8_t computed_signature[SHA256_DIGEST_LENGTH];
unsigned int sig_length = sizeof(computed_signature);
if (!HMAC(EVP_sha256(), &key[0], key.size(),
&encrypted_buffer[SHA256_DIGEST_LENGTH],
file_size - SHA256_DIGEST_LENGTH, computed_signature,
&sig_length)) {
LOGE("OldUsageTable: Could not recreate signature.");
table_.clear();
return;
}
if (memcmp(encrypted_table->signature, computed_signature, sig_length)) {
LOGE("OldUsageTable: Invalid signature given: %s",
wvcdm::HexEncode(&encrypted_buffer[0], sig_length).c_str());
LOGE("OldUsageTable: Invalid signature computed: %s",
wvcdm::HexEncode(computed_signature, sig_length).c_str());
table_.clear();
return;
}
// Next, decrypt the table.
uint8_t iv_buffer[wvoec::KEY_IV_SIZE];
memcpy(iv_buffer, encrypted_table->iv, wvoec::KEY_IV_SIZE);
AES_KEY aes_key;
AES_set_decrypt_key(&key[0], 128, &aes_key);
AES_cbc_encrypt(&encrypted_buffer[SHA256_DIGEST_LENGTH + wvoec::KEY_IV_SIZE],
&buffer[SHA256_DIGEST_LENGTH + wvoec::KEY_IV_SIZE],
file_size - SHA256_DIGEST_LENGTH - wvoec::KEY_IV_SIZE, &aes_key,
iv_buffer, AES_DECRYPT);
// Next, read the generation number from a different location.
// On a real implementation, you should NOT put the generation number in
// a file in user space. It should be stored in secure memory. For the
// reference implementation, we'll just pretend this is secure.
std::string filename2 = path + "GenerationNumber.dat";
file = file_system->Open(filename2, wvcdm::FileSystem::kReadOnly);
if (!file) {
LOGE("OldUsageTable: File open failed: %s (clearing table)", path.c_str());
generation_ = 0;
table_.clear();
return;
}
file->Read(reinterpret_cast<char *>(&generation_), sizeof(int64_t));
if ((stored_table->generation > generation_ + 1) ||
(stored_table->generation < generation_ - 1)) {
LOGE("OldUsageTable: Rollback detected. Clearing Usage Table. %lx -> %lx",
generation_, stored_table->generation);
table_.clear();
generation_ = 0;
return;
}
// At this point, the stored table looks valid. We can load in all the
// entries.
for (uint64_t i = 0; i < stored_table->count; i++) {
OldUsageTableEntry *entry =
new OldUsageTableEntry(this, &stored_table->entries[i].entry);
table_[entry->pst_hash()] = entry;
}
}
OldUsageTableEntry *OldUsageTable::FindEntry(const std::vector<uint8_t> &pst) {
std::unique_lock<std::mutex> lock(lock_);
return FindEntryLocked(pst);
}
OldUsageTableEntry *OldUsageTable::FindEntryLocked(
const std::vector<uint8_t> &pst) {
std::vector<uint8_t> pst_hash;
if (!ComputeHash(pst, pst_hash)) {
LOGE("OldUsageTable: Could not compute hash of pst.");
return NULL;
}
EntryMap::iterator it = table_.find(pst_hash);
if (it == table_.end()) {
return NULL;
}
return it->second;
}
OldUsageTableEntry *OldUsageTable::CreateEntry(
const std::vector<uint8_t> &pst) {
std::vector<uint8_t> pst_hash;
if (!ComputeHash(pst, pst_hash)) {
LOGE("OldUsageTable: Could not compute hash of pst.");
return NULL;
}
OldUsageTableEntry *entry = new OldUsageTableEntry(this, pst_hash);
std::unique_lock<std::mutex> lock(lock_);
table_[pst_hash] = entry;
return entry;
}
void OldUsageTable::Clear() {
std::unique_lock<std::mutex> lock(lock_);
for (EntryMap::iterator i = table_.begin(); i != table_.end(); ++i) {
if (i->second) delete i->second;
}
table_.clear();
}
void OldUsageTable::DeleteFile(CryptoEngine *ce) {
wvcdm::FileSystem *file_system = ce->file_system();
std::string path;
// Note: this path is OK for a real implementation, but using security level 1
// would be better.
// TODO(jfore): Address how this property is presented to the ref. For now,
// the path is empty.
/*if (!Properties::GetDeviceFilesBasePath(kSecurityLevelL3, &path)) {
LOGE("OldUsageTable: Unable to get base path");
return;
}*/
std::string filename = path + "UsageTable.dat";
if (file_system->Exists(filename)) {
if (!file_system->Remove(filename)) {
LOGE("DeleteOldUsageTable: error removing file.");
}
}
}
bool OldUsageTable::ComputeHash(const std::vector<uint8_t> &pst,
std::vector<uint8_t> &pst_hash) {
// The PST is not fixed size, and we have no promises that it is reasonbly
// sized, so we compute a hash of it, and store that instead.
pst_hash.resize(SHA256_DIGEST_LENGTH);
SHA256_CTX context;
if (!SHA256_Init(&context)) return false;
if (!SHA256_Update(&context, &pst[0], pst.size())) return false;
if (!SHA256_Final(&pst_hash[0], &context)) return false;
return true;
}
} // namespace wvoec_ref

View File

@@ -1,101 +0,0 @@
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
//
// Reference implementation of OEMCrypto APIs
//
// This is from the v12 version of oemcrypto usage tables. It is used for
// devices that upgrade from v12 to v13 in the field, and need to convert from
// the old type of usage table to the new.
#ifndef OEMCRYPTO_OLD_USAGE_TABLE_REF_H_
#define OEMCRYPTO_OLD_USAGE_TABLE_REF_H_
#include <stdint.h>
#include <map>
#include <mutex>
#include <string>
#include <vector>
#include "OEMCryptoCENC.h"
#include "oemcrypto_types.h"
#include "openssl/sha.h"
namespace wvoec_ref {
class CryptoEngine;
class OldUsageTable;
class UsagetTableEntry;
struct OldStoredUsageEntry {
// To save disk space, we only store a hash of the pst.
uint8_t pst_hash[SHA256_DIGEST_LENGTH];
int64_t time_of_license_received;
int64_t time_of_first_decrypt;
int64_t time_of_last_decrypt;
enum OEMCrypto_Usage_Entry_Status status;
uint8_t mac_key_server[wvoec::MAC_KEY_SIZE];
uint8_t mac_key_client[wvoec::MAC_KEY_SIZE];
};
typedef union {
struct OldStoredUsageEntry entry;
uint8_t padding[128]; // multiple of block size and bigger than entry size.
} AlignedOldStoredUsageEntry;
struct OldStoredUsageTable {
uint8_t signature[SHA256_DIGEST_LENGTH];
uint8_t iv[wvoec::KEY_IV_SIZE];
int64_t generation;
uint64_t count;
AlignedOldStoredUsageEntry entries[];
};
class OldUsageTableEntry {
public:
OldUsageTableEntry(OldUsageTable *old_usage_table,
const std::vector<uint8_t> &pst_hash);
OldUsageTableEntry(OldUsageTable *old_usage_table,
const OldStoredUsageEntry *buffer);
~OldUsageTableEntry();
const std::vector<uint8_t> &pst_hash() const { return pst_hash_; }
private:
std::vector<uint8_t> pst_hash_;
const OldUsageTable *old_usage_table_;
int64_t time_of_license_received_;
int64_t time_of_first_decrypt_;
int64_t time_of_last_decrypt_;
enum OEMCrypto_Usage_Entry_Status status_;
std::vector<uint8_t> mac_key_server_;
std::vector<uint8_t> mac_key_client_;
friend class UsageTableEntry;
friend class UsageTable;
};
class OldUsageTable {
public:
OldUsageTable(CryptoEngine *ce);
~OldUsageTable() { Clear(); }
OldUsageTableEntry *FindEntry(const std::vector<uint8_t> &pst);
OldUsageTableEntry *CreateEntry(const std::vector<uint8_t> &pst);
void Clear();
static void DeleteFile(CryptoEngine *ce);
private:
OldUsageTableEntry *FindEntryLocked(const std::vector<uint8_t> &pst);
bool ComputeHash(const std::vector<uint8_t> &pst,
std::vector<uint8_t> &pst_hash);
typedef std::map<std::vector<uint8_t>, OldUsageTableEntry *> EntryMap;
EntryMap table_;
std::mutex lock_;
int64_t generation_;
CryptoEngine *ce_;
friend class OldUsageTableEntry;
};
} // namespace wvoec_ref
#endif // OEMCRYPTO_OLD_USAGE_TABLE_REF_H_

View File

@@ -20,8 +20,10 @@
#include <string>
#include <utility>
#include <vector>
#include "file_store.h"
#include "log.h"
#include "odk.h"
#include "oemcrypto_engine_ref.h"
#include "oemcrypto_session.h"
#include "oemcrypto_usage_table_ref.h"
@@ -37,9 +39,9 @@ namespace {
const uint8_t kBakedInCertificateMagicBytes[] = {0xDE, 0xAD, 0xBE, 0xEF};
// Return uint32 referenced through a potentially unaligned pointer.
// If the pointer is NULL, return 0.
// If the pointer is nullptr, return 0.
uint32_t unaligned_dereference_uint32(const void* unaligned_ptr) {
if (unaligned_ptr == NULL) return 0;
if (unaligned_ptr == nullptr) return 0;
uint32_t value;
const uint8_t* src = reinterpret_cast<const uint8_t*>(unaligned_ptr);
uint8_t* dest = reinterpret_cast<uint8_t*>(&value);
@@ -124,8 +126,8 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_CloseSession(
OEMCRYPTO_API OEMCryptoResult OEMCrypto_GenerateDerivedKeys(
OEMCrypto_SESSION session, const uint8_t* mac_key_context,
uint32_t mac_key_context_length, const uint8_t* enc_key_context,
uint32_t enc_key_context_length) {
size_t mac_key_context_length, const uint8_t* enc_key_context,
size_t enc_key_context_length) {
if (crypto_engine == nullptr) {
LOGE("OEMCrypto_GenerateDerivedKeys: OEMCrypto not initialized.");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
@@ -139,7 +141,7 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_GenerateDerivedKeys(
}
SessionContext* session_ctx = crypto_engine->FindSession(session);
if (!session_ctx || !session_ctx->isValid()) {
if (session_ctx == nullptr || !session_ctx->isValid()) {
LOGE("[OEMCrypto_GenerateDerivedKeys(): ERROR_INVALID_SESSION]");
return OEMCrypto_ERROR_INVALID_SESSION;
}
@@ -157,7 +159,6 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_GenerateDerivedKeys(
return OEMCrypto_SUCCESS;
}
OEMCRYPTO_API OEMCryptoResult OEMCrypto_GenerateNonce(OEMCrypto_SESSION session,
uint32_t* nonce) {
if (crypto_engine == nullptr) {
@@ -165,7 +166,7 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_GenerateNonce(OEMCrypto_SESSION session,
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
SessionContext* session_ctx = crypto_engine->FindSession(session);
if (!session_ctx || !session_ctx->isValid()) {
if (session_ctx == nullptr || !session_ctx->isValid()) {
LOGE("[OEMCrypto_GenerateNonce(): ERROR_INVALID_SESSION]");
return OEMCrypto_ERROR_INVALID_SESSION;
}
@@ -199,38 +200,120 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_GenerateNonce(OEMCrypto_SESSION session,
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
}
session_ctx->AddNonce(nonce_value);
if (!session_ctx->set_nonce(nonce_value)) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
*nonce = nonce_value;
return OEMCrypto_SUCCESS;
}
OEMCRYPTO_API OEMCryptoResult OEMCrypto_GenerateSignature(
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
uint8_t* signature, size_t* signature_length) {
OEMCRYPTO_API OEMCryptoResult OEMCrypto_SignLicenseRequest(
OEMCrypto_SESSION session, const uint8_t* protobuf_message,
size_t protobuf_message_length, uint8_t* core_message,
size_t* core_message_length, uint8_t* signature, size_t* signature_length) {
if (crypto_engine == nullptr) {
LOGE("OEMCrypto_GenerateSignature: OEMCrypto Not Initialized.");
LOGE("OEMCrypto_SignLicenseRequest: OEMCrypto Not Initialized.");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (signature_length == nullptr || core_message_length == nullptr) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (*signature_length < SHA256_DIGEST_LENGTH) {
*signature_length = SHA256_DIGEST_LENGTH;
return OEMCrypto_ERROR_SHORT_BUFFER;
}
if (message == NULL || message_length == 0 || signature == NULL ||
signature_length == 0) {
LOGE("[OEMCrypto_GenerateSignature(): OEMCrypto_ERROR_INVALID_CONTEXT]");
if (protobuf_message == nullptr || protobuf_message_length == 0 ||
signature == nullptr) {
LOGE("[OEMCrypto_SignLicenseRequest(): OEMCrypto_ERROR_INVALID_CONTEXT]");
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
SessionContext* session_ctx = crypto_engine->FindSession(session);
if (!session_ctx || !session_ctx->isValid()) {
LOGE("[OEMCrypto_GenerateSignature(): ERROR_INVALID_SESSION]");
if (session_ctx == nullptr || !session_ctx->isValid()) {
LOGE("[OEMCrypto_SignLicenseRequest(): ERROR_INVALID_SESSION]");
return OEMCrypto_ERROR_INVALID_SESSION;
}
if (session_ctx->GenerateSignature(message, message_length, signature,
signature_length)) {
// TODO(b/135288420): Add core message functionality.
*core_message_length = 0;
if (session_ctx->GenerateSignature(protobuf_message, protobuf_message_length,
signature, signature_length, false)) {
return OEMCrypto_SUCCESS;
}
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
OEMCRYPTO_API OEMCryptoResult OEMCrypto_SignRenewalRequest(
OEMCrypto_SESSION session, const uint8_t* protobuf_message,
size_t protobuf_message_length, uint8_t* core_message,
size_t* core_message_length, uint8_t* signature, size_t* signature_length) {
if (crypto_engine == nullptr) {
LOGE("OEMCrypto_SignRenewalRequest: OEMCrypto Not Initialized.");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (signature_length == nullptr || core_message_length == nullptr) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (*signature_length < SHA256_DIGEST_LENGTH) {
*signature_length = SHA256_DIGEST_LENGTH;
return OEMCrypto_ERROR_SHORT_BUFFER;
}
if (protobuf_message == nullptr || protobuf_message_length == 0 ||
signature == nullptr) {
LOGE("[OEMCrypto_SignRenewalRequest(): OEMCrypto_ERROR_INVALID_CONTEXT]");
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
SessionContext* session_ctx = crypto_engine->FindSession(session);
if (session_ctx == nullptr || !session_ctx->isValid()) {
LOGE("[OEMCrypto_SignRenewalRequest(): ERROR_INVALID_SESSION]");
return OEMCrypto_ERROR_INVALID_SESSION;
}
// TODO(b/135288420): Add core message functionality.
*core_message_length = 0;
if (session_ctx->GenerateSignature(protobuf_message, protobuf_message_length,
signature, signature_length, true)) {
return OEMCrypto_SUCCESS;
}
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
OEMCRYPTO_API OEMCryptoResult OEMCrypto_SignProvisioningRequest(
OEMCrypto_SESSION session, const uint8_t* protobuf_message,
size_t protobuf_message_length, uint8_t* core_message,
size_t* core_message_length, uint8_t* signature, size_t* signature_length) {
if (crypto_engine == nullptr) {
LOGE("OEMCrypto_SignProvisioningRequest: OEMCrypto Not Initialized.");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (signature_length == nullptr || core_message_length == nullptr) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (*signature_length < SHA256_DIGEST_LENGTH) {
*signature_length = SHA256_DIGEST_LENGTH;
return OEMCrypto_ERROR_SHORT_BUFFER;
}
if (protobuf_message == nullptr || protobuf_message_length == 0 ||
signature == nullptr) {
LOGE("OEMCrypto_ERROR_INVALID_CONTEXT");
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
SessionContext* session_ctx = crypto_engine->FindSession(session);
if (session_ctx == nullptr || !session_ctx->isValid()) {
LOGE("[OEMCrypto_SignProvisioningRequest(): ERROR_INVALID_SESSION]");
return OEMCrypto_ERROR_INVALID_SESSION;
}
// TODO(b/135288420): Add core message functionality.
*core_message_length = 0;
if (session_ctx->GenerateSignature(protobuf_message, protobuf_message_length,
signature, signature_length, false)) {
return OEMCrypto_SUCCESS;
}
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
@@ -238,7 +321,7 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_GenerateSignature(
bool RangeCheck(const uint8_t* message, uint32_t message_length,
const uint8_t* field, uint32_t field_length, bool allow_null) {
if (field == NULL) return allow_null;
if (field == nullptr) return allow_null;
if (field < message) return false;
if (field + field_length > message + message_length) return false;
return true;
@@ -268,12 +351,12 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_LoadKeys(
return OEMCrypto_ERROR_KEYBOX_INVALID;
}
SessionContext* session_ctx = crypto_engine->FindSession(session);
if (!session_ctx || !session_ctx->isValid()) {
if (session_ctx == nullptr || !session_ctx->isValid()) {
LOGE("[OEMCrypto_LoadKeys(): ERROR_INVALID_SESSION]");
return OEMCrypto_ERROR_INVALID_SESSION;
}
if (message == NULL || message_length == 0 || signature == NULL ||
signature_length == 0 || key_array == NULL || num_keys == 0) {
if (message == nullptr || message_length == 0 || signature == nullptr ||
signature_length == 0 || key_array == nullptr || num_keys == 0) {
LOGE("[OEMCrypto_LoadKeys(): OEMCrypto_ERROR_INVALID_CONTEXT]");
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
@@ -321,12 +404,13 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_LoadKeys(
OEMCRYPTO_API OEMCryptoResult OEMCrypto_LoadEntitledContentKeys(
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
size_t num_keys, const OEMCrypto_EntitledContentKeyObject* key_array) {
if (num_keys == 0) {
const OEMCrypto_EntitledContentKeyObject* key_array,
size_t key_array_length) {
if (key_array_length == 0) {
LOGE("[OEMCrypto_LoadEntitledContentKeys(): key_array is empty.");
return OEMCrypto_SUCCESS;
}
if (!key_array) {
if (key_array == nullptr) {
LOGE("[OEMCrypto_LoadEntitledContentKeys(): missing key_array.");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
@@ -335,11 +419,11 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_LoadEntitledContentKeys(
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
SessionContext* session_ctx = crypto_engine->FindSession(session);
if (!session_ctx || !session_ctx->isValid()) {
if (session_ctx == nullptr || !session_ctx->isValid()) {
LOGE("[OEMCrypto_LoadEntitledContentKeys(): ERROR_INVALID_SESSION]");
return OEMCrypto_ERROR_INVALID_SESSION;
}
for (unsigned int i = 0; i < num_keys; i++) {
for (size_t i = 0; i < key_array_length; i++) {
if (!RangeCheck(message_length, key_array[i].entitlement_key_id, false) ||
!RangeCheck(message_length, key_array[i].content_key_id, false) ||
!RangeCheck(message_length, key_array[i].content_key_data_iv, false) ||
@@ -347,14 +431,13 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_LoadEntitledContentKeys(
LOGE(
"[OEMCrypto_LoadEntitledContentKeys(): "
"OEMCrypto_ERROR_INVALID_CONTEXT -range "
"check %d]",
"check %zu]",
i);
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
}
return session_ctx->LoadEntitledContentKeys(message, message_length, num_keys,
key_array);
return session_ctx->LoadEntitledContentKeys(message, message_length,
key_array, key_array_length);
}
OEMCRYPTO_API OEMCryptoResult OEMCrypto_RefreshKeys(
@@ -372,12 +455,12 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_RefreshKeys(
}
SessionContext* session_ctx = crypto_engine->FindSession(session);
if (!session_ctx || !session_ctx->isValid()) {
if (session_ctx == nullptr || !session_ctx->isValid()) {
LOGE("[OEMCrypto_RefreshKeys(): ERROR_INVALID_SESSION]");
return OEMCrypto_ERROR_INVALID_SESSION;
}
if (message == NULL || message_length == 0 || signature == NULL ||
if (message == nullptr || message_length == 0 || signature == nullptr ||
signature_length == 0 || num_keys == 0) {
LOGE("[OEMCrypto_RefreshKeys(): OEMCrypto_ERROR_INVALID_CONTEXT]");
return OEMCrypto_ERROR_INVALID_CONTEXT;
@@ -437,7 +520,6 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_RefreshKeys(
}
}
session_ctx->FlushNonces();
if (status != OEMCrypto_SUCCESS) {
return status;
}
@@ -454,13 +536,13 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_QueryKeyControl(
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
uint32_t* block = reinterpret_cast<uint32_t*>(key_control_block);
if ((key_control_block_length == NULL) ||
if ((key_control_block_length == nullptr) ||
(*key_control_block_length < wvoec::KEY_CONTROL_SIZE)) {
LOGE("[OEMCrypto_QueryKeyControl(): OEMCrypto_ERROR_SHORT_BUFFER]");
return OEMCrypto_ERROR_SHORT_BUFFER;
}
*key_control_block_length = wvoec::KEY_CONTROL_SIZE;
if (key_id == NULL) {
if (key_id == nullptr) {
LOGE(
"[OEMCrypto_QueryKeyControl(): key_id null. "
"OEMCrypto_ERROR_UNKNOWN_FAILURE]");
@@ -468,7 +550,7 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_QueryKeyControl(
}
SessionContext* session_ctx = crypto_engine->FindSession(session);
if (!session_ctx || !session_ctx->isValid()) {
if (session_ctx == nullptr || !session_ctx->isValid()) {
LOGE("[OEMCrypto_QueryKeyControl(): ERROR_INVALID_SESSION]");
return OEMCrypto_ERROR_INVALID_SESSION;
}
@@ -492,7 +574,7 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_SelectKey(
#endif
SessionContext* session_ctx = crypto_engine->FindSession(session);
if (!session_ctx || !session_ctx->isValid()) {
if (session_ctx == nullptr || !session_ctx->isValid()) {
LOGE("[OEMCrypto_SelectKey(): ERROR_INVALID_SESSION]");
return OEMCrypto_ERROR_INVALID_SESSION;
}
@@ -511,8 +593,8 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_DecryptCENC(
LOGE("OEMCrypto_DecryptCENC: OEMCrypto Not Initialized.");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (data_addr == NULL || data_length == 0 || iv == NULL ||
out_buffer == NULL) {
if (data_addr == nullptr || data_length == 0 || iv == nullptr ||
out_buffer == nullptr) {
LOGE("[OEMCrypto_DecryptCENC(): OEMCrypto_ERROR_INVALID_CONTEXT]");
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
@@ -537,7 +619,7 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_DecryptCENC(
#endif
SessionContext* session_ctx = crypto_engine->FindSession(session);
if (!session_ctx || !session_ctx->isValid()) {
if (session_ctx == nullptr || !session_ctx->isValid()) {
LOGE("[OEMCrypto_DecryptCENC(): ERROR_INVALID_SESSION]");
return OEMCrypto_ERROR_INVALID_SESSION;
}
@@ -556,7 +638,7 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_CopyBuffer(
LOGE("OEMCrypto_CopyBuffer: OEMCrypto Not Initialized.");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (data_addr == NULL || out_buffer == NULL) {
if (data_addr == nullptr || out_buffer == nullptr) {
LOGE("[OEMCrypto_CopyBuffer(): OEMCrypto_ERROR_INVALID_CONTEXT]");
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
@@ -570,7 +652,7 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_CopyBuffer(
OEMCryptoResult status =
crypto_engine->SetDestination(out_buffer, data_length, subsample_flags);
if (status != OEMCrypto_SUCCESS) return status;
if (crypto_engine->destination() != NULL) {
if (crypto_engine->destination() != nullptr) {
memmove(crypto_engine->destination(), data_addr, data_length);
}
return crypto_engine->PushDestination(out_buffer, subsample_flags);
@@ -661,25 +743,37 @@ OEMCRYPTO_API OEMCrypto_ProvisioningMethod OEMCrypto_GetProvisioningMethod() {
return crypto_engine->config_provisioning_method();
}
OEMCRYPTO_API OEMCryptoResult OEMCrypto_GetOEMPublicCertificate(
OEMCrypto_SESSION session, uint8_t* public_cert,
size_t* public_cert_length) {
OEMCRYPTO_API OEMCryptoResult
OEMCrypto_LoadOEMPrivateKey(OEMCrypto_SESSION session) {
if (crypto_engine == nullptr) {
LOGE("OEMCrypto_GetOEMPublicCertificate: OEMCrypto Not Initialized.");
LOGE("OEMCrypto Not Initialized.");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (crypto_engine->config_provisioning_method() != OEMCrypto_OEMCertificate) {
LOGE("OEMCrypto_GetOEMPublicCertificate: Provisioning method = %d.",
LOGE("Provisioning method = %d.",
crypto_engine->config_provisioning_method());
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
SessionContext* session_ctx = crypto_engine->FindSession(session);
if (!session_ctx || !session_ctx->isValid()) {
LOGE("[OEMCrypto_GetOEMPublicCertificate(): ERROR_INVALID_SESSION]");
if (session_ctx == nullptr || !session_ctx->isValid()) {
LOGE("ERROR_INVALID_SESSION");
return OEMCrypto_ERROR_INVALID_SESSION;
}
return crypto_engine->get_oem_certificate(session_ctx, public_cert,
public_cert_length);
return crypto_engine->load_oem_private_key(session_ctx);
}
OEMCRYPTO_API OEMCryptoResult OEMCrypto_GetOEMPublicCertificate(
uint8_t* public_cert, size_t* public_cert_length) {
if (crypto_engine == nullptr) {
LOGE("OEMCrypto Not Initialized.");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (crypto_engine->config_provisioning_method() != OEMCrypto_OEMCertificate) {
LOGE("Provisioning method = %d.",
crypto_engine->config_provisioning_method());
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
return crypto_engine->get_oem_certificate(public_cert, public_cert_length);
}
OEMCRYPTO_API OEMCryptoResult OEMCrypto_GetDeviceID(uint8_t* deviceID,
@@ -721,7 +815,7 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_GetKeyData(uint8_t* keyData,
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
size_t length = crypto_engine->DeviceRootTokenLength();
if (keyDataLength == NULL) {
if (keyDataLength == nullptr) {
LOGE("[OEMCrypto_GetKeyData(): null pointer. ERROR_UNKNOWN_FAILURE]");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
@@ -730,7 +824,7 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_GetKeyData(uint8_t* keyData,
LOGE("[OEMCrypto_GetKeyData(): ERROR_SHORT_BUFFER]");
return OEMCrypto_ERROR_SHORT_BUFFER;
}
if (keyData == NULL) {
if (keyData == nullptr) {
LOGE("[OEMCrypto_GetKeyData(): null pointer. ERROR_UNKNOWN_FAILURE]");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
@@ -762,14 +856,14 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey30(
const uint8_t* enc_rsa_key_iv, uint8_t* wrapped_rsa_key,
size_t* wrapped_rsa_key_length) {
uint32_t nonce = unaligned_dereference_uint32(unaligned_nonce);
if (unaligned_nonce == NULL) {
if (unaligned_nonce == nullptr) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
if (crypto_engine == nullptr) {
LOGE("OEMCrypto_RewrapDeviceRSAKey30: OEMCrypto Not Initialized.");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (wrapped_rsa_key_length == NULL) {
if (wrapped_rsa_key_length == nullptr) {
LOGE("[OEMCrypto_RewrapDeviceRSAKey30(): OEMCrypto_ERROR_INVALID_CONTEXT]");
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
@@ -779,7 +873,7 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey30(
// Important: This layout must match OEMCrypto_LoadDeviceRSAKey below.
size_t buffer_size = enc_rsa_key_length + sizeof(WrappedRSAKey);
if (wrapped_rsa_key == NULL || *wrapped_rsa_key_length < buffer_size) {
if (wrapped_rsa_key == nullptr || *wrapped_rsa_key_length < buffer_size) {
*wrapped_rsa_key_length = buffer_size;
return OEMCrypto_ERROR_SHORT_BUFFER;
}
@@ -789,13 +883,13 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey30(
return OEMCrypto_ERROR_KEYBOX_INVALID;
}
SessionContext* session_ctx = crypto_engine->FindSession(session);
if (!session_ctx || !session_ctx->isValid()) {
if (session_ctx == nullptr || !session_ctx->isValid()) {
LOGE("[OEMCrypto_RewrapDeviceRSAKey30(): ERROR_INVALID_SESSION]");
return OEMCrypto_ERROR_INVALID_SESSION;
}
if (encrypted_message_key == NULL || encrypted_message_key_length == 0 ||
enc_rsa_key == NULL || enc_rsa_key_iv == NULL ||
unaligned_nonce == NULL) {
if (encrypted_message_key == nullptr || encrypted_message_key_length == 0 ||
enc_rsa_key == nullptr || enc_rsa_key_iv == nullptr ||
unaligned_nonce == nullptr) {
LOGE("[OEMCrypto_RewrapDeviceRSAKey30(): OEMCrypto_ERROR_INVALID_CONTEXT]");
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
@@ -804,7 +898,6 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey30(
if (!session_ctx->CheckNonce(nonce)) {
return OEMCrypto_ERROR_INVALID_NONCE;
}
session_ctx->FlushNonces();
if (!session_ctx->InstallRSAEncryptedKey(encrypted_message_key,
encrypted_message_key_length)) {
@@ -879,9 +972,9 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(
size_t enc_rsa_key_length, const uint8_t* enc_rsa_key_iv,
uint8_t* wrapped_rsa_key, size_t* wrapped_rsa_key_length) {
uint32_t nonce = unaligned_dereference_uint32(unaligned_nonce);
if (unaligned_nonce == NULL) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
if (unaligned_nonce == nullptr) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
if (crypto_engine == nullptr) {
LOGE("OEMCrypto_RewrapDeviceRSAKey: OEMCrypto Not Initialized.");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
@@ -889,7 +982,7 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(
if (crypto_engine->config_provisioning_method() != OEMCrypto_Keybox) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
if (wrapped_rsa_key_length == NULL) {
if (wrapped_rsa_key_length == nullptr) {
LOGE("[OEMCrypto_RewrapDeviceRSAKey(): OEMCrypto_ERROR_INVALID_CONTEXT]");
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
@@ -899,7 +992,7 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(
// Important: This layout must match OEMCrypto_LoadDeviceRSAKey below.
size_t buffer_size = enc_rsa_key_length + sizeof(WrappedRSAKey);
if (wrapped_rsa_key == NULL || *wrapped_rsa_key_length < buffer_size) {
if (wrapped_rsa_key == nullptr || *wrapped_rsa_key_length < buffer_size) {
*wrapped_rsa_key_length = buffer_size;
return OEMCrypto_ERROR_SHORT_BUFFER;
}
@@ -909,12 +1002,13 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(
return OEMCrypto_ERROR_KEYBOX_INVALID;
}
SessionContext* session_ctx = crypto_engine->FindSession(session);
if (!session_ctx || !session_ctx->isValid()) {
if (session_ctx == nullptr || !session_ctx->isValid()) {
LOGE("[OEMCrypto_RewrapDeviceRSAKey(): ERROR_INVALID_SESSION]");
return OEMCrypto_ERROR_INVALID_SESSION;
}
if (message == NULL || message_length == 0 || signature == NULL ||
signature_length == 0 || unaligned_nonce == NULL || enc_rsa_key == NULL) {
if (message == nullptr || message_length == 0 || signature == nullptr ||
signature_length == 0 || unaligned_nonce == nullptr ||
enc_rsa_key == nullptr) {
LOGE("[OEMCrypto_RewrapDeviceRSAKey(): OEMCrypto_ERROR_INVALID_CONTEXT]");
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
@@ -934,7 +1028,6 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(
if (!session_ctx->CheckNonce(nonce)) {
return OEMCrypto_ERROR_INVALID_NONCE;
}
session_ctx->FlushNonces();
// Decrypt RSA key.
std::vector<uint8_t> pkcs8_rsa_key(enc_rsa_key_length);
@@ -995,7 +1088,7 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(
OEMCRYPTO_API OEMCryptoResult OEMCrypto_LoadDeviceRSAKey(
OEMCrypto_SESSION session, const uint8_t* wrapped_rsa_key,
size_t wrapped_rsa_key_length) {
if (wrapped_rsa_key == NULL) {
if (wrapped_rsa_key == nullptr) {
LOGE("[OEMCrypto_LoadDeviceRSAKey(): OEMCrypto_ERROR_INVALID_CONTEXT]");
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
@@ -1023,7 +1116,7 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_LoadDeviceRSAKey(
}
SessionContext* session_ctx = crypto_engine->FindSession(session);
if (!session_ctx || !session_ctx->isValid()) {
if (session_ctx == nullptr || !session_ctx->isValid()) {
LOGE("[OEMCrypto_LoadDeviceRSAKey(): ERROR_INVALID_SESSION]");
return OEMCrypto_ERROR_INVALID_SESSION;
}
@@ -1085,7 +1178,7 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_GenerateRSASignature(
}
SessionContext* session_ctx = crypto_engine->FindSession(session);
if (!session_ctx || !session_ctx->isValid()) {
if (session_ctx == nullptr || !session_ctx->isValid()) {
LOGE("[OEMCrypto_GenerateRSASignature(): ERROR_INVALID_SESSION]");
return OEMCrypto_ERROR_INVALID_SESSION;
}
@@ -1096,7 +1189,7 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_GenerateRSASignature(
return OEMCrypto_ERROR_SHORT_BUFFER;
}
if (message == NULL || message_length == 0 || signature == NULL ||
if (message == nullptr || message_length == 0 || signature == nullptr ||
signature_length == 0) {
LOGE("[OEMCrypto_GenerateRSASignature(): OEMCrypto_ERROR_INVALID_CONTEXT]");
return OEMCrypto_ERROR_INVALID_CONTEXT;
@@ -1122,7 +1215,7 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_DeriveKeysFromSessionKey(
}
SessionContext* session_ctx = crypto_engine->FindSession(session);
if (!session_ctx || !session_ctx->isValid()) {
if (session_ctx == nullptr || !session_ctx->isValid()) {
LOGE("[OEMCrypto_GenerateDerivedKeys(): ERROR_INVALID_SESSION]");
return OEMCrypto_ERROR_INVALID_SESSION;
}
@@ -1146,7 +1239,7 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_DeriveKeysFromSessionKey(
return OEMCrypto_SUCCESS;
}
OEMCRYPTO_API uint32_t OEMCrypto_APIVersion() { return 15; }
OEMCRYPTO_API uint32_t OEMCrypto_APIVersion() { return 16; }
OEMCRYPTO_API uint8_t OEMCrypto_Security_Patch_Level() {
uint8_t security_patch_level = crypto_engine->config_security_patch_level();
@@ -1164,8 +1257,8 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_GetHDCPCapability(
LOGE("OEMCrypto_GetHDCPCapability: OEMCrypto Not Initialized.");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (current == NULL) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
if (maximum == NULL) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
if (current == nullptr) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
if (maximum == nullptr) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
*current = crypto_engine->config_current_hdcp_capability();
*maximum = crypto_engine->config_maximum_hdcp_capability();
return OEMCrypto_SUCCESS;
@@ -1200,12 +1293,21 @@ OEMCRYPTO_API bool OEMCrypto_SupportsUsageTable() {
return supports_usage;
}
OEMCRYPTO_API size_t OEMCrypto_MaximumUsageTableHeaderSize() {
// TOOD(b/140080305): fill in a real value.
if (crypto_engine == nullptr) {
LOGE("OEMCrypto_MaximumUsageTableHeaderSize: OEMCrypto Not Initialized.");
return 0;
}
return 200;
}
OEMCRYPTO_API OEMCryptoResult OEMCrypto_GetNumberOfOpenSessions(size_t* count) {
if (crypto_engine == nullptr) {
LOGE("OEMCrypto_GetNumberOfOpenSessions: OEMCrypto Not Initialized.");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (count == NULL) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
if (count == nullptr) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
*count = crypto_engine->GetNumberOfOpenSessions();
return OEMCrypto_SUCCESS;
}
@@ -1216,7 +1318,7 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_GetMaxNumberOfSessions(
LOGE("OEMCrypto_GetMaxNumberOfSessions: OEMCrypto Not Initialized.");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (maximum == NULL) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
if (maximum == nullptr) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
*maximum = crypto_engine->GetMaxNumberOfSessions();
return OEMCrypto_SUCCESS;
}
@@ -1252,12 +1354,12 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_Generic_Encrypt(
return OEMCrypto_ERROR_KEYBOX_INVALID;
}
SessionContext* session_ctx = crypto_engine->FindSession(session);
if (!session_ctx || !session_ctx->isValid()) {
if (session_ctx == nullptr || !session_ctx->isValid()) {
LOGE("[OEMCrypto_Generic_Encrypt(): ERROR_INVALID_SESSION]");
return OEMCrypto_ERROR_INVALID_SESSION;
}
if (in_buffer == NULL || buffer_length == 0 || iv == NULL ||
out_buffer == NULL) {
if (in_buffer == nullptr || buffer_length == 0 || iv == nullptr ||
out_buffer == nullptr) {
LOGE("[OEMCrypto_Generic_Encrypt(): OEMCrypto_ERROR_INVALID_CONTEXT]");
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
@@ -1278,12 +1380,12 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_Generic_Decrypt(
return OEMCrypto_ERROR_KEYBOX_INVALID;
}
SessionContext* session_ctx = crypto_engine->FindSession(session);
if (!session_ctx || !session_ctx->isValid()) {
if (session_ctx == nullptr || !session_ctx->isValid()) {
LOGE("[OEMCrypto_Generic_Decrypt(): ERROR_INVALID_SESSION]");
return OEMCrypto_ERROR_INVALID_SESSION;
}
if (in_buffer == NULL || buffer_length == 0 || iv == NULL ||
out_buffer == NULL) {
if (in_buffer == nullptr || buffer_length == 0 || iv == nullptr ||
out_buffer == nullptr) {
LOGE("[OEMCrypto_Generic_Decrypt(): OEMCrypto_ERROR_INVALID_CONTEXT]");
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
@@ -1305,7 +1407,7 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_Generic_Sign(
return OEMCrypto_ERROR_KEYBOX_INVALID;
}
SessionContext* session_ctx = crypto_engine->FindSession(session);
if (!session_ctx || !session_ctx->isValid()) {
if (session_ctx == nullptr || !session_ctx->isValid()) {
LOGE("[OEMCrypto_Generic_Sign(): ERROR_INVALID_SESSION]");
return OEMCrypto_ERROR_INVALID_SESSION;
}
@@ -1313,7 +1415,7 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_Generic_Sign(
*signature_length = SHA256_DIGEST_LENGTH;
return OEMCrypto_ERROR_SHORT_BUFFER;
}
if (in_buffer == NULL || buffer_length == 0 || signature == NULL) {
if (in_buffer == nullptr || buffer_length == 0 || signature == nullptr) {
LOGE("[OEMCrypto_Generic_Sign(): OEMCrypto_ERROR_INVALID_CONTEXT]");
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
@@ -1335,14 +1437,14 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_Generic_Verify(
return OEMCrypto_ERROR_KEYBOX_INVALID;
}
SessionContext* session_ctx = crypto_engine->FindSession(session);
if (!session_ctx || !session_ctx->isValid()) {
if (session_ctx == nullptr || !session_ctx->isValid()) {
LOGE("[OEMCrypto_Generic_Verify(): ERROR_INVALID_SESSION]");
return OEMCrypto_ERROR_INVALID_SESSION;
}
if (signature_length != SHA256_DIGEST_LENGTH) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (in_buffer == NULL || buffer_length == 0 || signature == NULL) {
if (in_buffer == nullptr || buffer_length == 0 || signature == nullptr) {
LOGE("[OEMCrypto_Generic_Verify(): OEMCrypto_ERROR_INVALID_CONTEXT]");
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
@@ -1350,11 +1452,6 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_Generic_Verify(
signature, signature_length);
}
// TODO(fredgc): remove this.
OEMCRYPTO_API OEMCryptoResult OEMCrypto_UpdateUsageTable() {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
OEMCRYPTO_API OEMCryptoResult OEMCrypto_DeactivateUsageEntry(
OEMCrypto_SESSION session, const uint8_t* pst, size_t pst_length) {
if (crypto_engine == nullptr) {
@@ -1365,7 +1462,7 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_DeactivateUsageEntry(
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
SessionContext* session_ctx = crypto_engine->FindSession(session);
if (!session_ctx || !session_ctx->isValid()) {
if (session_ctx == nullptr || !session_ctx->isValid()) {
LOGE("[OEMCrypto_DeactivateUsageEntry(): ERROR_INVALID_SESSION]");
return OEMCrypto_ERROR_INVALID_SESSION;
}
@@ -1389,7 +1486,7 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_ReportUsage(OEMCrypto_SESSION session,
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
SessionContext* session_ctx = crypto_engine->FindSession(session);
if (!session_ctx || !session_ctx->isValid()) {
if (session_ctx == nullptr || !session_ctx->isValid()) {
LOGE("[OEMCrypto_ReportUsage(): ERROR_INVALID_SESSION]");
return OEMCrypto_ERROR_INVALID_SESSION;
}
@@ -1398,30 +1495,6 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_ReportUsage(OEMCrypto_SESSION session,
return sts;
}
OEMCRYPTO_API OEMCryptoResult OEMCrypto_DeleteUsageEntry(
OEMCrypto_SESSION, const uint8_t*, size_t, const uint8_t*, size_t,
const uint8_t*, size_t) {
// TODO(fredgc): delete this.
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
OEMCRYPTO_API OEMCryptoResult OEMCrypto_ForceDeleteUsageEntry(const uint8_t*,
size_t) {
// TODO(fredgc): delete this.
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
OEMCRYPTO_API OEMCryptoResult OEMCrypto_DeleteOldUsageTable() {
if (crypto_engine == nullptr) {
LOGE("OEMCrypto_DeleteOldUsageTable: OEMCrypto Not Initialized.");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (!crypto_engine->config_supports_usage_table()) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
return crypto_engine->usage_table().DeleteOldUsageTable();
}
OEMCRYPTO_API bool OEMCrypto_IsSRMUpdateSupported() {
if (crypto_engine == nullptr) {
LOGE("OEMCrypto_IsSRMUpdateSupported: OEMCrypto Not Initialized.");
@@ -1502,7 +1575,7 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_CreateNewUsageEntry(
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
SessionContext* session_ctx = crypto_engine->FindSession(session);
if (!session_ctx || !session_ctx->isValid()) {
if (session_ctx == nullptr || !session_ctx->isValid()) {
LOGE("[OEMCrypto_CreateNewUsageEntry(): ERROR_INVALID_SESSION]");
return OEMCrypto_ERROR_INVALID_SESSION;
}
@@ -1524,7 +1597,7 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_LoadUsageEntry(
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
SessionContext* session_ctx = crypto_engine->FindSession(session);
if (!session_ctx || !session_ctx->isValid()) {
if (session_ctx == nullptr || !session_ctx->isValid()) {
LOGE("[OEMCrypto_LoadUsageEntry(): ERROR_INVALID_SESSION]");
return OEMCrypto_ERROR_INVALID_SESSION;
}
@@ -1551,7 +1624,7 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_UpdateUsageEntry(
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
SessionContext* session_ctx = crypto_engine->FindSession(session);
if (!session_ctx || !session_ctx->isValid()) {
if (session_ctx == nullptr || !session_ctx->isValid()) {
LOGE("[OEMCrypto_UpdateUsageEntry(): ERROR_INVALID_SESSION]");
return OEMCrypto_ERROR_INVALID_SESSION;
}
@@ -1583,49 +1656,13 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_MoveEntry(OEMCrypto_SESSION session,
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
SessionContext* session_ctx = crypto_engine->FindSession(session);
if (!session_ctx || !session_ctx->isValid()) {
if (session_ctx == nullptr || !session_ctx->isValid()) {
LOGE("[OEMCrypto_MoveEntry(): ERROR_INVALID_SESSION]");
return OEMCrypto_ERROR_INVALID_SESSION;
}
return session_ctx->MoveEntry(new_index);
}
OEMCRYPTO_API OEMCryptoResult OEMCrypto_CopyOldUsageEntry(
OEMCrypto_SESSION session, const uint8_t* pst, size_t pst_length) {
if (crypto_engine == nullptr) {
LOGE("OEMCrypto_CopyOldUsageEntry: OEMCrypto Not Initialized.");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (!crypto_engine->config_supports_usage_table()) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
SessionContext* session_ctx = crypto_engine->FindSession(session);
if (!session_ctx || !session_ctx->isValid()) {
LOGE("[OEMCrypto_CopyOldUsageEntry(): ERROR_INVALID_SESSION]");
return OEMCrypto_ERROR_INVALID_SESSION;
}
std::vector<uint8_t> pstv(pst, pst + pst_length);
return session_ctx->CopyOldUsageEntry(pstv);
}
OEMCRYPTO_API OEMCryptoResult OEMCrypto_CreateOldUsageEntry(
uint64_t time_since_license_received, uint64_t time_since_first_decrypt,
uint64_t time_since_last_decrypt, OEMCrypto_Usage_Entry_Status status,
uint8_t* server_mac_key, uint8_t* client_mac_key, const uint8_t* pst,
size_t pst_length) {
if (crypto_engine == nullptr) {
LOGE("OEMCrypto_CreateOldUsageEntry: OEMCrypto Not Initialized.");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (!crypto_engine->config_supports_usage_table()) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
return crypto_engine->usage_table().CreateOldUsageEntry(
time_since_license_received, time_since_first_decrypt,
time_since_last_decrypt, status, server_mac_key, client_mac_key, pst,
pst_length);
}
OEMCRYPTO_API uint32_t OEMCrypto_SupportsDecryptHash() {
return OEMCrypto_CRC_Clear_Buffer;
}
@@ -1638,7 +1675,7 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_SetDecryptHash(
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
SessionContext* session_ctx = crypto_engine->FindSession(session);
if (!session_ctx || !session_ctx->isValid()) {
if (session_ctx == nullptr || !session_ctx->isValid()) {
LOGE("[OEMCrypto_SetDecryptHash(): ERROR_INVALID_SESSION]");
return OEMCrypto_ERROR_INVALID_SESSION;
}
@@ -1652,7 +1689,7 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_GetHashErrorCode(
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
SessionContext* session_ctx = crypto_engine->FindSession(session);
if (!session_ctx || !session_ctx->isValid()) {
if (session_ctx == nullptr || !session_ctx->isValid()) {
LOGE("[OEMCrypto_GetHashErrorCode(): ERROR_INVALID_SESSION]");
return OEMCrypto_ERROR_INVALID_SESSION;
}

View File

@@ -34,50 +34,50 @@ void RSA_shared_ptr::reset() {
RSA_free(rsa_key_);
}
key_owned_ = false;
rsa_key_ = NULL;
rsa_key_ = nullptr;
}
bool RSA_shared_ptr::LoadPkcs8RsaKey(const uint8_t* buffer, size_t length) {
assert(buffer != NULL);
assert(buffer != nullptr);
reset();
uint8_t* pkcs8_rsa_key = const_cast<uint8_t*>(buffer);
BIO* bio = BIO_new_mem_buf(pkcs8_rsa_key, length);
if (bio == NULL) {
if (bio == nullptr) {
LOGE("[LoadPkcs8RsaKey(): Could not allocate bio buffer]");
return false;
}
bool success = true;
PKCS8_PRIV_KEY_INFO* pkcs8_pki = d2i_PKCS8_PRIV_KEY_INFO_bio(bio, NULL);
if (pkcs8_pki == NULL) {
PKCS8_PRIV_KEY_INFO* pkcs8_pki = d2i_PKCS8_PRIV_KEY_INFO_bio(bio, nullptr);
if (pkcs8_pki == nullptr) {
BIO_reset(bio);
pkcs8_pki = d2i_PKCS8_PRIV_KEY_INFO_bio(bio, NULL);
if (pkcs8_pki == NULL) {
LOGE("[LoadPkcs8RsaKey(): d2i_PKCS8_PRIV_KEY_INFO_bio returned NULL]");
pkcs8_pki = d2i_PKCS8_PRIV_KEY_INFO_bio(bio, nullptr);
if (pkcs8_pki == nullptr) {
LOGE("[LoadPkcs8RsaKey(): d2i_PKCS8_PRIV_KEY_INFO_bio returned nullptr]");
dump_boringssl_error();
success = false;
}
}
EVP_PKEY* evp = NULL;
EVP_PKEY* evp = nullptr;
if (success) {
evp = EVP_PKCS82PKEY(pkcs8_pki);
if (evp == NULL) {
LOGE("[LoadPkcs8RsaKey(): EVP_PKCS82PKEY returned NULL]");
if (evp == nullptr) {
LOGE("[LoadPkcs8RsaKey(): EVP_PKCS82PKEY returned nullptr]");
dump_boringssl_error();
success = false;
}
}
if (success) {
rsa_key_ = EVP_PKEY_get1_RSA(evp);
if (rsa_key_ == NULL) {
if (rsa_key_ == nullptr) {
LOGE("[LoadPkcs8RsaKey(): PrivateKeyInfo did not contain an RSA key]");
success = false;
}
key_owned_ = true;
}
if (evp != NULL) {
if (evp != nullptr) {
EVP_PKEY_free(evp);
}
if (pkcs8_pki != NULL) {
if (pkcs8_pki != nullptr) {
PKCS8_PRIV_KEY_INFO_free(pkcs8_pki);
}
BIO_free(bio);

View File

@@ -18,7 +18,7 @@ namespace wvoec_ref {
// counting.
class RSA_shared_ptr {
public:
RSA_shared_ptr() : rsa_key_(NULL), key_owned_(false) {}
RSA_shared_ptr() : rsa_key_(nullptr), key_owned_(false) {}
~RSA_shared_ptr() { reset(); };
// Explicitly allow copy as share.
explicit RSA_shared_ptr(const RSA_shared_ptr& other) :

View File

@@ -23,14 +23,15 @@
#include <openssl/sha.h>
#include <openssl/x509.h>
#include "disallow_copy_and_assign.h"
#include "keys.h"
#include "log.h"
#include "odk.h"
#include "oemcrypto_engine_ref.h"
#include "oemcrypto_key_ref.h"
#include "oemcrypto_rsa_key_shared.h"
#include "oemcrypto_types.h"
#include "platform.h"
#include "disallow_copy_and_assign.h"
#include "string_conversions.h"
#include "wvcrc32.h"
@@ -159,11 +160,11 @@ EntitlementKey* EntitlementKeysContext::GetEntitlementKey(
SessionContext::~SessionContext() {
if (usage_entry_) {
delete usage_entry_;
usage_entry_ = NULL;
usage_entry_ = nullptr;
}
if (session_keys_) {
delete session_keys_;
session_keys_ = NULL;
session_keys_ = nullptr;
}
}
@@ -171,7 +172,7 @@ SessionContext::~SessionContext() {
bool SessionContext::DeriveKey(const std::vector<uint8_t>& key,
const std::vector<uint8_t>& context, int counter,
std::vector<uint8_t>* out) {
if (key.empty() || counter > 4 || context.empty() || out == NULL) {
if (key.empty() || counter > 4 || context.empty() || out == nullptr) {
LOGE("[DeriveKey(): OEMCrypto_ERROR_INVALID_CONTEXT]");
return false;
}
@@ -290,9 +291,10 @@ bool SessionContext::RSADeriveKeys(
bool SessionContext::GenerateSignature(const uint8_t* message,
size_t message_length,
uint8_t* signature,
size_t* signature_length) {
if (message == NULL || message_length == 0 || signature == NULL ||
signature_length == 0) {
size_t* signature_length,
bool renewal_message) {
if (message == nullptr || message_length == 0 || signature == nullptr ||
signature_length == nullptr) {
LOGE("[OEMCrypto_GenerateSignature(): OEMCrypto_ERROR_INVALID_CONTEXT]");
return false;
}
@@ -301,11 +303,18 @@ bool SessionContext::GenerateSignature(const uint8_t* message,
return false;
}
if (*signature_length < SHA256_DIGEST_LENGTH) {
if (*signature_length != SHA256_DIGEST_LENGTH) {
*signature_length = SHA256_DIGEST_LENGTH;
return false;
}
if (!renewal_message) {
if (state_request_signed_) {
return false;
}
state_request_signed_ = true;
}
unsigned int md_len = *signature_length;
if (HMAC(EVP_sha256(), &mac_key_client_[0], wvoec::MAC_KEY_SIZE, message,
message_length, signature, &md_len)) {
@@ -326,7 +335,7 @@ size_t SessionContext::RSASignatureSize() {
OEMCryptoResult SessionContext::GenerateRSASignature(
const uint8_t* message, size_t message_length, uint8_t* signature,
size_t* signature_length, RSA_Padding_Scheme padding_scheme) {
if (message == NULL || message_length == 0 || signature == NULL ||
if (message == nullptr || message_length == 0 || signature == nullptr ||
signature_length == 0) {
LOGE("[GenerateRSASignature(): OEMCrypto_ERROR_INVALID_CONTEXT]");
return OEMCrypto_ERROR_INVALID_CONTEXT;
@@ -344,21 +353,29 @@ OEMCryptoResult SessionContext::GenerateRSASignature(
return OEMCrypto_ERROR_INVALID_RSA_KEY;
}
// This is the standard padding scheme used for license requests.
// TODO(b/135288022): This first padding scheme will be used only for signing
// messages, as in OEMCrypto_Sign*Request.
if (padding_scheme == kSign_RSASSA_PSS) {
if (state_request_signed_) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
state_request_signed_ = true;
// Hash the message using SHA1.
uint8_t hash[SHA_DIGEST_LENGTH];
if (!SHA1(message, message_length, hash)) {
LOGE("[GeneratRSASignature(): error creating signature hash.]");
LOGE("[GenerateRSASignature(): error creating signature hash.]");
dump_boringssl_error();
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
// Add PSS padding.
std::vector<uint8_t> padded_digest(*signature_length);
int status = RSA_padding_add_PKCS1_PSS_mgf1(
rsa_key(), &padded_digest[0], hash, EVP_sha1(), NULL, kPssSaltLength);
int status =
RSA_padding_add_PKCS1_PSS_mgf1(rsa_key(), &padded_digest[0], hash,
EVP_sha1(), nullptr, kPssSaltLength);
if (status == -1) {
LOGE("[GeneratRSASignature(): error padding hash.]");
LOGE("[GenerateRSASignature(): error padding hash.]");
dump_boringssl_error();
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
@@ -367,21 +384,26 @@ OEMCryptoResult SessionContext::GenerateRSASignature(
status = RSA_private_encrypt(*signature_length, &padded_digest[0],
signature, rsa_key(), RSA_NO_PADDING);
if (status == -1) {
LOGE("[GeneratRSASignature(): error in private encrypt.]");
LOGE("[GenerateRSASignature(): error in private encrypt.]");
dump_boringssl_error();
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
// This is the alternate padding scheme used by cast receivers only.
} else if (padding_scheme == kSign_PKCS1_Block1) {
// TODO(b/135288022): Alternate padding scheme is not used for messages, so
// we do not need to keep track of the state, like we do above. This
// padding scheme will be left over as the only valid option for this
// function. The padding scheme above will be used for signing messages.
if (message_length > 83) {
LOGE("[GeneratRSASignature(): RSA digest too large.]");
LOGE("[GenerateRSASignature(): RSA digest too large.]");
return OEMCrypto_ERROR_SIGNATURE_FAILURE;
}
// Pad the message with PKCS1 padding, and then encrypt.
size_t status = RSA_private_encrypt(message_length, message, signature,
rsa_key(), RSA_PKCS1_PADDING);
if (status != *signature_length) {
LOGE("[GeneratRSASignature(): error in RSA private encrypt. status=%d]",
LOGE("[GenerateRSASignature(): error in RSA private encrypt. status=%d]",
status);
dump_boringssl_error();
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
@@ -500,8 +522,12 @@ OEMCryptoResult SessionContext::LoadKeys(
if (!ValidateMessage(message, message_length, signature, signature_length)) {
return OEMCrypto_ERROR_SIGNATURE_FAILURE;
}
if (state_response_loaded_) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
state_response_loaded_ = true;
if (!session_keys_) {
if (session_keys_ == nullptr) {
switch (license_type) {
case OEMCrypto_ContentLicense:
session_keys_ = new ContentKeysContext();
@@ -587,10 +613,9 @@ OEMCryptoResult SessionContext::LoadKeys(
break;
}
}
FlushNonces();
if (status != OEMCrypto_SUCCESS) return status;
// enc_mac_key can be NULL if license renewal is not supported
// enc_mac_key can be nullptr if license renewal is not supported
if (enc_mac_keys.length != 0) {
// V2.1 license protocol: update mac keys after processing license response
const std::vector<uint8_t> enc_mac_keys_str = std::vector<uint8_t>(
@@ -641,15 +666,16 @@ OEMCryptoResult SessionContext::LoadKeys(
}
OEMCryptoResult SessionContext::LoadEntitledContentKeys(
const uint8_t* message, size_t message_length, size_t num_keys,
const OEMCrypto_EntitledContentKeyObject* key_array) {
const uint8_t* message, size_t message_length,
const OEMCrypto_EntitledContentKeyObject* key_array,
size_t key_array_length) {
if (!key_array) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (!session_keys_ || session_keys_->type() != OEMCrypto_EntitlementLicense) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
for (size_t i = 0; i < num_keys; ++i) {
for (size_t i = 0; i < key_array_length; ++i) {
const OEMCrypto_EntitledContentKeyObject* key_data = &key_array[i];
std::vector<uint8_t> entitlement_key_id;
entitlement_key_id.assign(message + key_data->entitlement_key_id.offset,
@@ -753,7 +779,7 @@ OEMCryptoResult SessionContext::InstallKey(
}
Key key(content_key, key_control_block);
if (!session_keys_) {
if (session_keys_ == nullptr) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
session_keys_->Insert(key_id, key);
@@ -785,11 +811,11 @@ bool SessionContext::InstallRSAEncryptedKey(
OEMCryptoResult SessionContext::RefreshKey(
const KeyId& key_id, const std::vector<uint8_t>& key_control,
const std::vector<uint8_t>& key_control_iv) {
if (!session_keys_) {
if (session_keys_ == nullptr) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
if (key_id.empty()) {
// Key control is not encrypted if key id is NULL
// Key control is not encrypted if key id is nullptr
KeyControlBlock key_control_block(key_control);
if (!key_control_block.valid()) {
LOGE("Parse key control error.");
@@ -807,7 +833,7 @@ OEMCryptoResult SessionContext::RefreshKey(
Key* content_key = session_keys_->Find(key_id);
if (NULL == content_key) {
if (content_key == nullptr) {
LOGE("Key ID not found.");
return OEMCrypto_ERROR_NO_CONTENT_KEY;
}
@@ -963,7 +989,7 @@ OEMCryptoResult SessionContext::Generic_Encrypt(const uint8_t* in_buffer,
OEMCrypto_Algorithm algorithm,
uint8_t* out_buffer) {
// Check there is a content key
if (current_content_key() == NULL) {
if (current_content_key() == nullptr) {
LOGE("[Generic_Encrypt(): OEMCrypto_ERROR_NO_CONTENT_KEY]");
return OEMCrypto_ERROR_NO_CONTENT_KEY;
}
@@ -1003,7 +1029,7 @@ OEMCryptoResult SessionContext::Generic_Decrypt(const uint8_t* in_buffer,
OEMCrypto_Algorithm algorithm,
uint8_t* out_buffer) {
// Check there is a content key
if (current_content_key() == NULL) {
if (current_content_key() == nullptr) {
LOGE("[Generic_Decrypt(): OEMCrypto_ERROR_NO_CONTENT_KEY]");
return OEMCrypto_ERROR_NO_CONTENT_KEY;
}
@@ -1044,7 +1070,7 @@ OEMCryptoResult SessionContext::Generic_Sign(const uint8_t* in_buffer,
uint8_t* signature,
size_t* signature_length) {
// Check there is a content key
if (current_content_key() == NULL) {
if (current_content_key() == nullptr) {
LOGE("[Generic_Sign(): OEMCrypto_ERROR_NO_CONTENT_KEY]");
return OEMCrypto_ERROR_NO_CONTENT_KEY;
}
@@ -1082,7 +1108,7 @@ OEMCryptoResult SessionContext::Generic_Verify(const uint8_t* in_buffer,
const uint8_t* signature,
size_t signature_length) {
// Check there is a content key
if (current_content_key() == NULL) {
if (current_content_key() == nullptr) {
LOGE("[Decrypt_Verify(): OEMCrypto_ERROR_NO_CONTENT_KEY]");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
@@ -1132,11 +1158,11 @@ bool SessionContext::UpdateMacKeys(const std::vector<uint8_t>& enc_mac_keys,
}
bool SessionContext::QueryKeyControlBlock(const KeyId& key_id, uint32_t* data) {
if (!session_keys_) {
if (session_keys_ == nullptr) {
return false;
}
const Key* content_key = session_keys_->Find(key_id);
if (NULL == content_key) {
if (content_key == nullptr) {
LOGE("[QueryKeyControlBlock(): No key matches key id]");
return false;
}
@@ -1149,12 +1175,12 @@ bool SessionContext::QueryKeyControlBlock(const KeyId& key_id, uint32_t* data) {
OEMCryptoResult SessionContext::SelectContentKey(
const KeyId& key_id, OEMCryptoCipherMode cipher_mode) {
if (!session_keys_) {
if (session_keys_ == nullptr) {
LOGE("Select Key: no session keys.");
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
Key* content_key = session_keys_->Find(key_id);
if (NULL == content_key) {
if (content_key == nullptr) {
LOGE("No key matches key id");
return OEMCrypto_ERROR_NO_CONTENT_KEY;
}
@@ -1172,14 +1198,6 @@ OEMCryptoResult SessionContext::SelectContentKey(
return OEMCrypto_SUCCESS;
}
void SessionContext::AddNonce(uint32_t nonce) { nonce_table_.AddNonce(nonce); }
bool SessionContext::CheckNonce(uint32_t nonce) {
return nonce_table_.CheckNonce(nonce);
}
void SessionContext::FlushNonces() { nonce_table_.Flush(); }
bool SessionContext::CheckUsageEntry() {
if (!usage_entry_) return false;
return usage_entry_->CheckForUse();
@@ -1187,6 +1205,10 @@ bool SessionContext::CheckUsageEntry() {
OEMCryptoResult SessionContext::CreateNewUsageEntry(
uint32_t* usage_entry_number) {
if (usage_entry_) {
// Can only load one entry per session.
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
OEMCryptoResult result = ce_->usage_table().CreateNewUsageEntry(
this, &usage_entry_, usage_entry_number);
if (usage_entry_) {
@@ -1197,6 +1219,10 @@ OEMCryptoResult SessionContext::CreateNewUsageEntry(
OEMCryptoResult SessionContext::LoadUsageEntry(
uint32_t index, const std::vector<uint8_t>& buffer) {
if (usage_entry_) {
// Can only load one entry per session.
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
OEMCryptoResult result =
ce_->usage_table().LoadUsageEntry(this, &usage_entry_, index, buffer);
if (usage_entry_) {
@@ -1244,12 +1270,6 @@ OEMCryptoResult SessionContext::MoveEntry(uint32_t new_index) {
return ce_->usage_table().MoveEntry(usage_entry_, new_index);
}
OEMCryptoResult SessionContext::CopyOldUsageEntry(
const std::vector<uint8_t>& pst) {
if (!usage_entry_) return OEMCrypto_ERROR_INVALID_CONTEXT;
return usage_entry_->CopyOldUsageEntry(pst);
}
// Internal utility function to decrypt the message
bool SessionContext::DecryptMessage(const std::vector<uint8_t>& key,
const std::vector<uint8_t>& iv,
@@ -1279,7 +1299,7 @@ OEMCryptoResult SessionContext::DecryptCENC(
ChooseDecrypt(iv, block_offset, pattern, cipher_data, cipher_data_length,
is_encrypted, clear_data, buffer_type);
if (compute_hash_) {
if (current_content_key() == NULL ||
if (current_content_key() == nullptr ||
(current_content_key()->control().control_bits() &
wvoec::kControlAllowHashVerification) == 0) {
LOGE("[DecryptCENC(): OEMCrypto_ERROR_UNKNOWN_FAILURE]");
@@ -1327,7 +1347,7 @@ OEMCryptoResult SessionContext::ChooseDecrypt(
}
// Check there is a content key
if (current_content_key() == NULL) {
if (current_content_key() == nullptr) {
LOGE("[DecryptCTR(): OEMCrypto_ERROR_NO_CONTENT_KEY]");
return OEMCrypto_ERROR_DECRYPT_FAILED;
}
@@ -1484,7 +1504,7 @@ OEMCryptoResult SessionContext::DecryptCTR(const uint8_t* key_u8,
while (remaining) {
EVP_CIPHER_CTX* evp_cipher_ctx = EVP_CIPHER_CTX_new();
EVP_CIPHER_CTX_set_padding(evp_cipher_ctx, 0);
if (!EVP_DecryptInit_ex(evp_cipher_ctx, EVP_aes_128_ctr(), NULL, key_u8,
if (!EVP_DecryptInit_ex(evp_cipher_ctx, EVP_aes_128_ctr(), nullptr, key_u8,
aes_iv_u8)) {
LOGE("[DecryptCTR(): EVP_INIT ERROR]");
EVP_CIPHER_CTX_free(evp_cipher_ctx);
@@ -1551,10 +1571,17 @@ OEMCryptoResult SessionContext::SetDecryptHash(uint32_t frame_number,
OEMCryptoResult SessionContext::GetHashErrorCode(
uint32_t* failed_frame_number) {
if (failed_frame_number == NULL) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
if (failed_frame_number == nullptr) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
if (hash_error_ != OEMCrypto_SUCCESS)
*failed_frame_number = bad_frame_number_;
return hash_error_;
}
bool SessionContext::set_nonce(uint32_t nonce) {
if (state_nonce_created_) return false;
state_nonce_created_ = true;
nonce_ = nonce;
return true;
}
} // namespace wvoec_ref

View File

@@ -15,9 +15,9 @@
#include <openssl/rsa.h>
#include "OEMCryptoCENC.h"
#include "odk_structs.h"
#include "oemcrypto_auth_ref.h"
#include "oemcrypto_key_ref.h"
#include "oemcrypto_nonce_table.h"
#include "oemcrypto_rsa_key_shared.h"
#include "oemcrypto_session_key_table.h"
#include "oemcrypto_types.h"
@@ -66,17 +66,21 @@ class SessionContext {
: valid_(true),
ce_(ce),
id_(sid),
current_content_key_(NULL),
session_keys_(NULL),
current_content_key_(nullptr),
session_keys_(nullptr),
nonce_(0),
rsa_key_(rsa_key),
allowed_schemes_(kSign_RSASSA_PSS),
usage_entry_(NULL),
usage_entry_(nullptr),
srm_requirements_status_(NoSRMVersion),
usage_entry_status_(kNoUsageEntry),
compute_hash_(false),
current_hash_(0),
bad_frame_number_(0),
hash_error_(OEMCrypto_SUCCESS) {}
hash_error_(OEMCrypto_SUCCESS),
state_nonce_created_(false),
state_request_signed_(false),
state_response_loaded_(false) {}
virtual ~SessionContext();
bool isValid() { return valid_; }
@@ -87,8 +91,10 @@ class SessionContext {
virtual bool RSADeriveKeys(const std::vector<uint8_t>& enc_session_key,
const std::vector<uint8_t>& mac_context,
const std::vector<uint8_t>& enc_context);
// TODO(b/135288022): remove renewal_message hack.
virtual bool GenerateSignature(const uint8_t* message, size_t message_length,
uint8_t* signature, size_t* signature_length);
uint8_t* signature, size_t* signature_length,
bool renewal_message);
size_t RSASignatureSize();
virtual OEMCryptoResult GenerateRSASignature(
const uint8_t* message, size_t message_length, uint8_t* signature,
@@ -128,9 +134,10 @@ class SessionContext {
const OEMCrypto_KeyObject* key_array, OEMCrypto_Substring pst,
OEMCrypto_Substring srm_restriction_data,
OEMCrypto_LicenseType license_type);
OEMCryptoResult LoadEntitledContentKeys(
const uint8_t* message, size_t message_length, size_t num_keys,
const OEMCrypto_EntitledContentKeyObject* key_array);
virtual OEMCryptoResult LoadEntitledContentKeys(
const uint8_t* message, size_t message_length,
const OEMCrypto_EntitledContentKeyObject* key_array,
size_t key_array_length);
virtual OEMCryptoResult InstallKey(
const KeyId& key_id, const std::vector<uint8_t>& key_data,
const std::vector<uint8_t>& key_data_iv,
@@ -171,13 +178,13 @@ class SessionContext {
const std::vector<uint8_t>& encryption_key() { return encryption_key_; }
uint32_t allowed_schemes() const { return allowed_schemes_; }
void AddNonce(uint32_t nonce);
bool CheckNonce(uint32_t nonce);
// Verify that the nonce does not match any in this session's nonce table.
bool NonceCollision(uint32_t nonce) const {
return nonce_table_.NonceCollision(nonce);
}
void FlushNonces();
// Return true if nonce was set.
bool set_nonce(uint32_t nonce);
uint32_t nonce() const { return nonce_; }
bool CheckNonce(uint32_t nonce) const {
return nonce != 0 && nonce == nonce_;
};
virtual OEMCryptoResult CreateNewUsageEntry(uint32_t* usage_entry_number);
virtual OEMCryptoResult LoadUsageEntry(uint32_t index,
@@ -190,7 +197,6 @@ class SessionContext {
virtual OEMCryptoResult ReportUsage(const std::vector<uint8_t>& pst,
uint8_t* buffer, size_t* buffer_length);
OEMCryptoResult MoveEntry(uint32_t new_index);
OEMCryptoResult CopyOldUsageEntry(const std::vector<uint8_t>& pst);
protected:
bool DeriveKey(const std::vector<uint8_t>& key,
@@ -244,7 +250,7 @@ class SessionContext {
std::vector<uint8_t> session_key_;
const Key* current_content_key_;
SessionContextKeys* session_keys_;
NonceTable nonce_table_;
uint32_t nonce_;
RSA_shared_ptr rsa_key_;
uint32_t allowed_schemes_; // for RSA signatures.
time_t timer_start_;
@@ -265,6 +271,12 @@ class SessionContext {
uint32_t bad_frame_number_; // Frame number with bad hash.
OEMCryptoResult hash_error_; // Error code for first bad frame.
// The bare minimum state machine is to only call each of these function
// categories at most once.
bool state_nonce_created_;
bool state_request_signed_;
bool state_response_loaded_;
CORE_DISALLOW_COPY_AND_ASSIGN(SessionContext);
};

View File

@@ -13,7 +13,7 @@ namespace wvoec_ref {
SessionKeyTable::~SessionKeyTable() {
for (KeyMap::iterator i = keys_.begin(); i != keys_.end(); ++i) {
if (NULL != i->second) {
if (nullptr != i->second) {
delete i->second;
}
}
@@ -27,7 +27,7 @@ bool SessionKeyTable::Insert(const KeyId key_id, const Key& key_data) {
Key* SessionKeyTable::Find(const KeyId key_id) {
if (keys_.find(key_id) == keys_.end()) {
return NULL;
return nullptr;
}
return keys_[key_id];
}
@@ -59,11 +59,11 @@ Key* EntitlementKeyTable::Find(const KeyId key_id) {
ContentIdToEntitlementIdMap::iterator it =
contentid_to_entitlementid_.find(key_id);
if (it == contentid_to_entitlementid_.end()) {
return NULL;
return nullptr;
}
if (keys_.find(it->second) == keys_.end()) {
return NULL;
return nullptr;
}
return keys_[it->second];
}

View File

@@ -20,7 +20,6 @@
#include "file_store.h"
#include "log.h"
#include "oemcrypto_engine_ref.h"
#include "oemcrypto_old_usage_table_ref.h"
// TODO(fredgc): Setting the device files base bath is currently broken as
// wvcdm::Properties is no longer used by the reference code.
//#include "properties.h"
@@ -299,37 +298,6 @@ OEMCryptoResult UsageTableEntry::LoadData(CryptoEngine* ce, uint32_t index,
return OEMCrypto_SUCCESS;
}
OEMCryptoResult UsageTableEntry::CopyOldUsageEntry(
const std::vector<uint8_t>& pst) {
OldUsageTableEntry* old_entry = usage_table_->FindOldUsageEntry(pst);
if (!old_entry) return OEMCrypto_ERROR_WRONG_PST;
data_.time_of_license_received = old_entry->time_of_license_received_;
data_.time_of_first_decrypt = old_entry->time_of_first_decrypt_;
data_.time_of_last_decrypt = old_entry->time_of_last_decrypt_;
data_.status = old_entry->status_;
if (old_entry->mac_key_server_.size() != wvoec::MAC_KEY_SIZE) {
LOGE("CopyOldEntry: Old entry has bad server mac key.");
} else {
memcpy(data_.mac_key_server, &(old_entry->mac_key_server_[0]),
wvoec::MAC_KEY_SIZE);
}
if (old_entry->mac_key_client_.size() != wvoec::MAC_KEY_SIZE) {
LOGE("CopyOldEntry: Old entry has bad client mac key.");
} else {
memcpy(data_.mac_key_client, &(old_entry->mac_key_client_[0]),
wvoec::MAC_KEY_SIZE);
}
if (pst.size() > kMaxPSTLength) {
LOGE("CopyOldEntry: PST Length was too large. Truncating.");
data_.pst_length = kMaxPSTLength;
} else {
data_.pst_length = pst.size();
}
memcpy(data_.pst, pst.data(), data_.pst_length);
data_.pst[data_.pst_length] = '\0';
return OEMCrypto_SUCCESS;
}
size_t UsageTableEntry::SignedEntrySize() {
size_t base = sizeof(SignedEntryBlock);
// round up to make even number of blocks:
@@ -337,12 +305,7 @@ size_t UsageTableEntry::SignedEntrySize() {
return blocks * wvoec::KEY_IV_SIZE;
}
UsageTable::~UsageTable() {
if (old_table_) {
delete old_table_;
old_table_ = NULL;
}
}
UsageTable::~UsageTable() {}
size_t UsageTable::SignedHeaderSize(size_t count) {
size_t base = sizeof(SignedHeaderBlock) + count * sizeof(int64_t);
@@ -722,7 +685,7 @@ OEMCryptoResult UsageTable::CreateUsageTableHeader(
if (!LoadGenerationNumber(true)) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
// Make sure there are no entries that are currently tied to an open session.
for (size_t i = 0; i < sessions_.size(); ++i) {
if (sessions_[i] != NULL) {
if (sessions_[i] != nullptr) {
LOGE("CreateUsageTableHeader: index %d used by session.", i);
return OEMCrypto_ERROR_INVALID_SESSION;
}
@@ -733,41 +696,4 @@ OEMCryptoResult UsageTable::CreateUsageTableHeader(
return SaveUsageTableHeader(header_buffer, *header_buffer_length);
}
OldUsageTableEntry* UsageTable::FindOldUsageEntry(
const std::vector<uint8_t>& pst) {
if (!old_table_) old_table_ = new OldUsageTable(ce_);
return old_table_->FindEntry(pst);
}
OEMCryptoResult UsageTable::DeleteOldUsageTable() {
if (old_table_) {
old_table_->Clear();
delete old_table_;
old_table_ = NULL;
}
OldUsageTable::DeleteFile(ce_);
return OEMCrypto_SUCCESS;
}
OEMCryptoResult UsageTable::CreateOldUsageEntry(
uint64_t time_since_license_received, uint64_t time_since_first_decrypt,
uint64_t time_since_last_decrypt, OEMCrypto_Usage_Entry_Status status,
uint8_t* server_mac_key, uint8_t* client_mac_key, const uint8_t* pst,
size_t pst_length) {
if (!old_table_) old_table_ = new OldUsageTable(ce_);
std::vector<uint8_t> pstv(pst, pst + pst_length);
OldUsageTableEntry* old_entry = old_table_->CreateEntry(pstv);
int64_t now = ce_->RollbackCorrectedOfflineTime();
old_entry->time_of_license_received_ = now - time_since_license_received;
old_entry->time_of_first_decrypt_ = now - time_since_first_decrypt;
old_entry->time_of_last_decrypt_ = now - time_since_last_decrypt;
old_entry->status_ = status;
old_entry->mac_key_server_.assign(server_mac_key,
server_mac_key + wvoec::MAC_KEY_SIZE);
old_entry->mac_key_client_.assign(client_mac_key,
client_mac_key + wvoec::MAC_KEY_SIZE);
return OEMCrypto_SUCCESS;
}
} // namespace wvoec_ref

View File

@@ -21,8 +21,6 @@ namespace wvoec_ref {
class SessionContext;
class CryptoEngine;
class UsageTable;
class OldUsageTable;
class OldUsageTableEntry;
const size_t kMaxPSTLength = 255;
// This is the data we store offline.
@@ -62,7 +60,6 @@ class UsageTableEntry {
uint8_t* signed_buffer, size_t buffer_size);
OEMCryptoResult LoadData(CryptoEngine* ce, uint32_t index,
const std::vector<uint8_t>& buffer);
virtual OEMCryptoResult CopyOldUsageEntry(const std::vector<uint8_t>& pst);
int64_t generation_number() { return data_.generation_number; }
void set_generation_number(int64_t value) { data_.generation_number = value; }
void set_index(int32_t index) { data_.index = index; }
@@ -80,8 +77,7 @@ class UsageTableEntry {
class UsageTable {
public:
explicit UsageTable(CryptoEngine* ce)
: ce_(ce), header_loaded_(false), old_table_(NULL) {};
explicit UsageTable(CryptoEngine* ce) : ce_(ce), header_loaded_(false){};
virtual ~UsageTable();
OEMCryptoResult CreateNewUsageEntry(SessionContext* session,
@@ -106,15 +102,6 @@ class UsageTable {
void ReleaseEntry(uint32_t index) { sessions_[index] = 0; }
void IncrementGeneration();
static size_t SignedHeaderSize(size_t count);
OldUsageTableEntry* FindOldUsageEntry(const std::vector<uint8_t>& pst);
OEMCryptoResult DeleteOldUsageTable();
OEMCryptoResult CreateOldUsageEntry(uint64_t time_since_license_received,
uint64_t time_since_first_decrypt,
uint64_t time_since_last_decrypt,
OEMCrypto_Usage_Entry_Status status,
uint8_t* server_mac_key,
uint8_t* client_mac_key,
const uint8_t* pst, size_t pst_length);
protected:
virtual UsageTableEntry* MakeEntry(uint32_t index);
@@ -128,7 +115,6 @@ class UsageTable {
int64_t master_generation_number_;
std::vector<int64_t> generation_numbers_;
std::vector<SessionContext*> sessions_;
OldUsageTable* old_table_;
friend class UsageTableEntry;
};

View File

@@ -7,8 +7,13 @@
#include "oec_device_features.h"
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#ifdef _WIN32
# include <windows.h>
#else
# include <sys/types.h>
# include <unistd.h>
#endif
#include <cstring>
@@ -18,6 +23,43 @@ namespace wvoec {
DeviceFeatures global_features;
bool CanChangeTime() {
#ifdef _WIN32
LUID desired_id;
if (!LookupPrivilegeValue(nullptr, SE_SYSTEMTIME_NAME, &desired_id))
return false;
HANDLE token;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &token))
return false;
std::unique_ptr<void, decltype(&CloseHandle)> safe_token(token, &CloseHandle);
// This queries all the permissions given to the token to determine if we can
// change the system time. Note this is subtly different from PrivilegeCheck
// as that only checks "enabled" privileges; even with admin rights, the
// privilege is default disabled, even when granted.
DWORD size = 0;
// Determine how big we need to allocate first.
GetTokenInformation(token, TokenPrivileges, nullptr, 0, &size);
// Since TOKEN_PRIVILEGES uses a variable-length array, we need to use malloc
std::unique_ptr<TOKEN_PRIVILEGES, decltype(&free)> privileges(
(TOKEN_PRIVILEGES*)malloc(size), &free);
if (privileges && GetTokenInformation(token, TokenPrivileges,
privileges.get(), size, &size)) {
for (int i = 0; i < privileges->PrivilegeCount; i++) {
if (privileges->Privileges[i].Luid.HighPart == desired_id.HighPart &&
privileges->Privileges[i].Luid.LowPart == desired_id.LowPart) {
return true;
}
}
}
return false;
#else
return getuid() == 0;
#endif
}
void DeviceFeatures::Initialize(bool is_cast_receiver,
bool force_load_test_keybox) {
cast_receiver = is_cast_receiver;
@@ -141,6 +183,7 @@ void DeviceFeatures::Initialize(bool is_cast_receiver,
std::string DeviceFeatures::RestrictFilter(const std::string& initial_filter) {
std::string filter = initial_filter;
// clang-format off
if (!uses_keybox) FilterOut(&filter, "*KeyboxTest*");
if (derive_key_method
!= FORCE_TEST_KEYBOX) FilterOut(&filter, "*ForceKeybox*");
@@ -160,10 +203,12 @@ std::string DeviceFeatures::RestrictFilter(const std::string& initial_filter) {
if (api_version < 13) FilterOut(&filter, "*API13*");
if (api_version < 14) FilterOut(&filter, "*API14*");
if (api_version < 15) FilterOut(&filter, "*API15*");
if (api_version < 16) FilterOut(&filter, "*API16*");
// clang-format on
// Some tests may require root access. If user is not root, filter these tests
// out.
if (getuid()) {
FilterOut(&filter, "UsageTableTest.TimeRollbackPrevention");
if (!CanChangeTime()) {
FilterOut(&filter, "OEMCryptoUsageTableTest.TimeRollbackPrevention");
}
// Performance tests take a long time. Filter them out if they are not
// specifically requested.
@@ -197,7 +242,8 @@ void DeviceFeatures::PickDerivedKey() {
}
if (uses_keybox) {
// If device uses a keybox, try to load the test keybox.
if (OEMCrypto_ERROR_NOT_IMPLEMENTED != OEMCrypto_LoadTestKeybox(NULL, 0)) {
if (OEMCrypto_ERROR_NOT_IMPLEMENTED !=
OEMCrypto_LoadTestKeybox(nullptr, 0)) {
derive_key_method = LOAD_TEST_KEYBOX;
}
} else if (OEMCrypto_ERROR_NOT_IMPLEMENTED != OEMCrypto_LoadTestRSAKey()) {

View File

@@ -0,0 +1,169 @@
// 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.
//
// OEMCrypto unit tests
//
#include "oec_session_util.h"
#include <openssl/aes.h>
#include <openssl/bio.h>
#include <openssl/cmac.h>
#include <openssl/err.h>
#include <openssl/hmac.h>
#include <openssl/pem.h>
#include <openssl/rand.h>
#include <openssl/x509.h>
#include <openssl/x509_vfy.h>
#include <stdint.h>
#include <gtest/gtest.h>
#include <iostream>
#include <memory>
#include <string>
#include <vector>
#include "OEMCryptoCENC.h"
#include "disallow_copy_and_assign.h"
#include "log.h"
#include "oec_device_features.h"
#include "oec_test_data.h"
#include "oemcrypto_types.h"
#include "platform.h"
#include "string_conversions.h"
using namespace std;
namespace wvoec {
void Encryptor::set_enc_key(const std::vector<uint8_t>& enc_key) {
enc_key_ = enc_key;
}
void Encryptor::CBCEncrypt(const uint8_t* data, uint8_t* encrypted_data,
size_t data_length,
const uint8_t (&iv)[KEY_IV_SIZE]) const {
ASSERT_EQ(enc_key_.size(), KEY_SIZE);
ASSERT_NE(data, nullptr);
ASSERT_NE(encrypted_data, nullptr);
AES_KEY aes_key;
static const int key_size = KEY_SIZE * 8; // in bits.
AES_set_encrypt_key(enc_key_.data(), key_size, &aes_key);
uint8_t iv_buffer[KEY_IV_SIZE];
memcpy(iv_buffer, iv, KEY_IV_SIZE);
AES_cbc_encrypt(data, encrypted_data, data_length, &aes_key, iv_buffer,
AES_ENCRYPT);
}
void Encryptor::PadAndEncryptProvisioningMessage(
RSAPrivateKeyMessage* data, RSAPrivateKeyMessage* encrypted) const {
EXPECT_EQ(1, GetRandBytes(data->rsa_key_iv, KEY_IV_SIZE));
ASSERT_EQ(enc_key_.size(), KEY_SIZE);
*encrypted = *data;
size_t padding = AES_BLOCK_SIZE - (data->rsa_key_length % AES_BLOCK_SIZE);
memset(data->rsa_key + data->rsa_key_length, static_cast<uint8_t>(padding),
padding);
encrypted->rsa_key_length = data->rsa_key_length + padding;
AES_KEY aes_key;
static const int key_size = KEY_SIZE * 8; // in bits.
AES_set_encrypt_key(enc_key_.data(), key_size, &aes_key);
uint8_t iv_buffer[KEY_IV_SIZE];
memcpy(iv_buffer, &data->rsa_key_iv[0], KEY_IV_SIZE);
AES_cbc_encrypt(&data->rsa_key[0], &encrypted->rsa_key[0],
encrypted->rsa_key_length, &aes_key, iv_buffer, AES_ENCRYPT);
}
// This generates the data for deriving one key. If there are failures in
// this function, then there is something wrong with the test program and its
// dependency on BoringSSL.
void KeyDeriver::DeriveKey(const uint8_t* key, const vector<uint8_t>& context,
int counter, vector<uint8_t>* out) {
ASSERT_NE(key, nullptr);
ASSERT_FALSE(context.empty());
ASSERT_GE(4, counter);
ASSERT_LE(1, counter);
ASSERT_NE(out, nullptr);
const EVP_CIPHER* cipher = EVP_aes_128_cbc();
CMAC_CTX* cmac_ctx = CMAC_CTX_new();
ASSERT_NE(nullptr, cmac_ctx);
ASSERT_TRUE(CMAC_Init(cmac_ctx, key, KEY_SIZE, cipher, 0));
std::vector<uint8_t> message;
message.push_back(static_cast<uint8_t>(counter));
message.insert(message.end(), context.begin(), context.end());
ASSERT_TRUE(CMAC_Update(cmac_ctx, message.data(), message.size()));
size_t reslen;
uint8_t res[128];
ASSERT_TRUE(CMAC_Final(cmac_ctx, res, &reslen));
out->assign(res, res + reslen);
CMAC_CTX_free(cmac_ctx);
}
// This generates the data for deriving a set of keys. If there are failures in
// this function, then there is something wrong with the test program and its
// dependency on BoringSSL.
void KeyDeriver::DeriveKeys(const uint8_t* master_key,
const vector<uint8_t>& mac_key_context,
const vector<uint8_t>& enc_key_context) {
// Generate derived key for mac key
std::vector<uint8_t> mac_key_part2;
DeriveKey(master_key, mac_key_context, 1, &mac_key_server_);
DeriveKey(master_key, mac_key_context, 2, &mac_key_part2);
mac_key_server_.insert(mac_key_server_.end(), mac_key_part2.begin(),
mac_key_part2.end());
DeriveKey(master_key, mac_key_context, 3, &mac_key_client_);
DeriveKey(master_key, mac_key_context, 4, &mac_key_part2);
mac_key_client_.insert(mac_key_client_.end(), mac_key_part2.begin(),
mac_key_part2.end());
// Generate derived key for encryption key
std::vector<uint8_t> enc_key;
DeriveKey(master_key, enc_key_context, 1, &enc_key);
set_enc_key(enc_key);
}
void KeyDeriver::set_mac_keys(const uint8_t* mac_keys) {
ASSERT_EQ(mac_key_server_.size(), MAC_KEY_SIZE);
ASSERT_EQ(mac_key_client_.size(), MAC_KEY_SIZE);
memcpy(mac_key_server_.data(), mac_keys, MAC_KEY_SIZE);
memcpy(mac_key_client_.data(), mac_keys + MAC_KEY_SIZE, MAC_KEY_SIZE);
}
void KeyDeriver::ServerSignBuffer(const uint8_t* data, size_t data_length,
std::vector<uint8_t>* signature) {
ASSERT_LE(data_length, kMaxMessageSize);
ASSERT_EQ(mac_key_server_.size(), MAC_KEY_SIZE);
signature->assign(SHA256_DIGEST_LENGTH, 0);
unsigned int sig_len = SHA256_DIGEST_LENGTH;
ASSERT_TRUE(HMAC(EVP_sha256(), mac_key_server_.data(), mac_key_server_.size(),
data, data_length, signature->data(), &sig_len));
}
void KeyDeriver::ClientSignBuffer(const vector<uint8_t>& buffer,
std::vector<uint8_t>* signature) {
ASSERT_EQ(mac_key_client_.size(), MAC_KEY_SIZE);
signature->assign(SHA256_DIGEST_LENGTH, 0);
unsigned int sig_len = SHA256_DIGEST_LENGTH;
ASSERT_TRUE(HMAC(EVP_sha256(), mac_key_client_.data(), mac_key_client_.size(),
buffer.data(), buffer.size(), signature->data(), &sig_len));
}
void KeyDeriver::ClientSignPstReport(const vector<uint8_t>& pst_report_buffer,
std::vector<uint8_t>* signature) {
ASSERT_EQ(mac_key_client_.size(), MAC_KEY_SIZE);
signature->assign(SHA_DIGEST_LENGTH, 0);
unsigned int sig_len = SHA_DIGEST_LENGTH;
ASSERT_TRUE(HMAC(EVP_sha1(), mac_key_client_.data(), mac_key_client_.size(),
&pst_report_buffer[SHA_DIGEST_LENGTH],
pst_report_buffer.size() - SHA_DIGEST_LENGTH,
signature->data(), &sig_len));
}
} // namespace wvoec

View File

@@ -0,0 +1,90 @@
// 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 CDM_OEC_KEY_DERIVER_H_
#define CDM_OEC_KEY_DERIVER_H_
#include <openssl/aes.h>
#include <openssl/rsa.h>
#include <time.h>
#include <string>
#include <vector>
#include "oec_device_features.h"
#include "oemcrypto_types.h"
#include "pst_report.h"
namespace wvoec {
constexpr size_t kMaxTestRSAKeyLength = 2000; // Rough estimate.
// This structure will be signed to simulate a provisioning response from the
// server.
struct RSAPrivateKeyMessage {
uint8_t rsa_key[kMaxTestRSAKeyLength];
uint8_t rsa_key_iv[KEY_IV_SIZE];
size_t rsa_key_length;
uint32_t nonce;
};
// Holds an encryption key and can encrypt a provisioning message. It also can
// encrypt short buffers using CBC, such as content keys in a license.
class Encryptor {
public:
Encryptor() : enc_key_(KEY_SIZE, 0) {}
Encryptor(const std::vector<uint8_t>& enc_key) { set_enc_key(enc_key); };
Encryptor& operator=(const Encryptor&) = default;
void set_enc_key(const std::vector<uint8_t>& enc_key);
// This encrypts an RSAPrivateKeyMessage with encryption_key so that it may be
// loaded with OEMCrypto_RewrapDeviceRSAKey.
// This modifies the clear data: it adds padding and generates a random iv.
void PadAndEncryptProvisioningMessage(RSAPrivateKeyMessage* data,
RSAPrivateKeyMessage* encrypted) const;
void CBCEncrypt(const uint8_t* data, uint8_t* encrypted_data,
size_t data_length, const uint8_t (&iv)[KEY_IV_SIZE]) const;
private:
std::vector<uint8_t> enc_key_;
};
// Holds encryption and mac keys derived from a master key.
// Can be used to sign a buffer as either a server or client.
class KeyDeriver : public Encryptor {
public:
KeyDeriver()
: mac_key_server_(MAC_KEY_SIZE, 0), mac_key_client_(MAC_KEY_SIZE, 0) {}
KeyDeriver& operator=(const KeyDeriver&) = default;
// Generate mac and enc keys give the master key.
void DeriveKeys(const uint8_t* master_key,
const std::vector<uint8_t>& mac_key_context,
const std::vector<uint8_t>& enc_key_context);
// Sign the buffer with server's mac key.
void ServerSignBuffer(const uint8_t* data, size_t data_length,
std::vector<uint8_t>* signature);
// Sign the buffer with client's known mac key. Known test keys must be
// installed first. This uses HMAC with SHA256, so is suitable for a message.
void ClientSignBuffer(const std::vector<uint8_t>& buffer,
std::vector<uint8_t>* signature);
// Sign the pst buffer with client's known mac key. Known test keys must be
// installed first. This uses HMAC with SHA128, and skips the beginning of the
// buffer, so is only suitable for a pst report.
void ClientSignPstReport(const std::vector<uint8_t>& pst_report_buffer,
std::vector<uint8_t>* signature);
void set_mac_keys(const uint8_t* mac_keys);
private:
// Internal utility function to derive key using CMAC-128
void DeriveKey(const uint8_t* key, const std::vector<uint8_t>& context,
int counter, std::vector<uint8_t>* out);
std::vector<uint8_t> mac_key_server_;
std::vector<uint8_t> mac_key_client_;
};
} // namespace wvoec
#endif // CDM_OEC_KEY_DERIVER_H_

View File

@@ -22,7 +22,6 @@
#include <iostream>
#include <memory>
#include <string>
#include <sstream>
#include <vector>
#include "OEMCryptoCENC.h"
@@ -45,11 +44,6 @@ void PrintTo(const vector<uint8_t>& value, ostream* os) {
} // namespace std
namespace {
int GetRandBytes(unsigned char* buf, int num) {
// returns 1 on success, -1 if not supported, or 0 if other failure.
return RAND_bytes(buf, num);
}
void DeleteX509Stack(STACK_OF(X509)* stack) {
sk_X509_pop_free(stack, X509_free);
}
@@ -58,12 +52,17 @@ void DeleteX509Stack(STACK_OF(X509)* stack) {
namespace wvoec {
int GetRandBytes(unsigned char* buf, int num) {
// returns 1 on success, -1 if not supported, or 0 if other failure.
return RAND_bytes(buf, num);
}
// Increment counter for AES-CTR. The CENC spec specifies we increment only
// the low 64 bits of the IV counter, and leave the high 64 bits alone. This
// is different from the BoringSSL implementation, so we implement the CTR loop
// ourselves.
void ctr128_inc64(int64_t increaseBy, uint8_t* iv) {
ASSERT_NE(static_cast<void*>(NULL), iv);
ASSERT_NE(nullptr, iv);
uint64_t* counterBuffer = reinterpret_cast<uint64_t*>(&iv[8]);
(*counterBuffer) =
wvcdm::htonll64(wvcdm::ntohll64(*counterBuffer) + increaseBy);
@@ -85,7 +84,7 @@ void dump_boringssl_error() {
template <typename T, void (*func)(T*)>
class boringssl_ptr {
public:
explicit boringssl_ptr(T* p = NULL) : ptr_(p) {}
explicit boringssl_ptr(T* p = nullptr) : ptr_(p) {}
~boringssl_ptr() {
if (ptr_) func(ptr_);
}
@@ -123,9 +122,6 @@ Session::Session()
: open_(false),
forced_session_id_(false),
session_id_(0),
mac_key_server_(MAC_KEY_SIZE),
mac_key_client_(MAC_KEY_SIZE),
enc_key_(KEY_SIZE),
public_rsa_(0),
message_size_(sizeof(MessageData)),
// Most tests only use 4 keys. Other tests will explicitly call
@@ -201,62 +197,10 @@ void Session::FillDefaultContext(vector<uint8_t>* mac_context,
"180120002a0c31383836373837343035000000000080");
}
// This generates the truth data for deriving one key. If there are failures in
// this function, then there is something wrong with the test program and its
// dependency on BoringSSL.
void Session::DeriveKey(const uint8_t* key, const vector<uint8_t>& context,
int counter, vector<uint8_t>* out) {
ASSERT_FALSE(context.empty());
ASSERT_GE(4, counter);
ASSERT_NE(static_cast<void*>(NULL), out);
const EVP_CIPHER* cipher = EVP_aes_128_cbc();
CMAC_CTX* cmac_ctx = CMAC_CTX_new();
ASSERT_NE(static_cast<void*>(NULL), cmac_ctx);
ASSERT_EQ(1, CMAC_Init(cmac_ctx, key, KEY_SIZE, cipher, 0));
std::vector<uint8_t> message;
message.push_back(counter);
message.insert(message.end(), context.begin(), context.end());
ASSERT_EQ(1, CMAC_Update(cmac_ctx, message.data(), message.size()));
size_t reslen;
uint8_t res[128];
ASSERT_EQ(1, CMAC_Final(cmac_ctx, res, &reslen));
out->assign(res, res + reslen);
CMAC_CTX_free(cmac_ctx);
}
// This generates the truth data for deriving a set of keys. If there are
// failures in this function, then there is something wrong with the test
// program and its dependency on BoringSSL.
void Session::DeriveKeys(const uint8_t* master_key,
const vector<uint8_t>& mac_key_context,
const vector<uint8_t>& enc_key_context) {
// Generate derived key for mac key
std::vector<uint8_t> mac_key_part2;
DeriveKey(master_key, mac_key_context, 1, &mac_key_server_);
DeriveKey(master_key, mac_key_context, 2, &mac_key_part2);
mac_key_server_.insert(mac_key_server_.end(), mac_key_part2.begin(),
mac_key_part2.end());
DeriveKey(master_key, mac_key_context, 3, &mac_key_client_);
DeriveKey(master_key, mac_key_context, 4, &mac_key_part2);
mac_key_client_.insert(mac_key_client_.end(), mac_key_part2.begin(),
mac_key_part2.end());
// Generate derived key for encryption key
DeriveKey(master_key, enc_key_context, 1, &enc_key_);
}
// This should only be called if the device uses Provisioning 2.0. A failure in
// this function is probably caused by a bad keybox.
void Session::GenerateDerivedKeysFromKeybox(
const wvoec::WidevineKeybox& keybox) {
GenerateNonce();
vector<uint8_t> mac_context;
vector<uint8_t> enc_context;
FillDefaultContext(&mac_context, &enc_context);
@@ -264,16 +208,14 @@ void Session::GenerateDerivedKeysFromKeybox(
OEMCrypto_GenerateDerivedKeys(
session_id(), mac_context.data(), mac_context.size(),
enc_context.data(), enc_context.size()));
DeriveKeys(keybox.device_key_, mac_context, enc_context);
key_deriver_.DeriveKeys(keybox.device_key_, mac_context, enc_context);
}
void Session::GenerateDerivedKeysFromSessionKey() {
// Uses test certificate.
GenerateNonce();
vector<uint8_t> session_key;
vector<uint8_t> enc_session_key;
if (public_rsa_ == NULL) PreparePublicKey();
if (public_rsa_ == nullptr) PreparePublicKey();
// A failure here probably indicates that there is something wrong with the
// test program and its dependency on BoringSSL.
ASSERT_TRUE(GenerateRSASessionKey(&session_key, &enc_session_key));
@@ -287,7 +229,7 @@ void Session::GenerateDerivedKeysFromSessionKey() {
mac_context.data(), mac_context.size(), enc_context.data(),
enc_context.size()));
DeriveKeys(session_key.data(), mac_context, enc_context);
key_deriver_.DeriveKeys(session_key.data(), mac_context, enc_context);
}
void Session::LoadTestKeys(const std::string& provider_session_token,
@@ -308,9 +250,7 @@ void Session::LoadTestKeys(const std::string& provider_session_token,
signature_.size(), enc_mac_keys_iv, enc_mac_keys, num_keys_,
key_array_, pst, GetSubstring(), OEMCrypto_ContentLicense));
// Update new generated keys.
memcpy(mac_key_server_.data(), license_.mac_keys, MAC_KEY_SIZE);
memcpy(mac_key_client_.data(), license_.mac_keys + MAC_KEY_SIZE,
MAC_KEY_SIZE);
key_deriver_.set_mac_keys(license_.mac_keys);
} else {
ASSERT_EQ(OEMCrypto_SUCCESS,
OEMCrypto_LoadKeys(
@@ -342,9 +282,7 @@ void Session::LoadEntitlementTestKeys(const std::string& provider_session_token,
key_array_, pst, GetSubstring(),
OEMCrypto_EntitlementLicense));
// Update new generated keys.
memcpy(mac_key_server_.data(), license_.mac_keys, MAC_KEY_SIZE);
memcpy(mac_key_client_.data(), license_.mac_keys + MAC_KEY_SIZE,
MAC_KEY_SIZE);
key_deriver_.set_mac_keys(license_.mac_keys);
} else {
ASSERT_EQ(
expected_sts,
@@ -432,8 +370,9 @@ void Session::LoadEntitledContentKeys(OEMCryptoResult expected_sts) {
OEMCrypto_LoadEntitledContentKeys(
session_id(),
reinterpret_cast<const uint8_t*>(encrypted_entitled_message_.data()),
encrypted_entitled_message_.size(), num_keys_,
encrypted_entitled_key_array.data()));
encrypted_entitled_message_.size(),
encrypted_entitled_key_array.data(),
encrypted_entitled_key_array.size()));
if (expected_sts != OEMCrypto_SUCCESS) {
return;
}
@@ -502,8 +441,9 @@ void Session::RefreshTestKeys(const size_t key_count, uint32_t control_bits,
// message is not actually encrypted. It is, however, signed.
// FillRefreshMessage fills the message with a duration of kLongDuration.
FillRefreshMessage(key_count, control_bits, nonce);
ServerSignBuffer(reinterpret_cast<const uint8_t*>(&padded_message_),
message_size_, &signature_);
key_deriver_.ServerSignBuffer(
reinterpret_cast<const uint8_t*>(&padded_message_), message_size_,
&signature_);
std::vector<OEMCrypto_KeyRefreshObject> key_array(key_count);
FillRefreshArray(key_array.data(), key_count);
OEMCryptoResult sts = OEMCrypto_RefreshKeys(
@@ -555,9 +495,9 @@ void Session::FillSimpleMessage(uint32_t duration, uint32_t control,
if (global_features.api_version >= 12) {
// For version 12 and above, we require OEMCrypto to handle kcNN for all
// licenses.
std::stringstream stream;
stream << "kc" << global_features.api_version;
memcpy(license_.keys[i].control.verification, stream.str().c_str(), 4);
std::string kcVersion =
"kc" + std::to_string(global_features.api_version);
memcpy(license_.keys[i].control.verification, kcVersion.c_str(), 4);
} else if (control & wvoec::kControlSecurityPatchLevelMask) {
// For versions before 12, we require the special key control block only
// when there are newer features present.
@@ -599,9 +539,9 @@ void Session::FillSimpleEntitlementMessage(
if (global_features.api_version >= 12) {
// For version 12 and above, we require OEMCrypto to handle kcNN for all
// licenses.
std::stringstream stream;
stream << "kc" << global_features.api_version;
memcpy(license_.keys[i].control.verification, stream.str().c_str(), 4);
std::string kcVersion =
"kc" + std::to_string(global_features.api_version);
memcpy(license_.keys[i].control.verification, kcVersion.c_str(), 4);
} else if (control & wvoec::kControlSecurityPatchLevelMask) {
// For versions before 12, we require the special key control block only
// when there are newer features present.
@@ -632,10 +572,10 @@ void Session::FillRefreshMessage(size_t key_count, uint32_t control_bits,
if (global_features.api_version >= 12) {
// For version 12 and above, we require OEMCrypto to handle kcNN for all
// licenses.
std::stringstream stream;
stream << "kc" << global_features.api_version;
std::string kcVersion =
"kc" + std::to_string(global_features.api_version);
memcpy(encrypted_license().keys[i].control.verification,
stream.str().c_str(), 4);
kcVersion.c_str(), 4);
} else {
// For versions before 12, we require the special key control block only
// when there are newer features present.
@@ -673,84 +613,115 @@ void Session::EncryptAndSign() {
uint8_t iv_buffer[16];
memcpy(iv_buffer, &license_.mac_key_iv[0], KEY_IV_SIZE);
AES_KEY aes_key;
AES_set_encrypt_key(enc_key_.data(), 128, &aes_key);
AES_cbc_encrypt(&license_.mac_keys[0], &encrypted_license().mac_keys[0],
2 * MAC_KEY_SIZE, &aes_key, iv_buffer, AES_ENCRYPT);
key_deriver_.CBCEncrypt(&license_.mac_keys[0],
&encrypted_license().mac_keys[0], 2 * MAC_KEY_SIZE,
license_.mac_key_iv);
for (unsigned int i = 0; i < num_keys_; i++) {
memcpy(iv_buffer, &license_.keys[i].control_iv[0], KEY_IV_SIZE);
AES_KEY aes_key;
AES_set_encrypt_key(&license_.keys[i].key_data[0], 128, &aes_key);
AES_cbc_encrypt(
reinterpret_cast<const uint8_t*>(&license_.keys[i].control),
reinterpret_cast<uint8_t*>(&encrypted_license().keys[i].control),
KEY_SIZE, &aes_key, iv_buffer, AES_ENCRYPT);
memcpy(iv_buffer, &license_.keys[i].key_iv[0], KEY_IV_SIZE);
AES_set_encrypt_key(enc_key_.data(), 128, &aes_key);
AES_cbc_encrypt(
key_deriver_.CBCEncrypt(
&license_.keys[i].key_data[0], &encrypted_license().keys[i].key_data[0],
license_.keys[i].key_data_length, &aes_key, iv_buffer, AES_ENCRYPT);
license_.keys[i].key_data_length, license_.keys[i].key_iv);
}
memcpy(encrypted_license().pst, license_.pst, sizeof(license_.pst));
ServerSignBuffer(reinterpret_cast<const uint8_t*>(&padded_message_),
message_size_, &signature_);
key_deriver_.ServerSignBuffer(
reinterpret_cast<const uint8_t*>(&padded_message_), message_size_,
&signature_);
FillKeyArray(encrypted_license(), key_array_);
SetLoadKeysSubstringParams();
}
void Session::EncryptProvisioningMessage(
RSAPrivateKeyMessage* data, RSAPrivateKeyMessage* encrypted,
const vector<uint8_t>& encryption_key) {
ASSERT_EQ(encryption_key.size(), KEY_SIZE);
*encrypted = *data;
size_t padding = KEY_SIZE - (data->rsa_key_length % KEY_SIZE);
memset(data->rsa_key + data->rsa_key_length, static_cast<uint8_t>(padding),
padding);
encrypted->rsa_key_length = data->rsa_key_length + padding;
uint8_t iv_buffer[16];
memcpy(iv_buffer, &data->rsa_key_iv[0], KEY_IV_SIZE);
AES_KEY aes_key;
AES_set_encrypt_key(&encryption_key[0], 128, &aes_key);
AES_cbc_encrypt(&data->rsa_key[0], &encrypted->rsa_key[0],
encrypted->rsa_key_length, &aes_key, iv_buffer, AES_ENCRYPT);
}
void Session::ServerSignBuffer(const uint8_t* data, size_t data_length,
std::vector<uint8_t>* signature) {
ASSERT_LE(data_length, kMaxMessageSize);
signature->assign(SHA256_DIGEST_LENGTH, 0);
unsigned int md_len = SHA256_DIGEST_LENGTH;
HMAC(EVP_sha256(), mac_key_server_.data(), mac_key_server_.size(), data,
data_length, &(signature->front()), &md_len);
}
void Session::ClientSignMessage(const vector<uint8_t>& data,
std::vector<uint8_t>* signature) {
signature->assign(SHA256_DIGEST_LENGTH, 0);
unsigned int md_len = SHA256_DIGEST_LENGTH;
HMAC(EVP_sha256(), mac_key_client_.data(), mac_key_client_.size(),
&(data.front()), data.size(), &(signature->front()), &md_len);
}
void Session::VerifyClientSignature(size_t data_length) {
void Session::VerifyLicenseRequestSignature(size_t data_length) {
// In the real world, a message should be signed by the client and
// verified by the server. This simulates that.
vector<uint8_t> data(data_length);
for (size_t i = 0; i < data.size(); i++) data[i] = i % 0xFF;
for (size_t i = 0; i < data.size(); i++) data[i] = i & 0xFF;
OEMCryptoResult sts;
size_t gen_signature_length = 0;
sts = OEMCrypto_GenerateSignature(session_id(), data.data(), data.size(),
NULL, &gen_signature_length);
// TODO(b/135288420): Test core message functionality.
// This function should be split into three versions, one for each core
// message.
size_t core_message_length = 0;
sts = OEMCrypto_SignLicenseRequest(session_id(), data.data(), data.size(),
nullptr, &core_message_length, nullptr,
&gen_signature_length);
ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts);
ASSERT_EQ(static_cast<size_t>(32), gen_signature_length);
const size_t hmac_signature_size = 32u;
ASSERT_EQ(hmac_signature_size, gen_signature_length);
vector<uint8_t> gen_signature(gen_signature_length);
sts = OEMCrypto_GenerateSignature(session_id(), data.data(), data.size(),
gen_signature.data(),
&gen_signature_length);
sts = OEMCrypto_SignLicenseRequest(
session_id(), data.data(), data.size(), nullptr, &core_message_length,
gen_signature.data(), &gen_signature_length);
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
std::vector<uint8_t> expected_signature;
ClientSignMessage(data, &expected_signature);
key_deriver_.ClientSignBuffer(data, &expected_signature);
ASSERT_EQ(expected_signature, gen_signature);
}
// TODO(b/135288022): This function only handles the keybox case.
// It should do something different for Prov 3.0.
void Session::VerifyProvisioningRequestSignature(size_t data_length) {
// In the real world, a message should be signed by the client and
// verified by the server. This simulates that.
vector<uint8_t> data(data_length);
for (size_t i = 0; i < data.size(); i++) data[i] = i & 0xFF;
OEMCryptoResult sts;
size_t gen_signature_length = 0;
// TODO(b/135288420): Test core message functionality.
// This function should be split into three versions, one for each core
// message.
size_t core_message_length = 0;
sts = OEMCrypto_SignProvisioningRequest(
session_id(), data.data(), data.size(), nullptr, &core_message_length,
nullptr, &gen_signature_length);
ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts);
const size_t hmac_signature_size = 32u;
ASSERT_EQ(hmac_signature_size, gen_signature_length);
vector<uint8_t> gen_signature(gen_signature_length);
// TODO(b/135288022): This function should pick the right type of signature,
// and then call SignProvisioningRequest.
sts = OEMCrypto_SignLicenseRequest(
session_id(), data.data(), data.size(), nullptr, &core_message_length,
gen_signature.data(), &gen_signature_length);
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
std::vector<uint8_t> expected_signature;
key_deriver_.ClientSignBuffer(data, &expected_signature);
ASSERT_EQ(expected_signature, gen_signature);
}
void Session::VerifyRenewalRequestSignature(size_t data_length) {
// In the real world, a message should be signed by the client and
// verified by the server. This simulates that.
vector<uint8_t> data(data_length);
for (size_t i = 0; i < data.size(); i++) data[i] = i & 0xFF;
OEMCryptoResult sts;
size_t gen_signature_length = 0;
// TODO(b/135288420): Test core message functionality.
// This function should be split into three versions, one for each core
// message.
size_t core_message_length = 0;
sts = OEMCrypto_SignRenewalRequest(session_id(), data.data(), data.size(),
nullptr, &core_message_length, nullptr,
&gen_signature_length);
ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts);
const size_t hmac_signature_size = 32u;
ASSERT_EQ(hmac_signature_size, gen_signature_length);
vector<uint8_t> gen_signature(gen_signature_length);
sts = OEMCrypto_SignRenewalRequest(
session_id(), data.data(), data.size(), nullptr, &core_message_length,
gen_signature.data(), &gen_signature_length);
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
std::vector<uint8_t> expected_signature;
key_deriver_.ClientSignBuffer(data, &expected_signature);
ASSERT_EQ(expected_signature, gen_signature);
}
@@ -801,9 +772,9 @@ void Session::FillRefreshArray(OEMCrypto_KeyRefreshObject* key_array,
void Session::EncryptCTR(const vector<uint8_t>& in_buffer, const uint8_t* key,
const uint8_t* starting_iv,
vector<uint8_t>* out_buffer) {
ASSERT_NE(static_cast<void*>(NULL), key);
ASSERT_NE(static_cast<void*>(NULL), starting_iv);
ASSERT_NE(static_cast<void*>(NULL), out_buffer);
ASSERT_NE(nullptr, key);
ASSERT_NE(nullptr, starting_iv);
ASSERT_NE(nullptr, out_buffer);
AES_KEY aes_key;
AES_set_encrypt_key(key, AES_BLOCK_SIZE * 8, &aes_key);
out_buffer->resize(in_buffer.size());
@@ -830,7 +801,9 @@ void Session::TestDecryptCTR(bool select_key_first,
sts = OEMCrypto_SelectKey(session_id(), license_.keys[key_index].key_id,
license_.keys[key_index].key_id_length,
OEMCrypto_CipherMode_CTR);
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
if (expected_result == OEMCrypto_SUCCESS) {
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
}
}
vector<uint8_t> unencryptedData(256);
@@ -908,13 +881,12 @@ void Session::LoadOEMCert(bool verify_cert) {
vector<uint8_t> public_cert;
size_t public_cert_length = 0;
ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER,
OEMCrypto_GetOEMPublicCertificate(session_id(), NULL,
&public_cert_length));
OEMCrypto_GetOEMPublicCertificate(nullptr, &public_cert_length));
ASSERT_LT(0u, public_cert_length);
public_cert.resize(public_cert_length);
ASSERT_EQ(OEMCrypto_SUCCESS,
OEMCrypto_GetOEMPublicCertificate(session_id(), public_cert.data(),
&public_cert_length));
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_GetOEMPublicCertificate(
public_cert.data(), &public_cert_length));
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_LoadOEMPrivateKey(session_id()));
// Load the certificate chain into a BoringSSL X509 Stack
const boringssl_ptr<STACK_OF(X509), DeleteX509Stack> x509_stack(
@@ -940,7 +912,7 @@ void Session::LoadOEMCert(bool verify_cert) {
if (!public_rsa_) {
cout << "d2i_RSAPrivateKey failed.\n";
dump_boringssl_error();
ASSERT_TRUE(NULL != public_rsa_);
ASSERT_TRUE(nullptr != public_rsa_);
}
}
if (verify_cert) {
@@ -955,7 +927,7 @@ void Session::LoadOEMCert(bool verify_cert) {
X509_STORE_CTX_new());
ASSERT_TRUE(store_ctx.NotNull());
X509_STORE_CTX_init(store_ctx.get(), store.get(), x509_cert, NULL);
X509_STORE_CTX_init(store_ctx.get(), store.get(), x509_cert, nullptr);
// TODO(fredgc): Verify cert is signed by Google.
@@ -977,8 +949,8 @@ void Session::MakeRSACertificate(struct RSAPrivateKeyMessage* encrypted,
std::vector<uint8_t>* signature,
uint32_t allowed_schemes,
const vector<uint8_t>& rsa_key,
const vector<uint8_t>* encryption_key) {
if (encryption_key == NULL) encryption_key = &enc_key_;
const Encryptor* encryptor) {
if (encryptor == nullptr) encryptor = &key_deriver_;
struct RSAPrivateKeyMessage message;
if (allowed_schemes != kSign_RSASSA_PSS) {
uint32_t algorithm_n = htonl(allowed_schemes);
@@ -990,12 +962,11 @@ void Session::MakeRSACertificate(struct RSAPrivateKeyMessage* encrypted,
memcpy(message.rsa_key, rsa_key.data(), rsa_key.size());
message.rsa_key_length = rsa_key.size();
}
EXPECT_EQ(1, GetRandBytes(message.rsa_key_iv, KEY_IV_SIZE));
GenerateNonce();
message.nonce = nonce_;
EncryptProvisioningMessage(&message, encrypted, *encryption_key);
ServerSignBuffer(reinterpret_cast<const uint8_t*>(encrypted), message_size,
signature);
encryptor->PadAndEncryptProvisioningMessage(&message, encrypted);
key_deriver_.ServerSignBuffer(reinterpret_cast<const uint8_t*>(encrypted),
message_size, signature);
}
void Session::RewrapRSAKey(const struct RSAPrivateKeyMessage& encrypted,
@@ -1008,7 +979,7 @@ void Session::RewrapRSAKey(const struct RSAPrivateKeyMessage& encrypted,
OEMCrypto_RewrapDeviceRSAKey(
session_id(), message_ptr, message_size, signature.data(),
signature.size(), &encrypted.nonce, encrypted.rsa_key,
encrypted.rsa_key_length, encrypted.rsa_key_iv, NULL,
encrypted.rsa_key_length, encrypted.rsa_key_iv, nullptr,
&wrapped_key_length));
wrapped_key->clear();
wrapped_key->assign(wrapped_key_length, 0);
@@ -1033,7 +1004,7 @@ void Session::RewrapRSAKey30(const struct RSAPrivateKeyMessage& encrypted,
OEMCrypto_RewrapDeviceRSAKey30(
session_id(), &nonce_, encrypted_message_key.data(),
encrypted_message_key.size(), encrypted.rsa_key,
encrypted.rsa_key_length, encrypted.rsa_key_iv, NULL,
encrypted.rsa_key_length, encrypted.rsa_key_iv, nullptr,
&wrapped_key_length));
wrapped_key->clear();
wrapped_key->assign(wrapped_key_length, 0);
@@ -1050,7 +1021,7 @@ void Session::RewrapRSAKey30(const struct RSAPrivateKeyMessage& encrypted,
}
void Session::PreparePublicKey(const uint8_t* rsa_key, size_t rsa_key_length) {
if (rsa_key == NULL) {
if (rsa_key == nullptr) {
rsa_key = kTestRSAPKCS8PrivateKeyInfo2_2048;
rsa_key_length = sizeof(kTestRSAPKCS8PrivateKeyInfo2_2048);
}
@@ -1058,7 +1029,7 @@ void Session::PreparePublicKey(const uint8_t* rsa_key, size_t rsa_key_length) {
boringssl_ptr<BIO, BIO_vfree> bio(BIO_new_mem_buf(p, rsa_key_length));
ASSERT_TRUE(bio.NotNull());
boringssl_ptr<PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO_free> pkcs8_pki(
d2i_PKCS8_PRIV_KEY_INFO_bio(bio.get(), NULL));
d2i_PKCS8_PRIV_KEY_INFO_bio(bio.get(), nullptr));
ASSERT_TRUE(pkcs8_pki.NotNull());
boringssl_ptr<EVP_PKEY, EVP_PKEY_free> evp(EVP_PKCS82PKEY(pkcs8_pki.get()));
ASSERT_TRUE(evp.NotNull());
@@ -1088,10 +1059,10 @@ bool Session::VerifyPSSSignature(EVP_PKEY* pkey, const uint8_t* message,
EVP_MD_CTX md_ctx_struct;
EVP_MD_CTX* md_ctx = &md_ctx_struct;
EVP_MD_CTX_init(md_ctx);
EVP_PKEY_CTX* pkey_ctx = NULL;
EVP_PKEY_CTX* pkey_ctx = nullptr;
if (EVP_DigestVerifyInit(md_ctx, &pkey_ctx, EVP_sha1(), NULL /* no ENGINE */,
pkey) != 1) {
if (EVP_DigestVerifyInit(md_ctx, &pkey_ctx, EVP_sha1(),
nullptr /* no ENGINE */, pkey) != 1) {
LOGE("EVP_DigestVerifyInit failed in VerifyPSSSignature");
goto err;
}
@@ -1138,7 +1109,7 @@ void Session::VerifyRSASignature(const vector<uint8_t>& message,
const uint8_t* signature,
size_t signature_length,
RSA_Padding_Scheme padding_scheme) {
EXPECT_TRUE(NULL != public_rsa_)
EXPECT_TRUE(nullptr != public_rsa_)
<< "No public RSA key loaded in test code.\n";
EXPECT_EQ(static_cast<size_t>(RSA_size(public_rsa_)), signature_length)
@@ -1211,8 +1182,8 @@ void Session::UpdateUsageEntry(std::vector<uint8_t>* header_buffer) {
size_t entry_buffer_length = 0;
ASSERT_EQ(
OEMCrypto_ERROR_SHORT_BUFFER,
OEMCrypto_UpdateUsageEntry(session_id(), NULL, &header_buffer_length,
NULL, &entry_buffer_length));
OEMCrypto_UpdateUsageEntry(session_id(), nullptr, &header_buffer_length,
nullptr, &entry_buffer_length));
ASSERT_LT(0u, header_buffer_length);
header_buffer->resize(header_buffer_length);
ASSERT_LT(0u, entry_buffer_length);
@@ -1258,8 +1229,7 @@ void Session::GenerateReport(const std::string& pst,
Session* other) {
ASSERT_TRUE(open_);
if (other) { // If other is specified, copy mac keys.
mac_key_server_ = other->mac_key_server_;
mac_key_client_ = other->mac_key_client_;
key_deriver_ = other->key_deriver_;
}
size_t length = 0;
OEMCryptoResult sts = OEMCrypto_ReportUsage(
@@ -1280,10 +1250,7 @@ void Session::GenerateReport(const std::string& pst,
}
EXPECT_EQ(wvcdm::Unpacked_PST_Report::report_size(pst.length()), length);
vector<uint8_t> computed_signature(SHA_DIGEST_LENGTH);
unsigned int sig_len = SHA_DIGEST_LENGTH;
HMAC(EVP_sha1(), mac_key_client_.data(), mac_key_client_.size(),
&pst_report_buffer_[SHA_DIGEST_LENGTH], length - SHA_DIGEST_LENGTH,
computed_signature.data(), &sig_len);
key_deriver_.ClientSignPstReport(pst_report_buffer_, &computed_signature);
EXPECT_EQ(0, memcmp(computed_signature.data(), pst_report().signature(),
SHA_DIGEST_LENGTH));
EXPECT_GE(kInactiveUnused, pst_report().status());
@@ -1292,7 +1259,7 @@ void Session::GenerateReport(const std::string& pst,
EXPECT_EQ(0, memcmp(pst.c_str(), pst_report().pst(), pst.length()));
// Also, we the session to be able to sign the release message with the
// correct mac keys from the usage table entry.
ASSERT_NO_FATAL_FAILURE(VerifyClientSignature());
ASSERT_NO_FATAL_FAILURE(VerifyRenewalRequestSignature());
}
void Session::VerifyPST(const Test_PST_Report& expected) {
@@ -1301,7 +1268,7 @@ void Session::VerifyPST(const Test_PST_Report& expected) {
char* pst_ptr = reinterpret_cast<char *>(computed.pst());
std::string computed_pst(pst_ptr, pst_ptr + computed.pst_length());
ASSERT_EQ(expected.pst, computed_pst);
time_t now = time(NULL);
time_t now = time(nullptr);
int64_t age = now - expected.time_created; // How old is this report.
EXPECT_NEAR(expected.seconds_since_license_received + age,
computed.seconds_since_license_received(),
@@ -1316,14 +1283,7 @@ void Session::VerifyPST(const Test_PST_Report& expected) {
kUsageTableTimeTolerance);
}
std::vector<uint8_t> signature(SHA_DIGEST_LENGTH);
unsigned int md_len = SHA_DIGEST_LENGTH;
if (!HMAC(EVP_sha1(), mac_key_client_.data(), mac_key_client_.size(),
pst_report_buffer_.data() + SHA_DIGEST_LENGTH,
pst_report_buffer_.size() - SHA_DIGEST_LENGTH,
signature.data(), &md_len)) {
cout << "Error computing HMAC.\n";
dump_boringssl_error();
}
key_deriver_.ClientSignPstReport(pst_report_buffer_, &signature);
EXPECT_EQ(0, memcmp(computed.signature(), signature.data(),
SHA_DIGEST_LENGTH));
}
@@ -1341,7 +1301,7 @@ void Session::VerifyReport(Test_PST_Report expected,
int64_t time_license_received,
int64_t time_first_decrypt,
int64_t time_last_decrypt) {
time_t now = time(NULL);
time_t now = time(nullptr);
expected.seconds_since_license_received =
MaybeAdjustTime(time_license_received, now);
expected.seconds_since_first_decrypt =
@@ -1361,35 +1321,7 @@ void Session::GenerateVerifyReport(const std::string& pst,
time_first_decrypt, time_last_decrypt));
// The PST report was signed above. Below we verify that the entire message
// that is sent to the server will be signed by the right mac keys.
ASSERT_NO_FATAL_FAILURE(VerifyClientSignature());
}
void Session::CreateOldEntry(const Test_PST_Report& report) {
OEMCryptoResult result = OEMCrypto_CreateOldUsageEntry(
report.seconds_since_license_received,
report.seconds_since_first_decrypt,
report.seconds_since_last_decrypt,
report.status, mac_key_server_.data(),
mac_key_client_.data(),
reinterpret_cast<const uint8_t*>(report.pst.c_str()),
report.pst.length());
if (result == OEMCrypto_ERROR_NOT_IMPLEMENTED) return;
ASSERT_EQ(OEMCrypto_SUCCESS, result);
}
void Session::CopyAndVerifyOldEntry(const Test_PST_Report& report,
std::vector<uint8_t>* header_buffer) {
ASSERT_NO_FATAL_FAILURE(CreateNewUsageEntry());
OEMCryptoResult result = OEMCrypto_CopyOldUsageEntry(
session_id(), reinterpret_cast<const uint8_t*>(report.pst.c_str()),
report.pst.length());
if (result == OEMCrypto_ERROR_NOT_IMPLEMENTED) {
cout << "WARNING: OEMCrypto CANNOT copy old usage table to new." << endl;
return;
}
ASSERT_NO_FATAL_FAILURE(UpdateUsageEntry(header_buffer));
ASSERT_NO_FATAL_FAILURE(GenerateReport(report.pst));
ASSERT_NO_FATAL_FAILURE(VerifyPST(report));
ASSERT_NO_FATAL_FAILURE(VerifyRenewalRequestSignature());
}
const uint8_t* Session::message_ptr() {

View File

@@ -13,6 +13,7 @@
#include <vector>
#include "oec_device_features.h"
#include "oec_key_deriver.h"
#include "oemcrypto_types.h"
#include "pst_report.h"
@@ -60,7 +61,6 @@ const size_t kTestKeyIdMaxLength = 16;
// Most content will use a key id that is 16 bytes long.
const int kDefaultKeyIdLength = 16;
const size_t kMaxTestRSAKeyLength = 2000; // Rough estimate.
const size_t kMaxPSTLength = 255; // In specification.
const size_t kMaxMessageSize = 8 * 1024; // In specification.
@@ -86,19 +86,10 @@ struct MessageData {
uint8_t pst[kMaxPSTLength];
};
// This structure will be signed to simulate a provisioning response from the
// server.
struct RSAPrivateKeyMessage {
uint8_t rsa_key[kMaxTestRSAKeyLength];
uint8_t rsa_key_iv[KEY_IV_SIZE];
size_t rsa_key_length;
uint32_t nonce;
};
struct Test_PST_Report {
Test_PST_Report(const std::string& pst_in,
OEMCrypto_Usage_Entry_Status status_in)
: status(status_in), pst(pst_in), time_created(time(NULL)) {}
: status(status_in), pst(pst_in), time_created(time(nullptr)) {}
OEMCrypto_Usage_Entry_Status status;
int64_t seconds_since_license_received;
@@ -115,6 +106,9 @@ struct EntitledContentKeyData {
uint8_t content_key_data[KEY_SIZE];
};
// returns 1 on success, -1 if not supported, or 0 if other failure.
int GetRandBytes(unsigned char* buf, int num);
// Increment counter for AES-CTR. The CENC spec specifies we increment only
// the low 64 bits of the IV counter, and leave the high 64 bits alone. This
// is different from the OpenSSL implementation, so we implement the CTR loop
@@ -141,9 +135,9 @@ class Session {
// Returns the most recently generated nonce.
// Valid after call to GenerateNonce.
uint32_t get_nonce() { return nonce_; }
uint32_t nonce() const { return nonce_; }
// Valid after call to open().
uint32_t session_id() { return (uint32_t)session_id_; }
uint32_t session_id() const { return (uint32_t)session_id_; }
// Call OEMCrypto_OpenSession, with GTest ASSERTs.
void open();
// Call OEMCrypto_CloseSession, with GTest ASSERTs.
@@ -155,7 +149,7 @@ class Session {
// Generates one nonce. If error_counter is null, this will sleep 1 second
// and try again if a nonce flood has been detected. If error_counter is
// not null, it will be incremented when a nonce flood is detected.
void GenerateNonce(int* error_counter = NULL);
void GenerateNonce(int* error_counter = nullptr);
// Fill the vectors with test context which generate known mac and enc keys.
void FillDefaultContext(vector<uint8_t>* mac_context,
vector<uint8_t>* enc_context);
@@ -217,28 +211,22 @@ class Session {
// Sets the OEMCrypto_Substring parameters of the LoadKeys method.
// Specifically, it sets the |enc_mac_keys_iv|, |enc_mac_keys|, |pst|, and
// |srm_restriction_data| in that order. For testing purposes,
// |srm_restriction_data| will always be NULL.
// |srm_restriction_data| will always be nullptr.
void SetLoadKeysSubstringParams();
// This copies data from license_ to encrypted_license_, and then encrypts
// each field in the key array appropriately. It then signes the buffer with
// the server mac keys. It then fills out the key_array_ so that pointers in
// that array point to the locations in the encrypted message.
void EncryptAndSign();
// This encrypts an RSAPrivateKeyMessage with encryption_key so that it may be
// loaded with OEMCrypto_RewrapDeviceRSAKey.
void EncryptProvisioningMessage(RSAPrivateKeyMessage* data,
RSAPrivateKeyMessage* encrypted,
const vector<uint8_t>& encryption_key);
// Sign the buffer with server's mac key.
void ServerSignBuffer(const uint8_t* data, size_t data_length,
std::vector<uint8_t>* signature);
// Sign the buffer with client's known mac key. Known test keys must be
// installed first.
void ClientSignMessage(const vector<uint8_t>& data,
std::vector<uint8_t>* signature);
// This checks the signature generated by OEMCrypto_GenerateSignature against
// that generaged by ClientSignMessage.
void VerifyClientSignature(size_t data_length = 400);
// This checks the signature generated by OEMCrypto_SignProvisioningRequest
// against that generaged by ClientSignBuffer.
void VerifyProvisioningRequestSignature(size_t data_length = 400);
// This checks the signature generated by OEMCrypto_SignLicenseRequest against
// that generaged by ClientSignBuffer.
void VerifyLicenseRequestSignature(size_t data_length = 400);
// This checks the signature generated by OEMCrypto_SignRenewalRequest against
// that generaged by ClientSignBuffer.
void VerifyRenewalRequestSignature(size_t data_length = 400);
// Set the pointers in key_array[*] to point values inside data. This is
// needed to satisfy range checks in OEMCrypto_LoadKeys.
void FillKeyArray(const MessageData& data, OEMCrypto_KeyObject* key_array);
@@ -260,8 +248,8 @@ class Session {
// Verify that an attempt to select an expired key either succeeds, or gives
// an actionable error code.
void TestSelectExpired(unsigned int key_index);
// Calls OEMCrypto_GetOEMPublicCertificate and loads the OEM cert's public
// rsa key into public_rsa_.
// Calls OEMCrypto_GetOEMPublicCertificate and OEMCrypto_LoadOEMPrivateKey and
// loads the OEM cert's public rsa key into public_rsa_.
void LoadOEMCert(bool verify_cert = false);
// Creates RSAPrivateKeyMessage for the specified rsa_key, encrypts it with
// the specified encryption key, and then signs it with the server's mac key.
@@ -270,7 +258,7 @@ class Session {
size_t message_size, std::vector<uint8_t>* signature,
uint32_t allowed_schemes,
const vector<uint8_t>& rsa_key,
const vector<uint8_t>* encryption_key = NULL);
const Encryptor* encryptor = nullptr);
// Calls OEMCrypto_RewrapDeviceRSAKey with the given provisioning response
// message. If force is true, we assert that the key loads successfully.
void RewrapRSAKey(const struct RSAPrivateKeyMessage& encrypted,
@@ -278,7 +266,7 @@ class Session {
vector<uint8_t>* wrapped_key, bool force);
// Loads the specified RSA public key into public_rsa_. If rsa_key is null,
// the default test key is loaded.
void PreparePublicKey(const uint8_t* rsa_key = NULL,
void PreparePublicKey(const uint8_t* rsa_key = nullptr,
size_t rsa_key_length = 0);
// Verifies the given signature is from the given message and RSA key, pkey.
static bool VerifyPSSSignature(EVP_PKEY* pkey, const uint8_t* message,
@@ -306,7 +294,7 @@ class Session {
// Creates a new usage entry, and keeps track of the index.
// If status is null, we expect success, otherwise status is set to the
// return value.
void CreateNewUsageEntry(OEMCryptoResult *status = NULL);
void CreateNewUsageEntry(OEMCryptoResult* status = nullptr);
// Copy encrypted usage entry from other session, and then load it.
// This session must already be open.
void LoadUsageEntry(uint32_t index, const vector<uint8_t>& buffer);
@@ -414,20 +402,10 @@ class Session {
const uint8_t* encrypted_entitled_message_ptr();
private:
// Generate mac and enc keys give the master key.
void DeriveKeys(const uint8_t* master_key,
const vector<uint8_t>& mac_key_context,
const vector<uint8_t>& enc_key_context);
// Internal utility function to derive key using CMAC-128
void DeriveKey(const uint8_t* key, const vector<uint8_t>& context,
int counter, vector<uint8_t>* out);
bool open_;
bool forced_session_id_;
OEMCrypto_SESSION session_id_;
vector<uint8_t> mac_key_server_;
vector<uint8_t> mac_key_client_;
vector<uint8_t> enc_key_;
KeyDeriver key_deriver_;
uint32_t nonce_;
RSA* public_rsa_;
vector<uint8_t> pst_report_buffer_;

View File

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

View File

@@ -15,7 +15,7 @@ const uint8_t* find(const vector<uint8_t>& message,
vector<uint8_t>::const_iterator pos = search(
message.begin(), message.end(), substring.begin(), substring.end());
if (pos == message.end()) {
return NULL;
return nullptr;
}
return &(*pos);
}
@@ -29,7 +29,7 @@ void SessionUtil::CreateWrappedRSAKeyFromKeybox(uint32_t allowed_schemes,
ASSERT_NO_FATAL_FAILURE(s.GenerateDerivedKeysFromKeybox(keybox_));
// Provisioning request would be signed by the client and verified by the
// server.
ASSERT_NO_FATAL_FAILURE(s.VerifyClientSignature());
ASSERT_NO_FATAL_FAILURE(s.VerifyProvisioningRequestSignature());
struct RSAPrivateKeyMessage encrypted;
std::vector<uint8_t> signature;
ASSERT_NO_FATAL_FAILURE(
@@ -40,7 +40,7 @@ void SessionUtil::CreateWrappedRSAKeyFromKeybox(uint32_t allowed_schemes,
encrypted, sizeof(encrypted), signature, &wrapped_rsa_key_, force));
// Verify that the clear key is not contained in the wrapped key.
// It should be encrypted.
ASSERT_EQ(NULL, find(wrapped_rsa_key_, encoded_rsa_key_));
ASSERT_EQ(nullptr, find(wrapped_rsa_key_, encoded_rsa_key_));
}
// This creates a wrapped RSA key for devices using provisioning 3.0. If force
@@ -50,21 +50,21 @@ void SessionUtil::CreateWrappedRSAKeyFromOEMCert(
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
ASSERT_NO_FATAL_FAILURE(s.LoadOEMCert());
s.GenerateNonce();
struct RSAPrivateKeyMessage encrypted;
std::vector<uint8_t> signature;
std::vector<uint8_t> message_key;
std::vector<uint8_t> encrypted_message_key;
s.GenerateRSASessionKey(&message_key, &encrypted_message_key);
ASSERT_NO_FATAL_FAILURE(
s.MakeRSACertificate(&encrypted, sizeof(encrypted), &signature,
allowed_schemes, encoded_rsa_key_, &message_key));
Encryptor encryptor(message_key);
ASSERT_NO_FATAL_FAILURE(s.MakeRSACertificate(&encrypted, sizeof(encrypted),
&signature, allowed_schemes,
encoded_rsa_key_, &encryptor));
ASSERT_NO_FATAL_FAILURE(
s.RewrapRSAKey30(encrypted, encrypted_message_key,
&wrapped_rsa_key_, force));
// Verify that the clear key is not contained in the wrapped key.
// It should be encrypted.
ASSERT_EQ(NULL, find(wrapped_rsa_key_, encoded_rsa_key_));
ASSERT_EQ(nullptr, find(wrapped_rsa_key_, encoded_rsa_key_));
}
// If force is true, we assert that the key loads successfully.
@@ -89,10 +89,9 @@ void SessionUtil::InstallKeybox(const wvoec::WidevineKeybox& keybox,
uint8_t wrapped[sizeof(wvoec::WidevineKeybox)];
size_t length = sizeof(wvoec::WidevineKeybox);
keybox_ = keybox;
ASSERT_EQ(
OEMCrypto_SUCCESS,
OEMCrypto_WrapKeybox(reinterpret_cast<const uint8_t*>(&keybox),
sizeof(keybox), wrapped, &length, NULL, 0));
ASSERT_EQ(OEMCrypto_SUCCESS,
OEMCrypto_WrapKeybox(reinterpret_cast<const uint8_t*>(&keybox),
sizeof(keybox), wrapped, &length, nullptr, 0));
OEMCryptoResult sts = OEMCrypto_InstallKeybox(wrapped, sizeof(keybox));
if (good) {
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
@@ -105,8 +104,6 @@ void SessionUtil::EnsureTestKeys() {
switch (global_features.derive_key_method) {
case DeviceFeatures::LOAD_TEST_KEYBOX:
keybox_ = kTestKeybox;
// TODO(fredgc, b/119316243): REMOVE FOLLOWING LINE:
if (global_features.api_version < 14) keybox_ = kTestKeyboxForV13;
ASSERT_EQ(OEMCrypto_SUCCESS,
OEMCrypto_LoadTestKeybox(
reinterpret_cast<const uint8_t*>(&keybox_),

File diff suppressed because it is too large Load Diff

View File

@@ -61,37 +61,40 @@ TEST_F(OEMCryptoAndroidLMPTest, ValidKeyboxTest) {
TEST_F(OEMCryptoAndroidLMPTest, RewrapDeviceRSAKeyImplemented) {
if (OEMCrypto_Keybox == OEMCrypto_GetProvisioningMethod()) {
ASSERT_NE(OEMCrypto_ERROR_NOT_IMPLEMENTED,
OEMCrypto_RewrapDeviceRSAKey(0, NULL, 0, NULL, 0, NULL, NULL, 0,
NULL, NULL, NULL));
ASSERT_NE(
OEMCrypto_ERROR_NOT_IMPLEMENTED,
OEMCrypto_RewrapDeviceRSAKey(0, nullptr, 0, nullptr, 0, nullptr,
nullptr, 0, nullptr, nullptr, nullptr));
} else {
ASSERT_NE(OEMCrypto_ERROR_NOT_IMPLEMENTED,
OEMCrypto_RewrapDeviceRSAKey30(0, NULL, NULL, 0, NULL, 0, NULL,
NULL, NULL));
OEMCrypto_RewrapDeviceRSAKey30(0, nullptr, nullptr, 0, nullptr, 0,
nullptr, nullptr, nullptr));
}
}
// This verifies that the device can load a DRM Certificate.
TEST_F(OEMCryptoAndroidLMPTest, RSASignatureImplemented) {
ASSERT_NE(
OEMCrypto_ERROR_NOT_IMPLEMENTED,
OEMCrypto_GenerateRSASignature(0, NULL, 0, NULL, NULL, kSign_RSASSA_PSS));
ASSERT_NE(OEMCrypto_ERROR_NOT_IMPLEMENTED,
OEMCrypto_GenerateRSASignature(0, nullptr, 0, nullptr, nullptr,
kSign_RSASSA_PSS));
}
// The Generic Crypto API functions are required for Android.
TEST_F(OEMCryptoAndroidLMPTest, GenericCryptoImplemented) {
ASSERT_NE(OEMCrypto_ERROR_NOT_IMPLEMENTED,
OEMCrypto_Generic_Encrypt(0, NULL, 0, NULL,
OEMCrypto_AES_CBC_128_NO_PADDING, NULL));
ASSERT_NE(OEMCrypto_ERROR_NOT_IMPLEMENTED,
OEMCrypto_Generic_Decrypt(0, NULL, 0, NULL,
OEMCrypto_AES_CBC_128_NO_PADDING, NULL));
ASSERT_NE(
OEMCrypto_ERROR_NOT_IMPLEMENTED,
OEMCrypto_Generic_Sign(0, NULL, 0, OEMCrypto_HMAC_SHA256, NULL, NULL));
OEMCrypto_Generic_Encrypt(0, nullptr, 0, nullptr,
OEMCrypto_AES_CBC_128_NO_PADDING, nullptr));
ASSERT_NE(
OEMCrypto_ERROR_NOT_IMPLEMENTED,
OEMCrypto_Generic_Verify(0, NULL, 0, OEMCrypto_HMAC_SHA256, NULL, 0));
OEMCrypto_Generic_Decrypt(0, nullptr, 0, nullptr,
OEMCrypto_AES_CBC_128_NO_PADDING, nullptr));
ASSERT_NE(OEMCrypto_ERROR_NOT_IMPLEMENTED,
OEMCrypto_Generic_Sign(0, nullptr, 0, OEMCrypto_HMAC_SHA256,
nullptr, nullptr));
ASSERT_NE(OEMCrypto_ERROR_NOT_IMPLEMENTED,
OEMCrypto_Generic_Verify(0, nullptr, 0, OEMCrypto_HMAC_SHA256,
nullptr, 0));
}
// Android requires support of usage table. The usage table is used for Secure
@@ -134,15 +137,15 @@ TEST_F(OEMCryptoAndroidMNCTest, LoadsTestKeyboxImplemented) {
// Android requires implementation of these functions.
TEST_F(OEMCryptoAndroidMNCTest, NumberOfSessionsImplemented) {
ASSERT_NE(OEMCrypto_ERROR_NOT_IMPLEMENTED,
OEMCrypto_GetNumberOfOpenSessions(NULL));
OEMCrypto_GetNumberOfOpenSessions(nullptr));
ASSERT_NE(OEMCrypto_ERROR_NOT_IMPLEMENTED,
OEMCrypto_GetMaxNumberOfSessions(NULL));
OEMCrypto_GetMaxNumberOfSessions(nullptr));
}
// Android requires implementation of these functions.
TEST_F(OEMCryptoAndroidMNCTest, QueryKeyControlImplemented) {
ASSERT_NE(OEMCrypto_ERROR_NOT_IMPLEMENTED,
OEMCrypto_QueryKeyControl(0, NULL, 0, NULL, NULL));
OEMCrypto_QueryKeyControl(0, nullptr, 0, nullptr, nullptr));
}
// These tests are required for N Android devices.

View File

@@ -17,22 +17,27 @@ static void acknowledge_cast() {
// Also, the test filter is updated based on the feature list.
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
wvcdm::g_cutoff = wvcdm::LOG_INFO;
bool is_cast_receiver = false;
bool force_load_test_keybox = false;
bool filter_tests = true;
for (int i = 0; i < argc; i++) {
if (!strcmp(argv[i], "--cast")) {
int verbosity = 0;
// Skip the first element, which is the program name.
const std::vector<std::string> args(argv + 1, argv + argc);
for (const std::string& arg : args) {
if (arg == "--verbose" || arg == "-v") {
++verbosity;
} else if (arg == "--cast") {
acknowledge_cast();
is_cast_receiver = true;
}
if (!strcmp(argv[i], "--force_load_test_keybox")) {
if (arg == "--force_load_test_keybox") {
force_load_test_keybox = true;
}
if (!strcmp(argv[i], "--no_filter")) {
if (arg == "--no_filter") {
filter_tests = false;
}
}
wvcdm::g_cutoff = static_cast<wvcdm::LogPriority>(verbosity);
wvoec::global_features.Initialize(is_cast_receiver, force_load_test_keybox);
// If the user requests --no_filter, we don't change the filter, otherwise, we
// filter out features that are not supported.

View File

@@ -1,38 +0,0 @@
# This is a gyp file for building the OEMCrypto unit tests with the reference
# code from the stand-alone source code.
{
'variables': {
# Override the variables below for the location of various gyp files.
# Alternatively, set the environment variable CDM_DIR to point to a recent
# version of the source CDM.
'boringssl_dependency%': '<!(echo $CDM_DIR)/third_party/boringssl/boringssl.gyp:ssl',
'gtest_dependency%': '<!(echo $CDM_DIR)/third_party/gmock.gyp:gtest',
'gmock_dependency%': '<!(echo $CDM_DIR)/third_party/gmock.gyp:gmock',
'oemcrypto_dir%': '..',
'util_dir%': '../../util',
'platform_specific_dir%': '<!(echo $CDM_DIR)/linux/src',
},
'targets': [
{
'target_name': 'oemcrypto_unittests',
'type': 'executable',
'sources': [
'oemcrypto_test_main.cpp',
'<(platform_specific_dir)/file_store.cpp',
'<(platform_specific_dir)/log.cpp',
'<(util_dir)/src/platform.cpp',
'<(util_dir)/src/rw_lock.cpp',
'<(util_dir)/src/string_conversions.cpp',
],
'includes': [
'oemcrypto_unittests.gypi',
'../ref/oec_ref.gypi',
],
'dependencies': [
'<(boringssl_dependency)',
'<(gtest_dependency)',
'<(gmock_dependency)',
],
},
],
}

View File

@@ -7,6 +7,7 @@
{
'sources': [
'oec_device_features.cpp',
'oec_key_deriver.cpp',
'oec_session_util.cpp',
'oemcrypto_session_tests_helper.cpp',
'oemcrypto_test.cpp',
@@ -22,6 +23,7 @@
'OEMCRYPTO_TESTS',
],
'dependencies': [
'<(oemcrypto_dir)/odk/src/odk.gyp:odk',
'<(boringssl_dependency)',
],
}

20
util/include/arraysize.h Normal file
View File

@@ -0,0 +1,20 @@
// 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 WVCDM_UTIL_ARRAYSIZE_H_
#define WVCDM_UTIL_ARRAYSIZE_H_
#include <stdint.h>
namespace wvcdm {
// Returns the size of a fixed-length array.
template <typename T, size_t N>
constexpr size_t ArraySize(const T (&)[N]) {
return N;
}
} // namespace wvcdm
#endif // WVCDM_UTIL_ARRAYSIZE_H_

109
util/include/cdm_random.h Normal file
View File

@@ -0,0 +1,109 @@
// 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 WVCDM_CORE_CDM_RANDOM_H_
#define WVCDM_CORE_CDM_RANDOM_H_
#include <mutex>
#include <random>
#include <string>
namespace wvcdm {
// CdmRandomGenerator is a thread safe, pseudo-random number generator.
// It's purpose is to simplified interface for C++11's <random> library.
// Some of the methods use a "device specific" random seed, if the
// compiler/device does not support device specific randomizers, then the
// actual value supplied may not be random.
class CdmRandomGenerator {
public:
// The maximum number of bytes that can be generated at once for
// `RandomData()`.
static constexpr size_t kMaxRandomDataLength = 8192; // 8 kB
// Initializes the pseudo-random generator with a value from a device
// specific random number generator.
CdmRandomGenerator();
// Initializes the pseudo-random generator with the specified seed value.
explicit CdmRandomGenerator(unsigned int seed) : generator_(seed) {}
// All of these methods are thread-safe.
// Seeds the pseudo-random generator with a value from a device specific
// random number generator.
void Seed();
// Seeds the pseudo-random generator with the specified seed value.
// This is somewhat similar to `srand()` from the C standard library;
// except that the sequence generated from successive calls to `Rand()`
// will not necessarily be the same as they would be from the
// standard library `rand()`. This is due to the underlying pseudo-random
// generator that is used.
void Seed(unsigned int seed);
// Returns a pseudo-random integer.
// This is similar to `rand()` from the C standard library. The integer
// returned is in the range of [0, RAND_MAX].
int Rand();
// Allows for RNG to be callable, this is to make it similar to the
// C++11 generator interfaces.
int operator()() { return Rand(); }
// Returns a pseudo-random integer within the provided inclusive range.
uint64_t RandomInRange(uint64_t lower, uint64_t upper);
uint64_t RandomInRange(uint64_t upper) { return RandomInRange(0, upper); }
// Returns a byte string containing randomized bytes of the specified
// length.
// If |length| is greater than |CdmRandomGenerator::kMaxRandomDataLength|,
// then an error is logged and an empty string is returned.
std::string RandomData(size_t length);
// Random true/false using Bernoulli distribution of equal probability.
bool RandomBool();
private:
// Mutex is used to lock the object, and allowing it to be used
// concurrently in different threads.
std::mutex generator_lock_;
// The `default_random_engine` depends on the compiler used and
// potentially its version. This is important to know if you need to
// create reproducible tests between platforms.
std::default_random_engine generator_;
};
// Provides a static interface to a process-wide instance of
// CdmRandomGenerator.
class CdmRandom {
public:
static int Rand() { return GetInstance()->Rand(); }
static uint64_t RandomInRange(uint64_t lower, uint64_t upper) {
return GetInstance()->RandomInRange(lower, upper);
}
static uint64_t RandomInRange(uint64_t upper) {
return GetInstance()->RandomInRange(upper);
}
static std::string RandomData(size_t length) {
return GetInstance()->RandomData(length);
}
static bool RandomBool() { return GetInstance()->RandomBool(); }
private:
// These are intended to be used by tests if needed.
static void Seed(unsigned int seed) { GetInstance()->Seed(seed); }
static void Seed() { GetInstance()->Seed(); }
// Returns the process-wide instance of CdmRandomGenerator.
// It the global instance has not yet been created, then a new instance
// is created using a device-specific random seed.
static CdmRandomGenerator* GetInstance();
};
} // namespace wvcdm
#endif // WVCDM_CORE_CDM_RANDOM_H_

View File

@@ -11,7 +11,6 @@ namespace wvcdm {
TypeName(const TypeName&); \
void operator=(const TypeName&)
} // namespace wvcdm
#endif // WVCDM_UTIL_DISALLOW_COPY_AND_ASSIGN_H_

View File

@@ -33,22 +33,21 @@ extern LogPriority g_cutoff;
// unit tests.
CORE_UTIL_EXPORT void InitLogging();
CORE_UTIL_EXPORT void Log(
const char* file, const char* function, int line, LogPriority level,
const char* fmt, ...);
CORE_UTIL_EXPORT void Log(const char* file, const char* function, int line,
LogPriority level, const char* fmt, ...);
// Log APIs
#ifndef LOGE
#define LOGE(...) Log(__FILE__, __func__, __LINE__, \
wvcdm::LOG_ERROR, __VA_ARGS__)
#define LOGW(...) Log(__FILE__, __func__, __LINE__, \
wvcdm::LOG_WARN, __VA_ARGS__)
#define LOGI(...) Log(__FILE__, __func__, __LINE__, \
wvcdm::LOG_INFO, __VA_ARGS__)
#define LOGD(...) Log(__FILE__, __func__, __LINE__, \
wvcdm::LOG_DEBUG, __VA_ARGS__)
#define LOGV(...) Log(__FILE__, __func__, __LINE__, \
wvcdm::LOG_VERBOSE, __VA_ARGS__)
# define LOGE(...) \
Log(__FILE__, __func__, __LINE__, wvcdm::LOG_ERROR, __VA_ARGS__)
# define LOGW(...) \
Log(__FILE__, __func__, __LINE__, wvcdm::LOG_WARN, __VA_ARGS__)
# define LOGI(...) \
Log(__FILE__, __func__, __LINE__, wvcdm::LOG_INFO, __VA_ARGS__)
# define LOGD(...) \
Log(__FILE__, __func__, __LINE__, wvcdm::LOG_DEBUG, __VA_ARGS__)
# define LOGV(...) \
Log(__FILE__, __func__, __LINE__, wvcdm::LOG_VERBOSE, __VA_ARGS__)
#endif
} // namespace wvcdm

View File

@@ -10,24 +10,22 @@
#include "util_common.h"
#ifdef _WIN32
# include <wtypes.h>
# include <BaseTsd.h>
# include <winsock2.h> // For htonl and ntohl.
# define __PRETTY_FUNCTION__ __FUNCTION__
# undef NO_ERROR
# undef GetCurrentTime
# undef DeleteFile
# include <BaseTsd.h>
# include <winsock2.h> // For htonl and ntohl.
# include <wtypes.h>
# define __PRETTY_FUNCTION__ __FUNCTION__
# undef NO_ERROR
# undef GetCurrentTime
# undef DeleteFile
using ssize_t = SSIZE_T;
inline void sleep(int seconds) {
Sleep(seconds * 1000);
}
inline void sleep(int seconds) { Sleep(seconds * 1000); }
CORE_UTIL_EXPORT int setenv(const char* key, const char* value, int overwrite);
#else
# include <arpa/inet.h>
# include <sys/types.h>
# include <unistd.h>
# include <arpa/inet.h>
# include <sys/types.h>
# include <unistd.h>
#endif
#endif // WVCDM_UTIL_PLATFORM_H_

View File

@@ -7,6 +7,7 @@
#include <stddef.h>
#include <stdint.h>
#include <string>
#include <vector>
@@ -35,6 +36,8 @@ CORE_UTIL_EXPORT std::string IntToString(int value);
CORE_UTIL_EXPORT int64_t htonll64(int64_t x);
CORE_UTIL_EXPORT inline int64_t ntohll64(int64_t x) { return htonll64(x); }
CORE_UTIL_EXPORT std::string BytesToString(const uint8_t* bytes, unsigned size);
// Encode unsigned integer into a big endian formatted string
CORE_UTIL_EXPORT std::string EncodeUint32(unsigned int u);
} // namespace wvcdm

View File

@@ -6,17 +6,17 @@
#define WVCDM_UTIL_UTIL_COMMON_H_
#ifdef _WIN32
# ifdef CORE_UTIL_IMPLEMENTATION
# define CORE_UTIL_EXPORT __declspec(dllexport)
# else
# define CORE_UTIL_EXPORT __declspec(dllimport)
# endif
# ifdef CORE_UTIL_IMPLEMENTATION
# define CORE_UTIL_EXPORT __declspec(dllexport)
# else
# define CORE_UTIL_EXPORT __declspec(dllimport)
# endif
#else
# ifdef CORE_UTIL_IMPLEMENTATION
# define CORE_UTIL_EXPORT __attribute__((visibility("default")))
# else
# define CORE_UTIL_EXPORT
# endif
# ifdef CORE_UTIL_IMPLEMENTATION
# define CORE_UTIL_EXPORT __attribute__((visibility("default")))
# else
# define CORE_UTIL_EXPORT
# endif
#endif
#endif // WVCDM_UTIL_UTIL_COMMON_H_

107
util/src/cdm_random.cpp Normal file
View File

@@ -0,0 +1,107 @@
// 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 "cdm_random.h"
#include <stdlib.h>
#include <algorithm>
#include "log.h"
// This type alias is for convenience.
using CdmRandomLock = std::unique_lock<std::mutex>;
namespace wvcdm {
namespace {
// More information about C++11's random number generators can be found
// from the introductory paper https://isocpp.org/files/papers/n3551.pdf
// Attemps to get random data in a device specific manner. If the device
// does not support true random data, then a pseudo-random sequence might
// be used instead. The exact behaviour depends on the compiler and
// platform combination.
unsigned int GetDeviceRandomSeed() {
static std::random_device rdev;
static std::mutex rdev_mutex;
CdmRandomLock rdev_lock(rdev_mutex);
return rdev();
}
} // namespace
// CdmRandomGenerator.
CdmRandomGenerator::CdmRandomGenerator() : generator_(GetDeviceRandomSeed()) {}
void CdmRandomGenerator::Seed() {
CdmRandomLock lock(generator_lock_);
generator_.seed(GetDeviceRandomSeed());
}
void CdmRandomGenerator::Seed(unsigned int s) {
CdmRandomLock lock(generator_lock_);
generator_.seed(s);
}
int CdmRandomGenerator::Rand() {
CdmRandomLock lock(generator_lock_);
std::uniform_int_distribution<int> dist(0, RAND_MAX);
return dist(generator_);
}
uint64_t CdmRandomGenerator::RandomInRange(uint64_t lower, uint64_t upper) {
if (lower == upper) {
return lower;
}
CdmRandomLock lock(generator_lock_);
if (lower > upper) {
LOGW(
"Lower bound is larger than upper bound, swapping bounds: "
"lower = %llu, upper = %llu",
// Casting to insure this will work on 32-bit systems.
static_cast<unsigned long long>(lower),
static_cast<unsigned long long>(upper));
std::swap(lower, upper);
}
std::uniform_int_distribution<uint64_t> dist(lower, upper);
return dist(generator_);
}
std::string CdmRandomGenerator::RandomData(size_t length) {
if (length > kMaxRandomDataLength) {
LOGE("Maximum random data length exceeded: length = %zu, max_length = %zu",
length, kMaxRandomDataLength);
return std::string();
}
CdmRandomLock lock(generator_lock_);
std::uniform_int_distribution<uint8_t> dist; // Range of [0, 255].
std::string random_data(length, '\0');
std::generate(random_data.begin(), random_data.end(),
[&]() { return dist(generator_); });
return random_data;
}
bool CdmRandomGenerator::RandomBool() {
CdmRandomLock lock(generator_lock_);
std::bernoulli_distribution dist; // 50/50.
return dist(generator_);
}
// CdmRandom.
// static
CdmRandomGenerator* CdmRandom::GetInstance() {
static std::mutex g_instance_lock;
static CdmRandomGenerator* g_instance = nullptr;
CdmRandomLock lock(g_instance_lock);
if (g_instance == nullptr) {
LOGV("Initalizing CDM random number generator");
g_instance = new CdmRandomGenerator(GetDeviceRandomSeed());
}
return g_instance;
}
} // namespace wvcdm

View File

@@ -6,7 +6,6 @@
//
#include <windows.h>
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason,
LPVOID lpReserved) {
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved) {
return TRUE;
}

View File

@@ -8,6 +8,7 @@
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <vector>
@@ -24,12 +25,11 @@ static const char kBase64Codes[] =
// Gets the given (zero-indexed) bits [a, b) of |in|.
#define GET_BITS(in, a, b) GET_LOW_BITS((in) >> (a), (b) - (a))
// Calculates a/b using round-up division (only works for positive numbers).
#define CEIL_DIVIDE(a, b) ((((a) - 1) / (b)) + 1)
#define CEIL_DIVIDE(a, b) ((((a)-1) / (b)) + 1)
int DecodeBase64Char(char c) {
const char* it = strchr(kBase64Codes, c);
if (it == NULL)
return -1;
if (it == nullptr) return -1;
return it - kBase64Codes;
}
@@ -121,8 +121,8 @@ std::string Base64Encode(const std::vector<uint8_t>& bin_input) {
if (i % 3 == 2) {
result[out_index++] = kBase64Codes[GET_BITS(temp, 18, 24)];
result[out_index++] = kBase64Codes[GET_BITS(temp, 12, 18)];
result[out_index++] = kBase64Codes[GET_BITS(temp, 6, 12)];
result[out_index++] = kBase64Codes[GET_BITS(temp, 0, 6)];
result[out_index++] = kBase64Codes[GET_BITS(temp, 6, 12)];
result[out_index++] = kBase64Codes[GET_BITS(temp, 0, 6)];
temp = 0;
}
}
@@ -135,7 +135,7 @@ std::string Base64Encode(const std::vector<uint8_t>& bin_input) {
} else if (bin_input.size() % 3 == 2) {
result[out_index++] = kBase64Codes[GET_BITS(temp, 18, 24)];
result[out_index++] = kBase64Codes[GET_BITS(temp, 12, 18)];
result[out_index++] = kBase64Codes[GET_BITS(temp, 6, 12)];
result[out_index++] = kBase64Codes[GET_BITS(temp, 6, 12)];
result[out_index++] = '=';
}
@@ -208,8 +208,8 @@ std::vector<uint8_t> Base64Decode(const std::string& b64_input) {
if (i % 4 == 3) {
result[out_index++] = GET_BITS(temp, 16, 24);
result[out_index++] = GET_BITS(temp, 8, 16);
result[out_index++] = GET_BITS(temp, 0, 8);
result[out_index++] = GET_BITS(temp, 8, 16);
result[out_index++] = GET_BITS(temp, 0, 8);
temp = 0;
}
}
@@ -223,7 +223,7 @@ std::vector<uint8_t> Base64Decode(const std::string& b64_input) {
break;
case 3:
result[out_index++] = GET_BITS(temp, 16, 24);
result[out_index++] = GET_BITS(temp, 8, 16);
result[out_index++] = GET_BITS(temp, 8, 16);
break;
}
result.resize(out_index);
@@ -298,4 +298,14 @@ std::string BytesToString(const uint8_t* bytes, unsigned size) {
return std::string(char_bytes, char_bytes + size);
}
// Encode unsigned integer into a big endian formatted string
std::string EncodeUint32(unsigned int u) {
std::string s;
s.append(1, (u >> 24) & 0xFF);
s.append(1, (u >> 16) & 0xFF);
s.append(1, (u >> 8) & 0xFF);
s.append(1, (u >> 0) & 0xFF);
return s;
}
} // namespace wvcdm

View File

@@ -0,0 +1,147 @@
// 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 <stdlib.h>
#include <chrono>
#include <limits>
#include <string>
#include <thread>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "cdm_random.h"
namespace wvcdm {
namespace {
// Random data vector lengths.
constexpr size_t kVectorLength = 1024;
constexpr size_t kMaxRandomDataLength =
CdmRandomGenerator::kMaxRandomDataLength;
constexpr size_t kAboveMaxRandomDataLength = std::numeric_limits<size_t>::max();
constexpr size_t kRandomTrialCount = 100;
constexpr size_t kThreadCount = 16;
constexpr unsigned int kSeeds[] = {0, 1337, 1565904109, 776964657};
class CdmRandomGeneratorTest : public testing::TestWithParam<unsigned int> {};
} // namespace
TEST_P(CdmRandomGeneratorTest, AllMethods) {
const unsigned int seed = GetParam();
CdmRandomGenerator rng;
rng.Seed();
rng.Seed(seed);
rng.Rand();
rng();
rng.RandomInRange(1234, 1000000);
rng.RandomInRange(1000000);
rng.RandomData(kVectorLength);
rng.RandomBool();
}
TEST_P(CdmRandomGeneratorTest, RandomInRange) {
const unsigned int seed = GetParam();
CdmRandomGenerator rng(seed);
for (size_t i = 0; i < kRandomTrialCount; ++i) {
const int rand_int = rng.Rand();
EXPECT_GE(rand_int, 0);
EXPECT_LE(rand_int, RAND_MAX);
}
// Range size of 1.
const uint64_t rand_u64_1 = rng.RandomInRange(100, 100);
EXPECT_EQ(rand_u64_1, 100ul);
// Range size of 2.
const uint64_t rand_u64_2 = rng.RandomInRange(1234, 1235);
EXPECT_GE(rand_u64_2, 1234ul);
EXPECT_LE(rand_u64_2, 1235ul);
// Small range.
const uint64_t rand_u64_3 = rng.RandomInRange(10);
EXPECT_LE(rand_u64_3, 10ul);
// Max range, mainly checking that nothing crashes.
rng.RandomInRange(0, std::numeric_limits<uint64_t>::max());
// Invalid range representation. Should swap the bounds.
const uint64_t rand_u64_4 = rng.RandomInRange(1235, 1234);
EXPECT_GE(rand_u64_4, 1234ul);
EXPECT_LE(rand_u64_4, 1235ul);
}
TEST_P(CdmRandomGeneratorTest, RandomDataLength) {
const unsigned int seed = GetParam();
CdmRandomGenerator rng(seed);
const std::string empty_data = rng.RandomData(0);
EXPECT_EQ(empty_data.size(), 0ul);
const std::string data = rng.RandomData(kVectorLength);
EXPECT_EQ(data.size(), kVectorLength);
const std::string max_data = rng.RandomData(kMaxRandomDataLength);
EXPECT_EQ(max_data.size(), kMaxRandomDataLength);
// Requesting data above the maximum length will result in an error,
// returning an empty string.
const std::string error_data = rng.RandomData(kAboveMaxRandomDataLength);
EXPECT_EQ(error_data.size(), 0ul);
}
TEST_P(CdmRandomGeneratorTest, Reproducibility) {
const unsigned int seed = GetParam();
CdmRandomGenerator rng(seed);
const std::string random_data_1 = rng.RandomData(kVectorLength);
// Reset generator.
rng.Seed(seed);
const std::string random_data_2 = rng.RandomData(kVectorLength);
EXPECT_EQ(random_data_1, random_data_2);
}
TEST_P(CdmRandomGeneratorTest, ThreadSafety) {
const unsigned int seed = GetParam();
CdmRandomGenerator rng(seed);
bool barrier = true;
auto thread_job = [&]() {
while (barrier) {
std::this_thread::sleep_for(std::chrono::microseconds(1));
}
for (size_t i = 0; i < kRandomTrialCount; ++i) {
rng.Rand();
}
};
std::vector<std::thread> threads;
for (size_t i = 0; i < kThreadCount; ++i) {
threads.push_back(std::thread(thread_job));
}
std::this_thread::sleep_for(std::chrono::microseconds(100));
barrier = false;
for (auto& thread : threads) {
thread.join();
}
}
INSTANTIATE_TEST_CASE_P(VariousSeeds, CdmRandomGeneratorTest,
testing::ValuesIn(kSeeds));
TEST(CdmRandomTest, AllMethods) {
CdmRandom::Rand();
CdmRandom::RandomInRange(1234, 1000000);
CdmRandom::RandomInRange(1000000);
CdmRandom::RandomData(kVectorLength);
CdmRandom::RandomBool();
}
} // namespace wvcdm