diff --git a/docs/License_Duration_and_Renewal.pdf b/docs/License_Duration_and_Renewal.pdf new file mode 100644 index 0000000..1800d98 Binary files /dev/null and b/docs/License_Duration_and_Renewal.pdf differ diff --git a/docs/Widevine_Core_Message_Serialization.pdf b/docs/Widevine_Core_Message_Serialization.pdf new file mode 100644 index 0000000..082d167 Binary files /dev/null and b/docs/Widevine_Core_Message_Serialization.pdf differ diff --git a/docs/Widevine_Level_3_OEMCrypto_Guide.pdf b/docs/Widevine_Level_3_OEMCrypto_Guide.pdf new file mode 100644 index 0000000..50142d6 Binary files /dev/null and b/docs/Widevine_Level_3_OEMCrypto_Guide.pdf differ diff --git a/docs/Widevine_Modular_DRM_Version_16_Delta.pdf b/docs/Widevine_Modular_DRM_Version_16_Delta.pdf new file mode 100644 index 0000000..4803689 Binary files /dev/null and b/docs/Widevine_Modular_DRM_Version_16_Delta.pdf differ diff --git a/oemcrypto/include/OEMCryptoCENC.h b/oemcrypto/include/OEMCryptoCENC.h index fffb93a..5a84c47 100644 --- a/oemcrypto/include/OEMCryptoCENC.h +++ b/oemcrypto/include/OEMCryptoCENC.h @@ -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 diff --git a/oemcrypto/odk/include/odk.h b/oemcrypto/odk/include/odk.h new file mode 100644 index 0000000..a7c705d --- /dev/null +++ b/oemcrypto/odk/include/odk.h @@ -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 +#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 session’s 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_ diff --git a/oemcrypto/odk/include/odk_assert.h b/oemcrypto/odk/include/odk_assert.h new file mode 100644 index 0000000..e1a21fd --- /dev/null +++ b/oemcrypto/odk/include/odk_assert.h @@ -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 +# 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_ */ diff --git a/oemcrypto/odk/include/odk_overflow.h b/oemcrypto/odk/include/odk_overflow.h new file mode 100644 index 0000000..32aebe7 --- /dev/null +++ b/oemcrypto/odk/include/odk_overflow.h @@ -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_ */ diff --git a/oemcrypto/odk/include/odk_structs.h b/oemcrypto/odk/include/odk_structs.h new file mode 100644 index 0000000..80753ee --- /dev/null +++ b/oemcrypto/odk/include/odk_structs.h @@ -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 +#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 OEMCrypto’s 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_ diff --git a/oemcrypto/odk/include/odk_timer.h b/oemcrypto/odk/include/odk_timer.h new file mode 100644 index 0000000..aa237d7 --- /dev/null +++ b/oemcrypto/odk/include/odk_timer.h @@ -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 +#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_ */ diff --git a/oemcrypto/odk/src/odk.c b/oemcrypto/odk/src/odk.c new file mode 100644 index 0000000..d91b260 --- /dev/null +++ b/oemcrypto/odk/src/odk.c @@ -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 +#include +#include + +#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; +} diff --git a/oemcrypto/odk/src/odk.gyp b/oemcrypto/odk/src/odk.gyp new file mode 100644 index 0000000..0b68fdc --- /dev/null +++ b/oemcrypto/odk/src/odk.gyp @@ -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', + ], + } + }, + ], +} diff --git a/oemcrypto/odk/src/odk.gypi b/oemcrypto/odk/src/odk.gypi new file mode 100644 index 0000000..f8b5fb3 --- /dev/null +++ b/oemcrypto/odk/src/odk.gypi @@ -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', + ], +} + diff --git a/oemcrypto/odk/src/odk_overflow.c b/oemcrypto/odk/src/odk_overflow.c new file mode 100644 index 0000000..3a05f22 --- /dev/null +++ b/oemcrypto/odk/src/odk_overflow.c @@ -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 +#include + +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; +} diff --git a/oemcrypto/odk/src/odk_serialize.c b/oemcrypto/odk/src/odk_serialize.c new file mode 100644 index 0000000..da220da --- /dev/null +++ b/oemcrypto/odk/src/odk_serialize.c @@ -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); +} diff --git a/oemcrypto/odk/src/odk_serialize.h b/oemcrypto/odk/src/odk_serialize.h new file mode 100644 index 0000000..28a10e8 --- /dev/null +++ b/oemcrypto/odk/src/odk_serialize.h @@ -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_ */ diff --git a/oemcrypto/odk/src/odk_structs_priv.h b/oemcrypto/odk/src/odk_structs_priv.h new file mode 100644 index 0000000..c8d5b9c --- /dev/null +++ b/oemcrypto/odk/src/odk_structs_priv.h @@ -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 +#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_ diff --git a/oemcrypto/odk/src/odk_timer.c b/oemcrypto/odk/src/odk_timer.c new file mode 100644 index 0000000..014ef19 --- /dev/null +++ b/oemcrypto/odk/src/odk_timer.c @@ -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 +#include + +#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) {} diff --git a/oemcrypto/odk/src/serialization_base.c b/oemcrypto/odk/src/serialization_base.c new file mode 100644 index 0000000..c83e553 --- /dev/null +++ b/oemcrypto/odk/src/serialization_base.c @@ -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 +#include +#include + +#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); } diff --git a/oemcrypto/odk/src/serialization_base.h b/oemcrypto/odk/src/serialization_base.h new file mode 100644 index 0000000..3883615 --- /dev/null +++ b/oemcrypto/odk/src/serialization_base.h @@ -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 +#include + +#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_ diff --git a/oemcrypto/odk/test/odk_test.cpp b/oemcrypto/odk/test/odk_test.cpp new file mode 100644 index 0000000..b78a056 --- /dev/null +++ b/oemcrypto/odk/test/odk_test.cpp @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#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(field->value)); + memcpy(buf, &u32, sizeof(u32)); + break; + } + case ODK_UINT64: { + uint64_t u64 = htobe64(*static_cast(field->value)); + memcpy(buf, &u64, sizeof(u64)); + break; + } + case ODK_SUBSTRING: { + OEMCrypto_Substring* s = static_cast(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(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(field->value); + *u32p = be32toh(*u32p); + break; + } + case ODK_UINT64: { + memcpy(field->value, buf, sizeof(uint64_t)); + uint64_t* u64p = static_cast(field->value); + *u64p = be64toh(*u64p); + break; + } + case ODK_SUBSTRING: { + OEMCrypto_Substring* s = static_cast(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(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& 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(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& fields) { + return ODK_IterFields(ODK_READ, const_cast(buf), size_in, size_out, + fields); +} + +OEMCryptoResult ODK_WriteFields(uint8_t* const buf, const size_t size_in, + size_t* size_out, + std::vector& 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(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 +void ValidateRequest(uint32_t message_type, + std::vector& 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 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 +void ValidateResponse(uint32_t message_type, + std::vector& 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 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 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 fields(n); + std::srand(0); + size_t total_size = 0; + for (int i = 0; i < n; i++) { + fields[i].type = static_cast(std::rand() % + static_cast(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 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 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 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 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 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 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(); +} diff --git a/oemcrypto/odk/test/odk_test.gypi b/oemcrypto/odk/test/odk_test.gypi new file mode 100644 index 0000000..54035bb --- /dev/null +++ b/oemcrypto/odk/test/odk_test.gypi @@ -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', + ], +} + diff --git a/oemcrypto/odk/test/odk_test.h b/oemcrypto/odk/test/odk_test.h new file mode 100644 index 0000000..edbbdaa --- /dev/null +++ b/oemcrypto/odk/test/odk_test.h @@ -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_ diff --git a/oemcrypto/odk/test/odk_timer_test.cpp b/oemcrypto/odk/test/odk_timer_test.cpp new file mode 100644 index 0000000..72b5faf --- /dev/null +++ b/oemcrypto/odk/test/odk_timer_test.cpp @@ -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 + +#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); +} diff --git a/oemcrypto/oemcrypto_unittests.gyp b/oemcrypto/oemcrypto_unittests.gyp new file mode 100644 index 0000000..6c09275 --- /dev/null +++ b/oemcrypto/oemcrypto_unittests.gyp @@ -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%': '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(&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(&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; diff --git a/oemcrypto/ref/src/oemcrypto_engine_ref.h b/oemcrypto/ref/src/oemcrypto_engine_ref.h index cc132aa..182873c 100644 --- a/oemcrypto/ref/src/oemcrypto_engine_ref.h +++ b/oemcrypto/ref/src/oemcrypto_engine_ref.h @@ -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; } diff --git a/oemcrypto/ref/src/oemcrypto_key_ref.cpp b/oemcrypto/ref/src/oemcrypto_key_ref.cpp index cc08c27..feb2374 100644 --- a/oemcrypto/ref/src/oemcrypto_key_ref.cpp +++ b/oemcrypto/ref/src/oemcrypto_key_ref.cpp @@ -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 { diff --git a/oemcrypto/ref/src/oemcrypto_nonce_table.cpp b/oemcrypto/ref/src/oemcrypto_nonce_table.cpp deleted file mode 100644 index 65d9269..0000000 --- a/oemcrypto/ref/src/oemcrypto_nonce_table.cpp +++ /dev/null @@ -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 diff --git a/oemcrypto/ref/src/oemcrypto_nonce_table.h b/oemcrypto/ref/src/oemcrypto_nonce_table.h deleted file mode 100644 index 409e7fc..0000000 --- a/oemcrypto/ref/src/oemcrypto_nonce_table.h +++ /dev/null @@ -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 - -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_ diff --git a/oemcrypto/ref/src/oemcrypto_old_usage_table_ref.cpp b/oemcrypto/ref/src/oemcrypto_old_usage_table_ref.cpp deleted file mode 100644 index 5e9e9a7..0000000 --- a/oemcrypto/ref/src/oemcrypto_old_usage_table_ref.cpp +++ /dev/null @@ -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 -#include - -#include -#include - -#include -#include -#include -#include - -#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 &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 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 encrypted_buffer(file_size); - std::vector buffer(file_size); - OldStoredUsageTable *stored_table = - reinterpret_cast(&buffer[0]); - OldStoredUsageTable *encrypted_table = - reinterpret_cast(&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(&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 &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(&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 &pst) { - std::unique_lock lock(lock_); - return FindEntryLocked(pst); -} - -OldUsageTableEntry *OldUsageTable::FindEntryLocked( - const std::vector &pst) { - std::vector 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 &pst) { - std::vector 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 lock(lock_); - table_[pst_hash] = entry; - return entry; -} - -void OldUsageTable::Clear() { - std::unique_lock 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 &pst, - std::vector &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 diff --git a/oemcrypto/ref/src/oemcrypto_old_usage_table_ref.h b/oemcrypto/ref/src/oemcrypto_old_usage_table_ref.h deleted file mode 100644 index 452d504..0000000 --- a/oemcrypto/ref/src/oemcrypto_old_usage_table_ref.h +++ /dev/null @@ -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 -#include -#include -#include -#include - -#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 &pst_hash); - OldUsageTableEntry(OldUsageTable *old_usage_table, - const OldStoredUsageEntry *buffer); - ~OldUsageTableEntry(); - const std::vector &pst_hash() const { return pst_hash_; } - - private: - std::vector 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 mac_key_server_; - std::vector mac_key_client_; - - friend class UsageTableEntry; - friend class UsageTable; -}; - -class OldUsageTable { - public: - OldUsageTable(CryptoEngine *ce); - ~OldUsageTable() { Clear(); } - OldUsageTableEntry *FindEntry(const std::vector &pst); - OldUsageTableEntry *CreateEntry(const std::vector &pst); - void Clear(); - static void DeleteFile(CryptoEngine *ce); - - private: - OldUsageTableEntry *FindEntryLocked(const std::vector &pst); - bool ComputeHash(const std::vector &pst, - std::vector &pst_hash); - - typedef std::map, 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_ diff --git a/oemcrypto/ref/src/oemcrypto_ref.cpp b/oemcrypto/ref/src/oemcrypto_ref.cpp index 355fbf7..dc00cc5 100644 --- a/oemcrypto/ref/src/oemcrypto_ref.cpp +++ b/oemcrypto/ref/src/oemcrypto_ref.cpp @@ -20,8 +20,10 @@ #include #include #include + #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(unaligned_ptr); uint8_t* dest = reinterpret_cast(&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(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 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 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; } diff --git a/oemcrypto/ref/src/oemcrypto_rsa_key_shared.cpp b/oemcrypto/ref/src/oemcrypto_rsa_key_shared.cpp index b34a316..6c73ad8 100644 --- a/oemcrypto/ref/src/oemcrypto_rsa_key_shared.cpp +++ b/oemcrypto/ref/src/oemcrypto_rsa_key_shared.cpp @@ -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(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); diff --git a/oemcrypto/ref/src/oemcrypto_rsa_key_shared.h b/oemcrypto/ref/src/oemcrypto_rsa_key_shared.h index c80745d..98ea154 100644 --- a/oemcrypto/ref/src/oemcrypto_rsa_key_shared.h +++ b/oemcrypto/ref/src/oemcrypto_rsa_key_shared.h @@ -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) : diff --git a/oemcrypto/ref/src/oemcrypto_session.cpp b/oemcrypto/ref/src/oemcrypto_session.cpp index eb70320..c08a86d 100644 --- a/oemcrypto/ref/src/oemcrypto_session.cpp +++ b/oemcrypto/ref/src/oemcrypto_session.cpp @@ -23,14 +23,15 @@ #include #include +#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& key, const std::vector& context, int counter, std::vector* 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 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 enc_mac_keys_str = std::vector( @@ -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 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& key_control, const std::vector& 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& 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& 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& 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& key, const std::vector& 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 diff --git a/oemcrypto/ref/src/oemcrypto_session.h b/oemcrypto/ref/src/oemcrypto_session.h index 0956056..ca6aba1 100644 --- a/oemcrypto/ref/src/oemcrypto_session.h +++ b/oemcrypto/ref/src/oemcrypto_session.h @@ -15,9 +15,9 @@ #include #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& enc_session_key, const std::vector& mac_context, const std::vector& 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& key_data, const std::vector& key_data_iv, @@ -171,13 +178,13 @@ class SessionContext { const std::vector& 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& pst, uint8_t* buffer, size_t* buffer_length); OEMCryptoResult MoveEntry(uint32_t new_index); - OEMCryptoResult CopyOldUsageEntry(const std::vector& pst); protected: bool DeriveKey(const std::vector& key, @@ -244,7 +250,7 @@ class SessionContext { std::vector 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); }; diff --git a/oemcrypto/ref/src/oemcrypto_session_key_table.cpp b/oemcrypto/ref/src/oemcrypto_session_key_table.cpp index 42b8a58..e713197 100644 --- a/oemcrypto/ref/src/oemcrypto_session_key_table.cpp +++ b/oemcrypto/ref/src/oemcrypto_session_key_table.cpp @@ -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]; } diff --git a/oemcrypto/ref/src/oemcrypto_usage_table_ref.cpp b/oemcrypto/ref/src/oemcrypto_usage_table_ref.cpp index c24275d..a08d06c 100644 --- a/oemcrypto/ref/src/oemcrypto_usage_table_ref.cpp +++ b/oemcrypto/ref/src/oemcrypto_usage_table_ref.cpp @@ -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& 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& 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 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 diff --git a/oemcrypto/ref/src/oemcrypto_usage_table_ref.h b/oemcrypto/ref/src/oemcrypto_usage_table_ref.h index 3278852..31913f7 100644 --- a/oemcrypto/ref/src/oemcrypto_usage_table_ref.h +++ b/oemcrypto/ref/src/oemcrypto_usage_table_ref.h @@ -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& buffer); - virtual OEMCryptoResult CopyOldUsageEntry(const std::vector& 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& 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 generation_numbers_; std::vector sessions_; - OldUsageTable* old_table_; friend class UsageTableEntry; }; diff --git a/oemcrypto/test/oec_device_features.cpp b/oemcrypto/test/oec_device_features.cpp index 2e18ca1..d1c5624 100644 --- a/oemcrypto/test/oec_device_features.cpp +++ b/oemcrypto/test/oec_device_features.cpp @@ -7,8 +7,13 @@ #include "oec_device_features.h" #include -#include -#include + +#ifdef _WIN32 +# include +#else +# include +# include +#endif #include @@ -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 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 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()) { diff --git a/oemcrypto/test/oec_key_deriver.cpp b/oemcrypto/test/oec_key_deriver.cpp new file mode 100644 index 0000000..e56ffc1 --- /dev/null +++ b/oemcrypto/test/oec_key_deriver.cpp @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#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& 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(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& context, + int counter, vector* 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 message; + message.push_back(static_cast(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& mac_key_context, + const vector& enc_key_context) { + // Generate derived key for mac key + std::vector 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 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* 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& buffer, + std::vector* 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& pst_report_buffer, + std::vector* 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 diff --git a/oemcrypto/test/oec_key_deriver.h b/oemcrypto/test/oec_key_deriver.h new file mode 100644 index 0000000..beb82ed --- /dev/null +++ b/oemcrypto/test/oec_key_deriver.h @@ -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 +#include +#include +#include +#include + +#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& enc_key) { set_enc_key(enc_key); }; + Encryptor& operator=(const Encryptor&) = default; + void set_enc_key(const std::vector& 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 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& mac_key_context, + const std::vector& enc_key_context); + // Sign the buffer with server's mac key. + void ServerSignBuffer(const uint8_t* data, size_t data_length, + std::vector* 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& buffer, + std::vector* 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& pst_report_buffer, + std::vector* 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& context, + int counter, std::vector* out); + + std::vector mac_key_server_; + std::vector mac_key_client_; +}; + +} // namespace wvoec + +#endif // CDM_OEC_KEY_DERIVER_H_ diff --git a/oemcrypto/test/oec_session_util.cpp b/oemcrypto/test/oec_session_util.cpp index 737f473..21510f9 100644 --- a/oemcrypto/test/oec_session_util.cpp +++ b/oemcrypto/test/oec_session_util.cpp @@ -22,7 +22,6 @@ #include #include #include -#include #include #include "OEMCryptoCENC.h" @@ -45,11 +44,6 @@ void PrintTo(const vector& 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(NULL), iv); + ASSERT_NE(nullptr, iv); uint64_t* counterBuffer = reinterpret_cast(&iv[8]); (*counterBuffer) = wvcdm::htonll64(wvcdm::ntohll64(*counterBuffer) + increaseBy); @@ -85,7 +84,7 @@ void dump_boringssl_error() { template 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* 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& context, - int counter, vector* out) { - ASSERT_FALSE(context.empty()); - ASSERT_GE(4, counter); - ASSERT_NE(static_cast(NULL), out); - - const EVP_CIPHER* cipher = EVP_aes_128_cbc(); - CMAC_CTX* cmac_ctx = CMAC_CTX_new(); - ASSERT_NE(static_cast(NULL), cmac_ctx); - - ASSERT_EQ(1, CMAC_Init(cmac_ctx, key, KEY_SIZE, cipher, 0)); - - std::vector 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& mac_key_context, - const vector& enc_key_context) { - // Generate derived key for mac key - std::vector 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 mac_context; vector 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 session_key; vector 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(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(&padded_message_), - message_size_, &signature_); + key_deriver_.ServerSignBuffer( + reinterpret_cast(&padded_message_), message_size_, + &signature_); std::vector 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(&license_.keys[i].control), reinterpret_cast(&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(&padded_message_), - message_size_, &signature_); + key_deriver_.ServerSignBuffer( + reinterpret_cast(&padded_message_), message_size_, + &signature_); FillKeyArray(encrypted_license(), key_array_); SetLoadKeysSubstringParams(); } -void Session::EncryptProvisioningMessage( - RSAPrivateKeyMessage* data, RSAPrivateKeyMessage* encrypted, - const vector& 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(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* 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& data, - std::vector* 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 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(32), gen_signature_length); + const size_t hmac_signature_size = 32u; + ASSERT_EQ(hmac_signature_size, gen_signature_length); vector 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 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 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 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 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 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 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 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& in_buffer, const uint8_t* key, const uint8_t* starting_iv, vector* out_buffer) { - ASSERT_NE(static_cast(NULL), key); - ASSERT_NE(static_cast(NULL), starting_iv); - ASSERT_NE(static_cast(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 unencryptedData(256); @@ -908,13 +881,12 @@ void Session::LoadOEMCert(bool verify_cert) { vector 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 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* signature, uint32_t allowed_schemes, const vector& rsa_key, - const vector* 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(encrypted), message_size, - signature); + encryptor->PadAndEncryptProvisioningMessage(&message, encrypted); + key_deriver_.ServerSignBuffer(reinterpret_cast(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_new_mem_buf(p, rsa_key_length)); ASSERT_TRUE(bio.NotNull()); boringssl_ptr 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(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& 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(RSA_size(public_rsa_)), signature_length) @@ -1211,8 +1182,8 @@ void Session::UpdateUsageEntry(std::vector* 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 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(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 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(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* header_buffer) { - ASSERT_NO_FATAL_FAILURE(CreateNewUsageEntry()); - OEMCryptoResult result = OEMCrypto_CopyOldUsageEntry( - session_id(), reinterpret_cast(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() { diff --git a/oemcrypto/test/oec_session_util.h b/oemcrypto/test/oec_session_util.h index f162760..555e0e6 100644 --- a/oemcrypto/test/oec_session_util.h +++ b/oemcrypto/test/oec_session_util.h @@ -13,6 +13,7 @@ #include #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* mac_context, vector* 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& encryption_key); - // Sign the buffer with server's mac key. - void ServerSignBuffer(const uint8_t* data, size_t data_length, - std::vector* signature); - // Sign the buffer with client's known mac key. Known test keys must be - // installed first. - void ClientSignMessage(const vector& data, - std::vector* 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* signature, uint32_t allowed_schemes, const vector& rsa_key, - const vector* 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* 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& 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& mac_key_context, - const vector& enc_key_context); - // Internal utility function to derive key using CMAC-128 - void DeriveKey(const uint8_t* key, const vector& context, - int counter, vector* out); - bool open_; bool forced_session_id_; OEMCrypto_SESSION session_id_; - vector mac_key_server_; - vector mac_key_client_; - vector enc_key_; + KeyDeriver key_deriver_; uint32_t nonce_; RSA* public_rsa_; vector pst_report_buffer_; diff --git a/oemcrypto/test/oec_test_data.h b/oemcrypto/test/oec_test_data.h index 9739144..144c4d3 100644 --- a/oemcrypto/test/oec_test_data.h +++ b/oemcrypto/test/oec_test_data.h @@ -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. diff --git a/oemcrypto/test/oemcrypto_session_tests_helper.cpp b/oemcrypto/test/oemcrypto_session_tests_helper.cpp index c9643b8..7ec0704 100644 --- a/oemcrypto/test/oemcrypto_session_tests_helper.cpp +++ b/oemcrypto/test/oemcrypto_session_tests_helper.cpp @@ -15,7 +15,7 @@ const uint8_t* find(const vector& message, vector::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 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 signature; std::vector message_key; std::vector 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(&keybox), - sizeof(keybox), wrapped, &length, NULL, 0)); + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_WrapKeybox(reinterpret_cast(&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(&keybox_), diff --git a/oemcrypto/test/oemcrypto_test.cpp b/oemcrypto/test/oemcrypto_test.cpp index 3df68bf..63e1547 100644 --- a/oemcrypto/test/oemcrypto_test.cpp +++ b/oemcrypto/test/oemcrypto_test.cpp @@ -13,16 +13,20 @@ #include #include #include -#include #include +#ifdef _WIN32 +# include +#else +# include +#endif + #include #include #include #include #include #include -#include #include #include @@ -83,10 +87,36 @@ const size_t kMaxKeysPerSession [] = { 4, 20, 20}; // but are tested at the system level, so there are no unit tests for frame // rate. -int GetRandBytes(unsigned char* buf, int num) { - // returns 1 on success, -1 if not supported, or 0 if other failure. - return RAND_bytes(buf, num); +/** @return The Unix time of the given time point. */ +template +uint64_t UnixTime(const std::chrono::time_point& point) { + return point.time_since_epoch() / std::chrono::seconds(1); } + +#ifdef _WIN32 +using NativeTime = SYSTEMTIME; +#else +using NativeTime = timeval; +#endif + +void AddNativeTime(int64_t delta_seconds, NativeTime* time) { +#ifdef _WIN32 + // See remarks from this for why this series is used. + // https://msdn.microsoft.com/en-us/f77cdf86-0f97-4a89-b565-95b46fa7d65b + FILETIME file_time; + ASSERT_TRUE(SystemTimeToFileTime(time, &file_time)); + uint64_t long_time = static_cast(file_time.dwLowDateTime) | + (static_cast(file_time.dwHighDateTime) << 32); + long_time += delta_seconds * 1e7; // long_time is in 100-nanosecond intervals. + file_time.dwLowDateTime = long_time & ((1ull << 32) - 1); + file_time.dwHighDateTime = long_time >> 32; + ASSERT_TRUE(FileTimeToSystemTime(&file_time, time)); +#else + time->tv_sec += delta_seconds; +#endif +} + } // namespace class OEMCryptoClientTest : public ::testing::Test, public SessionUtil { @@ -95,7 +125,6 @@ class OEMCryptoClientTest : public ::testing::Test, public SessionUtil { void SetUp() override { ::testing::Test::SetUp(); - wvcdm::g_cutoff = wvcdm::LOG_INFO; const ::testing::TestInfo* const test_info = ::testing::UnitTest::GetInstance()->current_test_info(); LOGD("Running test %s.%s", test_info->test_case_name(), test_info->name()); @@ -113,7 +142,7 @@ class OEMCryptoClientTest : public ::testing::Test, public SessionUtil { vector::const_iterator pos = search( message.begin(), message.end(), substring.begin(), substring.end()); if (pos == message.end()) { - return NULL; + return nullptr; } return &(*pos); } @@ -125,7 +154,7 @@ class OEMCryptoClientTest : public ::testing::Test, public SessionUtil { // tests are failing when the device has the wrong keybox installed. TEST_F(OEMCryptoClientTest, VersionNumber) { const char* level = OEMCrypto_SecurityLevel(); - ASSERT_NE((char*)NULL, level); + ASSERT_NE(nullptr, level); ASSERT_EQ('L', level[0]); cout << " OEMCrypto Security Level is " << level << endl; uint32_t version = OEMCrypto_APIVersion(); @@ -137,13 +166,13 @@ TEST_F(OEMCryptoClientTest, VersionNumber) { } if (version >= 15) { const char* build_info = OEMCrypto_BuildInformation(); - ASSERT_TRUE(build_info != NULL); + ASSERT_NE(nullptr, build_info); ASSERT_TRUE(strnlen(build_info, 256) <= 256) << "BuildInformation should be a short printable string."; cout << " BuildInformation: " << build_info << endl; } ASSERT_GE(version, 8u); - ASSERT_LE(version, 15u); + ASSERT_LE(version, 16u); } // The resource rating is a number from 1 to 3, defined API 15. @@ -202,7 +231,7 @@ TEST_F(OEMCryptoClientTest, CheckSRMCapabilityV13) { OEMCryptoResult current_result = OEMCrypto_GetCurrentSRMVersion(&version); if (current_result == OEMCrypto_SUCCESS) { printf(" Current SRM Version: %d.\n", version); - EXPECT_NE(OEMCrypto_SUCCESS, OEMCrypto_GetCurrentSRMVersion(NULL)); + EXPECT_NE(OEMCrypto_SUCCESS, OEMCrypto_GetCurrentSRMVersion(nullptr)); } else if (current_result == OEMCrypto_LOCAL_DISPLAY_ONLY) { printf(" Current SRM Status: Local Display Only.\n"); } else { @@ -227,6 +256,13 @@ TEST_F(OEMCryptoClientTest, CheckMaxNumberOfSessionsAPI10) { ASSERT_GE(maximum, required_max); } +TEST_F(OEMCryptoClientTest, CheckUsageTableSizeAPI16) { + size_t maximum = OEMCrypto_MaximumUsageTableHeaderSize(); + printf(" Max Usage Table Size: %zu.\n", maximum); + const size_t minimum_capacity = 200u; + ASSERT_GE(maximum, minimum_capacity); +} + // // initialization tests // @@ -346,47 +382,10 @@ TEST_F(OEMCryptoClientTest, GenerateNonce) { s.GenerateNonce(); } -TEST_F(OEMCryptoClientTest, GenerateTwoNonces) { - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - s.GenerateNonce(); - uint32_t nonce1 = s.get_nonce(); - s.GenerateNonce(); - uint32_t nonce2 = s.get_nonce(); - ASSERT_TRUE(nonce1 != nonce2); // Very unlikely to be equal. -} - -// OEMCrypto should limit the number of nonces that it can generate in one -// second. A flood of nonce requests can be used for a replay attack, which we -// wish to protect against. -TEST_F(OEMCryptoClientTest, PreventNonceFloodAPI09) { - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - int error_counter = 0; - time_t test_start = time(NULL); - // More than 20 nonces per second should generate an error. - // To allow for some slop, we actually test for more. - const int kFloodCount = 80; - for (int i = 0; i < kFloodCount; i++) { - s.GenerateNonce(&error_counter); - } - time_t test_end = time(NULL); - int valid_counter = kFloodCount - error_counter; - // Either oemcrypto should enforce a delay, or it should return an error from - // GenerateNonce -- in either case the number of valid nonces is rate - // limited. We add two seconds to allow for round off error in both - // test_start and test_end. - EXPECT_LE(valid_counter, 20 * (test_end - test_start + 2)); - error_counter = 0; - sleep(2); // After a pause, we should be able to regenerate nonces. - s.GenerateNonce(&error_counter); - EXPECT_EQ(0, error_counter); -} - // Prevent a nonce flood even if each nonce is in a different session. TEST_F(OEMCryptoClientTest, PreventNonceFlood2API09) { int error_counter = 0; - time_t test_start = time(NULL); + time_t test_start = time(nullptr); // More than 20 nonces per second should generate an error. // To allow for some slop, we actually test for more. const int kFloodCount = 80; @@ -395,7 +394,7 @@ TEST_F(OEMCryptoClientTest, PreventNonceFlood2API09) { ASSERT_NO_FATAL_FAILURE(s.open()); s.GenerateNonce(&error_counter); } - time_t test_end = time(NULL); + time_t test_end = time(nullptr); int valid_counter = kFloodCount - error_counter; // Either oemcrypto should enforce a delay, or it should return an error from // GenerateNonce -- in either case the number of valid nonces is rate @@ -417,18 +416,18 @@ TEST_F(OEMCryptoClientTest, PreventNonceFlood2API09) { TEST_F(OEMCryptoClientTest, PreventNonceFlood3API09) { int request_counter = 0; int error_counter = 0; - time_t test_start = time(NULL); + time_t test_start = time(nullptr); // More than 20 nonces per second should generate an error. // To allow for some slop, we actually test for more. - Session s[8]; - for (int i = 0; i < 8; i++) { - ASSERT_NO_FATAL_FAILURE(s[i].open()); - for (int j = 0; j < 10; j++) { + for (int i = 0; i < 10; i++) { + Session s[8]; + for (int j = 0; j < 8; j++) { + ASSERT_NO_FATAL_FAILURE(s[j].open()); request_counter++; - s[i].GenerateNonce(&error_counter); + s[j].GenerateNonce(&error_counter); } } - time_t test_end = time(NULL); + time_t test_end = time(nullptr); int valid_counter = request_counter - error_counter; // Either oemcrypto should enforce a delay, or it should return an error from // GenerateNonce -- in either case the number of valid nonces is rate @@ -437,7 +436,9 @@ TEST_F(OEMCryptoClientTest, PreventNonceFlood3API09) { EXPECT_LE(valid_counter, 20 * (test_end - test_start + 2)); error_counter = 0; sleep(2); // After a pause, we should be able to regenerate nonces. - s[0].GenerateNonce(&error_counter); + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + s.GenerateNonce(&error_counter); EXPECT_EQ(0, error_counter); } @@ -461,13 +462,13 @@ TEST_F(OEMCryptoClientTest, ClearCopyTestAPI10) { ASSERT_EQ(input_buffer, output_buffer); ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, OEMCrypto_CopyBuffer( - s.session_id(), NULL, input_buffer.size(), &dest_buffer, + s.session_id(), nullptr, input_buffer.size(), &dest_buffer, OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample)); ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, OEMCrypto_CopyBuffer( - s.session_id(), input_buffer.data(), input_buffer.size(), NULL, - OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample)); - dest_buffer.buffer.clear.address = NULL; + s.session_id(), input_buffer.data(), input_buffer.size(), + nullptr, OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample)); + dest_buffer.buffer.clear.address = nullptr; ASSERT_EQ( OEMCrypto_ERROR_INVALID_CONTEXT, OEMCrypto_CopyBuffer(s.session_id(), input_buffer.data(), @@ -539,22 +540,21 @@ TEST_F(OEMCryptoKeyboxTest, NormalGetDeviceId) { TEST_F(OEMCryptoKeyboxTest, GetDeviceIdShortBuffer) { OEMCryptoResult sts; uint8_t dev_id[128]; - uint32_t req_len = 0; for (int i = 0; i < 128; ++i) { dev_id[i] = 0x55; } dev_id[127] = '\0'; - size_t dev_id_len = req_len; + size_t dev_id_len = 0; sts = OEMCrypto_GetDeviceID(dev_id, &dev_id_len); ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts); // On short buffer error, function should return minimum buffer length - ASSERT_TRUE(dev_id_len > req_len); + ASSERT_GT(dev_id_len, 0u); // Should also return short buffer if passed a zero length and a null buffer. - dev_id_len = req_len; - sts = OEMCrypto_GetDeviceID(NULL, &dev_id_len); + dev_id_len = 0; + sts = OEMCrypto_GetDeviceID(nullptr, &dev_id_len); ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts); // On short buffer error, function should return minimum buffer length - ASSERT_TRUE(dev_id_len > req_len); + ASSERT_GT(dev_id_len, 0u); } TEST_F(OEMCryptoKeyboxTest, NormalGetKeyData) { @@ -572,7 +572,7 @@ TEST_F(OEMCryptoKeyboxTest, NormalGetKeyData) { TEST_F(OEMCryptoKeyboxTest, GetKeyDataNullPointer) { OEMCryptoResult sts; uint8_t key_data[256]; - sts = OEMCrypto_GetKeyData(key_data, NULL); + sts = OEMCrypto_GetKeyData(key_data, nullptr); ASSERT_NE(OEMCrypto_SUCCESS, sts); } @@ -727,6 +727,37 @@ TEST_F(OEMCryptoProv30Test, OEMCertSignatureLargeBuffer) { data, signature.data(), signature_length, kSign_RSASSA_PSS)); } +// Calling OEMCrypto_GetOEMPublicCertificate should not change the session's +// private key. +TEST_F(OEMCryptoProv30Test, GetCertOnlyAPI16) { + if (wrapped_rsa_key_.size() == 0) { + // If we don't have a wrapped key yet, create one. + // This wrapped key will be shared by all sessions in the test. + ASSERT_NO_FATAL_FAILURE(CreateWrappedRSAKey(kSign_RSASSA_PSS, true)); + } + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + // Install the DRM Cert's RSA key. + ASSERT_NO_FATAL_FAILURE(s.InstallRSASessionTestKey(wrapped_rsa_key_)); + // Request the OEM Cert. -- This should NOT load the OEM Private key. + vector public_cert; + size_t public_cert_length = 0; + ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, + OEMCrypto_GetOEMPublicCertificate(nullptr, &public_cert_length)); + ASSERT_LT(0u, public_cert_length); + public_cert.resize(public_cert_length); + ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_GetOEMPublicCertificate( + public_cert.data(), &public_cert_length)); + // Derive keys from the session key -- this should use the DRM Cert's key. It + // should NOT use the OEM Private key because that key should not have been + // loaded. + ASSERT_NO_FATAL_FAILURE(s.GenerateDerivedKeysFromSessionKey()); + // Now fill a message and try to load it. + ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(kDuration, 0, 0)); + ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys()); +} + // // AddKey Tests // @@ -747,7 +778,7 @@ class OEMCryptoSessionTests : public OEMCryptoClientTest { void CreateUsageTableHeader(bool expect_success = true) { size_t header_buffer_length = 0; OEMCryptoResult sts = - OEMCrypto_CreateUsageTableHeader(NULL, &header_buffer_length); + OEMCrypto_CreateUsageTableHeader(nullptr, &header_buffer_length); if (expect_success) { ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts); } else { @@ -827,6 +858,8 @@ TEST_F(OEMCryptoSessionTestKeyboxTest, BadDataForceKeybox) { ASSERT_EQ(OEMCrypto_ERROR_BAD_CRC, sts); } +// TODO(b/140764295) -- replace this test with a provisioning test. +#if 0 // Verify that keys can be derived from the test keybox, and then those derived // keys can be used to sign a message. TEST_F(OEMCryptoSessionTestKeyboxTest, GenerateSignature) { @@ -842,14 +875,19 @@ TEST_F(OEMCryptoSessionTestKeyboxTest, GenerateSignature) { "b76a3a85f046523e10011a09393837363534333231180120002a0c3138383637" "38373430350000"); - static const uint32_t SignatureBufferMaxLength = 256; + static const uint32_t SignatureBufferMaxLength = SHA256_DIGEST_LENGTH; vector signature(SignatureBufferMaxLength); size_t signature_length = signature.size(); + // 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; + OEMCryptoResult sts; - sts = OEMCrypto_GenerateSignature(s.session_id(), context.data(), - context.size(), signature.data(), - &signature_length); + sts = OEMCrypto_SignProvisioningRequest( + s.session_id(), context.data(), context.size(), nullptr, + &core_message_length, signature.data(), &signature_length); ASSERT_EQ(OEMCrypto_SUCCESS, sts); static const uint32_t SignatureExpectedLength = 32; @@ -857,16 +895,17 @@ TEST_F(OEMCryptoSessionTestKeyboxTest, GenerateSignature) { signature.resize(signature_length); std::vector expected_signature; - s.ClientSignMessage(context, &expected_signature); + s.ClientSignBuffer(context, &expected_signature); ASSERT_EQ(expected_signature, signature); } +#endif // Verify that a license may be loaded without a nonce. TEST_F(OEMCryptoSessionTests, LoadKeyNoNonce) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(kDuration, 0, 42)); + ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(kDuration, 0, 0)); ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys()); } @@ -876,7 +915,7 @@ TEST_F(OEMCryptoSessionTests, LoadKeyNoNonceTwice) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(kDuration, 0, 42)); + ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(kDuration, 0, 0)); ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys()); ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); @@ -894,8 +933,9 @@ TEST_F(OEMCryptoSessionTests, LoadKeyWithNonce) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); ASSERT_NO_FATAL_FAILURE( - s.FillSimpleMessage(0, wvoec::kControlNonceEnabled, s.get_nonce())); + s.FillSimpleMessage(0, wvoec::kControlNonceEnabled, s.nonce())); ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys()); } @@ -905,8 +945,9 @@ TEST_F(OEMCryptoSessionTests, LoadKeyWithNonceTwice) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); ASSERT_NO_FATAL_FAILURE( - s.FillSimpleMessage(0, wvoec::kControlNonceEnabled, s.get_nonce())); + s.FillSimpleMessage(0, wvoec::kControlNonceEnabled, s.nonce())); ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys()); ASSERT_NE( @@ -918,23 +959,9 @@ TEST_F(OEMCryptoSessionTests, LoadKeyWithNonceTwice) { GetSubstring(), OEMCrypto_ContentLicense)); } -// This asks for several nonce. This simulates several license requests being -// lost. OEMCrypto is required to keep up to four nonce in the nonce table. -TEST_F(OEMCryptoSessionTests, LoadKeySeveralNonce) { - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); - uint32_t first_nonce = - s.get_nonce(); // Nonce generated when installing keys. - s.GenerateNonce(); // two. - s.GenerateNonce(); // three. - s.GenerateNonce(); // four. - ASSERT_NO_FATAL_FAILURE( - s.FillSimpleMessage(0, wvoec::kControlNonceEnabled, first_nonce)); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys()); -} - +// TODO(b/140764295): this test should be replaced something that generates a +// renewal request. +#if 0 // A license might update the mac keys and it might not. This tests that // OEMCrypto keeps the old mac keys if the license does not update them. TEST_F(OEMCryptoSessionTests, LoadKeyWithNoMAC) { @@ -952,14 +979,19 @@ TEST_F(OEMCryptoSessionTests, LoadKeyWithNoMAC) { "b76a3a85f046523e10011a09393837363534333231180120002a0c3138383637" "38373430350000"); - static const uint32_t SignatureBufferMaxLength = 256; + static const uint32_t SignatureBufferMaxLength = SHA256_DIGEST_LENGTH; vector signature(SignatureBufferMaxLength); size_t signature_length = signature.size(); + // TODO(http://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; + OEMCryptoResult sts; - sts = OEMCrypto_GenerateSignature(s.session_id(), context.data(), - context.size(), signature.data(), - &signature_length); + sts = OEMCrypto_SignLicenseRequest( + s.session_id(), context.data(), context.size(), nullptr, + &core_message_length, signature.data(), &signature_length); ASSERT_EQ(OEMCrypto_SUCCESS, sts); @@ -968,9 +1000,10 @@ TEST_F(OEMCryptoSessionTests, LoadKeyWithNoMAC) { signature.resize(signature_length); std::vector expected_signature; - s.ClientSignMessage(context, &expected_signature); + s.ClientSignBuffer(context, &expected_signature); ASSERT_EQ(expected_signature, signature); } +#endif // This verifies that entitlement keys and entitled content keys can be loaded. TEST_F(OEMCryptoSessionTests, LoadEntitlementKeysAPI14) { @@ -1017,12 +1050,16 @@ TEST_F(OEMCryptoSessionTests, LoadEntitlementKeysWrongEntitlementKeysAPI14) { s.LoadEntitledContentKeys(OEMCrypto_KEY_NOT_ENTITLED); } +// TODO(b/140764295): Replace by sign request for each request type. +#if 0 // This tests GenerateSignature with an 8k licnese request. TEST_F(OEMCryptoSessionTests, ClientSignatureLargeBuffer) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(0, 0, 0)); + ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); + ASSERT_NO_FATAL_FAILURE( + s.FillSimpleMessage(0, wvoec::kControlNonceEnabled, s.nonce())); ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys("", false)); @@ -1030,14 +1067,19 @@ TEST_F(OEMCryptoSessionTests, ClientSignatureLargeBuffer) { for (size_t i = 0; i < kMaxMessageSize; i++) { context[i] = i % 0x100; } - static const uint32_t SignatureBufferMaxLength = 256; + static const uint32_t SignatureBufferMaxLength = SHA256_DIGEST_LENGTH; vector signature(SignatureBufferMaxLength); size_t signature_length = signature.size(); + // TODO(http://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; + OEMCryptoResult sts; - sts = OEMCrypto_GenerateSignature(s.session_id(), context.data(), - context.size(), signature.data(), - &signature_length); + sts = OEMCrypto_SignLicenseRequest( + s.session_id(), context.data(), context.size(), nullptr, + &core_message_length, signature.data(), &signature_length); ASSERT_EQ(OEMCrypto_SUCCESS, sts); static const uint32_t SignatureExpectedLength = 32; @@ -1045,17 +1087,21 @@ TEST_F(OEMCryptoSessionTests, ClientSignatureLargeBuffer) { signature.resize(signature_length); std::vector expected_signature; - s.ClientSignMessage(context, &expected_signature); + s.ClientSignBuffer(context, &expected_signature); ASSERT_EQ(expected_signature, signature); } +#endif + // This tests LoadKeys with an 8k license response. TEST_F(OEMCryptoSessionTests, LoadKeyLargeBuffer) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); s.set_message_size(kMaxMessageSize); - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(0, 0, 0)); + ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); + ASSERT_NO_FATAL_FAILURE( + s.FillSimpleMessage(0, wvoec::kControlNonceEnabled, s.nonce())); ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys()); } @@ -1272,7 +1318,7 @@ TEST_F(OEMCryptoSessionTests, LoadKeyWithNullKeyControlIv) { ASSERT_NE(OEMCrypto_SUCCESS, sts); } -// Verify that LoadKeys fails when a key's nonce is not in the table. +// Verify that LoadKeys fails when a key's nonce is not in the session. TEST_F(OEMCryptoSessionTests, LoadKeyWithBadNonce) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); @@ -1294,7 +1340,8 @@ TEST_F(OEMCryptoSessionTests, LoadKeyWithRepeatNonce) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); - uint32_t nonce = s.get_nonce(); + ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); + uint32_t nonce = s.nonce(); ASSERT_NO_FATAL_FAILURE( s.FillSimpleMessage(0, wvoec::kControlNonceEnabled, nonce)); ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); @@ -1320,19 +1367,20 @@ TEST_F(OEMCryptoSessionTests, LoadKeyWithRepeatNonce) { // This tests that a nonce cannot be used in new session. This is similar to // the previous test, but does not use the nonce in the first session. The nonce -// table should be tied to a session, so generating a nonce in the first session -// and then using it in the second session should fail. +// should be tied to a session, so generating a nonce in the first session and +// then using it in the second session should fail. TEST_F(OEMCryptoSessionTests, LoadKeyNonceReopenSession) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); - uint32_t nonce = s.get_nonce(); + ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); + uint32_t nonce = s.nonce(); // Do not use the nonce now. Close session and use it after re-opening. ASSERT_NO_FATAL_FAILURE(s.close()); // Actually, this isn't the same session. OEMCrypto opens a new session, but // we are guarding against the possiblity that it re-uses the session data - // and might not clear out the nonce table correctly. + // and might not clear out the nonce correctly. ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(0, wvoec::kControlNonceEnabled, @@ -1353,14 +1401,16 @@ TEST_F(OEMCryptoSessionTests, LoadKeyNonceWrongSession) { Session s1; ASSERT_NO_FATAL_FAILURE(s1.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s1)); - uint32_t nonce = s1.get_nonce(); + ASSERT_NO_FATAL_FAILURE(s1.GenerateNonce()); + uint32_t nonce = s1.nonce(); // Do not use the nonce. Also, leave the session open. We want to make sure - // that s and s1 do NOT share a nonce table. This is different from the + // that s and s1 do NOT share a nonce. This is different from the // LoadKeyNonceReopenSession in that we do not close s1. Session s2; ASSERT_NO_FATAL_FAILURE(s2.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s2)); + ASSERT_NO_FATAL_FAILURE(s2.GenerateNonce()); ASSERT_NO_FATAL_FAILURE(s2.FillSimpleMessage(0, wvoec::kControlNonceEnabled, nonce)); // nonce from session s1 ASSERT_NO_FATAL_FAILURE(s2.EncryptAndSign()); @@ -1396,8 +1446,9 @@ TEST_F(OEMCryptoSessionTests, LoadKeyUnalignedMessage) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( - kDuration, wvoec::kControlNonceEnabled, s.get_nonce())); + ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); + ASSERT_NO_FATAL_FAILURE( + s.FillSimpleMessage(kDuration, wvoec::kControlNonceEnabled, s.nonce())); ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); std::vector buffer(1, '0'); // A string of 1 byte long. size_t offset = buffer.size(); @@ -1419,8 +1470,9 @@ TEST_F(OEMCryptoSessionTests, LoadKeyUnalignedMessage) { // This tests each key control block verification string in the range kc09-kc1?. // This test is parameterized by the API number in the key control lock. -class SessionTestAlternateVerification : public OEMCryptoSessionTests, - public WithParamInterface { +class OEMCryptoSessionTestAlternateVerification + : public OEMCryptoSessionTests, + public WithParamInterface { public: void SetUp() override { OEMCryptoSessionTests::SetUp(); @@ -1431,7 +1483,7 @@ class SessionTestAlternateVerification : public OEMCryptoSessionTests, uint32_t target_api_; }; -TEST_P(SessionTestAlternateVerification, LoadKeys) { +TEST_P(OEMCryptoSessionTestAlternateVerification, LoadKeys) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); @@ -1463,7 +1515,7 @@ TEST_P(SessionTestAlternateVerification, LoadKeys) { // Range of API versions to test. This should start at 8, and go to // the current API + 2. We use +2 because we want to test at least 1 // future API, and the ::testing::Range is not inclusive. -INSTANTIATE_TEST_CASE_P(TestAll, SessionTestAlternateVerification, +INSTANTIATE_TEST_CASE_P(TestAll, OEMCryptoSessionTestAlternateVerification, Range(8, 15 + 2)); TEST_F(OEMCryptoSessionTests, LoadKeysBadSignature) { @@ -1501,7 +1553,7 @@ TEST_F(OEMCryptoSessionTests, LoadKeyNoKeys) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(kDuration, 0, 42)); + ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(kDuration, 0, 0)); ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); int kNoKeys = 0; ASSERT_NE(OEMCrypto_SUCCESS, @@ -1517,8 +1569,9 @@ TEST_F(OEMCryptoSessionTests, LoadKeyNoKeyWithNonce) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); ASSERT_NO_FATAL_FAILURE( - s.FillSimpleMessage(0, wvoec::kControlNonceEnabled, s.get_nonce())); + s.FillSimpleMessage(0, wvoec::kControlNonceEnabled, s.nonce())); ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); int kNoKeys = 0; ASSERT_NE(OEMCrypto_SUCCESS, @@ -1535,15 +1588,42 @@ TEST_F(OEMCryptoSessionTests, SelectKeyNotThereAPI15) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); ASSERT_NO_FATAL_FAILURE( - s.FillSimpleMessage(0, wvoec::kControlNonceEnabled, s.get_nonce())); + s.FillSimpleMessage(0, wvoec::kControlNonceEnabled, s.nonce())); ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys()); const char* key_id = "no_key"; - ASSERT_EQ(OEMCrypto_ERROR_NO_CONTENT_KEY, - OEMCrypto_SelectKey( - s.session_id(), reinterpret_cast(key_id), - strlen(key_id), OEMCrypto_CipherMode_CTR)); + OEMCryptoResult sts = OEMCrypto_SelectKey( + s.session_id(), reinterpret_cast(key_id), strlen(key_id), + OEMCrypto_CipherMode_CTR); + if (sts != OEMCrypto_SUCCESS) { + EXPECT_EQ(OEMCrypto_ERROR_NO_CONTENT_KEY, sts); + } else { + // Delayed error code. If select key was a success, then we should + // eventually see the error when we decrypt. + vector in_buffer(256); + for (size_t i = 0; i < in_buffer.size(); i++) in_buffer[i] = i % 256; + vector encryptionIv(AES_BLOCK_SIZE); + EXPECT_EQ(1, GetRandBytes(encryptionIv.data(), AES_BLOCK_SIZE)); + // Describe the output + vector out_buffer(in_buffer.size()); + const bool is_encrypted = true; + OEMCrypto_DestBufferDesc destBuffer; + destBuffer.type = OEMCrypto_BufferType_Clear; + destBuffer.buffer.clear.address = out_buffer.data(); + destBuffer.buffer.clear.max_length = out_buffer.size(); + OEMCrypto_CENCEncryptPatternDesc pattern; + pattern.encrypt = 0; + pattern.skip = 0; + pattern.offset = 0; + // Decrypt the data + sts = OEMCrypto_DecryptCENC( + s.session_id(), in_buffer.data(), in_buffer.size(), is_encrypted, + encryptionIv.data(), 0, &destBuffer, &pattern, + OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample); + EXPECT_EQ(OEMCrypto_ERROR_NO_CONTENT_KEY, sts); + } } // After loading keys, we should be able to query the key control block. If we @@ -1553,8 +1633,9 @@ TEST_F(OEMCryptoSessionTests, QueryKeyControl) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); ASSERT_NO_FATAL_FAILURE( - s.FillSimpleMessage(0, wvoec::kControlNonceEnabled, s.get_nonce())); + s.FillSimpleMessage(0, wvoec::kControlNonceEnabled, s.nonce())); ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys()); // Note: successful cases are tested in VerifyTestKeys. @@ -1674,8 +1755,8 @@ TEST_F(OEMCryptoSessionTests, MinimumKeysAPI12) { // Used to test the different HDCP versions. This test is parameterized by the // required HDCP version in the key control block. -class SessionTestDecryptWithHDCP : public OEMCryptoSessionTests, - public WithParamInterface { +class OEMCryptoSessionTestDecryptWithHDCP : public OEMCryptoSessionTests, + public WithParamInterface { public: void DecryptWithHDCP(OEMCrypto_HDCP_Capability version) { OEMCryptoResult sts; @@ -1702,11 +1783,12 @@ class SessionTestDecryptWithHDCP : public OEMCryptoSessionTests, } }; -TEST_P(SessionTestDecryptWithHDCP, DecryptAPI09) { +TEST_P(OEMCryptoSessionTestDecryptWithHDCP, DecryptAPI09) { // Test parameterized by HDCP version. DecryptWithHDCP(static_cast(GetParam())); } -INSTANTIATE_TEST_CASE_P(TestHDCP, SessionTestDecryptWithHDCP, Range(1, 6)); +INSTANTIATE_TEST_CASE_P(TestHDCP, OEMCryptoSessionTestDecryptWithHDCP, + Range(1, 6)); // // Load, Refresh Keys Test @@ -1716,7 +1798,7 @@ INSTANTIATE_TEST_CASE_P(TestHDCP, SessionTestDecryptWithHDCP, Range(1, 6)); // the license. // 2. The number of keys refreshed in the refresh method. If the number of keys // is zero, then all of the keys should be refreshed. -class SessionTestRefreshKeyTest +class OEMCryptoSessionTestRefreshKeyTestAPI16 : public OEMCryptoSessionTests, public WithParamInterface > { public: @@ -1732,25 +1814,24 @@ class SessionTestRefreshKeyTest size_t num_keys_; // Number of keys to refresh. }; -// Refresh keys should work if the license does not use a nonce. -TEST_P(SessionTestRefreshKeyTest, RefreshWithNonce) { +// Refresh keys should work if the license uses a nonce. +TEST_P(OEMCryptoSessionTestRefreshKeyTestAPI16, RefreshWithNonce) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( - kDuration, wvoec::kControlNonceEnabled, s.get_nonce())); + ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); + ASSERT_NO_FATAL_FAILURE( + s.FillSimpleMessage(0, wvoec::kControlNonceEnabled, s.nonce())); ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys("", new_mac_keys_)); - s.GenerateNonce(); // License renewal message is signed by client and verified by the server. - ASSERT_NO_FATAL_FAILURE(s.VerifyClientSignature()); - ASSERT_NO_FATAL_FAILURE(s.RefreshTestKeys(num_keys_, - wvoec::kControlNonceEnabled, - s.get_nonce(), OEMCrypto_SUCCESS)); + ASSERT_NO_FATAL_FAILURE(s.VerifyRenewalRequestSignature()); + ASSERT_NO_FATAL_FAILURE(s.RefreshTestKeys( + num_keys_, wvoec::kControlNonceEnabled, s.nonce(), OEMCrypto_SUCCESS)); } -// Refresh keys should work if the license does use a nonce. -TEST_P(SessionTestRefreshKeyTest, RefreshNoNonce) { +// Refresh keys should work if the license does not use a nonce. +TEST_P(OEMCryptoSessionTestRefreshKeyTestAPI16, RefreshNoNonce) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); @@ -1758,98 +1839,82 @@ TEST_P(SessionTestRefreshKeyTest, RefreshNoNonce) { ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys("", new_mac_keys_)); // License renewal message is signed by client and verified by the server. - ASSERT_NO_FATAL_FAILURE(s.VerifyClientSignature()); - ASSERT_NO_FATAL_FAILURE( - s.RefreshTestKeys(num_keys_, 0, 0, OEMCrypto_SUCCESS)); + ASSERT_NO_FATAL_FAILURE(s.VerifyRenewalRequestSignature()); + ASSERT_NO_FATAL_FAILURE(s.RefreshTestKeys( + num_keys_, wvoec::kControlNonceEnabled, 0, OEMCrypto_SUCCESS)); } -// Refresh keys should fail if the nonce has already been used. -TEST_P(SessionTestRefreshKeyTest, RefreshOldNonceAPI11) { +// TODO(b/136281032): Nonce should be same as one in license. +// Refresh keys should fail if the nonce is not in the session. +TEST_P(OEMCryptoSessionTestRefreshKeyTestAPI16, RefreshBadNonceAPI11) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); - uint32_t nonce = s.get_nonce(); + ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); ASSERT_NO_FATAL_FAILURE( - s.FillSimpleMessage(kDuration, wvoec::kControlNonceEnabled, nonce)); + s.FillSimpleMessage(kDuration, wvoec::kControlNonceEnabled, s.nonce())); ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys("", new_mac_keys_)); // License renewal message is signed by client and verified by the server. - ASSERT_NO_FATAL_FAILURE(s.VerifyClientSignature()); - // Tryinng to reuse the same nonce. - ASSERT_NO_FATAL_FAILURE( - s.RefreshTestKeys(num_keys_, wvoec::kControlNonceEnabled, nonce, - OEMCrypto_ERROR_INVALID_NONCE)); -} - -// Refresh keys should fail if the nonce is not in the table. -TEST_P(SessionTestRefreshKeyTest, RefreshBadNonceAPI11) { - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( - kDuration, wvoec::kControlNonceEnabled, s.get_nonce())); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys("", new_mac_keys_)); - s.GenerateNonce(); - // License renewal message is signed by client and verified by the server. - ASSERT_NO_FATAL_FAILURE(s.VerifyClientSignature()); - uint32_t nonce = s.get_nonce() ^ 42; + ASSERT_NO_FATAL_FAILURE(s.VerifyRenewalRequestSignature()); + uint32_t nonce = s.nonce() ^ 42; ASSERT_NO_FATAL_FAILURE( s.RefreshTestKeys(num_keys_, wvoec::kControlNonceEnabled, nonce, OEMCrypto_ERROR_INVALID_NONCE)); } // Refresh keys should handle the maximum message size. -TEST_P(SessionTestRefreshKeyTest, RefreshLargeBuffer) { +TEST_P(OEMCryptoSessionTestRefreshKeyTestAPI16, RefreshLargeBuffer) { Session s; s.set_message_size(kMaxMessageSize); ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( - kDuration, wvoec::kControlNonceEnabled, s.get_nonce())); + ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); + ASSERT_NO_FATAL_FAILURE( + s.FillSimpleMessage(kDuration, wvoec::kControlNonceEnabled, s.nonce())); ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys("", new_mac_keys_)); - s.GenerateNonce(); // License renewal message is signed by client and verified by the server. // This uses a large buffer for the renewal message. - ASSERT_NO_FATAL_FAILURE(s.VerifyClientSignature(kMaxMessageSize)); - ASSERT_NO_FATAL_FAILURE(s.RefreshTestKeys(num_keys_, - wvoec::kControlNonceEnabled, - s.get_nonce(), OEMCrypto_SUCCESS)); + ASSERT_NO_FATAL_FAILURE(s.VerifyRenewalRequestSignature(kMaxMessageSize)); + ASSERT_NO_FATAL_FAILURE(s.RefreshTestKeys( + num_keys_, wvoec::kControlNonceEnabled, s.nonce(), OEMCrypto_SUCCESS)); } +// TODO(b/140764295): Replace with proper signing tests. +#if 0 // This situation would occur if an app only uses one key in the license. When // that happens, SelectKey would be called before the first decrypt, and then // would not need to be called again, even if the license is refreshed. -TEST_P(SessionTestRefreshKeyTest, RefreshWithNoSelectKey) { +TEST_P(OEMCryptoSessionTestRefreshKeyTestAPI16, RefreshWithNoSelectKey) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( - kDuration, wvoec::kControlNonceEnabled, s.get_nonce())); + ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); + ASSERT_NO_FATAL_FAILURE( + s.FillSimpleMessage(kDuration, wvoec::kControlNonceEnabled, s.nonce())); ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys("", new_mac_keys_)); // Call select key before the refresh. No calls below to TestDecryptCTR with // select key set to true. ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR(true)); - s.GenerateNonce(); // License renewal message is signed by client and verified by the server. - ASSERT_NO_FATAL_FAILURE(s.VerifyClientSignature()); + ASSERT_NO_FATAL_FAILURE(s.VerifyRenewalRequestSignature()); // Note: we store the message in encrypted_license_, but the refresh key // message is not actually encrypted. It is, however, signed. // FillRefreshMessage fills the message with a duration of kLongDuration. - ASSERT_NO_FATAL_FAILURE(s.FillRefreshMessage( - num_keys_, wvoec::kControlNonceEnabled, s.get_nonce())); + ASSERT_NO_FATAL_FAILURE( + s.FillRefreshMessage(num_keys_, wvoec::kControlNonceEnabled, s.nonce())); s.ServerSignBuffer(reinterpret_cast(&s.encrypted_license()), s.message_size(), &s.signature()); std::vector key_array(num_keys_); s.FillRefreshArray(key_array.data(), num_keys_); - ASSERT_EQ(OEMCrypto_SUCCESS, - OEMCrypto_RefreshKeys(s.session_id(), s.message_ptr(), - s.message_size(), s.signature().data(), - s.signature().size(), num_keys_, - key_array.data())); + ASSERT_EQ( + OEMCrypto_SUCCESS, + OEMCrypto_RefreshKeys(s.session_id(), s.message_ptr(), s.message_size(), + s.signature().data(), s.signature().size(), + num_keys_, key_array.data())); ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR(false)); // This should still be valid key, even if the refresh failed, because this // is before the original license duration. @@ -1861,14 +1926,17 @@ TEST_P(SessionTestRefreshKeyTest, RefreshWithNoSelectKey) { sleep(kShortSleep + kLongSleep); ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR(false)); } +#endif // If only one key control block in the refesh, we update all the keys. -INSTANTIATE_TEST_CASE_P(TestRefreshAllKeys, SessionTestRefreshKeyTest, +INSTANTIATE_TEST_CASE_P(TestRefreshAllKeys, + OEMCryptoSessionTestRefreshKeyTestAPI16, Values(std::make_pair(true, 1), std::make_pair(false, 1))); // If multiple key control blocks, we update each key separately. -INSTANTIATE_TEST_CASE_P(TestRefreshEachKeys, SessionTestRefreshKeyTest, +INSTANTIATE_TEST_CASE_P(TestRefreshEachKeys, + OEMCryptoSessionTestRefreshKeyTestAPI16, Values(std::make_pair(true, 4), std::make_pair(false, 4))); @@ -1930,42 +1998,9 @@ TEST_F(OEMCryptoSessionTests, SimultaneousDecrypt) { ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s[i])); } for (int i = 0; i < 8; i++) { + ASSERT_NO_FATAL_FAILURE(s[i].GenerateNonce()); ASSERT_NO_FATAL_FAILURE( - s[i].FillSimpleMessage(kLongDuration, 0, s[i].get_nonce())); - ASSERT_NO_FATAL_FAILURE(s[i].EncryptAndSign()); - } - for (int i = 0; i < 8; i++) { - ASSERT_NO_FATAL_FAILURE(s[i].LoadTestKeys()); - } - for (int i = 0; i < 8; i++) { - ASSERT_NO_FATAL_FAILURE(s[i].TestDecryptCTR()); - } - // Second call to decrypt for each session. - for (int i = 0; i < 8; i++) { - ASSERT_NO_FATAL_FAILURE(s[i].TestDecryptCTR()); - } -} - -// This test generates several test keys, as if a license request was lost. -// This is only valid for (obsolete) devices that use a keybox to talk to a -// license server. -TEST_F(OEMCryptoSessionTests, SimultaneousDecryptWithLostMessageKeyboxTest) { - vector s(8); - for (int i = 0; i < 8; i++) { - ASSERT_NO_FATAL_FAILURE(s[i].open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s[i])); - } - for (int i = 0; i < 8; i++) { - ASSERT_NO_FATAL_FAILURE(s[i].GenerateDerivedKeysFromKeybox(keybox_)); - ASSERT_NO_FATAL_FAILURE( - s[i].FillSimpleMessage(kLongDuration, 0, s[i].get_nonce())); - ASSERT_NO_FATAL_FAILURE(s[i].EncryptAndSign()); - } - // First set of messages are lost. Generate second set. - for (int i = 0; i < 8; i++) { - ASSERT_NO_FATAL_FAILURE(s[i].GenerateDerivedKeysFromKeybox(keybox_)); - ASSERT_NO_FATAL_FAILURE( - s[i].FillSimpleMessage(kLongDuration, 0, s[i].get_nonce())); + s[i].FillSimpleMessage(kLongDuration, 0, s[i].nonce())); ASSERT_NO_FATAL_FAILURE(s[i].EncryptAndSign()); } for (int i = 0; i < 8; i++) { @@ -2001,14 +2036,26 @@ struct SampleInitData { // buffers to be the same. class OEMCryptoSessionTestsDecryptTests : public OEMCryptoSessionTests, - public WithParamInterface > { + public WithParamInterface< + tuple > { protected: void SetUp() override { OEMCryptoSessionTests::SetUp(); pattern_ = ::testing::get<0>(GetParam()); cipher_mode_ = ::testing::get<1>(GetParam()); decrypt_inplace_ = ::testing::get<2>(GetParam()); + verify_crc_ = global_features.supports_crc; + // Pick a random key. + EXPECT_EQ(1, GetRandBytes(key_, AES_BLOCK_SIZE)); + // Pick a random starting iv. Some tests override this before using it. + starting_iv_.resize(AES_BLOCK_SIZE); + EXPECT_EQ(1, GetRandBytes(starting_iv_.data(), starting_iv_.size())); + total_size_ = -1; + } + + void TearDown() override { + ASSERT_NO_FATAL_FAILURE(session_.close()); + OEMCryptoSessionTests::TearDown(); } void FindTotalSize() { @@ -2019,24 +2066,47 @@ class OEMCryptoSessionTestsDecryptTests } } - void EncryptData(const vector& key, - const vector& starting_iv, - const vector& in_buffer, - vector* out_buffer) { + // Set up the input buffer and output buffer. + // This should be called after FindTotalSize(). + void MakeBuffers() { + ASSERT_GT(total_size_, 0u); + encrypted_buffer_.resize(total_size_); + truth_buffer_.resize(total_size_); + for (size_t i = 0; i < total_size_; i++) truth_buffer_[i] = i % 256; + output_descriptor_.type = OEMCrypto_BufferType_Clear; + if (decrypt_inplace_) { + output_descriptor_.buffer.clear.address = encrypted_buffer_.data(); + } else { + // Add some padding to verify there is no overrun. + clear_buffer_.resize(total_size_ + 16, 0xaa); + output_descriptor_.buffer.clear.address = clear_buffer_.data(); + } + output_descriptor_.buffer.clear.max_length = total_size_; + } + + void UpdateOutputOffset(size_t offset) { + if (decrypt_inplace_) { + output_descriptor_.buffer.clear.address = + encrypted_buffer_.data() + offset; + } else { + output_descriptor_.buffer.clear.address = clear_buffer_.data() + offset; + } + output_descriptor_.buffer.clear.max_length = total_size_ - offset; + } + + void EncryptData() { AES_KEY aes_key; - AES_set_encrypt_key(key.data(), AES_BLOCK_SIZE * 8, &aes_key); - out_buffer->resize(in_buffer.size()); + AES_set_encrypt_key(key_, AES_BLOCK_SIZE * 8, &aes_key); uint8_t iv[AES_BLOCK_SIZE]; // Current iv. - - memcpy(iv, starting_iv.data(), AES_BLOCK_SIZE); + memcpy(iv, starting_iv_.data(), AES_BLOCK_SIZE); size_t buffer_index = 0; // byte index into in and out. size_t block_offset = 0; // byte index into current block. for (size_t i = 0; i < subsample_size_.size(); i++) { // Copy clear content. if (subsample_size_[i].clear_size > 0) { - memcpy(&(*out_buffer)[buffer_index], &in_buffer[buffer_index], + memcpy(&encrypted_buffer_[buffer_index], &truth_buffer_[buffer_index], subsample_size_[i].clear_size); buffer_index += subsample_size_[i].clear_size; } @@ -2063,15 +2133,17 @@ class OEMCryptoSessionTestsDecryptTests // can put whatever we want in the output buffer. if (skip_block || ((cipher_mode_ == OEMCrypto_CipherMode_CBC) && (size < AES_BLOCK_SIZE))) { - memcpy(&(*out_buffer)[buffer_index], &in_buffer[buffer_index], size); + memcpy(&encrypted_buffer_[buffer_index], &truth_buffer_[buffer_index], + size); block_offset = 0; // Next block should be complete. } else { if (cipher_mode_ == OEMCrypto_CipherMode_CTR) { uint8_t aes_output[AES_BLOCK_SIZE]; AES_encrypt(iv, aes_output, &aes_key); for (size_t n = 0; n < size; n++) { - (*out_buffer)[buffer_index + n] = - aes_output[n + block_offset] ^ in_buffer[buffer_index + n]; + encrypted_buffer_[buffer_index + n] = + aes_output[n + block_offset] ^ + truth_buffer_[buffer_index + n]; } if (size + block_offset < AES_BLOCK_SIZE) { // Partial block. Don't increment iv. Compute next block offset. @@ -2086,10 +2158,10 @@ class OEMCryptoSessionTestsDecryptTests } else { uint8_t aes_input[AES_BLOCK_SIZE]; for (size_t n = 0; n < size; n++) { - aes_input[n] = in_buffer[buffer_index + n] ^ iv[n]; + aes_input[n] = truth_buffer_[buffer_index + n] ^ iv[n]; } - AES_encrypt(aes_input, &(*out_buffer)[buffer_index], &aes_key); - memcpy(iv, &(*out_buffer)[buffer_index], AES_BLOCK_SIZE); + AES_encrypt(aes_input, &encrypted_buffer_[buffer_index], &aes_key); + memcpy(iv, &encrypted_buffer_[buffer_index], AES_BLOCK_SIZE); // CBC mode should always start on block boundary. block_offset = 0; } @@ -2099,73 +2171,57 @@ class OEMCryptoSessionTestsDecryptTests } } - void TestDecryptCENC(const vector& key, - const vector& /* encryptionIv */, - const vector& encryptedData, - const vector& unencryptedData) { + void LoadLicense() { + // First we open a session and load a license. + ASSERT_NO_FATAL_FAILURE(session_.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&session_)); + uint32_t control = 0; + if (verify_crc_) control |= kControlAllowHashVerification; + ASSERT_NO_FATAL_FAILURE(session_.FillSimpleMessage(kDuration, control, 0)); + memcpy(session_.license().keys[0].key_data, key_, sizeof(key_)); + session_.license().keys[0].cipher_mode = cipher_mode_; + ASSERT_NO_FATAL_FAILURE(session_.EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(session_.LoadTestKeys()); + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_SelectKey( + session_.session_id(), session_.license().keys[0].key_id, + session_.license().keys[0].key_id_length, cipher_mode_)); + } + + void TestDecryptCENC() { OEMCryptoResult sts; - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); - ASSERT_NO_FATAL_FAILURE( - s.FillSimpleMessage(kDuration, kControlAllowHashVerification, 0)); - memcpy(s.license().keys[0].key_data, key.data(), key.size()); - s.license().keys[0].cipher_mode = cipher_mode_; - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys()); - if (global_features.supports_crc) { - uint32_t hash = - wvcrc32(unencryptedData.data(), unencryptedData.size()); + // If supported, initialize the decrypt hash. + if (verify_crc_) { + uint32_t hash = wvcrc32(truth_buffer_.data(), truth_buffer_.size()); ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_SetDecryptHash( - s.session_id(), 1, reinterpret_cast(&hash), - sizeof(hash))); - } - sts = OEMCrypto_SelectKey(s.session_id(), s.license().keys[0].key_id, - s.license().keys[0].key_id_length, - cipher_mode_); - ASSERT_EQ(OEMCrypto_SUCCESS, sts); - // We decrypt each subsample. - vector output_buffer(total_size_ + 16, 0xaa); - const uint8_t *input_buffer = NULL; - if (decrypt_inplace_) { // Use same buffer for input and output. - // Copy the useful data from encryptedData to output_buffer, which - // will be the same as input_buffer. Leave the 0xaa padding at the end. - for(size_t i=0; i < total_size_; i++) output_buffer[i] = encryptedData[i]; - // Now let input_buffer point to the same data. - input_buffer = output_buffer.data(); - } else { - input_buffer = encryptedData.data(); + session_.session_id(), 1, + reinterpret_cast(&hash), sizeof(hash))); } size_t buffer_offset = 0; for (size_t i = 0; i < subsample_size_.size(); i++) { OEMCrypto_CENCEncryptPatternDesc pattern = pattern_; pattern.offset = 0; // Final CENC spec says pattern offset always 0. bool is_encrypted = false; - OEMCrypto_DestBufferDesc destBuffer; size_t block_offset = 0; uint8_t subsample_flags = 0; if (subsample_size_[i].clear_size > 0) { - destBuffer.type = OEMCrypto_BufferType_Clear; - destBuffer.buffer.clear.address = &output_buffer[buffer_offset]; - destBuffer.buffer.clear.max_length = total_size_ - buffer_offset; + ASSERT_NO_FATAL_FAILURE(UpdateOutputOffset(buffer_offset)); if (i == 0) subsample_flags |= OEMCrypto_FirstSubsample; if ((i == subsample_size_.size() - 1) && (subsample_size_[i].encrypted_size == 0)) { subsample_flags |= OEMCrypto_LastSubsample; } - sts = - OEMCrypto_DecryptCENC(s.session_id(), input_buffer + buffer_offset, - subsample_size_[i].clear_size, is_encrypted, - sample_init_data_[i].iv, block_offset, - &destBuffer, &pattern, subsample_flags); + sts = OEMCrypto_DecryptCENC( + session_.session_id(), &encrypted_buffer_[buffer_offset], + subsample_size_[i].clear_size, is_encrypted, + sample_init_data_[i].iv, block_offset, &output_descriptor_, + &pattern, subsample_flags); ASSERT_EQ(OEMCrypto_SUCCESS, sts); buffer_offset += subsample_size_[i].clear_size; } if (subsample_size_[i].encrypted_size > 0) { - destBuffer.type = OEMCrypto_BufferType_Clear; - destBuffer.buffer.clear.address = &output_buffer[buffer_offset]; - destBuffer.buffer.clear.max_length = total_size_ - buffer_offset; + ASSERT_NO_FATAL_FAILURE(UpdateOutputOffset(buffer_offset)); is_encrypted = true; block_offset = sample_init_data_[i].block_offset; subsample_flags = 0; @@ -2176,10 +2232,10 @@ class OEMCryptoSessionTestsDecryptTests subsample_flags |= OEMCrypto_LastSubsample; } sts = OEMCrypto_DecryptCENC( - s.session_id(), input_buffer + buffer_offset, + session_.session_id(), &encrypted_buffer_[buffer_offset], subsample_size_[i].encrypted_size, is_encrypted, - sample_init_data_[i].iv, block_offset, &destBuffer, &pattern, - subsample_flags); + sample_init_data_[i].iv, block_offset, &output_descriptor_, + &pattern, subsample_flags); // CBC mode should not accept a block offset. if ((block_offset > 0) && (cipher_mode_ == OEMCrypto_CipherMode_CBC)) { ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, sts) @@ -2190,14 +2246,23 @@ class OEMCryptoSessionTestsDecryptTests buffer_offset += subsample_size_[i].encrypted_size; } } - EXPECT_EQ(0xaa, output_buffer[total_size_]) << "Buffer overrun."; - output_buffer.resize(total_size_); - EXPECT_EQ(unencryptedData, output_buffer); + if (output_descriptor_.type == OEMCrypto_BufferType_Clear) { + if (decrypt_inplace_) { + // We expect encrypted buffer to have been changed by OEMCrypto. + EXPECT_EQ(encrypted_buffer_, truth_buffer_); + } else { + // If we are not decrypting in place, then look at the one byte just + // after the data that was written. It should not have changed from the + // original 0xaa that we set in MakeBuffersession_. + EXPECT_EQ(0xaa, clear_buffer_[total_size_]) << "Buffer overrun."; + clear_buffer_.resize(total_size_); // Remove padding. + EXPECT_EQ(clear_buffer_, truth_buffer_); + } + } if (global_features.supports_crc) { uint32_t frame; ASSERT_EQ(OEMCrypto_SUCCESS, - OEMCrypto_GetHashErrorCode(s.session_id(), &frame)); - + OEMCrypto_GetHashErrorCode(session_.session_id(), &frame)); } } @@ -2206,7 +2271,17 @@ class OEMCryptoSessionTestsDecryptTests bool decrypt_inplace_; // If true, input and output buffers are the same. vector subsample_size_; size_t total_size_; + bool verify_crc_; vector sample_init_data_; + // Encrypted data -- this is input to OEMCrypto, and output from EncryptData. + std::vector encrypted_buffer_; + std::vector clear_buffer_; // OEMCrypto store clear output here. + void* secure_handle_; // OEMCrypto stores secure output here. + std::vector truth_buffer_; // Truth data for clear text. + OEMCrypto_DestBufferDesc output_descriptor_; + uint8_t key_[AES_BLOCK_SIZE]; // Encryption Key. + std::vector starting_iv_; // Starting IV. + Session session_; }; // Tests that generate partial ending blocks. These tests should not be used @@ -2220,15 +2295,10 @@ TEST_P(OEMCryptoSessionTestsDecryptTests, SingleLargeSubsample) { // full patterns if we have more than 320 -- round up to 400. subsample_size_.push_back(SampleSize(0, 400)); FindTotalSize(); - vector unencryptedData(total_size_); - vector encryptedData(total_size_); - vector encryptionIv(AES_BLOCK_SIZE); - vector key(AES_BLOCK_SIZE); - EXPECT_EQ(1, GetRandBytes(encryptionIv.data(), AES_BLOCK_SIZE)); - EXPECT_EQ(1, GetRandBytes(key.data(), AES_BLOCK_SIZE)); - for (size_t i = 0; i < total_size_; i++) unencryptedData[i] = i % 256; - EncryptData(key, encryptionIv, unencryptedData, &encryptedData); - TestDecryptCENC(key, encryptionIv, encryptedData, unencryptedData); + ASSERT_NO_FATAL_FAILURE(MakeBuffers()); + ASSERT_NO_FATAL_FAILURE(LoadLicense()); + ASSERT_NO_FATAL_FAILURE(EncryptData()); + ASSERT_NO_FATAL_FAILURE(TestDecryptCENC()); } // When the pattern length is 10 blocks, there is a discrepancy between the @@ -2238,30 +2308,20 @@ TEST_P(OEMCryptoSessionTestsDecryptTests, SingleLargeSubsample) { TEST_P(OEMCryptoSessionTestsDecryptTests, PatternPlusOneBlock) { subsample_size_.push_back(SampleSize(0, 160 + 16)); FindTotalSize(); - vector unencryptedData(total_size_); - vector encryptedData(total_size_); - vector encryptionIv(AES_BLOCK_SIZE); - vector key(AES_BLOCK_SIZE); - EXPECT_EQ(1, GetRandBytes(encryptionIv.data(), AES_BLOCK_SIZE)); - EXPECT_EQ(1, GetRandBytes(key.data(), AES_BLOCK_SIZE)); - for (size_t i = 0; i < total_size_; i++) unencryptedData[i] = i % 256; - EncryptData(key, encryptionIv, unencryptedData, &encryptedData); - TestDecryptCENC(key, encryptionIv, encryptedData, unencryptedData); + ASSERT_NO_FATAL_FAILURE(MakeBuffers()); + ASSERT_NO_FATAL_FAILURE(LoadLicense()); + ASSERT_NO_FATAL_FAILURE(EncryptData()); + ASSERT_NO_FATAL_FAILURE(TestDecryptCENC()); } // Test that a single block can be decrypted. TEST_P(OEMCryptoSessionTestsDecryptTests, OneBlock) { subsample_size_.push_back(SampleSize(0, 16)); FindTotalSize(); - vector unencryptedData(total_size_); - vector encryptedData(total_size_); - vector encryptionIv(AES_BLOCK_SIZE); - vector key(AES_BLOCK_SIZE); - EXPECT_EQ(1, GetRandBytes(encryptionIv.data(), AES_BLOCK_SIZE)); - EXPECT_EQ(1, GetRandBytes(key.data(), AES_BLOCK_SIZE)); - for (size_t i = 0; i < total_size_; i++) unencryptedData[i] = i % 256; - EncryptData(key, encryptionIv, unencryptedData, &encryptedData); - TestDecryptCENC(key, encryptionIv, encryptedData, unencryptedData); + ASSERT_NO_FATAL_FAILURE(MakeBuffers()); + ASSERT_NO_FATAL_FAILURE(LoadLicense()); + ASSERT_NO_FATAL_FAILURE(EncryptData()); + ASSERT_NO_FATAL_FAILURE(TestDecryptCENC()); } // This tests the ability to decrypt multiple subsamples with no offset. @@ -2272,41 +2332,36 @@ TEST_P(OEMCryptoSessionTestsDecryptTests, NoOffset) { subsample_size_.push_back(SampleSize(50, 256)); subsample_size_.push_back(SampleSize(25, 160)); FindTotalSize(); - vector unencryptedData(total_size_); - vector encryptedData(total_size_); - vector encryptionIv(AES_BLOCK_SIZE); - vector key(AES_BLOCK_SIZE); - EXPECT_EQ(1, GetRandBytes(encryptionIv.data(), AES_BLOCK_SIZE)); - EXPECT_EQ(1, GetRandBytes(key.data(), AES_BLOCK_SIZE)); - for (size_t i = 0; i < total_size_; i++) unencryptedData[i] = i % 256; - EncryptData(key, encryptionIv, unencryptedData, &encryptedData); - TestDecryptCENC(key, encryptionIv, encryptedData, unencryptedData); + ASSERT_NO_FATAL_FAILURE(MakeBuffers()); + ASSERT_NO_FATAL_FAILURE(LoadLicense()); + ASSERT_NO_FATAL_FAILURE(EncryptData()); + ASSERT_NO_FATAL_FAILURE(TestDecryptCENC()); } // This tests an offset into the block for the second encrypted subsample. // This should only work for CTR mode, for CBC mode an error is expected in // the decrypt step. -// If this test fails for CTR mode, then it is probably handleing the +// If this test fails for CTR mode, then it is probably handling the // block_offset incorrectly. TEST_P(OEMCryptoSessionTestsPartialBlockTests, EvenOffset) { subsample_size_.push_back(SampleSize(25, 8)); subsample_size_.push_back(SampleSize(25, 32)); subsample_size_.push_back(SampleSize(25, 50)); FindTotalSize(); - vector unencryptedData(total_size_); - vector encryptedData(total_size_); - vector encryptionIv(AES_BLOCK_SIZE); - vector key(AES_BLOCK_SIZE); - EXPECT_EQ(1, GetRandBytes(key.data(), AES_BLOCK_SIZE)); + ASSERT_NO_FATAL_FAILURE(MakeBuffers()); + ASSERT_NO_FATAL_FAILURE(LoadLicense()); // CTR Mode is self-inverse -- i.e. We can pick the encrypted data and // compute the unencrypted data. By picking the encrypted data to be all 0, // it is easier to re-encrypt the data and debug problems. Similarly, we // pick an iv = 0. - EncryptData(key, encryptionIv, encryptedData, &unencryptedData); - // Run EncryptData again to correctly compute intermediate IV vectors. - // For CBC mode, this also computes the real encrypted data. - EncryptData(key, encryptionIv, unencryptedData, &encryptedData); - TestDecryptCENC(key, encryptionIv, encryptedData, unencryptedData); + starting_iv_.assign(AES_BLOCK_SIZE, 0); + truth_buffer_.assign(total_size_, 0); + ASSERT_NO_FATAL_FAILURE(EncryptData()); + truth_buffer_ = encrypted_buffer_; // truth_buffer_ = encrypted zero buffer. + // Run EncryptData to re-encrypt this buffer. For CTR mode, we should get + // back to zeros. + ASSERT_NO_FATAL_FAILURE(EncryptData()); + ASSERT_NO_FATAL_FAILURE(TestDecryptCENC()); } // If the EvenOffset test passes, but this one doesn't, then DecryptCTR might @@ -2321,15 +2376,10 @@ TEST_P(OEMCryptoSessionTestsPartialBlockTests, OddOffset) { subsample_size_.push_back(SampleSize(10, 75)); subsample_size_.push_back(SampleSize(10, 25)); FindTotalSize(); - vector unencryptedData(total_size_); - vector encryptedData(total_size_); - vector encryptionIv(AES_BLOCK_SIZE); - vector key(AES_BLOCK_SIZE); - EXPECT_EQ(1, GetRandBytes(encryptionIv.data(), AES_BLOCK_SIZE)); - EXPECT_EQ(1, GetRandBytes(key.data(), AES_BLOCK_SIZE)); - for (size_t i = 0; i < total_size_; i++) unencryptedData[i] = i % 256; - EncryptData(key, encryptionIv, unencryptedData, &encryptedData); - TestDecryptCENC(key, encryptionIv, encryptedData, unencryptedData); + ASSERT_NO_FATAL_FAILURE(MakeBuffers()); + ASSERT_NO_FATAL_FAILURE(LoadLicense()); + ASSERT_NO_FATAL_FAILURE(EncryptData()); + ASSERT_NO_FATAL_FAILURE(TestDecryptCENC()); } // This tests that the algorithm used to increment the counter for @@ -2343,17 +2393,13 @@ TEST_P(OEMCryptoSessionTestsPartialBlockTests, OddOffset) { // If you start with an IV of 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE, after you // increment twice, you should get 0xFFFFFFFFFFFFFFFF0000000000000000. TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptWithNearWrap) { + starting_iv_ = wvcdm::a2b_hex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE"); subsample_size_.push_back(SampleSize(0, 256)); FindTotalSize(); - vector unencryptedData(total_size_); - vector encryptedData(total_size_); - vector encryptionIv(AES_BLOCK_SIZE); - vector key(AES_BLOCK_SIZE); - EXPECT_EQ(1, GetRandBytes(key.data(), AES_BLOCK_SIZE)); - encryptionIv = wvcdm::a2b_hex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE"); - for (size_t i = 0; i < total_size_; i++) unencryptedData[i] = i % 256; - EncryptData(key, encryptionIv, unencryptedData, &encryptedData); - TestDecryptCENC(key, encryptionIv, encryptedData, unencryptedData); + ASSERT_NO_FATAL_FAILURE(MakeBuffers()); + ASSERT_NO_FATAL_FAILURE(LoadLicense()); + ASSERT_NO_FATAL_FAILURE(EncryptData()); + ASSERT_NO_FATAL_FAILURE(TestDecryptCENC()); } // This tests the case where an encrypted sample is not an even number of @@ -2365,15 +2411,10 @@ TEST_P(OEMCryptoSessionTestsPartialBlockTests, PartialBlock) { // other tests, e.g. (7, 3). 3*16 < 50 and 7*16 > 50. subsample_size_.push_back(SampleSize(0, 50)); FindTotalSize(); - vector unencryptedData(total_size_); - vector encryptedData(total_size_); - vector encryptionIv(AES_BLOCK_SIZE); - vector key(AES_BLOCK_SIZE); - EXPECT_EQ(1, GetRandBytes(encryptionIv.data(), AES_BLOCK_SIZE)); - EXPECT_EQ(1, GetRandBytes(key.data(), AES_BLOCK_SIZE)); - for (size_t i = 0; i < total_size_; i++) unencryptedData[i] = i % 256; - EncryptData(key, encryptionIv, unencryptedData, &encryptedData); - TestDecryptCENC(key, encryptionIv, encryptedData, unencryptedData); + ASSERT_NO_FATAL_FAILURE(MakeBuffers()); + ASSERT_NO_FATAL_FAILURE(LoadLicense()); + ASSERT_NO_FATAL_FAILURE(EncryptData()); + ASSERT_NO_FATAL_FAILURE(TestDecryptCENC()); } // Based on the resource rating, oemcrypto should handle at least @@ -2385,20 +2426,15 @@ TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptMaxSample) { if (num_subsamples * max_subsample_size > max_size) { max_subsample_size = max_size / num_subsamples; } - for(size_t i = 0; i < num_subsamples/2; i += 2) { + for (size_t i = 0; i < num_subsamples / 2; i += 2) { subsample_size_.push_back(SampleSize(max_subsample_size, 0)); subsample_size_.push_back(SampleSize(0, max_subsample_size)); } FindTotalSize(); - vector unencryptedData(total_size_); - vector encryptedData(total_size_); - vector encryptionIv(AES_BLOCK_SIZE); - vector key(AES_BLOCK_SIZE); - EXPECT_EQ(1, GetRandBytes(encryptionIv.data(), AES_BLOCK_SIZE)); - EXPECT_EQ(1, GetRandBytes(key.data(), AES_BLOCK_SIZE)); - for (size_t i = 0; i < total_size_; i++) unencryptedData[i] = i % 256; - EncryptData(key, encryptionIv, unencryptedData, &encryptedData); - TestDecryptCENC(key, encryptionIv, encryptedData, unencryptedData); + ASSERT_NO_FATAL_FAILURE(MakeBuffers()); + ASSERT_NO_FATAL_FAILURE(LoadLicense()); + ASSERT_NO_FATAL_FAILURE(EncryptData()); + ASSERT_NO_FATAL_FAILURE(TestDecryptCENC()); } // This tests that we can decrypt the required maximum number of subsamples. @@ -2407,30 +2443,20 @@ TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptMaxSubsample) { subsample_size_.push_back(SampleSize(max_subsample_size, 0)); subsample_size_.push_back(SampleSize(0, max_subsample_size)); FindTotalSize(); - vector unencryptedData(total_size_); - vector encryptedData(total_size_); - vector encryptionIv(AES_BLOCK_SIZE); - vector key(AES_BLOCK_SIZE); - EXPECT_EQ(1, GetRandBytes(encryptionIv.data(), AES_BLOCK_SIZE)); - EXPECT_EQ(1, GetRandBytes(key.data(), AES_BLOCK_SIZE)); - for (size_t i = 0; i < total_size_; i++) unencryptedData[i] = i % 256; - EncryptData(key, encryptionIv, unencryptedData, &encryptedData); - TestDecryptCENC(key, encryptionIv, encryptedData, unencryptedData); + ASSERT_NO_FATAL_FAILURE(MakeBuffers()); + ASSERT_NO_FATAL_FAILURE(LoadLicense()); + ASSERT_NO_FATAL_FAILURE(EncryptData()); + ASSERT_NO_FATAL_FAILURE(TestDecryptCENC()); } // There are probably no frames this small, but we should handle them anyway. TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptSmallBuffer) { subsample_size_.push_back(SampleSize(5, 5)); FindTotalSize(); - vector unencryptedData(total_size_); - vector encryptedData(total_size_); - vector encryptionIv(AES_BLOCK_SIZE); - vector key(AES_BLOCK_SIZE); - EXPECT_EQ(1, GetRandBytes(encryptionIv.data(), AES_BLOCK_SIZE)); - EXPECT_EQ(1, GetRandBytes(key.data(), AES_BLOCK_SIZE)); - for (size_t i = 0; i < total_size_; i++) unencryptedData[i] = i % 256; - EncryptData(key, encryptionIv, unencryptedData, &encryptedData); - TestDecryptCENC(key, encryptionIv, encryptedData, unencryptedData); + ASSERT_NO_FATAL_FAILURE(MakeBuffers()); + ASSERT_NO_FATAL_FAILURE(LoadLicense()); + ASSERT_NO_FATAL_FAILURE(EncryptData()); + ASSERT_NO_FATAL_FAILURE(TestDecryptCENC()); } // Test the case where there is only a clear subsample and no encrypted @@ -2438,47 +2464,25 @@ TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptSmallBuffer) { TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptUnencrypted) { subsample_size_.push_back(SampleSize(256, 0)); FindTotalSize(); - vector unencryptedData(total_size_); - vector encryptedData(total_size_); - vector encryptionIv(AES_BLOCK_SIZE); - vector key(AES_BLOCK_SIZE); - EXPECT_EQ(1, GetRandBytes(encryptionIv.data(), AES_BLOCK_SIZE)); - EXPECT_EQ(1, GetRandBytes(key.data(), AES_BLOCK_SIZE)); - for (size_t i = 0; i < total_size_; i++) unencryptedData[i] = i % 256; - EncryptData(key, encryptionIv, unencryptedData, &encryptedData); - TestDecryptCENC(key, encryptionIv, encryptedData, unencryptedData); + ASSERT_NO_FATAL_FAILURE(MakeBuffers()); + ASSERT_NO_FATAL_FAILURE(LoadLicense()); + ASSERT_NO_FATAL_FAILURE(EncryptData()); + ASSERT_NO_FATAL_FAILURE(TestDecryptCENC()); } -TEST_F(OEMCryptoSessionTests, DecryptUnencryptedNoKey) { - OEMCryptoResult sts; - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - // Clear data should be copied even if there is no key selected. - // Set up our expected input and output - // This is dummy decrypted data. - vector in_buffer(256); - for (size_t i = 0; i < in_buffer.size(); i++) in_buffer[i] = i % 256; - vector encryptionIv(AES_BLOCK_SIZE); - EXPECT_EQ(1, GetRandBytes(encryptionIv.data(), AES_BLOCK_SIZE)); - // Describe the output - vector out_buffer(in_buffer.size()); - OEMCrypto_DestBufferDesc destBuffer; - destBuffer.type = OEMCrypto_BufferType_Clear; - destBuffer.buffer.clear.address = out_buffer.data(); - destBuffer.buffer.clear.max_length = out_buffer.size(); - OEMCrypto_CENCEncryptPatternDesc pattern; - pattern.encrypt = 0; - pattern.skip = 0; - pattern.offset = 0; - - // Decrypt the data - sts = - OEMCrypto_DecryptCENC(s.session_id(), in_buffer.data(), in_buffer.size(), - false, encryptionIv.data(), 0, &destBuffer, - &pattern, - OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample); - ASSERT_EQ(OEMCrypto_SUCCESS, sts); - ASSERT_EQ(in_buffer, out_buffer); +TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptUnencryptedNoKey) { + ASSERT_NO_FATAL_FAILURE(session_.open()); + // Single clear subsample + subsample_size_.push_back(SampleSize(400, 0)); + // Do not try to compute the CRC because we have not loaded a license. + verify_crc_ = false; + FindTotalSize(); + ASSERT_NO_FATAL_FAILURE(MakeBuffers()); + // Clear data should be copied even if there is no key selected, and no + // license loaded. + // ASSERT_NO_FATAL_FAILURE(LoadLicense()); + ASSERT_NO_FATAL_FAILURE(EncryptData()); + ASSERT_NO_FATAL_FAILURE(TestDecryptCENC()); } // Used to construct a specific pattern. @@ -2539,8 +2543,7 @@ TEST_F(OEMCryptoSessionTests, DecryptSecureToClear) { ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( - kDuration, - wvoec::kControlObserveDataPath | wvoec::kControlDataPathSecure, + kDuration, wvoec::kControlObserveDataPath | wvoec::kControlDataPathSecure, 0)); ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys()); @@ -2553,8 +2556,8 @@ TEST_F(OEMCryptoSessionTests, DecryptNoAnalogToClearAPI13) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( - kDuration, wvoec::kControlDisableAnalogOutput, 0)); + ASSERT_NO_FATAL_FAILURE( + s.FillSimpleMessage(kDuration, wvoec::kControlDisableAnalogOutput, 0)); ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys()); ASSERT_NO_FATAL_FAILURE( @@ -2566,8 +2569,9 @@ TEST_F(OEMCryptoSessionTests, KeyDuration) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( - kDuration, wvoec::kControlNonceEnabled, s.get_nonce())); + ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); + ASSERT_NO_FATAL_FAILURE( + s.FillSimpleMessage(kDuration, wvoec::kControlNonceEnabled, s.nonce())); ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys()); ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR(true, OEMCrypto_SUCCESS)); @@ -2598,7 +2602,7 @@ TEST_F(OEMCryptoLoadsCertificate, CertificateProvision) { CreateWrappedRSAKey(kSign_RSASSA_PSS, true); // We should not be able to find the rsa key 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_)); } // Verify that RewrapDeviceRSAKey checks pointers are within the provisioning @@ -2607,7 +2611,7 @@ TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange1KeyboxTest) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(s.GenerateDerivedKeysFromKeybox(keybox_)); - ASSERT_NO_FATAL_FAILURE(s.VerifyClientSignature()); + ASSERT_NO_FATAL_FAILURE(s.VerifyProvisioningRequestSignature()); struct RSAPrivateKeyMessage encrypted; std::vector signature; ASSERT_NO_FATAL_FAILURE(s.MakeRSACertificate(&encrypted, sizeof(encrypted), @@ -2621,7 +2625,7 @@ TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange1KeyboxTest) { s.session_id(), message_ptr, sizeof(encrypted), signature.data(), signature.size(), &encrypted.nonce, encrypted.rsa_key, encrypted.rsa_key_length, - encrypted.rsa_key_iv, NULL, &wrapped_key_length)); + encrypted.rsa_key_iv, nullptr, &wrapped_key_length)); wrapped_key.clear(); wrapped_key.assign(wrapped_key_length, 0); uint32_t nonce = encrypted.nonce; @@ -2640,7 +2644,7 @@ TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange2KeyboxTest) { ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(s.GenerateDerivedKeysFromKeybox(keybox_)); // Provisioning request would be signed by client and verified by server. - ASSERT_NO_FATAL_FAILURE(s.VerifyClientSignature()); + ASSERT_NO_FATAL_FAILURE(s.VerifyProvisioningRequestSignature()); struct RSAPrivateKeyMessage encrypted; std::vector signature; s.MakeRSACertificate(&encrypted, sizeof(encrypted), &signature, @@ -2653,7 +2657,7 @@ TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange2KeyboxTest) { s.session_id(), message_ptr, sizeof(encrypted), signature.data(), signature.size(), &encrypted.nonce, encrypted.rsa_key, encrypted.rsa_key_length, - encrypted.rsa_key_iv, NULL, &wrapped_key_length)); + encrypted.rsa_key_iv, nullptr, &wrapped_key_length)); wrapped_key.clear(); wrapped_key.assign(wrapped_key_length, 0); vector bad_buffer(encrypted.rsa_key, @@ -2674,7 +2678,7 @@ TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange3KeyboxTest) { ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(s.GenerateDerivedKeysFromKeybox(keybox_)); // Provisioning request would be signed by client and verified by server. - ASSERT_NO_FATAL_FAILURE(s.VerifyClientSignature()); + ASSERT_NO_FATAL_FAILURE(s.VerifyProvisioningRequestSignature()); struct RSAPrivateKeyMessage encrypted; std::vector signature; s.MakeRSACertificate(&encrypted, sizeof(encrypted), &signature, @@ -2688,7 +2692,7 @@ TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange3KeyboxTest) { s.session_id(), message_ptr, sizeof(encrypted), signature.data(), signature.size(), &encrypted.nonce, encrypted.rsa_key, encrypted.rsa_key_length, - encrypted.rsa_key_iv, NULL, &wrapped_key_length)); + encrypted.rsa_key_iv, nullptr, &wrapped_key_length)); wrapped_key.clear(); wrapped_key.assign(wrapped_key_length, 0); vector bad_buffer(encrypted.rsa_key, @@ -2708,7 +2712,7 @@ TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadSignatureKeyboxTest) { ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(s.GenerateDerivedKeysFromKeybox(keybox_)); // Provisioning request would be signed by client and verified by server. - ASSERT_NO_FATAL_FAILURE(s.VerifyClientSignature()); + ASSERT_NO_FATAL_FAILURE(s.VerifyProvisioningRequestSignature()); struct RSAPrivateKeyMessage encrypted; std::vector signature; s.MakeRSACertificate(&encrypted, sizeof(encrypted), &signature, @@ -2722,7 +2726,7 @@ TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadSignatureKeyboxTest) { s.session_id(), message_ptr, sizeof(encrypted), signature.data(), signature.size(), &encrypted.nonce, encrypted.rsa_key, encrypted.rsa_key_length, - encrypted.rsa_key_iv, NULL, &wrapped_key_length)); + encrypted.rsa_key_iv, nullptr, &wrapped_key_length)); wrapped_key.clear(); wrapped_key.assign(wrapped_key_length, 0); signature[4] ^= 42; // bad signature. @@ -2740,7 +2744,7 @@ TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadNonceKeyboxTest) { ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(s.GenerateDerivedKeysFromKeybox(keybox_)); // Provisioning request would be signed by client and verified by server. - ASSERT_NO_FATAL_FAILURE(s.VerifyClientSignature()); + ASSERT_NO_FATAL_FAILURE(s.VerifyProvisioningRequestSignature()); struct RSAPrivateKeyMessage encrypted; std::vector signature; s.MakeRSACertificate(&encrypted, sizeof(encrypted), &signature, @@ -2754,7 +2758,7 @@ TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadNonceKeyboxTest) { s.session_id(), message_ptr, sizeof(encrypted), signature.data(), signature.size(), &encrypted.nonce, encrypted.rsa_key, encrypted.rsa_key_length, - encrypted.rsa_key_iv, NULL, &wrapped_key_length)); + encrypted.rsa_key_iv, nullptr, &wrapped_key_length)); wrapped_key.clear(); wrapped_key.assign(wrapped_key_length, 0); encrypted.nonce ^= 42; // Almost surely a bad nonce. @@ -2772,7 +2776,7 @@ TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRSAKeyKeyboxTest) { ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(s.GenerateDerivedKeysFromKeybox(keybox_)); // Provisioning request would be signed by client and verified by server. - ASSERT_NO_FATAL_FAILURE(s.VerifyClientSignature()); + ASSERT_NO_FATAL_FAILURE(s.VerifyProvisioningRequestSignature()); struct RSAPrivateKeyMessage encrypted; std::vector signature; s.MakeRSACertificate(&encrypted, sizeof(encrypted), &signature, @@ -2786,7 +2790,7 @@ TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRSAKeyKeyboxTest) { s.session_id(), message_ptr, sizeof(encrypted), signature.data(), signature.size(), &encrypted.nonce, encrypted.rsa_key, encrypted.rsa_key_length, - encrypted.rsa_key_iv, NULL, &wrapped_key_length)); + encrypted.rsa_key_iv, nullptr, &wrapped_key_length)); wrapped_key.clear(); wrapped_key.assign(wrapped_key_length, 0); encrypted.rsa_key[1] ^= 42; // Almost surely a bad key. @@ -2804,7 +2808,7 @@ TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionLargeBufferKeyboxTest) { ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(s.GenerateDerivedKeysFromKeybox(keybox_)); // Provisioning request would be signed by client and verified by server. - ASSERT_NO_FATAL_FAILURE(s.VerifyClientSignature()); + ASSERT_NO_FATAL_FAILURE(s.VerifyProvisioningRequestSignature()); struct LargeRSAPrivateKeyMessage : public RSAPrivateKeyMessage { uint8_t padding[kMaxMessageSize - sizeof(RSAPrivateKeyMessage)]; } encrypted; @@ -2816,7 +2820,7 @@ TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionLargeBufferKeyboxTest) { signature, &wrapped_key, true)); // Verify that the clear key is not contained in the wrapped key. // It should be encrypted. - ASSERT_EQ(NULL, find(wrapped_key, encoded_rsa_key_)); + ASSERT_EQ(nullptr, find(wrapped_key, encoded_rsa_key_)); } // Test that RewrapDeviceRSAKey30 verifies the nonce. @@ -2824,22 +2828,22 @@ TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadNonceProv30Test) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(s.LoadOEMCert()); - s.GenerateNonce(); - uint32_t bad_nonce = s.get_nonce() ^ 42; struct RSAPrivateKeyMessage encrypted; std::vector signature; std::vector message_key; std::vector encrypted_message_key; s.GenerateRSASessionKey(&message_key, &encrypted_message_key); + Encryptor encryptor(message_key); ASSERT_NO_FATAL_FAILURE(s.MakeRSACertificate(&encrypted, sizeof(encrypted), &signature, kSign_RSASSA_PSS, - encoded_rsa_key_, &message_key)); + encoded_rsa_key_, &encryptor)); + uint32_t bad_nonce = s.nonce() ^ 42; size_t wrapped_key_length = 0; ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, OEMCrypto_RewrapDeviceRSAKey30( s.session_id(), &bad_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)); vector wrapped_key(wrapped_key_length, 0); ASSERT_EQ(OEMCrypto_ERROR_INVALID_NONCE, @@ -2855,22 +2859,22 @@ TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRSAKeyProv30Test) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(s.LoadOEMCert()); - s.GenerateNonce(); struct RSAPrivateKeyMessage encrypted; std::vector signature; std::vector message_key; std::vector encrypted_message_key; s.GenerateRSASessionKey(&message_key, &encrypted_message_key); + Encryptor encryptor(message_key); ASSERT_NO_FATAL_FAILURE(s.MakeRSACertificate(&encrypted, sizeof(encrypted), &signature, kSign_RSASSA_PSS, - encoded_rsa_key_, &message_key)); + encoded_rsa_key_, &encryptor)); size_t wrapped_key_length = 0; - uint32_t nonce = s.get_nonce(); + uint32_t nonce = s.nonce(); ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, OEMCrypto_RewrapDeviceRSAKey30( s.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)); vector wrapped_key(wrapped_key_length, 0); encrypted.rsa_key[1] ^= 42; // Almost surely a bad key. @@ -3039,7 +3043,7 @@ TEST_F(OEMCryptoLoadsCertificate, RSAPerformance) { GetRandBytes(licenseRequest.data(), licenseRequest.size()); size_t signature_length = 0; sts = OEMCrypto_GenerateRSASignature(s.session_id(), licenseRequest.data(), - licenseRequest.size(), NULL, + licenseRequest.size(), nullptr, &signature_length, kSign_RSASSA_PSS); ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts); ASSERT_NE(static_cast(0), signature_length); @@ -3240,7 +3244,7 @@ class OEMCryptoLoadsCertificateAlternates : public OEMCryptoLoadsCertificate { GetRandBytes(licenseRequest.data(), licenseRequest.size()); size_t signature_length = 0; sts = OEMCrypto_GenerateRSASignature(s.session_id(), licenseRequest.data(), - licenseRequest.size(), NULL, + licenseRequest.size(), nullptr, &signature_length, scheme); ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts); ASSERT_NE(static_cast(0), signature_length); @@ -3525,8 +3529,8 @@ class OEMCryptoCastReceiverTest : public OEMCryptoLoadsCertificateAlternates { // OEMCrypto will apply the padding, and encrypt to generate the signature. size_t signature_length = 0; sts = OEMCrypto_GenerateRSASignature(s.session_id(), digest.data(), - digest.size(), NULL, &signature_length, - scheme); + digest.size(), nullptr, + &signature_length, scheme); ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts); ASSERT_NE(static_cast(0), signature_length); @@ -4254,11 +4258,11 @@ TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_20) { } // This class is for testing the generic crypto functionality. -class GenericCryptoTest : public OEMCryptoSessionTests { +class OEMCryptoGenericCryptoTest : public OEMCryptoSessionTests { protected: // buffer_size_ must be a multiple of encryption block size, 16. We'll use a // reasonable number of blocks for most of the tests. - GenericCryptoTest() : buffer_size_(160) {} + OEMCryptoGenericCryptoTest() : buffer_size_(160) {} void SetUp() override { OEMCryptoSessionTests::SetUp(); @@ -4424,10 +4428,10 @@ class GenericCryptoTest : public OEMCryptoSessionTests { Session session_; }; -TEST_F(GenericCryptoTest, GenericKeyLoad) { EncryptAndLoadKeys(); } +TEST_F(OEMCryptoGenericCryptoTest, GenericKeyLoad) { EncryptAndLoadKeys(); } // Test that the Generic_Encrypt function works correctly. -TEST_F(GenericCryptoTest, GenericKeyEncrypt) { +TEST_F(OEMCryptoGenericCryptoTest, GenericKeyEncrypt) { EncryptAndLoadKeys(); unsigned int key_index = 0; vector expected_encrypted; @@ -4448,7 +4452,7 @@ TEST_F(GenericCryptoTest, GenericKeyEncrypt) { } // Test that the Generic_Encrypt function fails when not allowed. -TEST_F(GenericCryptoTest, GenericKeyBadEncrypt) { +TEST_F(OEMCryptoGenericCryptoTest, GenericKeyBadEncrypt) { EncryptAndLoadKeys(); BadEncrypt(0, OEMCrypto_HMAC_SHA256, buffer_size_); // The buffer size must be a multiple of 16, so subtracting 10 is bad. @@ -4460,7 +4464,7 @@ TEST_F(GenericCryptoTest, GenericKeyBadEncrypt) { // Test that the Generic_Encrypt works if the input and output buffers are the // same. -TEST_F(GenericCryptoTest, GenericKeyEncryptSameBufferAPI12) { +TEST_F(OEMCryptoGenericCryptoTest, GenericKeyEncryptSameBufferAPI12) { EncryptAndLoadKeys(); unsigned int key_index = 0; vector expected_encrypted; @@ -4481,7 +4485,7 @@ TEST_F(GenericCryptoTest, GenericKeyEncryptSameBufferAPI12) { } // Test Generic_Decrypt works correctly. -TEST_F(GenericCryptoTest, GenericKeyDecrypt) { +TEST_F(OEMCryptoGenericCryptoTest, GenericKeyDecrypt) { EncryptAndLoadKeys(); unsigned int key_index = 1; vector encrypted; @@ -4502,7 +4506,7 @@ TEST_F(GenericCryptoTest, GenericKeyDecrypt) { // Test that Generic_Decrypt works correctly when the input and output buffers // are the same. -TEST_F(GenericCryptoTest, GenericKeyDecryptSameBufferAPI12) { +TEST_F(OEMCryptoGenericCryptoTest, GenericKeyDecryptSameBufferAPI12) { EncryptAndLoadKeys(); unsigned int key_index = 1; vector encrypted; @@ -4523,7 +4527,7 @@ TEST_F(GenericCryptoTest, GenericKeyDecryptSameBufferAPI12) { // Test that Generic_Decrypt fails to decrypt to an insecure buffer if the key // requires a secure data path. -TEST_F(GenericCryptoTest, GenericSecureToClear) { +TEST_F(OEMCryptoGenericCryptoTest, GenericSecureToClear) { session_.license().keys[1].control.control_bits |= htonl( wvoec::kControlObserveDataPath | wvoec::kControlDataPathSecure); EncryptAndLoadKeys(); @@ -4545,7 +4549,7 @@ TEST_F(GenericCryptoTest, GenericSecureToClear) { } // Test that the Generic_Decrypt function fails when not allowed. -TEST_F(GenericCryptoTest, GenericKeyBadDecrypt) { +TEST_F(OEMCryptoGenericCryptoTest, GenericKeyBadDecrypt) { EncryptAndLoadKeys(); BadDecrypt(1, OEMCrypto_HMAC_SHA256, buffer_size_); // The buffer size must be a multiple of 16, so subtracting 10 is bad. @@ -4555,7 +4559,7 @@ TEST_F(GenericCryptoTest, GenericKeyBadDecrypt) { BadDecrypt(3, OEMCrypto_AES_CBC_128_NO_PADDING, buffer_size_); } -TEST_F(GenericCryptoTest, GenericKeySign) { +TEST_F(OEMCryptoGenericCryptoTest, GenericKeySign) { EncryptAndLoadKeys(); unsigned int key_index = 2; vector expected_signature; @@ -4571,7 +4575,7 @@ TEST_F(GenericCryptoTest, GenericKeySign) { ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, OEMCrypto_Generic_Sign(session_.session_id(), clear_buffer_.data(), clear_buffer_.size(), OEMCrypto_HMAC_SHA256, - NULL, &gen_signature_length)); + nullptr, &gen_signature_length)); ASSERT_EQ(static_cast(SHA256_DIGEST_LENGTH), gen_signature_length); vector signature(SHA256_DIGEST_LENGTH); ASSERT_EQ(OEMCrypto_SUCCESS, @@ -4582,7 +4586,7 @@ TEST_F(GenericCryptoTest, GenericKeySign) { } // Test that the Generic_Sign function fails when not allowed. -TEST_F(GenericCryptoTest, GenericKeyBadSign) { +TEST_F(OEMCryptoGenericCryptoTest, GenericKeyBadSign) { EncryptAndLoadKeys(); BadSign(0, OEMCrypto_HMAC_SHA256); // Can't sign with encrypt key. BadSign(1, OEMCrypto_HMAC_SHA256); // Can't sign with decrypt key. @@ -4590,7 +4594,7 @@ TEST_F(GenericCryptoTest, GenericKeyBadSign) { BadSign(2, OEMCrypto_AES_CBC_128_NO_PADDING); // Bad signing algorithm. } -TEST_F(GenericCryptoTest, GenericKeyVerify) { +TEST_F(OEMCryptoGenericCryptoTest, GenericKeyVerify) { EncryptAndLoadKeys(); unsigned int key_index = 3; vector signature; @@ -4610,7 +4614,7 @@ TEST_F(GenericCryptoTest, GenericKeyVerify) { } // Test that the Generic_Verify function fails when not allowed. -TEST_F(GenericCryptoTest, GenericKeyBadVerify) { +TEST_F(OEMCryptoGenericCryptoTest, GenericKeyBadVerify) { EncryptAndLoadKeys(); BadVerify(0, OEMCrypto_HMAC_SHA256, SHA256_DIGEST_LENGTH, false); BadVerify(1, OEMCrypto_HMAC_SHA256, SHA256_DIGEST_LENGTH, false); @@ -4622,7 +4626,7 @@ TEST_F(GenericCryptoTest, GenericKeyBadVerify) { } // Test Generic_Encrypt with the maximum buffer size. -TEST_F(GenericCryptoTest, GenericKeyEncryptLargeBuffer) { +TEST_F(OEMCryptoGenericCryptoTest, GenericKeyEncryptLargeBuffer) { buffer_size_ = GetResourceValue(kMaxGenericBuffer); EncryptAndLoadKeys(); unsigned int key_index = 0; @@ -4644,7 +4648,7 @@ TEST_F(GenericCryptoTest, GenericKeyEncryptLargeBuffer) { } // Test Generic_Decrypt with the maximum buffer size. -TEST_F(GenericCryptoTest, GenericKeyDecryptLargeBuffer) { +TEST_F(OEMCryptoGenericCryptoTest, GenericKeyDecryptLargeBuffer) { // Some applications are known to pass in a block that is almost 400k. buffer_size_ = GetResourceValue(kMaxGenericBuffer); EncryptAndLoadKeys(); @@ -4666,7 +4670,7 @@ TEST_F(GenericCryptoTest, GenericKeyDecryptLargeBuffer) { } // Test Generic_Sign with the maximum buffer size. -TEST_F(GenericCryptoTest, GenericKeySignLargeBuffer) { +TEST_F(OEMCryptoGenericCryptoTest, GenericKeySignLargeBuffer) { buffer_size_ = GetResourceValue(kMaxGenericBuffer); EncryptAndLoadKeys(); unsigned int key_index = 2; @@ -4683,7 +4687,7 @@ TEST_F(GenericCryptoTest, GenericKeySignLargeBuffer) { ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, OEMCrypto_Generic_Sign(session_.session_id(), clear_buffer_.data(), clear_buffer_.size(), OEMCrypto_HMAC_SHA256, - NULL, &gen_signature_length)); + nullptr, &gen_signature_length)); ASSERT_EQ(static_cast(SHA256_DIGEST_LENGTH), gen_signature_length); vector signature(SHA256_DIGEST_LENGTH); ASSERT_EQ(OEMCrypto_SUCCESS, @@ -4694,7 +4698,7 @@ TEST_F(GenericCryptoTest, GenericKeySignLargeBuffer) { } // Test Generic_Verify with the maximum buffer size. -TEST_F(GenericCryptoTest, GenericKeyVerifyLargeBuffer) { +TEST_F(OEMCryptoGenericCryptoTest, GenericKeyVerifyLargeBuffer) { buffer_size_ = GetResourceValue(kMaxGenericBuffer); EncryptAndLoadKeys(); unsigned int key_index = 3; @@ -4715,7 +4719,7 @@ TEST_F(GenericCryptoTest, GenericKeyVerifyLargeBuffer) { } // Test Generic_Encrypt when the key duration has expired. -TEST_F(GenericCryptoTest, KeyDurationEncrypt) { +TEST_F(OEMCryptoGenericCryptoTest, KeyDurationEncrypt) { EncryptAndLoadKeys(); vector expected_encrypted; EncryptBuffer(0, clear_buffer_, &expected_encrypted); @@ -4750,7 +4754,7 @@ TEST_F(GenericCryptoTest, KeyDurationEncrypt) { } // Test Generic_Decrypt when the key duration has expired. -TEST_F(GenericCryptoTest, KeyDurationDecrypt) { +TEST_F(OEMCryptoGenericCryptoTest, KeyDurationDecrypt) { EncryptAndLoadKeys(); unsigned int key_index = 1; @@ -4785,7 +4789,7 @@ TEST_F(GenericCryptoTest, KeyDurationDecrypt) { } // Test Generic_Sign when the key duration has expired. -TEST_F(GenericCryptoTest, KeyDurationSign) { +TEST_F(OEMCryptoGenericCryptoTest, KeyDurationSign) { EncryptAndLoadKeys(); unsigned int key_index = 2; @@ -4822,7 +4826,7 @@ TEST_F(GenericCryptoTest, KeyDurationSign) { } // Test Generic_Verify when the key duration has expired. -TEST_F(GenericCryptoTest, KeyDurationVerify) { +TEST_F(OEMCryptoGenericCryptoTest, KeyDurationVerify) { EncryptAndLoadKeys(); unsigned int key_index = 3; @@ -4857,10 +4861,11 @@ TEST_F(GenericCryptoTest, KeyDurationVerify) { const unsigned int kLongKeyId = 2; // Test that short key ids are allowed. -class GenericCryptoKeyIdLengthTest : public GenericCryptoTest { +class OEMCryptoGenericCryptoKeyIdLengthTest + : public OEMCryptoGenericCryptoTest { protected: void SetUp() override { - GenericCryptoTest::SetUp(); + OEMCryptoGenericCryptoTest::SetUp(); const uint32_t kNoNonce = 0; session_.set_num_keys(5); ASSERT_NO_FATAL_FAILURE(session_.FillSimpleMessage( @@ -4913,32 +4918,52 @@ class GenericCryptoKeyIdLengthTest : public GenericCryptoTest { } }; -TEST_F(GenericCryptoKeyIdLengthTest, MediumKeyId) { TestWithKey(0); } +TEST_F(OEMCryptoGenericCryptoKeyIdLengthTest, MediumKeyId) { TestWithKey(0); } -TEST_F(GenericCryptoKeyIdLengthTest, ShortKeyId) { TestWithKey(1); } +TEST_F(OEMCryptoGenericCryptoKeyIdLengthTest, ShortKeyId) { TestWithKey(1); } -TEST_F(GenericCryptoKeyIdLengthTest, LongKeyId) { TestWithKey(2); } +TEST_F(OEMCryptoGenericCryptoKeyIdLengthTest, LongKeyId) { TestWithKey(2); } -TEST_F(GenericCryptoKeyIdLengthTest, FourteenByteKeyId) { TestWithKey(3); } +TEST_F(OEMCryptoGenericCryptoKeyIdLengthTest, FourteenByteKeyId) { + TestWithKey(3); +} -TEST_F(GenericCryptoKeyIdLengthTest, VeryShortKeyId) { TestWithKey(4); } +TEST_F(OEMCryptoGenericCryptoKeyIdLengthTest, VeryShortKeyId) { + TestWithKey(4); +} -TEST_F(GenericCryptoKeyIdLengthTest, UniformShortKeyId) { +TEST_F(OEMCryptoGenericCryptoKeyIdLengthTest, UniformShortKeyId) { SetUniformKeyIdLength(5); TestWithKey(2); } -TEST_F(GenericCryptoKeyIdLengthTest, UniformLongKeyId) { +TEST_F(OEMCryptoGenericCryptoKeyIdLengthTest, UniformLongKeyId) { SetUniformKeyIdLength(kTestKeyIdMaxLength); TestWithKey(2); } // Test usage table functionality. -class UsageTableTest : public GenericCryptoTest { +class OEMCryptoUsageTableTest : public OEMCryptoGenericCryptoTest { public: void SetUp() override { - GenericCryptoTest::SetUp(); + OEMCryptoGenericCryptoTest::SetUp(); new_mac_keys_ = true; + did_change_system_time_ = false; + test_start_steady_ = steady_clock_.now(); +#ifdef _WIN32 + GetSystemTime(&test_start_wall_); +#else + ASSERT_EQ(0, gettimeofday(&test_start_wall_, nullptr)); +#endif + } + + void TearDown() override { + if (did_change_system_time_) { + const auto delta = steady_clock_.now() - test_start_steady_; + const int64_t delta_sec = delta / std::chrono::seconds(1); + ASSERT_NO_FATAL_FAILURE(SetWallTimeDelta(delta_sec)); + } + OEMCryptoGenericCryptoTest::TearDown(); } virtual void ShutDown() { @@ -4956,8 +4981,9 @@ class UsageTableTest : public GenericCryptoTest { void LoadOfflineLicense(Session& s, const std::string& pst) { ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( - 0, wvoec::kControlNonceOrEntry, s.get_nonce(), pst)); + ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); + ASSERT_NO_FATAL_FAILURE( + s.FillSimpleMessage(0, wvoec::kControlNonceOrEntry, s.nonce(), pst)); ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry()); ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); @@ -4969,10 +4995,10 @@ class UsageTableTest : public GenericCryptoTest { void PrintDotsWhileSleep(time_t total_seconds, time_t interval_seconds) { time_t dot_time = interval_seconds; time_t elapsed_time = 0; - time_t start_time = time(NULL); + time_t start_time = time(nullptr); do { sleep(1); - elapsed_time = time(NULL) - start_time; + elapsed_time = time(nullptr) - start_time; if (elapsed_time >= dot_time) { cout << "."; cout.flush(); @@ -4983,7 +5009,26 @@ class UsageTableTest : public GenericCryptoTest { } protected: + /** + * Sets the current wall-clock time to a delta based on the start of the + * test. + */ + void SetWallTimeDelta(int64_t delta_seconds) { + did_change_system_time_ = true; + NativeTime time = test_start_wall_; + ASSERT_NO_FATAL_FAILURE(AddNativeTime(delta_seconds, &time)); +#ifdef _WIN32 + ASSERT_TRUE(SetSystemTime(&time)); +#else + ASSERT_EQ(0, settimeofday(&time, nullptr)); +#endif + } + bool new_mac_keys_; + bool did_change_system_time_; + std::chrono::steady_clock steady_clock_; + std::chrono::time_point test_start_steady_; + NativeTime test_start_wall_; }; // Some usage tables we want to check a license either with or without a @@ -4991,25 +5036,26 @@ class UsageTableTest : public GenericCryptoTest { // the license is loaded. // This test is parameterized by a boolean which determines if the license // installs new mac keys in LoadKeys. -class UsageTableTestWithMAC : public UsageTableTest, - public WithParamInterface { +class OEMCryptoUsageTableTestWithMAC : public OEMCryptoUsageTableTest, + public WithParamInterface { public: void SetUp() override { - UsageTableTest::SetUp(); + OEMCryptoUsageTableTest::SetUp(); new_mac_keys_ = GetParam(); } }; // Test an online or streaming license with PST. This license requires a valid // nonce and can only be loaded once. -TEST_P(UsageTableTestWithMAC, OnlineLicense) { +TEST_P(OEMCryptoUsageTableTestWithMAC, OnlineLicense) { std::string pst = "my_pst"; Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( - 0, wvoec::kControlNonceEnabled | wvoec::kControlNonceRequired, - s.get_nonce(), pst)); + 0, wvoec::kControlNonceEnabled | wvoec::kControlNonceRequired, s.nonce(), + pst)); ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry()); ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); @@ -5040,14 +5086,15 @@ TEST_P(UsageTableTestWithMAC, OnlineLicense) { // Test the usage report when the license is loaded but the keys are never used // for decryption. -TEST_P(UsageTableTestWithMAC, OnlineLicenseUnused) { +TEST_P(OEMCryptoUsageTableTestWithMAC, OnlineLicenseUnused) { std::string pst = "my_pst"; Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( - 0, wvoec::kControlNonceEnabled | wvoec::kControlNonceRequired, - s.get_nonce(), pst)); + 0, wvoec::kControlNonceEnabled | wvoec::kControlNonceRequired, s.nonce(), + pst)); ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry()); ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); @@ -5072,14 +5119,15 @@ TEST_P(UsageTableTestWithMAC, OnlineLicenseUnused) { // Test that the usage table has been updated and saved before a report can be // generated. -TEST_P(UsageTableTestWithMAC, ForbidReportWithNoUpdate) { +TEST_P(OEMCryptoUsageTableTestWithMAC, ForbidReportWithNoUpdate) { std::string pst = "my_pst"; Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( - 0, wvoec::kControlNonceEnabled | wvoec::kControlNonceRequired, - s.get_nonce(), pst)); + 0, wvoec::kControlNonceEnabled | wvoec::kControlNonceRequired, s.nonce(), + pst)); ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry()); ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); @@ -5103,27 +5151,26 @@ TEST_P(UsageTableTestWithMAC, ForbidReportWithNoUpdate) { } // Test an online license with a license renewal. -TEST_P(UsageTableTestWithMAC, OnlineLicenseWithRefresh) { +TEST_P(OEMCryptoUsageTableTestWithMAC, OnlineLicenseWithRefreshAPI16) { std::string pst = "my_pst"; Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( - 0, wvoec::kControlNonceEnabled | wvoec::kControlNonceRequired, - s.get_nonce(), pst)); + 0, wvoec::kControlNonceEnabled | wvoec::kControlNonceRequired, s.nonce(), + pst)); ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry()); ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); - time_t loaded = time(NULL); + time_t loaded = time(nullptr); ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); - s.GenerateNonce(); // License renewal message is signed by client and verified by the server. - ASSERT_NO_FATAL_FAILURE(s.VerifyClientSignature()); + ASSERT_NO_FATAL_FAILURE(s.VerifyRenewalRequestSignature()); size_t kAllKeys = 1; ASSERT_NO_FATAL_FAILURE(s.RefreshTestKeys( - kAllKeys, - wvoec::kControlNonceEnabled | wvoec::kControlNonceRequired, - s.get_nonce(), OEMCrypto_SUCCESS)); + kAllKeys, wvoec::kControlNonceEnabled | wvoec::kControlNonceRequired, + s.nonce(), OEMCrypto_SUCCESS)); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE( s.GenerateVerifyReport(pst, kActive, @@ -5133,14 +5180,15 @@ TEST_P(UsageTableTestWithMAC, OnlineLicenseWithRefresh) { } // Verify that a streaming license cannot be reloaded. -TEST_F(UsageTableTest, RepeatOnlineLicense) { +TEST_F(OEMCryptoUsageTableTest, RepeatOnlineLicense) { std::string pst = "my_pst"; Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( - 0, wvoec::kControlNonceEnabled | wvoec::kControlNonceRequired, - s.get_nonce(), pst)); + 0, wvoec::kControlNonceEnabled | wvoec::kControlNonceRequired, s.nonce(), + pst)); ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry()); ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); @@ -5163,13 +5211,14 @@ TEST_F(UsageTableTest, RepeatOnlineLicense) { } // A license with non-zero replay control bits needs a valid pst. -TEST_F(UsageTableTest, OnlineEmptyPST) { +TEST_F(OEMCryptoUsageTableTest, OnlineEmptyPST) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( 0, wvoec::kControlNonceEnabled | wvoec::kControlNonceRequired, - s.get_nonce())); + s.nonce())); ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry()); OEMCryptoResult sts = OEMCrypto_LoadKeys( @@ -5182,14 +5231,15 @@ TEST_F(UsageTableTest, OnlineEmptyPST) { } // A license with non-zero replay control bits needs a valid pst. -TEST_F(UsageTableTest, OnlineMissingEntry) { +TEST_F(OEMCryptoUsageTableTest, OnlineMissingEntry) { std::string pst = "my_pst"; Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( - 0, wvoec::kControlNonceEnabled | wvoec::kControlNonceRequired, - s.get_nonce(), pst)); + 0, wvoec::kControlNonceEnabled | wvoec::kControlNonceRequired, s.nonce(), + pst)); ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); // ENTRY NOT CREATED: ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry()); OEMCryptoResult sts = OEMCrypto_LoadKeys( @@ -5202,9 +5252,10 @@ TEST_F(UsageTableTest, OnlineMissingEntry) { } // Test generic encrypt when the license uses a PST. -TEST_P(UsageTableTestWithMAC, GenericCryptoEncrypt) { +TEST_P(OEMCryptoUsageTableTestWithMAC, GenericCryptoEncrypt) { std::string pst = "A PST"; - uint32_t nonce = session_.get_nonce(); + ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); + uint32_t nonce = session_.nonce(); MakeFourKeys(0, wvoec::kControlNonceEnabled | wvoec::kControlNonceRequired, nonce, pst); ASSERT_NO_FATAL_FAILURE(session_.EncryptAndSign()); @@ -5239,9 +5290,10 @@ TEST_P(UsageTableTestWithMAC, GenericCryptoEncrypt) { } // Test generic decrypt when the license uses a PST. -TEST_P(UsageTableTestWithMAC, GenericCryptoDecrypt) { +TEST_P(OEMCryptoUsageTableTestWithMAC, GenericCryptoDecrypt) { std::string pst = "my_pst"; - uint32_t nonce = session_.get_nonce(); + ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); + uint32_t nonce = session_.nonce(); MakeFourKeys( 0, wvoec::kControlNonceEnabled | wvoec::kControlNonceRequired, nonce, pst); @@ -5277,9 +5329,10 @@ TEST_P(UsageTableTestWithMAC, GenericCryptoDecrypt) { } // Test generic sign when the license uses a PST. -TEST_P(UsageTableTestWithMAC, GenericCryptoSign) { +TEST_P(OEMCryptoUsageTableTestWithMAC, GenericCryptoSign) { std::string pst = "my_pst"; - uint32_t nonce = session_.get_nonce(); + ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); + uint32_t nonce = session_.nonce(); MakeFourKeys( 0, wvoec::kControlNonceEnabled | wvoec::kControlNonceRequired, nonce, pst); @@ -5301,7 +5354,7 @@ TEST_P(UsageTableTestWithMAC, GenericCryptoSign) { size_t gen_signature_length = 0; sts = OEMCrypto_Generic_Sign(session_.session_id(), clear_buffer_.data(), clear_buffer_.size(), OEMCrypto_HMAC_SHA256, - NULL, &gen_signature_length); + nullptr, &gen_signature_length); ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts); ASSERT_EQ(static_cast(SHA256_DIGEST_LENGTH), gen_signature_length); vector signature(SHA256_DIGEST_LENGTH); @@ -5326,9 +5379,10 @@ TEST_P(UsageTableTestWithMAC, GenericCryptoSign) { } // Test generic verify when the license uses a PST. -TEST_P(UsageTableTestWithMAC, GenericCryptoVerify) { +TEST_P(OEMCryptoUsageTableTestWithMAC, GenericCryptoVerify) { std::string pst = "my_pst"; - uint32_t nonce = session_.get_nonce(); + ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); + uint32_t nonce = session_.nonce(); MakeFourKeys( 0, wvoec::kControlNonceEnabled | wvoec::kControlNonceRequired, nonce, pst); @@ -5362,7 +5416,7 @@ TEST_P(UsageTableTestWithMAC, GenericCryptoVerify) { } // Test that an offline license can be loaded. -TEST_P(UsageTableTestWithMAC, OfflineLicense) { +TEST_P(OEMCryptoUsageTableTestWithMAC, OfflineLicense) { std::string pst = "my_pst"; Session s; ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s, pst)); @@ -5370,21 +5424,21 @@ TEST_P(UsageTableTestWithMAC, OfflineLicense) { // Test that an offline license can be loaded and that the license can be // renewed. -TEST_P(UsageTableTestWithMAC, OfflineLicenseRefresh) { +TEST_P(OEMCryptoUsageTableTestWithMAC, OfflineLicenseRefresh) { std::string pst = "my_pst"; Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( - 0, wvoec::kControlNonceOrEntry, s.get_nonce(), pst)); + ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); + ASSERT_NO_FATAL_FAILURE( + s.FillSimpleMessage(0, wvoec::kControlNonceOrEntry, s.nonce(), pst)); ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry()); ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); - time_t loaded = time(NULL); + time_t loaded = time(nullptr); ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); - s.GenerateNonce(); // License renewal message is signed by client and verified by the server. - ASSERT_NO_FATAL_FAILURE(s.VerifyClientSignature()); + ASSERT_NO_FATAL_FAILURE(s.VerifyRenewalRequestSignature()); size_t kAllKeys = 1; ASSERT_NO_FATAL_FAILURE(s.RefreshTestKeys( kAllKeys, wvoec::kControlNonceOrEntry, 0, OEMCrypto_SUCCESS)); @@ -5398,7 +5452,7 @@ TEST_P(UsageTableTestWithMAC, OfflineLicenseRefresh) { } // Test that an offline license can be reloaded in a new session. -TEST_P(UsageTableTestWithMAC, ReloadOfflineLicense) { +TEST_P(OEMCryptoUsageTableTestWithMAC, ReloadOfflineLicense) { std::string pst = "my_pst"; Session s; ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s, pst)); @@ -5419,11 +5473,11 @@ TEST_P(UsageTableTestWithMAC, ReloadOfflineLicense) { // Test that an offline license can be reloaded in a new session, and then // refreshed. -TEST_P(UsageTableTestWithMAC, ReloadOfflineLicenseWithRefresh) { +TEST_P(OEMCryptoUsageTableTestWithMAC, ReloadOfflineLicenseWithRefresh) { std::string pst = "my_pst"; Session s; ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s, pst)); - time_t loaded = time(NULL); + time_t loaded = time(nullptr); ASSERT_NO_FATAL_FAILURE(s.open()); // We will reuse the encrypted and signed message, so we don't call @@ -5434,7 +5488,7 @@ TEST_P(UsageTableTestWithMAC, ReloadOfflineLicenseWithRefresh) { ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kUnused, loaded, 0, 0)); ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); - time_t decrypt_time = time(NULL); + time_t decrypt_time = time(nullptr); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE( s.GenerateVerifyReport(pst, kActive, @@ -5456,7 +5510,7 @@ TEST_P(UsageTableTestWithMAC, ReloadOfflineLicenseWithRefresh) { // Verify that we can still reload an offline license after OEMCrypto_Terminate // and Initialize are called. This is as close to a reboot as we can do in a // unit test. -TEST_P(UsageTableTestWithMAC, ReloadOfflineLicenseWithTerminate) { +TEST_P(OEMCryptoUsageTableTestWithMAC, ReloadOfflineLicenseWithTerminate) { std::string pst = "my_pst"; Session s; ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s, pst)); @@ -5483,18 +5537,19 @@ TEST_P(UsageTableTestWithMAC, ReloadOfflineLicenseWithTerminate) { // If we attempt to load a second license with the same usage entry as the // first, but it has different mac keys, then the attempt should fail. This is // how we verify that we are reloading the same license. -TEST_P(UsageTableTestWithMAC, BadReloadOfflineLicense) { +TEST_P(OEMCryptoUsageTableTestWithMAC, BadReloadOfflineLicense) { std::string pst = "my_pst"; Session s; ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s, pst)); - time_t loaded = time(NULL); + time_t loaded = time(nullptr); // Offline license with new mac keys should fail. Session s2; ASSERT_NO_FATAL_FAILURE(s2.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s2)); - ASSERT_NO_FATAL_FAILURE(s2.FillSimpleMessage( - 0, wvoec::kControlNonceOrEntry, s2.get_nonce(), pst)); + ASSERT_NO_FATAL_FAILURE(s2.GenerateNonce()); + ASSERT_NO_FATAL_FAILURE( + s2.FillSimpleMessage(0, wvoec::kControlNonceOrEntry, s2.nonce(), pst)); ASSERT_NO_FATAL_FAILURE(s2.EncryptAndSign()); ASSERT_NO_FATAL_FAILURE(s2.LoadUsageEntry(s)); ASSERT_NE( @@ -5519,12 +5574,13 @@ TEST_P(UsageTableTestWithMAC, BadReloadOfflineLicense) { } // An offline license should not load on the first call if the nonce is bad. -TEST_P(UsageTableTestWithMAC, OfflineBadNonce) { +TEST_P(OEMCryptoUsageTableTestWithMAC, OfflineBadNonce) { std::string pst = "my_pst"; Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry()); + ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); ASSERT_NO_FATAL_FAILURE( s.FillSimpleMessage(0, wvoec::kControlNonceOrEntry, 42, pst)); ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); @@ -5538,13 +5594,14 @@ TEST_P(UsageTableTestWithMAC, OfflineBadNonce) { } // An offline license needs a valid pst. -TEST_P(UsageTableTestWithMAC, OfflineEmptyPST) { +TEST_P(OEMCryptoUsageTableTestWithMAC, OfflineEmptyPST) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry()); + ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); ASSERT_NO_FATAL_FAILURE( - s.FillSimpleMessage(0, wvoec::kControlNonceOrEntry, s.get_nonce())); + s.FillSimpleMessage(0, wvoec::kControlNonceOrEntry, s.nonce())); ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); OEMCryptoResult sts = OEMCrypto_LoadKeys( s.session_id(), s.message_ptr(), s.message_size(), s.signature().data(), @@ -5556,7 +5613,7 @@ TEST_P(UsageTableTestWithMAC, OfflineEmptyPST) { } // If we try to reload a license with a different PST, the attempt should fail. -TEST_P(UsageTableTestWithMAC, ReloadOfflineWrongPST) { +TEST_P(OEMCryptoUsageTableTestWithMAC, ReloadOfflineWrongPST) { std::string pst = "my_pst1"; Session s; ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s, pst)); @@ -5577,7 +5634,7 @@ TEST_P(UsageTableTestWithMAC, ReloadOfflineWrongPST) { // Once a license has been deactivated, the keys can no longer be used for // decryption. However, we can still generate a usage report. -TEST_P(UsageTableTestWithMAC, DeactivateOfflineLicense) { +TEST_P(OEMCryptoUsageTableTestWithMAC, DeactivateOfflineLicense) { std::string pst = "my_pst"; Session s; ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s, pst)); @@ -5626,7 +5683,7 @@ TEST_P(UsageTableTestWithMAC, DeactivateOfflineLicense) { // The usage report should indicate that the keys were never used for // decryption. -TEST_P(UsageTableTestWithMAC, DeactivateOfflineLicenseUnused) { +TEST_P(OEMCryptoUsageTableTestWithMAC, DeactivateOfflineLicenseUnused) { std::string pst = "my_pst"; Session s1; ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s1, pst)); @@ -5675,14 +5732,15 @@ TEST_P(UsageTableTestWithMAC, DeactivateOfflineLicenseUnused) { // If the PST pointers are not contained in the message, then LoadKeys should // reject the attempt. -TEST_P(UsageTableTestWithMAC, BadRange) { +TEST_P(OEMCryptoUsageTableTestWithMAC, BadRange) { std::string pst = "my_pst"; Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); ASSERT_NO_FATAL_FAILURE(session_.CreateNewUsageEntry()); - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( - 0, wvoec::kControlNonceOrEntry, s.get_nonce(), pst)); + ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); + ASSERT_NO_FATAL_FAILURE( + s.FillSimpleMessage(0, wvoec::kControlNonceOrEntry, s.nonce(), pst)); ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); std::string double_message = DuplicateMessage(s.encrypted_license()); OEMCrypto_Substring wrong_pst = s.pst_substr(); @@ -5698,14 +5756,15 @@ TEST_P(UsageTableTestWithMAC, BadRange) { } // Test update usage table fails when passed a null pointer. -TEST_F(UsageTableTest, UpdateFailsWithNullPtr) { +TEST_F(OEMCryptoUsageTableTest, UpdateFailsWithNullPtr) { std::string pst = "my_pst"; Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( - 0, wvoec::kControlNonceEnabled | wvoec::kControlNonceRequired, - s.get_nonce(), pst)); + 0, wvoec::kControlNonceEnabled | wvoec::kControlNonceRequired, s.nonce(), + pst)); ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry()); ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); @@ -5714,28 +5773,26 @@ TEST_F(UsageTableTest, UpdateFailsWithNullPtr) { size_t entry_buffer_length = s.encrypted_usage_entry().size(); vector buffer(entry_buffer_length); // Now try to pass in null pointers for the buffers. This should fail. - ASSERT_NE(OEMCrypto_SUCCESS, - OEMCrypto_UpdateUsageEntry( - s.session_id(), NULL, &header_buffer_length, - buffer.data(), &entry_buffer_length)); + ASSERT_NE( + OEMCrypto_SUCCESS, + OEMCrypto_UpdateUsageEntry(s.session_id(), nullptr, &header_buffer_length, + buffer.data(), &entry_buffer_length)); ASSERT_NE(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageEntry( s.session_id(), encrypted_usage_header_.data(), - &header_buffer_length, NULL, &entry_buffer_length)); + &header_buffer_length, nullptr, &entry_buffer_length)); } // Class used to test usage table defragmentation. -class UsageTableDefragTest : public UsageTableTest { +class OEMCryptoUsageTableDefragTest : public OEMCryptoUsageTableTest { protected: void LoadFirstLicense(Session* s, uint32_t index) { ASSERT_NO_FATAL_FAILURE(s->open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(s)); - std::string pst = "pst "; - char c1 = 'A' + (index / 26); - char c2 = 'A' + (index % 26); - pst = pst + c1 + c2; - ASSERT_NO_FATAL_FAILURE(s->FillSimpleMessage( - 0, wvoec::kControlNonceOrEntry, s->get_nonce(), pst)); + std::string pst = "pst " + std::to_string(index); + ASSERT_NO_FATAL_FAILURE(s->GenerateNonce()); + ASSERT_NO_FATAL_FAILURE( + s->FillSimpleMessage(0, wvoec::kControlNonceOrEntry, s->nonce(), pst)); ASSERT_NO_FATAL_FAILURE(s->EncryptAndSign()); ASSERT_NO_FATAL_FAILURE(s->CreateNewUsageEntry()); ASSERT_EQ(s->usage_entry_number(), index); @@ -5779,8 +5836,8 @@ class UsageTableDefragTest : public UsageTableTest { void ShrinkHeader(uint32_t new_size, OEMCryptoResult expected_result = OEMCrypto_SUCCESS) { size_t header_buffer_length = 0; - OEMCryptoResult sts = - OEMCrypto_ShrinkUsageTableHeader(new_size, NULL, &header_buffer_length); + OEMCryptoResult sts = OEMCrypto_ShrinkUsageTableHeader( + new_size, nullptr, &header_buffer_length); if (expected_result == OEMCrypto_SUCCESS) { ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts); } else { @@ -5797,14 +5854,14 @@ class UsageTableDefragTest : public UsageTableTest { }; // Verify that usage table entries can be moved around in the table. -TEST_F(UsageTableDefragTest, MoveUsageEntries) { +TEST_F(OEMCryptoUsageTableDefragTest, MoveUsageEntries) { const size_t ENTRY_COUNT = 10; vector sessions(ENTRY_COUNT); vector start(ENTRY_COUNT); for (size_t i = 0; i < ENTRY_COUNT; i++) { ASSERT_NO_FATAL_FAILURE(LoadFirstLicense(&sessions[i], i)) << "On license " << i << " pst=" << sessions[i].pst(); - start[i] = time(NULL); + start[i] = time(nullptr); } for (size_t i = 0; i < ENTRY_COUNT; i++) { ASSERT_NO_FATAL_FAILURE(ReloadLicense(&sessions[i], start[i])) @@ -5834,7 +5891,7 @@ TEST_F(UsageTableDefragTest, MoveUsageEntries) { // A usage table entry cannot be moved into an entry where an open session is // currently using the entry. -TEST_F(UsageTableDefragTest, MoveUsageEntriesToOpenSession) { +TEST_F(OEMCryptoUsageTableDefragTest, MoveUsageEntriesToOpenSession) { Session s0; Session s1; LoadFirstLicense(&s0, 0); @@ -5848,7 +5905,7 @@ TEST_F(UsageTableDefragTest, MoveUsageEntriesToOpenSession) { // The usage table cannot be shrunk if any session is using an entry that would // be deleted. -TEST_F(UsageTableDefragTest, ShrinkOverOpenSessions) { +TEST_F(OEMCryptoUsageTableDefragTest, ShrinkOverOpenSessions) { Session s0; Session s1; LoadFirstLicense(&s0, 0); @@ -5864,7 +5921,7 @@ TEST_F(UsageTableDefragTest, ShrinkOverOpenSessions) { } // Verify the usage table size can be increased. -TEST_F(UsageTableDefragTest, EnlargeHeader) { +TEST_F(OEMCryptoUsageTableDefragTest, EnlargeHeader) { Session s0; Session s1; LoadFirstLicense(&s0, 0); @@ -5874,7 +5931,7 @@ TEST_F(UsageTableDefragTest, EnlargeHeader) { } // A new header can only be created while no entries are in use. -TEST_F(UsageTableDefragTest, CreateNewHeaderWhileUsingOldOne) { +TEST_F(OEMCryptoUsageTableDefragTest, CreateNewHeaderWhileUsingOldOne) { Session s0; Session s1; LoadFirstLicense(&s0, 0); @@ -5887,7 +5944,7 @@ TEST_F(UsageTableDefragTest, CreateNewHeaderWhileUsingOldOne) { // Verify that a usage table entry can only be loaded into the correct index of // the table. -TEST_F(UsageTableDefragTest, ReloadUsageEntryWrongIndex) { +TEST_F(OEMCryptoUsageTableDefragTest, ReloadUsageEntryWrongIndex) { Session s0; Session s1; LoadFirstLicense(&s0, 0); @@ -5898,7 +5955,7 @@ TEST_F(UsageTableDefragTest, ReloadUsageEntryWrongIndex) { } // Verify that a usage table entry cannot be loaded if it has been altered. -TEST_F(UsageTableDefragTest, ReloadUsageEntryBadData) { +TEST_F(OEMCryptoUsageTableDefragTest, ReloadUsageEntryBadData) { Session s; LoadFirstLicense(&s, 0); ASSERT_NO_FATAL_FAILURE(s.open()); @@ -5913,13 +5970,11 @@ TEST_F(UsageTableDefragTest, ReloadUsageEntryBadData) { } static std::string MakePST(size_t n) { - std::stringstream stream; - stream << "pst-" << n; - return stream.str(); + return "pst-" + std::to_string(n); } // This verifies we can actually create two hundered usage table entries. -TEST_F(UsageTableDefragTest, TwoHundredEntries) { +TEST_F(OEMCryptoUsageTableDefragTest, TwoHundredEntries) { // OEMCrypto is required to store at least 200 entries in the usage table // header, but it is allowed to store more. This test verifies that if we keep // adding entries, the error indicates a resource limit. It then verifies @@ -5933,8 +5988,9 @@ TEST_F(UsageTableDefragTest, TwoHundredEntries) { ASSERT_NO_FATAL_FAILURE(sessions[i].open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&sessions[i])); std::string pst = MakePST(i); + ASSERT_NO_FATAL_FAILURE(sessions[i].GenerateNonce()); ASSERT_NO_FATAL_FAILURE(sessions[i].FillSimpleMessage( - 0, wvoec::kControlNonceOrEntry, sessions[i].get_nonce(), pst)); + 0, wvoec::kControlNonceOrEntry, sessions[i].nonce(), pst)); ASSERT_NO_FATAL_FAILURE(sessions[i].EncryptAndSign()); // We attempt to create a new usage table entry for this session. OEMCryptoResult status; @@ -6002,60 +6058,9 @@ TEST_F(UsageTableDefragTest, TwoHundredEntries) { } } -// This verifies that copying the old usage table to the new one works. -TEST_F(UsageTableTest, CopyOldEntries) { - // First create three old entries. We open sessions first to force creation - // of the mac keys. - - Session s1; - ASSERT_NO_FATAL_FAILURE(s1.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s1)); - ASSERT_NO_FATAL_FAILURE(s1.FillSimpleMessage(0, 0, 0, "pst number 1")); - ASSERT_NO_FATAL_FAILURE(s1.EncryptAndSign()); - - Test_PST_Report report1(s1.pst(), kUnused); - report1.seconds_since_license_received = 30; - report1.seconds_since_first_decrypt = 20; - report1.seconds_since_last_decrypt = 10; - ASSERT_NO_FATAL_FAILURE(s1.CreateOldEntry(report1)); - - Session s2; - ASSERT_NO_FATAL_FAILURE(s2.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s2)); - ASSERT_NO_FATAL_FAILURE(s2.FillSimpleMessage(0, 0, 0, "pst number 2")); - ASSERT_NO_FATAL_FAILURE(s2.EncryptAndSign()); - - Test_PST_Report report2(s2.pst(), kActive); - report2.seconds_since_license_received = 60; - report2.seconds_since_first_decrypt = 50; - report2.seconds_since_last_decrypt = 40; - ASSERT_NO_FATAL_FAILURE(s2.CreateOldEntry(report2)); - - Session s3; - ASSERT_NO_FATAL_FAILURE(s3.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s3)); - ASSERT_NO_FATAL_FAILURE(s3.FillSimpleMessage(0, 0, 0, "pst number 3")); - ASSERT_NO_FATAL_FAILURE(s3.EncryptAndSign()); - - Test_PST_Report report3(s3.pst(), kInactive); - report3.seconds_since_license_received = 90; - report3.seconds_since_first_decrypt = 80; - report3.seconds_since_last_decrypt = 70; - ASSERT_NO_FATAL_FAILURE(s3.CreateOldEntry(report3)); - - // Now we copy and verify each one. The order is changed to make - // sure there are no order dependecies. - ASSERT_NO_FATAL_FAILURE( - s2.CopyAndVerifyOldEntry(report2, &encrypted_usage_header_)); - ASSERT_NO_FATAL_FAILURE( - s1.CopyAndVerifyOldEntry(report1, &encrypted_usage_header_)); - ASSERT_NO_FATAL_FAILURE( - s3.CopyAndVerifyOldEntry(report3, &encrypted_usage_header_)); -} - // This verifies that the usage table header can be loaded if the generation // number is off by one, but not off by two. -TEST_F(UsageTableTest, ReloadUsageTableWithSkew) { +TEST_F(OEMCryptoUsageTableTest, ReloadUsageTableWithSkew) { // This also tests a few other error conditions with usage table headers. std::string pst = "my_pst"; Session s; @@ -6078,11 +6083,10 @@ TEST_F(UsageTableTest, ReloadUsageTableWithSkew) { ShutDown(); Restart(); // Null pointer generates error. - ASSERT_NE(OEMCrypto_SUCCESS, - OEMCrypto_LoadUsageTableHeader(NULL, - old_usage_header_2_.size())); + ASSERT_NE(OEMCrypto_SUCCESS, OEMCrypto_LoadUsageTableHeader( + nullptr, old_usage_header_2_.size())); ASSERT_NO_FATAL_FAILURE(s.open()); - // Cannot load an entry with if header didn't load. + // Cannot load an entry if header didn't load. ASSERT_EQ( OEMCrypto_ERROR_UNKNOWN_FAILURE, OEMCrypto_LoadUsageEntry(s.session_id(), s.usage_entry_number(), @@ -6098,7 +6102,7 @@ TEST_F(UsageTableTest, ReloadUsageTableWithSkew) { OEMCrypto_LoadUsageTableHeader(bad_header.data(), bad_header.size())); ASSERT_NO_FATAL_FAILURE(s.open()); - // Cannot load an entry with if header didn't load. + // Cannot load an entry if header didn't load. ASSERT_EQ( OEMCrypto_ERROR_UNKNOWN_FAILURE, OEMCrypto_LoadUsageEntry(s.session_id(), s.usage_entry_number(), @@ -6112,7 +6116,7 @@ TEST_F(UsageTableTest, ReloadUsageTableWithSkew) { OEMCrypto_LoadUsageTableHeader(old_usage_header_2_.data(), old_usage_header_2_.size())); ASSERT_NO_FATAL_FAILURE(s.open()); - // Cannot load an entry with if header didn't load. + // Cannot load an entry if header didn't load. ASSERT_NE( OEMCrypto_SUCCESS, OEMCrypto_LoadUsageEntry(s.session_id(), s.usage_entry_number(), @@ -6139,13 +6143,14 @@ TEST_F(UsageTableTest, ReloadUsageTableWithSkew) { } // A usage report with the wrong pst should fail. -TEST_F(UsageTableTest, GenerateReportWrongPST) { +TEST_F(OEMCryptoUsageTableTest, GenerateReportWrongPST) { std::string pst = "my_pst"; Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( - 0, wvoec::kControlNonceOrEntry, s.get_nonce(), pst)); + ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); + ASSERT_NO_FATAL_FAILURE( + s.FillSimpleMessage(0, wvoec::kControlNonceOrEntry, s.nonce(), pst)); ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry()); ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); @@ -6155,7 +6160,7 @@ TEST_F(UsageTableTest, GenerateReportWrongPST) { } // Test usage table timing. -TEST_F(UsageTableTest, TimingTest) { +TEST_F(OEMCryptoUsageTableTest, TimingTest) { std::string pst1 = "my_pst_1"; std::string pst2 = "my_pst_2"; std::string pst3 = "my_pst_3"; @@ -6163,29 +6168,29 @@ TEST_F(UsageTableTest, TimingTest) { Session s2; Session s3; ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s1, pst1)); - time_t loaded1 = time(NULL); + time_t loaded1 = time(nullptr); ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s2, pst2)); - time_t loaded2 = time(NULL); + time_t loaded2 = time(nullptr); ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s3, pst3)); - time_t loaded3 = time(NULL); + time_t loaded3 = time(nullptr); sleep(kLongSleep); ASSERT_NO_FATAL_FAILURE(s1.open()); ASSERT_NO_FATAL_FAILURE(s1.ReloadUsageEntry()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s1)); ASSERT_NO_FATAL_FAILURE(s1.LoadTestKeys(pst1, new_mac_keys_)); - time_t first_decrypt1 = time(NULL); + time_t first_decrypt1 = time(nullptr); ASSERT_NO_FATAL_FAILURE(s1.TestDecryptCTR()); ASSERT_NO_FATAL_FAILURE(s2.open()); ASSERT_NO_FATAL_FAILURE(s2.ReloadUsageEntry()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s2)); ASSERT_NO_FATAL_FAILURE(s2.LoadTestKeys(pst2, new_mac_keys_)); - time_t first_decrypt2 = time(NULL); + time_t first_decrypt2 = time(nullptr); ASSERT_NO_FATAL_FAILURE(s2.TestDecryptCTR()); sleep(kLongSleep); - time_t second_decrypt = time(NULL); + time_t second_decrypt = time(nullptr); ASSERT_NO_FATAL_FAILURE(s1.TestDecryptCTR()); ASSERT_NO_FATAL_FAILURE(s2.TestDecryptCTR()); @@ -6207,7 +6212,7 @@ TEST_F(UsageTableTest, TimingTest) { // After a reboot, we should be able to reload keys, and generate reports. sleep(kLongSleep); - time_t third_decrypt = time(NULL); + time_t third_decrypt = time(nullptr); ASSERT_NO_FATAL_FAILURE(s2.open()); ASSERT_NO_FATAL_FAILURE(s2.ReloadUsageEntry()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s2)); @@ -6242,18 +6247,19 @@ TEST_F(UsageTableTest, TimingTest) { // is 10 seconds. This acceptable error is called slop. This test needs to run // long enough that the reported values are distinct, even after accounting for // this slop. -TEST_F(UsageTableTest, VerifyUsageTimes) { +TEST_F(OEMCryptoUsageTableTest, VerifyUsageTimes) { std::string pst = "my_pst"; Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( - 0, wvoec::kControlNonceEnabled | wvoec::kControlNonceRequired, - s.get_nonce(), pst)); + 0, wvoec::kControlNonceEnabled | wvoec::kControlNonceRequired, s.nonce(), + pst)); ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry()); ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); - time_t load_time = time(NULL); + time_t load_time = time(nullptr); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kUnused)); @@ -6278,7 +6284,7 @@ TEST_F(UsageTableTest, VerifyUsageTimes) { time_t dot_time = kDotIntervalInSeconds; time_t playback_time = 0; - time_t start_time = time(NULL); + time_t start_time = time(nullptr); do { ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); @@ -6286,7 +6292,7 @@ TEST_F(UsageTableTest, VerifyUsageTimes) { load_time, start_time, 0)); // last decrypt = now. - playback_time = time(NULL) - start_time; + playback_time = time(nullptr) - start_time; ASSERT_LE(0, playback_time); if (playback_time >= dot_time) { cout << "."; @@ -6338,29 +6344,25 @@ TEST_F(UsageTableTest, VerifyUsageTimes) { // We don't test roll-forward protection or instances where the user rolls back // the time to the last decrypt call since this requires hardware-secure clocks // to guarantee. -TEST_F(UsageTableTest, TimeRollbackPrevention) { +TEST_F(OEMCryptoUsageTableTest, TimeRollbackPrevention) { std::string pst = "my_pst"; Session s1; cout << "This test temporarily rolls back the system time in order to verify " << "that the usage report accounts for the change. It then rolls " << "the time back forward to the absolute time." << endl; - // We use clock_gettime(CLOCK_REALTIME, ...) over time(...) so we can easily - // set the time using clock_settime. - timespec current_time; - ASSERT_EQ(0, clock_gettime(CLOCK_REALTIME, ¤t_time)); - time_t loaded = current_time.tv_sec; + std::chrono::system_clock wall_clock; + std::chrono::steady_clock monotonic_clock; + const auto loaded = wall_clock.now(); ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s1, pst)); ASSERT_NO_FATAL_FAILURE(s1.open()); ASSERT_NO_FATAL_FAILURE(s1.ReloadUsageEntry()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s1)); ASSERT_NO_FATAL_FAILURE(s1.LoadTestKeys(pst, new_mac_keys_)); - ASSERT_EQ(0, clock_gettime(CLOCK_REALTIME, ¤t_time)); - time_t first_decrypt = current_time.tv_sec; + const auto first_decrypt = wall_clock.now(); // Monotonic clock can't be changed. We use this since system clock will be // unreliable. - ASSERT_EQ(0, clock_gettime(CLOCK_MONOTONIC, ¤t_time)); - time_t first_decrypt_monotonic = current_time.tv_sec; + const auto first_decrypt_monotonic = monotonic_clock.now(); ASSERT_NO_FATAL_FAILURE(s1.TestDecryptCTR()); ASSERT_NO_FATAL_FAILURE(s1.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(s1.close()); @@ -6376,20 +6378,17 @@ TEST_F(UsageTableTest, TimeRollbackPrevention) { ASSERT_NO_FATAL_FAILURE(s1.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(s1.close()); - ASSERT_EQ(0, clock_gettime(CLOCK_REALTIME, ¤t_time)); // Rollback the wall clock time. cout << "Rolling the system time back..." << endl; - timeval current_time_of_day = {}; - current_time_of_day.tv_sec = current_time.tv_sec - kLongDuration * 10; - ASSERT_EQ(0, settimeofday(¤t_time_of_day, NULL)); + ASSERT_NO_FATAL_FAILURE(SetWallTimeDelta( + -static_cast(kLongDuration) * 10)); // Try to playback again. ASSERT_NO_FATAL_FAILURE(s1.open()); ASSERT_NO_FATAL_FAILURE(s1.ReloadUsageEntry()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s1)); ASSERT_NO_FATAL_FAILURE(s1.LoadTestKeys(pst, new_mac_keys_)); - ASSERT_EQ(0, clock_gettime(CLOCK_MONOTONIC, ¤t_time)); - time_t third_decrypt = current_time.tv_sec; + const auto third_decrypt_monotonic = monotonic_clock.now(); ASSERT_NO_FATAL_FAILURE(s1.TestDecryptCTR()); ASSERT_NO_FATAL_FAILURE(s1.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(s1.GenerateReport(pst)); @@ -6397,23 +6396,23 @@ TEST_F(UsageTableTest, TimeRollbackPrevention) { // Restore wall clock to its original position to verify that OEMCrypto does // not report negative times. - ASSERT_EQ(0, clock_gettime(CLOCK_MONOTONIC, ¤t_time)); - current_time_of_day.tv_sec = - first_decrypt + current_time.tv_sec - first_decrypt_monotonic; + const auto test_duration = third_decrypt_monotonic - first_decrypt_monotonic; cout << "Rolling the system time forward to the absolute time..." << endl; - ASSERT_EQ(0, settimeofday(¤t_time_of_day, NULL)); + ASSERT_NO_FATAL_FAILURE(SetWallTimeDelta( + test_duration / std::chrono::seconds(1))); // Need to update time created since the verification checks the time of PST // report creation. - expected.time_created = current_time_of_day.tv_sec; + expected.time_created = UnixTime(wall_clock.now()); + const auto end_time = first_decrypt + test_duration; ASSERT_NO_FATAL_FAILURE( - s1.VerifyReport(expected, loaded, first_decrypt, - first_decrypt + third_decrypt - first_decrypt_monotonic)); + s1.VerifyReport(expected, UnixTime(loaded), UnixTime(first_decrypt), + UnixTime(end_time))); ASSERT_NO_FATAL_FAILURE(s1.close()); } // Verify that a large PST can be used with usage table entries. -TEST_F(UsageTableTest, PSTLargeBuffer) { +TEST_F(OEMCryptoUsageTableTest, PSTLargeBuffer) { std::string pst(kMaxPSTLength, 'a'); // A large PST. Session s; ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s, pst)); @@ -6454,6 +6453,6 @@ TEST_F(UsageTableTest, PSTLargeBuffer) { EXPECT_EQ(kInactiveUsed, s3.pst_report().status()); } -INSTANTIATE_TEST_CASE_P(TestUsageTables, UsageTableTestWithMAC, +INSTANTIATE_TEST_CASE_P(TestUsageTables, OEMCryptoUsageTableTestWithMAC, Values(true, false)); // With and without new_mac_keys. } // namespace wvoec diff --git a/oemcrypto/test/oemcrypto_test_android.cpp b/oemcrypto/test/oemcrypto_test_android.cpp index e0739ea..1a4de9b 100644 --- a/oemcrypto/test/oemcrypto_test_android.cpp +++ b/oemcrypto/test/oemcrypto_test_android.cpp @@ -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. diff --git a/oemcrypto/test/oemcrypto_test_main.cpp b/oemcrypto/test/oemcrypto_test_main.cpp index baf839b..2b5f40c 100644 --- a/oemcrypto/test/oemcrypto_test_main.cpp +++ b/oemcrypto/test/oemcrypto_test_main.cpp @@ -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 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(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. diff --git a/oemcrypto/test/oemcrypto_unittests.gyp b/oemcrypto/test/oemcrypto_unittests.gyp deleted file mode 100644 index 714aa93..0000000 --- a/oemcrypto/test/oemcrypto_unittests.gyp +++ /dev/null @@ -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%': ' + +namespace wvcdm { + +// Returns the size of a fixed-length array. +template +constexpr size_t ArraySize(const T (&)[N]) { + return N; +} + +} // namespace wvcdm + +#endif // WVCDM_UTIL_ARRAYSIZE_H_ diff --git a/util/include/cdm_random.h b/util/include/cdm_random.h new file mode 100644 index 0000000..dd3767c --- /dev/null +++ b/util/include/cdm_random.h @@ -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 +#include +#include + +namespace wvcdm { + +// CdmRandomGenerator is a thread safe, pseudo-random number generator. +// It's purpose is to simplified interface for C++11's 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_ diff --git a/util/include/disallow_copy_and_assign.h b/util/include/disallow_copy_and_assign.h index 0aefbc3..abc1048 100644 --- a/util/include/disallow_copy_and_assign.h +++ b/util/include/disallow_copy_and_assign.h @@ -11,7 +11,6 @@ namespace wvcdm { TypeName(const TypeName&); \ void operator=(const TypeName&) - } // namespace wvcdm #endif // WVCDM_UTIL_DISALLOW_COPY_AND_ASSIGN_H_ diff --git a/util/include/log.h b/util/include/log.h index d79ff00..37706fa 100644 --- a/util/include/log.h +++ b/util/include/log.h @@ -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 diff --git a/util/include/platform.h b/util/include/platform.h index ad2325d..4168e44 100644 --- a/util/include/platform.h +++ b/util/include/platform.h @@ -10,24 +10,22 @@ #include "util_common.h" #ifdef _WIN32 -# include -# include -# include // For htonl and ntohl. -# define __PRETTY_FUNCTION__ __FUNCTION__ -# undef NO_ERROR -# undef GetCurrentTime -# undef DeleteFile +# include +# include // For htonl and ntohl. +# include +# 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 -# include -# include +# include +# include +# include #endif #endif // WVCDM_UTIL_PLATFORM_H_ diff --git a/util/include/string_conversions.h b/util/include/string_conversions.h index 3bb73dc..cb6c9ee 100644 --- a/util/include/string_conversions.h +++ b/util/include/string_conversions.h @@ -7,6 +7,7 @@ #include #include + #include #include @@ -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 diff --git a/util/include/util_common.h b/util/include/util_common.h index 9a1f309..100e138 100644 --- a/util/include/util_common.h +++ b/util/include/util_common.h @@ -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_ diff --git a/util/src/cdm_random.cpp b/util/src/cdm_random.cpp new file mode 100644 index 0000000..591ac5f --- /dev/null +++ b/util/src/cdm_random.cpp @@ -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 + +#include + +#include "log.h" + +// This type alias is for convenience. +using CdmRandomLock = std::unique_lock; + +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 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(lower), + static_cast(upper)); + std::swap(lower, upper); + } + std::uniform_int_distribution 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 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 diff --git a/util/src/dllmain.cpp b/util/src/dllmain.cpp index f559b10..8edea4a 100644 --- a/util/src/dllmain.cpp +++ b/util/src/dllmain.cpp @@ -6,7 +6,6 @@ // #include -BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, - LPVOID lpReserved) { +BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved) { return TRUE; } \ No newline at end of file diff --git a/util/src/string_conversions.cpp b/util/src/string_conversions.cpp index db92f08..828b92f 100644 --- a/util/src/string_conversions.cpp +++ b/util/src/string_conversions.cpp @@ -8,6 +8,7 @@ #include #include #include + #include #include @@ -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& 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& 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 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 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 diff --git a/util/test/cdm_random_unittest.cpp b/util/test/cdm_random_unittest.cpp new file mode 100644 index 0000000..22706e5 --- /dev/null +++ b/util/test/cdm_random_unittest.cpp @@ -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 + +#include +#include +#include +#include + +#include +#include + +#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::max(); + +constexpr size_t kRandomTrialCount = 100; +constexpr size_t kThreadCount = 16; +constexpr unsigned int kSeeds[] = {0, 1337, 1565904109, 776964657}; + +class CdmRandomGeneratorTest : public testing::TestWithParam {}; +} // 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::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 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