From 9fa2cbdb676193386020299298064c99a1f8dff2 Mon Sep 17 00:00:00 2001 From: Fred Gylys-Colwell Date: Sat, 1 Feb 2020 11:56:34 -0800 Subject: [PATCH] OEMCrypto v16.1 -- update ODK This CL updates the ODK library to address review comments. --- oemcrypto/include/OEMCryptoCENC.h | 6 +- oemcrypto/odk/Android.bp | 84 +++++++ oemcrypto/odk/include/OEMCryptoCENCCommon.h | 6 +- .../odk/include/core_message_deserialize.h | 12 +- .../odk/include/core_message_serialize.h | 12 +- .../include/core_message_serialize_proto.h | 6 +- oemcrypto/odk/include/core_message_types.h | 10 +- oemcrypto/odk/include/odk.h | 8 +- oemcrypto/odk/include/odk_assert.h | 27 +++ oemcrypto/odk/include/odk_overflow.h | 33 +++ oemcrypto/odk/include/odk_serialize.h | 44 ++++ oemcrypto/odk/include/odk_structs.h | 6 +- oemcrypto/odk/include/odk_structs_priv.h | 54 +++++ oemcrypto/odk/include/serialization_base.h | 91 ++++++++ oemcrypto/odk/kdo/include/oec_util.h | 172 ++++++++++++++ oemcrypto/odk/kdo/include/oec_util_proto.h | 59 +++++ .../odk/src/core_message_deserialize.cpp | 11 +- oemcrypto/odk/src/core_message_serialize.cpp | 10 +- oemcrypto/odk/src/odk.c | 11 +- oemcrypto/odk/src/odk_assert.h | 6 +- oemcrypto/odk/src/odk_endian.h | 6 +- oemcrypto/odk/src/odk_overflow.h | 6 +- oemcrypto/odk/src/odk_serialize.h | 6 +- oemcrypto/odk/src/odk_structs_priv.h | 6 +- oemcrypto/odk/src/serialization_base.h | 6 +- oemcrypto/odk/test/odk_fuzz.cpp | 21 +- oemcrypto/odk/test/odk_test.cpp | 21 +- oemcrypto/odk/test/odk_test.h | 64 ++++++ oemcrypto/ref/src/oemcrypto_ref.cpp | 6 +- oemcrypto/ref/src/oemcrypto_session.cpp | 6 +- .../ref/src/oemcrypto_usage_table_ref.cpp | 17 +- oemcrypto/renewal/derive_key.cpp | 176 ++++++++++++++ oemcrypto/test/oec_session_util.cpp | 83 ++++--- oemcrypto/test/oec_session_util.h | 14 +- oemcrypto/test/oemcrypto_test.cpp | 217 +++++++++--------- util/include/cdm_random.h | 22 +- util/src/cdm_random.cpp | 4 +- util/test/cdm_random_unittest.cpp | 40 ++++ 38 files changed, 1121 insertions(+), 268 deletions(-) create mode 100644 oemcrypto/odk/Android.bp create mode 100644 oemcrypto/odk/include/odk_assert.h create mode 100644 oemcrypto/odk/include/odk_overflow.h create mode 100644 oemcrypto/odk/include/odk_serialize.h create mode 100644 oemcrypto/odk/include/odk_structs_priv.h create mode 100644 oemcrypto/odk/include/serialization_base.h create mode 100644 oemcrypto/odk/kdo/include/oec_util.h create mode 100644 oemcrypto/odk/kdo/include/oec_util_proto.h create mode 100644 oemcrypto/odk/test/odk_test.h create mode 100644 oemcrypto/renewal/derive_key.cpp diff --git a/oemcrypto/include/OEMCryptoCENC.h b/oemcrypto/include/OEMCryptoCENC.h index 80584bb..c54d6ea 100644 --- a/oemcrypto/include/OEMCryptoCENC.h +++ b/oemcrypto/include/OEMCryptoCENC.h @@ -1407,7 +1407,7 @@ OEMCryptoResult OEMCrypto_LoadKeys( * Message Serialization". * * Below, all fields are found in the struct ODK_ParsedLicense parsed_license - * returend by ODK_ParseLicense. + * returned by ODK_ParseLicense. * * The keys will be decrypted using the current encrypt_key (AES-128-CBC) and * the IV given in the KeyObject. Each key control block will be decrypted @@ -1722,7 +1722,7 @@ OEMCryptoResult OEMCrypto_LoadEntitledContentKeys( * timer value. * - ODK_DISABLE_TIMER: Success, but disable timer. Unlimited playback is * allowed. - * - ODK_TIMER_EXPIRED: Set timer as diabled. Playback is not allowed. + * - ODK_TIMER_EXPIRED: Set timer as disabled. Playback is not allowed. * - ODK_STALE_RENEWAL: This renewal is not the most recently signed. It is * rejected. Return this error * - Any other error - OEMCrypto shall pass any other error up to the @@ -3861,7 +3861,7 @@ uint32_t OEMCrypto_ResourceRatingTier(void); * Message Serialization". * * Below, all fields are found in the struct ODK_ParsedLicense parsed_license - * returend by ODK_ParsedProvisioning. + * returned by ODK_ParsedProvisioning. * * After decrypting enc_rsa_key, If the first four bytes of the buffer are * the string "SIGN", then the actual RSA key begins on the 9th byte of the diff --git a/oemcrypto/odk/Android.bp b/oemcrypto/odk/Android.bp new file mode 100644 index 0000000..bbbe288 --- /dev/null +++ b/oemcrypto/odk/Android.bp @@ -0,0 +1,84 @@ + + +// ---------------------------------------------------------------- +// Builds libwv_odk.a, The ODK Library (libwv_odk) is used by +// the CDM and by oemcrypto implementations. +cc_library_static { + name: "libwv_odk", + include_dirs: [ + "vendor/widevine/libwvdrmengine/oemcrypto/include", + "vendor/widevine/libwvdrmengine/oemcrypto/odk/include", + "vendor/widevine/libwvdrmengine/oemcrypto/odk/src", + ], + + srcs: [ + "src/odk.c", + "src/odk_overflow.c", + "src/odk_serialize.c", + "src/odk_timer.c", + "src/serialization_base.c", + ], + proprietary: true, + + owner: "widevine", +} + +// ---------------------------------------------------------------- +// Builds libwv_kdo.a, The ODK Library companion (libwv_kdo) is used by +// the CDM and by oemcrypto tests, but not by oemcrypto implementations. +cc_library_static { + name: "libwv_kdo", + include_dirs: [ + "vendor/widevine/libwvdrmengine/oemcrypto/include", + "vendor/widevine/libwvdrmengine/oemcrypto/odk/include", + "vendor/widevine/libwvdrmengine/oemcrypto/odk/src", + ], + + srcs: [ + "src/core_message_deserialize.cpp", + "src/core_message_serialize.cpp", + "src/core_message_serialize_proto.cpp", + ], + + static_libs: [ + "libcdm_protos", + "libwv_odk", + ], + + proprietary: true, + + owner: "widevine", +} + +// ---------------------------------------------------------------- +// Builds odk_test executable, which tests the ODK library. +cc_test { + name: "odk_test", + include_dirs: [ + "vendor/widevine/libwvdrmengine/oemcrypto/include", + "vendor/widevine/libwvdrmengine/oemcrypto/odk/include", + "vendor/widevine/libwvdrmengine/oemcrypto/odk/src", + ], + + // WARNING: Module tags are not supported in Soong. + // For native test binaries, use the "cc_test" module type. Some differences: + // - If you don't use gtest, set "gtest: false" + // - Binaries will be installed into /data/nativetest[64]// + // - Both 32 & 64 bit versions will be built (as appropriate) + + owner: "widevine", + proprietary: true, + + static_libs: [ + "libcdm_protos", + "libcdm", + "libwv_odk", + "libwv_kdo", + ], + + srcs: [ + "test/odk_test.cpp", + "test/odk_timer_test.cpp", + ], + +} diff --git a/oemcrypto/odk/include/OEMCryptoCENCCommon.h b/oemcrypto/odk/include/OEMCryptoCENCCommon.h index 0172309..53aba87 100644 --- a/oemcrypto/odk/include/OEMCryptoCENCCommon.h +++ b/oemcrypto/odk/include/OEMCryptoCENCCommon.h @@ -9,8 +9,8 @@ * *********************************************************************/ -#ifndef VIDEO_WIDEVINE_EXPORT_COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_INCLUDE_OEMCRYPTOCENCCOMMON_H_ -#define VIDEO_WIDEVINE_EXPORT_COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_INCLUDE_OEMCRYPTOCENCCOMMON_H_ +#ifndef WIDEVINE_ODK_INCLUDE_OEMCRYPTOCENCCOMMON_H_ +#define WIDEVINE_ODK_INCLUDE_OEMCRYPTOCENCCOMMON_H_ #include #include @@ -152,4 +152,4 @@ typedef struct { } #endif -#endif /* ...ODK_INCLUDE_OEMCRYPTOCENCCOMMON_H_ */ +#endif /* WIDEVINE_ODK_INCLUDE_OEMCRYPTOCENCCOMMON_H_ */ diff --git a/oemcrypto/odk/include/core_message_deserialize.h b/oemcrypto/odk/include/core_message_deserialize.h index a248652..c2967dd 100644 --- a/oemcrypto/odk/include/core_message_deserialize.h +++ b/oemcrypto/odk/include/core_message_deserialize.h @@ -14,8 +14,8 @@ * *********************************************************************/ -#ifndef VIDEO_WIDEVINE_EXPORT_COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_INCLUDE_CORE_MESSAGE_DESERIALIZE_H_ -#define VIDEO_WIDEVINE_EXPORT_COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_INCLUDE_CORE_MESSAGE_DESERIALIZE_H_ +#ifndef WIDEVINE_ODK_INCLUDE_CORE_MESSAGE_DESERIALIZE_H_ +#define WIDEVINE_ODK_INCLUDE_CORE_MESSAGE_DESERIALIZE_H_ #include "core_message_types.h" @@ -29,7 +29,7 @@ namespace deserialize { * [in] oemcrypto_core_message * [out] core_license_request */ -bool CoreLicenseRequestFromMessage(const string& oemcrypto_core_message, +bool CoreLicenseRequestFromMessage(const std::string& oemcrypto_core_message, ODK_LicenseRequest* core_license_request); /** @@ -39,7 +39,7 @@ bool CoreLicenseRequestFromMessage(const string& oemcrypto_core_message, * [in] oemcrypto_core_message * [out] core_renewal_request */ -bool CoreRenewalRequestFromMessage(const string& oemcrypto_core_message, +bool CoreRenewalRequestFromMessage(const std::string& oemcrypto_core_message, ODK_RenewalRequest* core_renewal_request); /** @@ -50,10 +50,10 @@ bool CoreRenewalRequestFromMessage(const string& oemcrypto_core_message, * [out] core_provisioning_request */ bool CoreProvisioningRequestFromMessage( - const string& oemcrypto_core_message, + const std::string& oemcrypto_core_message, ODK_ProvisioningRequest* core_provisioning_request); } /* namespace deserialize */ } /* namespace oemcrypto_core_message */ -#endif /* ...ODK_INCLUDE_CORE_MESSAGE_DESERIALIZE_H_ */ +#endif /* WIDEVINE_ODK_INCLUDE_CORE_MESSAGE_DESERIALIZE_H_ */ diff --git a/oemcrypto/odk/include/core_message_serialize.h b/oemcrypto/odk/include/core_message_serialize.h index 0e4ee9d..38c5c51 100644 --- a/oemcrypto/odk/include/core_message_serialize.h +++ b/oemcrypto/odk/include/core_message_serialize.h @@ -14,8 +14,8 @@ * *********************************************************************/ -#ifndef VIDEO_WIDEVINE_EXPORT_COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_INCLUDE_CORE_MESSAGE_SERIALIZE_H_ -#define VIDEO_WIDEVINE_EXPORT_COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_INCLUDE_CORE_MESSAGE_SERIALIZE_H_ +#ifndef WIDEVINE_ODK_INCLUDE_CORE_MESSAGE_SERIALIZE_H_ +#define WIDEVINE_ODK_INCLUDE_CORE_MESSAGE_SERIALIZE_H_ #include "core_message_types.h" #include "odk_structs.h" @@ -34,7 +34,7 @@ namespace serialize { */ bool CreateCoreLicenseResponse(const ODK_ParsedLicense& parsed_lic, const ODK_LicenseRequest& core_request, - string* oemcrypto_core_message); + std::string* oemcrypto_core_message); /** * Counterpart (serializer) of ODK_ParseRenewal (deserializer) @@ -44,7 +44,7 @@ bool CreateCoreLicenseResponse(const ODK_ParsedLicense& parsed_lic, * [out] oemcrypto_core_message */ bool CreateCoreRenewalResponse(const ODK_RenewalRequest& core_request, - string* oemcrypto_core_message); + std::string* oemcrypto_core_message); /** * Counterpart (serializer) of ODK_ParseProvisioning (deserializer) @@ -57,8 +57,8 @@ bool CreateCoreRenewalResponse(const ODK_RenewalRequest& core_request, */ bool CreateCoreProvisioningResponse(const ODK_ParsedProvisioning& parsed_prov, const ODK_ProvisioningRequest& core_request, - string* oemcrypto_core_message); + std::string* oemcrypto_core_message); } /* namespace serialize */ } /* namespace oemcrypto_core_message */ -#endif /* ...ODK_INCLUDE_CORE_MESSAGE_SERIALIZE_H_ */ +#endif /* WIDEVINE_ODK_INCLUDE_CORE_MESSAGE_SERIALIZE_H_ */ diff --git a/oemcrypto/odk/include/core_message_serialize_proto.h b/oemcrypto/odk/include/core_message_serialize_proto.h index 594569d..9b7cdad 100644 --- a/oemcrypto/odk/include/core_message_serialize_proto.h +++ b/oemcrypto/odk/include/core_message_serialize_proto.h @@ -11,8 +11,8 @@ * message. *********************************************************************/ -#ifndef VIDEO_WIDEVINE_EXPORT_COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_INCLUDE_CORE_MESSAGE_SERIALIZE_PROTO_H_ -#define VIDEO_WIDEVINE_EXPORT_COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_INCLUDE_CORE_MESSAGE_SERIALIZE_PROTO_H_ +#ifndef WIDEVINE_ODK_INCLUDE_CORE_MESSAGE_SERIALIZE_PROTO_H_ +#define WIDEVINE_ODK_INCLUDE_CORE_MESSAGE_SERIALIZE_PROTO_H_ #include #include @@ -56,4 +56,4 @@ bool CreateCoreProvisioningResponseFromProto( } /* namespace serialize */ } /* namespace oemcrypto_core_message */ -#endif /* ...ODK_INCLUDE_CORE_MESSAGE_SERIALIZE_PROTO_H_ */ +#endif /* WIDEVINE_ODK_INCLUDE_CORE_MESSAGE_SERIALIZE_PROTO_H_ */ diff --git a/oemcrypto/odk/include/core_message_types.h b/oemcrypto/odk/include/core_message_types.h index fd4a1d3..2b53aab 100644 --- a/oemcrypto/odk/include/core_message_types.h +++ b/oemcrypto/odk/include/core_message_types.h @@ -49,16 +49,14 @@ *********************************************************************/ /* clang-format on */ -#ifndef VIDEO_WIDEVINE_EXPORT_COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_INCLUDE_CORE_MESSAGE_TYPES_H_ -#define VIDEO_WIDEVINE_EXPORT_COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_INCLUDE_CORE_MESSAGE_TYPES_H_ +#ifndef WIDEVINE_ODK_INCLUDE_CORE_MESSAGE_TYPES_H_ +#define WIDEVINE_ODK_INCLUDE_CORE_MESSAGE_TYPES_H_ #include #include namespace oemcrypto_core_message { -using std::string; - /* @ input/output structs */ /** @@ -90,9 +88,9 @@ struct ODK_ProvisioningRequest { uint32_t api_version; uint32_t nonce; uint32_t session_id; - string device_id; + std::string device_id; }; } /* namespace oemcrypto_core_message */ -#endif /* ...ODK_INCLUDE_CORE_MESSAGE_TYPES_H_ */ +#endif /* WIDEVINE_ODK_INCLUDE_CORE_MESSAGE_TYPES_H_ */ diff --git a/oemcrypto/odk/include/odk.h b/oemcrypto/odk/include/odk.h index c2a2961..c6db79d 100644 --- a/oemcrypto/odk/include/odk.h +++ b/oemcrypto/odk/include/odk.h @@ -42,8 +42,8 @@ * *********************************************************************/ -#ifndef VIDEO_WIDEVINE_EXPORT_COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_INCLUDE_ODK_H_ -#define VIDEO_WIDEVINE_EXPORT_COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_INCLUDE_ODK_H_ +#ifndef WIDEVINE_ODK_INCLUDE_ODK_H_ +#define WIDEVINE_ODK_INCLUDE_ODK_H_ #include @@ -304,7 +304,7 @@ OEMCryptoResult ODK_PrepareCoreLicenseRequest( * the message. (in) size of buffer reserved for the core message, in * bytes. (out) actual length of the core message, in bytes. * [in] nonce_values: pointer to the session's nonce data. - * [in/out] clock_values: the session's clock values. + * [in] clock_values: the session's clock values. * [in] system_time_seconds: the current time on OEMCrypto's clock, in * seconds. * @@ -590,4 +590,4 @@ OEMCryptoResult ODK_ParseProvisioning( } #endif -#endif /* ...ODK_INCLUDE_ODK_H_ */ +#endif /* WIDEVINE_ODK_INCLUDE_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_serialize.h b/oemcrypto/odk/include/odk_serialize.h new file mode 100644 index 0000000..c9488d7 --- /dev/null +++ b/oemcrypto/odk/include/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 + +/* odk pack */ +void Pack_ODK_PreparedLicense(Message* msg, ODK_PreparedLicense const* obj); +void Pack_ODK_RenewalMessage(Message* msg, ODK_RenewalMessage const* obj); +void Pack_ODK_ProvisioningMessage(Message* msg, + ODK_ProvisioningMessage const* obj); + +/* odk unpack */ +void Unpack_ODK_LicenseResponse(Message* msg, ODK_LicenseResponse* obj); +void Unpack_ODK_RenewalMessage(Message* msg, ODK_RenewalMessage* obj); +void Unpack_ODK_ProvisioningResponse(Message* msg, + ODK_ProvisioningResponse* obj); + +/* kdo pack */ +void Pack_ODK_LicenseResponse(Message* msg, ODK_LicenseResponse const* obj); +void Pack_ODK_ProvisioningResponse(Message* msg, + ODK_ProvisioningResponse const* obj); + +/* kdo unpack */ +void Unpack_ODK_PreparedLicense(Message* msg, ODK_PreparedLicense* obj); +void Unpack_ODK_ProvisioningMessage(Message* msg, ODK_ProvisioningMessage* obj); + +#ifdef __cplusplus +} // extern "C" +#endif +#endif /* ODKITEE_SERIALIZER_H_ */ diff --git a/oemcrypto/odk/include/odk_structs.h b/oemcrypto/odk/include/odk_structs.h index fb07f6f..16b4de6 100644 --- a/oemcrypto/odk/include/odk_structs.h +++ b/oemcrypto/odk/include/odk_structs.h @@ -2,8 +2,8 @@ /* source code may only be used and distributed under the Widevine Master */ /* License Agreement. */ -#ifndef VIDEO_WIDEVINE_EXPORT_COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_INCLUDE_ODK_STRUCTS_H_ -#define VIDEO_WIDEVINE_EXPORT_COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_INCLUDE_ODK_STRUCTS_H_ +#ifndef WIDEVINE_ODK_INCLUDE_ODK_STRUCTS_H_ +#define WIDEVINE_ODK_INCLUDE_ODK_STRUCTS_H_ #include @@ -93,4 +93,4 @@ typedef struct { uint32_t session_id; } ODK_NonceValues; -#endif /* ...ODK_INCLUDE_ODK_STRUCTS_H_ */ +#endif /* WIDEVINE_ODK_INCLUDE_ODK_STRUCTS_H_ */ diff --git a/oemcrypto/odk/include/odk_structs_priv.h b/oemcrypto/odk/include/odk_structs_priv.h new file mode 100644 index 0000000..8b3ee35 --- /dev/null +++ b/oemcrypto/odk/include/odk_structs_priv.h @@ -0,0 +1,54 @@ +/* + * 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 "OEMCryptoCENCCommon.h" +#include "odk_structs.h" + +typedef enum { + ODK_License_Request_Type = 1, + ODK_License_Response_Type = 2, + ODK_Renewal_Request_Type = 3, + ODK_Renewal_Response_Type = 4, + ODK_Provisioning_Request_Type = 5, + ODK_Provisioning_Response_Type = 6, +} ODK_MessageType; + +typedef struct { + uint32_t message_type; + uint32_t message_length; + ODK_NonceValues nonce_values; +} ODK_CoreMessage; + +typedef struct { + ODK_CoreMessage core_message; +} ODK_PreparedLicense; + +typedef struct { + ODK_CoreMessage core_message; + uint64_t playback_time; +} ODK_RenewalMessage; + +typedef struct { + ODK_CoreMessage core_message; + uint32_t device_id_length; + uint8_t device_id[ODK_DEVICE_ID_LEN_MAX]; +} ODK_ProvisioningMessage; + +typedef struct { + ODK_CoreMessage core_message; + ODK_ParsedLicense* parsed_license; +} ODK_LicenseResponse; + +typedef struct { + ODK_ProvisioningMessage core_provisioning; + ODK_ParsedProvisioning* parsed_provisioning; +} ODK_ProvisioningResponse; + +#endif // ODK_STRUCTS_PRIV_H_ diff --git a/oemcrypto/odk/include/serialization_base.h b/oemcrypto/odk/include/serialization_base.h new file mode 100644 index 0000000..cc1a3d1 --- /dev/null +++ b/oemcrypto/odk/include/serialization_base.h @@ -0,0 +1,91 @@ +/* + * 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 "OEMCryptoCENCCommon.h" + +#define SIZE_OF_MESSAGE_STRUCT 64 + +/* + * Description: + * Point |msg| to stack-array |blk|. + * |blk| is guaranteed large enough to hold a |Message| struct. + * |blk| cannot be used in the same scope as a variable name. + * |msg| points to valid memory in the same scope |AllocateMessage| is used. + * Parameters: + * msg: pointer to pointer to |Message| struct + * blk: variable name for stack-array + */ +#define AllocateMessage(msg, blk) \ + uint8_t blk[SIZE_OF_MESSAGE_STRUCT]; \ + *(msg) = (Message*)(blk); + +typedef struct _Message Message; + +bool ValidMessage(Message* message); + +void Pack_uint32_t(Message* message, const uint32_t* value); +void Pack_uint64_t(Message* message, const uint64_t* value); +void PackArray(Message* message, const uint8_t* base, size_t size); +void Pack_OEMCrypto_Substring(Message* msg, const OEMCrypto_Substring* obj); + +void Unpack_uint32_t(Message* message, uint32_t* value); +void Unpack_uint64_t(Message* message, uint64_t* value); +void UnpackArray(Message* message, uint8_t* base, size_t size); /* copy out */ +void Unpack_OEMCrypto_Substring(Message* msg, OEMCrypto_Substring* obj); + +typedef enum { + MESSAGE_STATUS_OK, + MESSAGE_STATUS_UNKNOWN_ERROR, + MESSAGE_STATUS_OVERFLOW_ERROR, + MESSAGE_STATUS_UNDERFLOW_ERROR, + MESSAGE_STATUS_PARSE_ERROR, + MESSAGE_STATUS_NULL_POINTER_ERROR, + MESSAGE_STATUS_API_VALUE_ERROR +} MessageStatus; + +/* + * Create a message from a buffer. The message structure consumes the first + * sizeof(Message) bytes of the buffer. The caller is responsible for ensuring + * that the buffer remains allocated for the lifetime of the message. + */ +Message* CreateMessage(uint8_t* buffer, size_t buffer_size); + +/* + * Initialize a message structure to reference a separate buffer. The caller + * is responsible for ensuring that the buffer remains allocated for the + * lifetime of the message. + */ +void InitMessage(Message* message, uint8_t* buffer, size_t capacity); + +/* + * Reset an existing the message to an empty state + */ +void ResetMessage(Message* message); +uint8_t* GetBase(Message* message); +size_t GetCapacity(Message* message); +size_t GetSize(Message* message); +void SetSize(Message* message, size_t size); +MessageStatus GetStatus(Message* message); +void SetStatus(Message* message, MessageStatus status); +size_t GetOffset(Message* message); + +size_t SizeOfMessageStruct(); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // ODKITEE_SERIALIZATION_BASE_H_ diff --git a/oemcrypto/odk/kdo/include/oec_util.h b/oemcrypto/odk/kdo/include/oec_util.h new file mode 100644 index 0000000..20bee97 --- /dev/null +++ b/oemcrypto/odk/kdo/include/oec_util.h @@ -0,0 +1,172 @@ +/* + * Copyright 2019 Google LLC. All Rights Reserved. This file and proprietary + * source code may only be used and distributed under the Widevine Master + * License Agreement. + */ + +// clang-format off +/********************************************************************* + * oec_util.h + * + * OEMCrypto v16 Core Message Serialization library counterpart (a.k.a. KDO) + * + * For Widevine Modular DRM, there are six message types between a server and + * a client device: license request and response, provisioning request and + * response, and renewal request and response. + * + * In OEMCrypto v15 and earlier, messages from the server were parsed by the + * CDM layer above OEMCrypto; the CDM in turn gave OEMCrypto a collection of + * pointers to protected data within the message. However, the pointers + * themselves were not signed by the server. + * + * Starting from OEMCrypto v16, all fields used by OEMCrypto in each of these + * messages have been identified in the document "Widevine Core Message + * Serialization". These fields are called the core of the message. Core + * message fields are (de)serialized using the ODK, a C library provided by + * Widevine. OEMCrypto will parse and verify the core of the message with + * help from the ODK. + * + * The KDO library is the counterpart of ODK used in the CDM & Widevine + * servers. For each message type generated by the ODK, KDO provides a + * corresponding parser. For each message type to be parsed by the ODK, + * KDO provides a corresponding writer. + * + * Table: ODK vs KDO (s: serialize; d: deserialize) + * +----------------------------------------+------------------------------------+ + * | ODK | KDO | + * +---+------------------------------------+---+--------------------------------+ + * | s | ODK_PrepareCoreLicenseRequest | d | ParseLicenseRequest | + * | +------------------------------------+ +--------------------------------+ + * | | ODK_PrepareCoreRenewalRequest | | ParseRenewalRequest | + * | +------------------------------------+ +--------------------------------+ + * | | ODK_PrepareCoreProvisioningRequest | | ParseProvisioningRequest | + * +---+------------------------------------+---+--------------------------------+ + * | d | ODK_ParseLicense | s | CreateCoreLicenseResponse | + * | +------------------------------------+ +--------------------------------+ + * | | ODK_ParseRenewal | | CreateCoreRenewalResponse | + * | +------------------------------------+ +--------------------------------+ + * | | ODK_ParseProvisioning | | CreateCoreProvisioningResponse | + * +---+------------------------------------+---+--------------------------------+ + * + *********************************************************************/ +// clang-format on + +#ifndef OEC_UTIL_H_ +#define OEC_UTIL_H_ + +#include +#include + +#include "odk_structs.h" + +using namespace std; + +namespace oec_util { + +// @ input/output structs + +/** + * Output structure for ParseLicenseRequest + * Input structure for CreateCoreLicenseResponse + */ +struct ODK_LicenseRequest { + uint32_t api_version; + uint32_t nonce; + uint32_t session_id; +}; + +/** + * Output structure for ParseRenewalRequest + * Input structure for CreateCoreRenewalResponse + */ +struct ODK_RenewalRequest { + uint32_t api_version; + uint32_t nonce; + uint32_t session_id; + uint64_t playback_time; +}; + +/** + * Output structure for ParseProvisioningRequest + * Input structure for CreateCoreProvisioningResponse + */ +struct ODK_ProvisioningRequest { + uint32_t api_version; + uint32_t nonce; + uint32_t session_id; + string device_id; +}; + +// @ public parse request (deserializer) functions + +/** + * Counterpart (deserializer) of ODK_PrepareCoreLicenseRequest (serializer) + * + * Parameters: + * [in] oemcrypto_core_message + * [out] core_license_request + */ +bool ParseLicenseRequest(const string& oemcrypto_core_message, + ODK_LicenseRequest* core_license_request); + +/** + * Counterpart (deserializer) of ODK_PrepareCoreRenewalRequest (serializer) + * + * Parameters: + * [in] oemcrypto_core_message + * [out] core_renewal_request + */ +bool ParseRenewalRequest(const string& oemcrypto_core_message, + ODK_RenewalRequest* core_renewal_request); + +/** + * Counterpart (deserializer) of ODK_PrepareCoreProvisioningRequest (serializer) + * + * Parameters: + * [in] oemcrypto_core_message + * [out] core_provisioning_request + */ +bool ParseProvisioningRequest( + const string& oemcrypto_core_message, + ODK_ProvisioningRequest* core_provisioning_request); + +// @ public create response (serializer) functions + +/** + * Counterpart (serializer) of ODK_ParseLicense (deserializer) + * struct-input variant + * + * Parameters: + * [in] parsed_lic + * [in] core_request + * [out] oemcrypto_core_message + */ +bool CreateCoreLicenseResponse(const ODK_ParsedLicense& parsed_lic, + const ODK_LicenseRequest& core_request, + string* oemcrypto_core_message); + +/** + * Counterpart (serializer) of ODK_ParseRenewal (deserializer) + * + * Parameters: + * [in] core_request + * [out] oemcrypto_core_message + */ +bool CreateCoreRenewalResponse(const ODK_RenewalRequest& core_request, + string* oemcrypto_core_message); + +/** + * Counterpart (serializer) of ODK_ParseProvisioning (deserializer) + * struct-input variant + * + * Parameters: + * [in] parsed_prov + * [in] core_request + * [out] oemcrypto_core_message + */ +bool CreateCoreProvisioningResponse(const ODK_ParsedProvisioning& parsed_prov, + const ODK_ProvisioningRequest& core_request, + string* oemcrypto_core_message); +} // namespace oec_util + +#endif // OEC_UTIL_H_ diff --git a/oemcrypto/odk/kdo/include/oec_util_proto.h b/oemcrypto/odk/kdo/include/oec_util_proto.h new file mode 100644 index 0000000..f9d8017 --- /dev/null +++ b/oemcrypto/odk/kdo/include/oec_util_proto.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. + */ + +/********************************************************************* + * oec_util_proto.h + * + * These functions are an extension of those found in oec_util.h. The + * difference is that these use the license and provisioning messages + * in protobuf format to create the core message. + *********************************************************************/ + +#ifndef OEC_UTIL_PROTO_H_ +#define OEC_UTIL_PROTO_H_ + +#include +#include + +#include "license_protocol.pb.h" +#include "oec_util.h" + +using namespace std; +using video_widevine::License; +using video_widevine::License_KeyContainer; + +namespace oec_util { + +// @ public create response (serializer) functions + +/** + * Counterpart (serializer) of ODK_ParseLicense (deserializer) + * + * Parameters: + * [in] license + * [in] core_request + * [out] oemcrypto_core_message + */ +bool CreateCoreLicenseResponse(const video_widevine::License& license, + const ODK_LicenseRequest& core_request, + string* oemcrypto_core_message); + +/** + * Counterpart (serializer) of ODK_ParseProvisioning (deserializer) + * + * Parameters: + * [in] provisioning_response + * [in] core_request + * [out] oemcrypto_core_message + */ +bool CreateCoreProvisioningResponse( + const video_widevine::ProvisioningResponse& provisioning_response, + const ODK_ProvisioningRequest& core_request, + string* oemcrypto_core_message); + +} // namespace oec_util + +#endif // OEC_UTIL_PROTO_H_ diff --git a/oemcrypto/odk/src/core_message_deserialize.cpp b/oemcrypto/odk/src/core_message_deserialize.cpp index 8a2952d..b3d0059 100644 --- a/oemcrypto/odk/src/core_message_deserialize.cpp +++ b/oemcrypto/odk/src/core_message_deserialize.cpp @@ -31,8 +31,9 @@ const int LATEST_OEMCRYPTO_VERSION = 16; * U: auto-generated deserializing function for |T| */ template -bool ParseRequest(uint32_t message_type, const string& oemcrypto_core_message, - S* core_request, T* prepared, const U unpacker) { +bool ParseRequest(uint32_t message_type, + const std::string& oemcrypto_core_message, S* core_request, + T* prepared, const U unpacker) { if (core_request == nullptr || prepared == nullptr) { return false; } @@ -63,7 +64,7 @@ bool ParseRequest(uint32_t message_type, const string& oemcrypto_core_message, } // namespace -bool CoreLicenseRequestFromMessage(const string& oemcrypto_core_message, +bool CoreLicenseRequestFromMessage(const std::string& oemcrypto_core_message, ODK_LicenseRequest* core_license_request) { const auto unpacker = Unpack_ODK_PreparedLicense; ODK_PreparedLicense prepared_license = {}; @@ -71,7 +72,7 @@ bool CoreLicenseRequestFromMessage(const string& oemcrypto_core_message, core_license_request, &prepared_license, unpacker); } -bool CoreRenewalRequestFromMessage(const string& oemcrypto_core_message, +bool CoreRenewalRequestFromMessage(const std::string& oemcrypto_core_message, ODK_RenewalRequest* core_renewal_request) { const auto unpacker = Unpack_ODK_RenewalMessage; ODK_RenewalMessage prepared_renewal = {}; @@ -84,7 +85,7 @@ bool CoreRenewalRequestFromMessage(const string& oemcrypto_core_message, } bool CoreProvisioningRequestFromMessage( - const string& oemcrypto_core_message, + const std::string& oemcrypto_core_message, ODK_ProvisioningRequest* core_provisioning_request) { const auto unpacker = Unpack_ODK_ProvisioningMessage; ODK_ProvisioningMessage prepared_provision = {}; diff --git a/oemcrypto/odk/src/core_message_serialize.cpp b/oemcrypto/odk/src/core_message_serialize.cpp index 6f95e9c..1ff5a59 100644 --- a/oemcrypto/odk/src/core_message_serialize.cpp +++ b/oemcrypto/odk/src/core_message_serialize.cpp @@ -30,7 +30,7 @@ namespace { */ template bool CreateResponse(uint32_t message_type, const S& core_request, - string* oemcrypto_core_message, T& response, + std::string* oemcrypto_core_message, T& response, const P& packer) { if (!oemcrypto_core_message) { return false; @@ -64,7 +64,7 @@ bool CreateResponse(uint32_t message_type, const S& core_request, bool CopyDeviceId(const ODK_ProvisioningRequest& src, ODK_ProvisioningResponse* dest) { auto& core_provisioning = dest->core_provisioning; - const string& device_id = src.device_id; + const std::string& device_id = src.device_id; core_provisioning.device_id_length = device_id.size(); if (core_provisioning.device_id_length > sizeof(core_provisioning.device_id)) { @@ -80,7 +80,7 @@ bool CopyDeviceId(const ODK_ProvisioningRequest& src, bool CreateCoreLicenseResponse(const ODK_ParsedLicense& parsed_lic, const ODK_LicenseRequest& core_request, - string* oemcrypto_core_message) { + std::string* oemcrypto_core_message) { ODK_LicenseResponse license_response{ {}, const_cast(&parsed_lic)}; return CreateResponse(ODK_License_Response_Type, core_request, @@ -89,7 +89,7 @@ bool CreateCoreLicenseResponse(const ODK_ParsedLicense& parsed_lic, } bool CreateCoreRenewalResponse(const ODK_RenewalRequest& core_request, - string* oemcrypto_core_message) { + std::string* oemcrypto_core_message) { ODK_RenewalMessage renewal{{}, core_request.playback_time_seconds}; renewal.playback_time = core_request.playback_time_seconds; return CreateResponse(ODK_Renewal_Response_Type, core_request, @@ -99,7 +99,7 @@ bool CreateCoreRenewalResponse(const ODK_RenewalRequest& core_request, bool CreateCoreProvisioningResponse(const ODK_ParsedProvisioning& parsed_prov, const ODK_ProvisioningRequest& core_request, - string* oemcrypto_core_message) { + std::string* oemcrypto_core_message) { ODK_ProvisioningResponse prov_response{ {}, const_cast(&parsed_prov)}; if (!CopyDeviceId(core_request, &prov_response)) { diff --git a/oemcrypto/odk/src/odk.c b/oemcrypto/odk/src/odk.c index 08e2e81..696f546 100644 --- a/oemcrypto/odk/src/odk.c +++ b/oemcrypto/odk/src/odk.c @@ -192,7 +192,8 @@ OEMCryptoResult ODK_ParseLicense( const uint8_t* request_hash, ODK_TimerLimits* timer_limits, ODK_ClockValues* clock_values, ODK_NonceValues* nonce_values, ODK_ParsedLicense* parsed_license) { - if (!nonce_values || !parsed_license) { + if (!message || !request_hash || !timer_limits || !clock_values || + !nonce_values || !parsed_license) { return ODK_ERROR_CORE_MESSAGE; } @@ -238,8 +239,6 @@ OEMCryptoResult ODK_ParseLicense( if (usage_entry_present && parsed_license->pst.length == 0) { return ODK_ERROR_CORE_MESSAGE; } - /* If the license loaded OK, then we should save off the timer limits. */ - if (!timer_limits) return OEMCrypto_ERROR_UNKNOWN_FAILURE; *timer_limits = parsed_license->timer_limits; return err; } @@ -251,7 +250,7 @@ OEMCryptoResult ODK_ParseRenewal(const uint8_t* message, size_t message_length, const ODK_TimerLimits* timer_limits, ODK_ClockValues* clock_values, uint64_t* timer_value) { - if (!nonce_values || !timer_limits || !clock_values) { + if (!message || !nonce_values || !timer_limits || !clock_values) { return ODK_ERROR_CORE_MESSAGE; } @@ -301,7 +300,7 @@ OEMCryptoResult ODK_ParseProvisioning( const uint8_t* message, size_t message_length, size_t core_message_length, const ODK_NonceValues* nonce_values, const uint8_t* device_id, size_t device_id_length, ODK_ParsedProvisioning* parsed_response) { - if (!nonce_values || !device_id || !parsed_response) { + if (!message || !nonce_values || !device_id || !parsed_response) { return ODK_ERROR_CORE_MESSAGE; } @@ -313,7 +312,7 @@ OEMCryptoResult ODK_ParseProvisioning( return ODK_ERROR_CORE_MESSAGE; } - OEMCryptoResult err = + const OEMCryptoResult err = ODK_ParseResponse(message, message_length, core_message_length, ODK_Provisioning_Response_Type, nonce_values, &provisioning_response.core_provisioning.core_message); diff --git a/oemcrypto/odk/src/odk_assert.h b/oemcrypto/odk/src/odk_assert.h index 7f1e952..2057c40 100644 --- a/oemcrypto/odk/src/odk_assert.h +++ b/oemcrypto/odk/src/odk_assert.h @@ -2,8 +2,8 @@ /* source code may only be used and distributed under the Widevine Master */ /* License Agreement. */ -#ifndef VIDEO_WIDEVINE_EXPORT_COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_SRC_ODK_ASSERT_H_ -#define VIDEO_WIDEVINE_EXPORT_COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_SRC_ODK_ASSERT_H_ +#ifndef WIDEVINE_ODK_SRC_ODK_ASSERT_H_ +#define WIDEVINE_ODK_SRC_ODK_ASSERT_H_ #ifdef __cplusplus extern "C" { @@ -21,4 +21,4 @@ extern "C" { } #endif -#endif /* ...ODK_SRC_ODK_ASSERT_H_ */ +#endif /* WIDEVINE_ODK_SRC_ODK_ASSERT_H_ */ diff --git a/oemcrypto/odk/src/odk_endian.h b/oemcrypto/odk/src/odk_endian.h index f3a813e..bfbb9bf 100644 --- a/oemcrypto/odk/src/odk_endian.h +++ b/oemcrypto/odk/src/odk_endian.h @@ -2,8 +2,8 @@ /* source code may only be used and distributed under the Widevine Master */ /* License Agreement. */ -#ifndef VIDEO_WIDEVINE_EXPORT_COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_SRC_ODK_ENDIAN_H_ -#define VIDEO_WIDEVINE_EXPORT_COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_SRC_ODK_ENDIAN_H_ +#ifndef WIDEVINE_ODK_SRC_ODK_ENDIAN_H_ +#define WIDEVINE_ODK_SRC_ODK_ENDIAN_H_ #ifdef __cplusplus extern "C" { @@ -26,4 +26,4 @@ uint64_t oemcrypto_be64toh(uint64_t u64); } #endif -#endif /* ...ODK_SRC_ODK_ENDIAN_H_ */ +#endif /* WIDEVINE_ODK_SRC_ODK_ENDIAN_H_ */ diff --git a/oemcrypto/odk/src/odk_overflow.h b/oemcrypto/odk/src/odk_overflow.h index b4c5923..23cc440 100644 --- a/oemcrypto/odk/src/odk_overflow.h +++ b/oemcrypto/odk/src/odk_overflow.h @@ -2,8 +2,8 @@ /* source code may only be used and distributed under the Widevine Master */ /* License Agreement. */ -#ifndef VIDEO_WIDEVINE_EXPORT_COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_SRC_ODK_OVERFLOW_H_ -#define VIDEO_WIDEVINE_EXPORT_COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_SRC_ODK_OVERFLOW_H_ +#ifndef WIDEVINE_ODK_SRC_ODK_OVERFLOW_H_ +#define WIDEVINE_ODK_SRC_ODK_OVERFLOW_H_ #ifdef __cplusplus extern "C" { @@ -28,4 +28,4 @@ int odk_add_overflow_ux(size_t a, size_t b, size_t* c); } #endif -#endif /* ...ODK_SRC_ODK_OVERFLOW_H_ */ +#endif /* WIDEVINE_ODK_SRC_ODK_OVERFLOW_H_ */ diff --git a/oemcrypto/odk/src/odk_serialize.h b/oemcrypto/odk/src/odk_serialize.h index b2e7c7a..1b57018 100644 --- a/oemcrypto/odk/src/odk_serialize.h +++ b/oemcrypto/odk/src/odk_serialize.h @@ -5,8 +5,8 @@ /* * This code is auto-generated, do not edit */ -#ifndef VIDEO_WIDEVINE_EXPORT_COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_SRC_ODK_SERIALIZE_H_ -#define VIDEO_WIDEVINE_EXPORT_COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_SRC_ODK_SERIALIZE_H_ +#ifndef WIDEVINE_ODK_SRC_ODK_SERIALIZE_H_ +#define WIDEVINE_ODK_SRC_ODK_SERIALIZE_H_ #include "odk_structs_priv.h" #include "serialization_base.h" @@ -39,4 +39,4 @@ void Unpack_ODK_ProvisioningMessage(Message* msg, ODK_ProvisioningMessage* obj); #ifdef __cplusplus } /* extern "C" */ #endif -#endif /* ...ODK_SRC_ODK_SERIALIZE_H_ */ +#endif /* WIDEVINE_ODK_SRC_ODK_SERIALIZE_H_ */ diff --git a/oemcrypto/odk/src/odk_structs_priv.h b/oemcrypto/odk/src/odk_structs_priv.h index 630e66e..d21fbe2 100644 --- a/oemcrypto/odk/src/odk_structs_priv.h +++ b/oemcrypto/odk/src/odk_structs_priv.h @@ -2,8 +2,8 @@ /* source code may only be used and distributed under the Widevine Master */ /* License Agreement. */ -#ifndef VIDEO_WIDEVINE_EXPORT_COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_SRC_ODK_STRUCTS_PRIV_H_ -#define VIDEO_WIDEVINE_EXPORT_COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_SRC_ODK_STRUCTS_PRIV_H_ +#ifndef WIDEVINE_ODK_SRC_ODK_STRUCTS_PRIV_H_ +#define WIDEVINE_ODK_SRC_ODK_STRUCTS_PRIV_H_ #include @@ -50,4 +50,4 @@ typedef struct { ODK_ParsedProvisioning* parsed_provisioning; } ODK_ProvisioningResponse; -#endif /* ...ODK_SRC_ODK_STRUCTS_PRIV_H_ */ +#endif /* WIDEVINE_ODK_SRC_ODK_STRUCTS_PRIV_H_ */ diff --git a/oemcrypto/odk/src/serialization_base.h b/oemcrypto/odk/src/serialization_base.h index eb3b3d7..e969cb3 100644 --- a/oemcrypto/odk/src/serialization_base.h +++ b/oemcrypto/odk/src/serialization_base.h @@ -2,8 +2,8 @@ /* source code may only be used and distributed under the Widevine Master */ /* License Agreement. */ -#ifndef VIDEO_WIDEVINE_EXPORT_COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_SRC_SERIALIZATION_BASE_H_ -#define VIDEO_WIDEVINE_EXPORT_COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_SRC_SERIALIZATION_BASE_H_ +#ifndef WIDEVINE_ODK_SRC_SERIALIZATION_BASE_H_ +#define WIDEVINE_ODK_SRC_SERIALIZATION_BASE_H_ #ifdef __cplusplus extern "C" { @@ -87,4 +87,4 @@ size_t SizeOfMessageStruct(); } /* extern "C" */ #endif -#endif /* ...ODK_SRC_SERIALIZATION_BASE_H_ */ +#endif /* WIDEVINE_ODK_SRC_SERIALIZATION_BASE_H_ */ diff --git a/oemcrypto/odk/test/odk_fuzz.cpp b/oemcrypto/odk/test/odk_fuzz.cpp index ac72955..8d0244a 100644 --- a/oemcrypto/odk/test/odk_fuzz.cpp +++ b/oemcrypto/odk/test/odk_fuzz.cpp @@ -20,8 +20,8 @@ #include "odk_structs.h" #include "odk_structs_priv.h" -// TODO: remove this: using namespace std; -// TODO: remove this: using namespace oec_util; +// TODO(b/147297226): remove this: using namespace std; +// TODO(b/147297226): remove this: using namespace oec_util; typedef std::function roundtrip_fun; @@ -46,7 +46,7 @@ static OEMCryptoResult odk_fun_RenewalRequest( static OEMCryptoResult odk_fun_ProvisioningRequest( uint8_t* out, size_t* size, uint32_t api_version, uint32_t nonce, uint32_t session_id, const ODK_ProvisioningRequest& core_provisioning) { - const string& device_id = core_provisioning.device_id; + const std::string& device_id = core_provisioning.device_id; return ODK_PrepareCoreProvisioningRequest( out, SIZE_MAX, size, api_version, nonce, session_id, reinterpret_cast(device_id.data()), device_id.size()); @@ -55,7 +55,7 @@ static OEMCryptoResult odk_fun_ProvisioningRequest( template static roundtrip_fun kdo_odk(const F& kdo_fun, const G& odk_fun) { auto roundtrip = [&](const uint8_t* in, uint8_t* out, size_t size) -> size_t { - string input(reinterpret_cast(in), size); + std::string input(reinterpret_cast(in), size); T t = {}; if (!kdo_fun(input, &t)) { return 0; @@ -104,7 +104,7 @@ static OEMCryptoResult odk_fun_LicenseResponse( static bool kdo_fun_LicenseResponse(const ODK_ParseLicense_Args* args, const ODK_ParsedLicense& parsed_lic, - string* oemcrypto_core_message) { + std::string* oemcrypto_core_message) { const auto& common = args->common; ODK_LicenseRequest core_request{common.api_version, common.nonce, common.session_id}; @@ -133,7 +133,7 @@ static OEMCryptoResult odk_fun_RenewalResponse( static bool kdo_fun_RenewalResponse(const ODK_ParseRenewal_Args* args, const ODK_RenewalMessage& renewal_msg, - string* oemcrypto_core_message) { + std::string* oemcrypto_core_message) { const auto& common = args->common; ODK_RenewalRequest core_request{common.api_version, common.nonce, common.session_id, renewal_msg.playback_time}; @@ -150,13 +150,14 @@ static OEMCryptoResult odk_fun_ProvisioningResponse( static bool kdo_fun_ProvisioningResponse( const ODK_ParseProvisioning_Args* args, - const ODK_ParsedProvisioning& parsed_prov, string* oemcrypto_core_message) { + const ODK_ParsedProvisioning& parsed_prov, + std::string* oemcrypto_core_message) { const auto& common = args->common; assert(args->device_id_length <= sizeof(args->device_id)); ODK_ProvisioningRequest core_request{ common.api_version, common.nonce, common.session_id, - string(reinterpret_cast(args->device_id), - args->device_id_length)}; + std::string(reinterpret_cast(args->device_id), + args->device_id_length)}; return CreateCoreProvisioningResponse(parsed_prov, core_request, oemcrypto_core_message); } @@ -181,7 +182,7 @@ static roundtrip_fun odk_kdo(const F& odk_fun, const G& kdo_fun) { return 0; } - string oemcrypto_core_message; + std::string oemcrypto_core_message; if (!kdo_fun(args, t, &oemcrypto_core_message)) { return 0; } diff --git a/oemcrypto/odk/test/odk_test.cpp b/oemcrypto/odk/test/odk_test.cpp index f63edbb..77c4bfc 100644 --- a/oemcrypto/odk/test/odk_test.cpp +++ b/oemcrypto/odk/test/odk_test.cpp @@ -4,18 +4,12 @@ #include "odk.h" -#include -#include - -#include -#include #include #include #include #include #include #include -#include #include #include #include @@ -260,7 +254,13 @@ void expect_eq_buf(const void* s1, const void* s2, size_t n, const void* buffers[] = {s1, s2}; for (int i = 0; i < 2; i++) { char _tmp[] = "/tmp/fileXXXXXX"; - mkstemp(_tmp); + const int temp_fd = mkstemp(_tmp); + if (temp_fd >= 0) { + close(temp_fd); + } else { + std::cerr << "Failed to open temp file." << std::endl; + break; + } std::string tmp(_tmp); std::fstream out(tmp, std::ios::out | std::ios::binary); out.write(static_cast(buffers[i]), n); @@ -465,9 +465,9 @@ TEST(OdkTest, LicenseRequest) { } TEST(OdkTest, RenewalRequest) { - uint64_t system_time_seconds = 0xBADDCAFE000FF1CE; + const uint64_t system_time_seconds = 0xBADDCAFE000FF1CE; uint64_t playback_time = 0xCAFE00000000; - uint64_t playback_start = system_time_seconds - playback_time; + const uint64_t playback_start = system_time_seconds - playback_time; std::vector extra_fields = { {ODK_UINT64, &playback_time, "playback_time"}, }; @@ -617,8 +617,9 @@ TEST(OdkTest, LicenseResponse) { auto odk_parse_func = [&](const uint8_t* buf, size_t size, ODK_NonceValues* nonce_values) { ODK_TimerLimits timer_limits; + ODK_ClockValues clock_values; return ODK_ParseLicense(buf, size + 128, size, true, false, request_hash, - &timer_limits, nullptr, nonce_values, + &timer_limits, &clock_values, nonce_values, &parsed_license); }; auto kdo_prepare_func = [&](const ODK_LicenseRequest& core_request, diff --git a/oemcrypto/odk/test/odk_test.h b/oemcrypto/odk/test/odk_test.h new file mode 100644 index 0000000..a8114a1 --- /dev/null +++ b/oemcrypto/odk/test/odk_test.h @@ -0,0 +1,64 @@ +/* + * Copyright 2019 Google LLC. All Rights Reserved. This file and proprietary + * source code may only be used and distributed under the Widevine Master + * License Agreement. + */ + +#ifndef ODK_TEST_H_ +#define ODK_TEST_H_ + +#include "OEMCryptoCENCCommon.h" + +typedef enum { + ODK_License_Request_Type = 1, + ODK_License_Response_Type = 2, + ODK_Renewal_Request_Type = 3, + ODK_Renewal_Response_Type = 4, + ODK_Provisioning_Request_Type = 5, + ODK_Provisioning_Response_Type = 6, +} ODK_MessageType; + +typedef enum { + ODK_UINT32, + ODK_UINT64, + ODK_SUBSTRING, + ODK_DEVICEID, + ODK_HASH, + ODK_NUMTYPES, +} ODK_FieldType; + +typedef enum { + ODK_READ, + ODK_WRITE, +} ODK_FieldMode; + +typedef struct { + ODK_FieldType type; + void* value; +} ODK_Field; + +#define DEVICE_ID_MAX (64) + +#ifdef __cplusplus +extern "C" { +#endif + +size_t ODK_FieldLength(ODK_FieldType type); +OEMCryptoResult ODK_WriteSingleField(uint8_t* const buf, + const ODK_Field* const field); +OEMCryptoResult ODK_ReadSingleField(const uint8_t* const buf, + const ODK_Field* const field); + +OEMCryptoResult ODK_ReadFields(const uint8_t* const buf, const size_t size_in, + size_t* size_out, const size_t n, + const ODK_Field* const fields); + +OEMCryptoResult ODK_WriteFields(uint8_t* const buf, const size_t size_in, + size_t* size_out, const size_t n, + const ODK_Field* const fields); + +#ifdef __cplusplus +} +#endif + +#endif // ODK_TEST_H_ diff --git a/oemcrypto/ref/src/oemcrypto_ref.cpp b/oemcrypto/ref/src/oemcrypto_ref.cpp index 05e6f26..fb18138 100644 --- a/oemcrypto/ref/src/oemcrypto_ref.cpp +++ b/oemcrypto/ref/src/oemcrypto_ref.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -356,8 +357,9 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_LoadKeys( "range check iv]"); return OEMCrypto_ERROR_INVALID_CONTEXT; } else { - if (memcmp(message + enc_mac_keys.offset - wvoec::KEY_IV_SIZE, - message + enc_mac_keys_iv.offset, wvoec::KEY_IV_SIZE) == 0) { + if (CRYPTO_memcmp(message + enc_mac_keys.offset - wvoec::KEY_IV_SIZE, + message + enc_mac_keys_iv.offset, + wvoec::KEY_IV_SIZE) == 0) { LOGE("[OEMCrypto_LoadKeys(): OEMCrypto_ERROR_INVALID_CONTEXT - " "suspicious iv]"); return OEMCrypto_ERROR_INVALID_CONTEXT; diff --git a/oemcrypto/ref/src/oemcrypto_session.cpp b/oemcrypto/ref/src/oemcrypto_session.cpp index ece666b..736adea 100644 --- a/oemcrypto/ref/src/oemcrypto_session.cpp +++ b/oemcrypto/ref/src/oemcrypto_session.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -643,7 +644,7 @@ bool SessionContext::ValidateMessage(const uint8_t* given_message, LOGE("ValidateMessage: Could not compute signature"); return false; } - if (memcmp(given_signature, computed_signature, signature_length)) { + if (CRYPTO_memcmp(given_signature, computed_signature, signature_length)) { LOGE("Invalid signature given: %s", wvcdm::HexEncode(given_signature, signature_length).c_str()); LOGE("Invalid signature computed: %s", @@ -1375,7 +1376,8 @@ OEMCryptoResult SessionContext::Generic_Verify(const uint8_t* in_buffer, uint8_t computed_signature[SHA256_DIGEST_LENGTH]; if (HMAC(EVP_sha256(), &key[0], key.size(), in_buffer, buffer_length, computed_signature, &md_len)) { - if (0 == memcmp(signature, computed_signature, SHA256_DIGEST_LENGTH)) { + if (0 == + CRYPTO_memcmp(signature, computed_signature, SHA256_DIGEST_LENGTH)) { return OEMCrypto_SUCCESS; } else { return OEMCrypto_ERROR_SIGNATURE_FAILURE; diff --git a/oemcrypto/ref/src/oemcrypto_usage_table_ref.cpp b/oemcrypto/ref/src/oemcrypto_usage_table_ref.cpp index 1c68c48..abe6167 100644 --- a/oemcrypto/ref/src/oemcrypto_usage_table_ref.cpp +++ b/oemcrypto/ref/src/oemcrypto_usage_table_ref.cpp @@ -14,6 +14,7 @@ #include #include +#include #include #include @@ -79,15 +80,17 @@ bool UsageTableEntry::VerifyPST(const uint8_t* pst, size_t pst_length) { if (pst_length > kMaxPSTLength) return false; if (data_.pst_length != pst_length) return false; if (!pst || !pst_length) return false; - return 0 == memcmp(pst, data_.pst, pst_length); + return 0 == CRYPTO_memcmp(pst, data_.pst, pst_length); } bool UsageTableEntry::VerifyMacKeys(const std::vector& server, const std::vector& client) { return (server.size() == wvoec::MAC_KEY_SIZE) && (client.size() == wvoec::MAC_KEY_SIZE) && - (0 == memcmp(&server[0], data_.mac_key_server, wvoec::MAC_KEY_SIZE)) && - (0 == memcmp(&client[0], data_.mac_key_client, wvoec::MAC_KEY_SIZE)); + (0 == CRYPTO_memcmp(&server[0], data_.mac_key_server, + wvoec::MAC_KEY_SIZE)) && + (0 == + CRYPTO_memcmp(&client[0], data_.mac_key_client, wvoec::MAC_KEY_SIZE)); } bool UsageTableEntry::SetMacKeys(const std::vector& server, @@ -117,7 +120,7 @@ OEMCryptoResult UsageTableEntry::ReportUsage(const std::vector& pst, data_.pst_length); return OEMCrypto_ERROR_WRONG_PST; } - if (memcmp(&pst[0], data_.pst, data_.pst_length)) { + if (CRYPTO_memcmp(&pst[0], data_.pst, data_.pst_length)) { LOGE("ReportUsage: wrong pst %s, should be %s.", wvcdm::b2a_hex(pst).c_str(), wvcdm::HexEncode(data_.pst, data_.pst_length).c_str()); return OEMCrypto_ERROR_WRONG_PST; @@ -254,7 +257,8 @@ OEMCryptoResult UsageTableEntry::LoadData(CryptoEngine* ce, uint32_t index, LOGE("LoadUsageEntry: Could not sign entry."); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } - if (memcmp(clear->signature, encrypted->signature, SHA256_DIGEST_LENGTH)) { + if (CRYPTO_memcmp(clear->signature, encrypted->signature, + SHA256_DIGEST_LENGTH)) { LOGE("LoadUsageEntry: Signature did not match."); LOGE("LoadUsageEntry: Invalid signature given: %s", wvcdm::HexEncode(encrypted->signature, sig_length).c_str()); @@ -530,7 +534,8 @@ OEMCryptoResult UsageTable::LoadUsageTableHeader( LOGE("LoadUsageTableHeader: Could not sign entry."); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } - if (memcmp(clear->signature, encrypted->signature, SHA256_DIGEST_LENGTH)) { + if (CRYPTO_memcmp(clear->signature, encrypted->signature, + SHA256_DIGEST_LENGTH)) { LOGE("LoadUsageTableHeader: Signature did not match."); LOGE("LoadUsageTableHeader: Invalid signature given: %s", wvcdm::HexEncode(encrypted->signature, sig_length).c_str()); diff --git a/oemcrypto/renewal/derive_key.cpp b/oemcrypto/renewal/derive_key.cpp new file mode 100644 index 0000000..8db2afa --- /dev/null +++ b/oemcrypto/renewal/derive_key.cpp @@ -0,0 +1,176 @@ +// 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 + +// clang-format off + +// This is a test keybox. It will not be accepted by production systems. +static std::vector test_keybox = { + // sample keybox used for test vectors + // deviceID = WidevineTestOnlyKeybox000 + 0x57, 0x69, 0x64, 0x65, 0x76, 0x69, 0x6e, 0x65, + 0x54, 0x65, 0x73, 0x74, 0x4f, 0x6e, 0x6c, 0x79, + 0x4b, 0x65, 0x79, 0x62, 0x6f, 0x78, 0x30, 0x30, + 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // key + 0xe4, 0xff, 0x57, 0x4c, 0x32, 0x2e, 0xf5, 0x34, + 0x26, 0x21, 0x2c, 0xb3, 0xed, 0x37, 0xf3, 0x5e, + + // data (system ID 7912 = 1EE8). + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x1e, 0xe8, + 0xca, 0x1e, 0x71, 0x7c, 0xfb, 0xe8, 0xa3, 0x94, + 0x52, 0x0a, 0x6b, 0x71, 0x37, 0xd2, 0x69, 0xfa, + 0x5a, 0xc6, 0xb5, 0x4c, 0x6b, 0x46, 0x63, 0x9b, + 0xbe, 0x80, 0x3d, 0xbb, 0x4f, 0xf7, 0x4c, 0x5f, + 0x6f, 0x55, 0x0e, 0x3d, 0x3d, 0x9a, 0xcf, 0x81, + 0x12, 0x5d, 0x52, 0xe0, 0x47, 0x8c, 0xda, 0x0b, + 0xf4, 0x31, 0x41, 0x13, 0xd0, 0xd5, 0x2d, 0xa0, + 0x5b, 0x20, 0x9a, 0xed, 0x51, 0x5d, 0x13, 0xd6, + + // magic + 0x6b, 0x62, 0x6f, 0x78, + + // Crc + 0x39, 0xf2, 0x94, 0xa7, +}; + +const size_t DEVICE_KEY_OFFSET = 0x20; +const size_t CA_TOKEN_OFFSET = 0x30; +const size_t KEYBOX_VERSION_OFFSET = CA_TOKEN_OFFSET; +const size_t SYSTEM_ID_OFFSET = CA_TOKEN_OFFSET + sizeof(uint32_t); +const size_t CA_TOKEN_SIZE = 0x48; +const size_t CA_TOKEN_END = CA_TOKEN_OFFSET + CA_TOKEN_SIZE; + +// sample renewal key for testing +static const std::vector renewal_key = { + 0xfa, 0xfd, 0xc1, 0x1b, 0x55, 0x6f, 0xac, 0xd3, + 0x14, 0x62, 0x50, 0xa0, 0xf7, 0xa5, 0x1a, 0x0e +}; + +static const std::string label = "Keyboxv3"; +static const std::vector new_system_id = {0x00, 0x00, 0x23, 0x45}; +static const std::vector new_keybox_version = {0x00, 0x00, 0x00, 0x03}; + +// clang-format on + +static std::string GetSSLError() { + char error_buffer[128]; + ERR_error_string_n(ERR_get_error(), error_buffer, sizeof(error_buffer)); + return error_buffer; +} + +// Derive the new device key from renewal key and context using +// NIST 800-108 key derivation with 128-bit AES-128-CMAC as the pseudorandom +// function in counter mode: +// +// New Device Key := PRF(renewal_key, 1 || label || 0x00 || context || L) +// PRF := AES-128-CMAC +// label := “Keyboxv3” +// L=(unsigned long)0x80: 0x00|0x00|0x00|0x80 +static bool DeriveKey(const std::vector& key, + const std::vector& context, + std::vector* out) { + if (key.empty() || context.empty() || out == NULL) { + std::cerr << "DeriveKey(): Invalid inputs" << std::endl; + return false; + } + + std::vector cmac_input; + cmac_input.push_back(1); + std::copy(label.begin(), label.end(), std::back_inserter(cmac_input)); + cmac_input.push_back(0x00); + std::copy(context.begin(), context.end(), std::back_inserter(cmac_input)); + cmac_input.push_back(0x00); + cmac_input.push_back(0x00); + cmac_input.push_back(0x00); + cmac_input.push_back(0x80); + + const EVP_CIPHER* cipher = EVP_aes_128_cbc(); + CMAC_CTX* cmac_ctx = CMAC_CTX_new(); + + if (!cmac_ctx) { + std::cerr << "DeriveKey(): Failed to create context: " << GetSSLError() + << std::endl; + return false; + } + + if (!CMAC_Init(cmac_ctx, &key[0], key.size(), cipher, 0)) { + std::cerr << "DeriveKey(): Failed to initialize: " << GetSSLError() + << std::endl; + CMAC_CTX_free(cmac_ctx); + return false; + } + + if (!CMAC_Update(cmac_ctx, &cmac_input[0], cmac_input.size())) { + std::cerr << "DeriveKey(): Failed to update: " << GetSSLError() + << std::endl; + CMAC_CTX_free(cmac_ctx); + return false; + } + + size_t reslen; + uint8_t res[128]; + if (!CMAC_Final(cmac_ctx, res, &reslen)) { + std::cerr << "DeriveKey(): Failed to finalize: " << GetSSLError() + << std::endl; + CMAC_CTX_free(cmac_ctx); + return false; + } + out->assign(res, res + reslen); + CMAC_CTX_free(cmac_ctx); + + return true; +} + +int main(int argc, char** argv) { + if (argc != 2) { + std::cerr << "usage: " << argv[0] << " " << std::endl; + exit(0); + } + + std::string filename = argv[1]; + std::ofstream new_key_file; + new_key_file.open(filename, std::ios::binary); + if (!new_key_file) { + std::cerr << "unable to open " << filename << " for writing" << std::endl; + exit(-1); + } + + // patch the keybox with version 3 and system ID + std::copy(new_keybox_version.begin(), new_keybox_version.end(), + test_keybox.begin() + KEYBOX_VERSION_OFFSET); + std::copy(new_system_id.begin(), new_system_id.end(), + test_keybox.begin() + SYSTEM_ID_OFFSET); + + // context is Device Key || CA token + std::vector context(test_keybox.begin() + DEVICE_KEY_OFFSET, + test_keybox.begin() + CA_TOKEN_END); + + // derive the new device key + std::vector new_device_key; + if (!DeriveKey(renewal_key, context, &new_device_key)) { + std::cerr << "Failed to derive new renewal key" << std::endl; + exit(-1); + } else { + std::ostream_iterator output_iterator(new_key_file); + std::copy(new_device_key.begin(), new_device_key.end(), output_iterator); + new_key_file.close(); + std::cout << "New key written to " << filename << std::endl; + ; + } +} diff --git a/oemcrypto/test/oec_session_util.cpp b/oemcrypto/test/oec_session_util.cpp index 4e2ec1b..776e822 100644 --- a/oemcrypto/test/oec_session_util.cpp +++ b/oemcrypto/test/oec_session_util.cpp @@ -981,15 +981,12 @@ void Session::EncryptCTR(const vector& in_buffer, const uint8_t* key, void Session::TestDecryptCTR(bool select_key_first, OEMCryptoResult expected_result, int key_index) { - OEMCryptoResult sts; + OEMCryptoResult select_result = OEMCrypto_SUCCESS; if (select_key_first) { // Select the key (from FillSimpleMessage) - sts = OEMCrypto_SelectKey(session_id(), license_.keys[key_index].key_id, - license_.keys[key_index].key_id_length, - OEMCrypto_CipherMode_CTR); - if (expected_result == OEMCrypto_SUCCESS) { - ASSERT_EQ(OEMCrypto_SUCCESS, sts); - } + select_result = OEMCrypto_SelectKey( + session_id(), license_.keys[key_index].key_id, + license_.keys[key_index].key_id_length, OEMCrypto_CipherMode_CTR); } // Create test sample description @@ -1012,43 +1009,50 @@ void Session::TestDecryptCTR(bool select_key_first, OEMCrypto_CENCEncryptPatternDesc pattern = {0, 0}; // Decrypt the data - sts = OEMCrypto_DecryptCENC(session_id(), &sample_description, 1, &pattern); - + const OEMCryptoResult decrypt_result = + OEMCrypto_DecryptCENC(session_id(), &sample_description, 1, &pattern); // We only have a few errors that we test are reported. + ASSERT_NO_FATAL_FAILURE( + TestDecryptResult(expected_result, select_result, decrypt_result)) + << "Either SelectKey or DecryptCENC should return " << expected_result + << ", but they returned " << select_result << " and " << decrypt_result + << ", respectively."; if (expected_result == OEMCrypto_SUCCESS) { // No error. - ASSERT_EQ(sts, OEMCrypto_SUCCESS); - ASSERT_EQ(output_buffer, unencrypted_data); + ASSERT_EQ(unencrypted_data, output_buffer); } else { - ASSERT_NO_FATAL_FAILURE(TestDecryptResult(expected_result, sts)); - ASSERT_NE(output_buffer, unencrypted_data); + ASSERT_NE(unencrypted_data, output_buffer); } } void Session::TestDecryptResult(OEMCryptoResult expected_result, - OEMCryptoResult actual_result) { - + OEMCryptoResult actual_select_result, + OEMCryptoResult actual_decrypt_result) { + // In most cases, we expect the result to come from either the select key or + // from the decrypt call. if (expected_result == OEMCrypto_SUCCESS) { // No error. - ASSERT_EQ(OEMCrypto_SUCCESS, actual_result); - } else if (expected_result == OEMCrypto_ERROR_KEY_EXPIRED && - global_features.api_version >= 9) { - // Report stale keys, required in v9 and beyond. - ASSERT_EQ(OEMCrypto_ERROR_KEY_EXPIRED, actual_result); + ASSERT_EQ(OEMCrypto_SUCCESS, actual_select_result); + ASSERT_EQ(OEMCrypto_SUCCESS, actual_decrypt_result); + } else if (expected_result == OEMCrypto_ERROR_KEY_EXPIRED || + expected_result == OEMCrypto_ERROR_INSUFFICIENT_HDCP || + expected_result == OEMCrypto_ERROR_ANALOG_OUTPUT) { + // Key expired or output problems may be reported from select key or + // decrypt, but must be reported. + ASSERT_TRUE(actual_select_result == expected_result || + actual_decrypt_result == expected_result); } else if (expected_result == OEMCrypto_WARNING_MIXED_OUTPUT_PROTECTION && global_features.api_version >= kCoreMessagesAPI) { // OEMCrypto is allowed to report either this warning or // OEMCrypto_ERROR_INSUFFICIENT_HDCP depending on if it can disable // restricted displays. - ASSERT_TRUE(actual_result == OEMCrypto_WARNING_MIXED_OUTPUT_PROTECTION || - actual_result == OEMCrypto_ERROR_INSUFFICIENT_HDCP); - } else if (expected_result == OEMCrypto_ERROR_INSUFFICIENT_HDCP) { - // Report HDCP errors. - ASSERT_EQ(OEMCrypto_ERROR_INSUFFICIENT_HDCP, actual_result); - } else if (expected_result == OEMCrypto_ERROR_ANALOG_OUTPUT) { - // Report analog errors. - ASSERT_EQ(OEMCrypto_ERROR_ANALOG_OUTPUT, actual_result); + ASSERT_TRUE( + actual_select_result == OEMCrypto_WARNING_MIXED_OUTPUT_PROTECTION || + actual_select_result == OEMCrypto_ERROR_INSUFFICIENT_HDCP || + actual_decrypt_result == OEMCrypto_WARNING_MIXED_OUTPUT_PROTECTION || + actual_decrypt_result == OEMCrypto_ERROR_INSUFFICIENT_HDCP); } else { // OEM's can fine tune other error codes for debugging. - ASSERT_NE(OEMCrypto_SUCCESS, actual_result); + ASSERT_TRUE(actual_select_result != OEMCrypto_SUCCESS || + actual_decrypt_result != OEMCrypto_SUCCESS); } } @@ -1398,25 +1402,20 @@ void Session::VerifyPST(const Test_PST_Report& expected) { SHA_DIGEST_LENGTH)); } -// This might adjust t to be "seconds since now". If t is small, we assume it -// is "seconds since now", but if the value of t is large, assume it is -// "absolute time" and convert to "seconds since now". -static int64_t MaybeAdjustTime(int64_t t, int64_t now) { - int64_t k10Minutes = 60 * 10; // in seconds. - if (t > k10Minutes) return now - t; - return t; -} - void Session::VerifyReport(Test_PST_Report expected, int64_t time_license_received, int64_t time_first_decrypt, int64_t time_last_decrypt) { const int64_t now = wvcdm::Clock().GetCurrentTime(); - expected.seconds_since_license_received = - MaybeAdjustTime(time_license_received, now); + expected.seconds_since_license_received = now - time_license_received; expected.seconds_since_first_decrypt = - MaybeAdjustTime(time_first_decrypt, now); - expected.seconds_since_last_decrypt = MaybeAdjustTime(time_last_decrypt, now); + (time_first_decrypt > 0 && time_first_decrypt < now) + ? now - time_first_decrypt + : 0; + expected.seconds_since_last_decrypt = + (time_last_decrypt > 0 && time_last_decrypt < now) + ? now - time_last_decrypt + : 0; ASSERT_NO_FATAL_FAILURE(VerifyPST(expected)); } } // namespace wvoec diff --git a/oemcrypto/test/oec_session_util.h b/oemcrypto/test/oec_session_util.h index 7924205..2e8a58e 100644 --- a/oemcrypto/test/oec_session_util.h +++ b/oemcrypto/test/oec_session_util.h @@ -167,8 +167,8 @@ class RoundTrip { virtual void SignAndVerifyRequest(); // Create a default |response_data| and |core_response|. virtual void CreateDefaultResponse() = 0; - // Copy fields from |reponse_data| to |padded_response_data|, encrypting those - // that should be encrypted. Serialize the core message. Then sign the + // Copy fields from |response_data| to |padded_response_data|, encrypting + // those that should be encrypted. Serialize the core message. Then sign the // response. virtual void EncryptAndSignResponse() = 0; // Attempt to load the response and return the error. Short buffer errors are @@ -460,10 +460,6 @@ class Session { void TestDecryptCTR(bool select_key_first = true, OEMCryptoResult expected_result = OEMCrypto_SUCCESS, int key_index = 0); - // This compares the actual result with the expected result. If OEMCrypto is - // an older version, we allow it to report an equivalent error code. - void TestDecryptResult(OEMCryptoResult expected_result, - OEMCryptoResult actual_result); // Verify that an attempt to select an expired key either succeeds, or gives // an actionable error code. void TestSelectExpired(unsigned int key_index); @@ -572,6 +568,12 @@ class Session { } private: + // This compares the actual result with the expected result. If OEMCrypto is + // an older version, we allow it to report an equivalent error code. + void TestDecryptResult(OEMCryptoResult expected_result, + OEMCryptoResult actual_select_result, + OEMCryptoResult actual_decryt_result); + bool open_; bool forced_session_id_; OEMCrypto_SESSION session_id_; diff --git a/oemcrypto/test/oemcrypto_test.cpp b/oemcrypto/test/oemcrypto_test.cpp index 50ca22e..27b023b 100644 --- a/oemcrypto/test/oemcrypto_test.cpp +++ b/oemcrypto/test/oemcrypto_test.cpp @@ -816,7 +816,7 @@ class OEMCryptoLicenseTest : public OEMCryptoLicenseTestAPI16, public WithParamInterface { protected: void SetUp() override { - // The only difference between this class and it's parent is that we use a + // The only difference between this class and its parent is that we use a // different license api: license_api_version_ = GetParam(); license_messages_.set_api_version(license_api_version_); @@ -827,7 +827,7 @@ class OEMCryptoLicenseTest : public OEMCryptoLicenseTestAPI16, // This class is used to test a license that is only for v15 license. class OEMCryptoLicenseTestAPI15 : public OEMCryptoLicenseTestAPI16 { void SetUp() override { - // The only difference between this class and it's parent is that we use a + // The only difference between this class and its parent is that we use a // different license api: license_api_version_ = 15; license_messages_.set_api_version(license_api_version_); @@ -2037,6 +2037,9 @@ class OEMCryptoSessionTestsDecryptTests switch (output_descriptor.type) { case OEMCrypto_BufferType_Clear: if (decrypt_inplace_) { + // Add some padding to verify there is no overrun. + sample.encrypted_buffer.resize(total_size + kBufferOverrunPadding, + 0xaa); output_descriptor.buffer.clear.address = sample.encrypted_buffer.data(); } else { @@ -2227,15 +2230,20 @@ class OEMCryptoSessionTestsDecryptTests for (TestSample& sample : samples_) { if (sample.description.buffers.output_descriptor.type == OEMCrypto_BufferType_Clear) { + const size_t total_size = sample.description.buffers.input_data_length; + // To verify there is no buffer overrun after decrypting, look at the + // padded bytes just after the data buffer that was written. It should + // not have changed from the original 0xaa that we set in MakeBuffer + // function. if (decrypt_inplace_) { + EXPECT_EQ(std::count(sample.encrypted_buffer.begin() + total_size, + sample.encrypted_buffer.end(), 0xaa), + static_cast(kBufferOverrunPadding)) + << "Buffer overrun."; + sample.encrypted_buffer.resize(total_size); // Remove padding. // We expect encrypted buffer to have been changed by OEMCrypto. EXPECT_EQ(sample.encrypted_buffer, sample.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_. - const size_t total_size = - sample.description.buffers.input_data_length; EXPECT_EQ(std::count(sample.clear_buffer.begin() + total_size, sample.clear_buffer.end(), 0xaa), static_cast(kBufferOverrunPadding)) @@ -2337,8 +2345,17 @@ TEST_P(OEMCryptoSessionTestsDecryptTests, EvenOffset) { TestSample& sample = samples_[0]; // There is only one sample in this test sample.truth_buffer.assign(sample.description.buffers.input_data_length, 0); ASSERT_NO_FATAL_FAILURE(EncryptData()); - sample.truth_buffer = - sample.encrypted_buffer; // truth_buffer_ = encrypted zero buffer. + if (decrypt_inplace_) { + const size_t total_size = sample.description.buffers.input_data_length; + // In case of decrypt_inplace_, encrypted_buffer contains padded bytes + // which is used for buffer overrun validation. Do not copy the padded + // bytes to truth_buffer. + sample.truth_buffer.assign(sample.encrypted_buffer.begin(), + sample.encrypted_buffer.begin() + total_size); + } else { + sample.truth_buffer = + sample.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()); @@ -2536,7 +2553,9 @@ INSTANTIATE_TEST_CASE_P( Combine( Values(MakePattern(3, 7), MakePattern(9, 1), // HLS edge cases. We should follow the CENC spec, not HLS spec. - MakePattern(1, 9), MakePattern(1, 0)), + MakePattern(1, 9), MakePattern(1, 0), + // AV1 patterns not already covered above. + MakePattern(5, 5), MakePattern(10, 0)), Values(OEMCrypto_CipherMode_CBC), ::testing::ValuesIn(global_features.GetOutputTypes()))); @@ -4596,8 +4615,7 @@ TEST_P(OEMCryptoGenericCryptoTest, KeyDurationEncrypt) { OEMCryptoResult status = OEMCrypto_Generic_Encrypt( session_.session_id(), clear_buffer_.data(), clear_buffer_.size(), iv_, OEMCrypto_AES_CBC_128_NO_PADDING, encrypted.data()); - ASSERT_NO_FATAL_FAILURE( - session_.TestDecryptResult(OEMCrypto_ERROR_KEY_EXPIRED, status)); + ASSERT_EQ(OEMCrypto_ERROR_KEY_EXPIRED, status); ASSERT_NE(encrypted, expected_encrypted); ASSERT_NO_FATAL_FAILURE(session_.TestSelectExpired(key_index)); } @@ -4631,8 +4649,7 @@ TEST_P(OEMCryptoGenericCryptoTest, KeyDurationDecrypt) { OEMCryptoResult status = OEMCrypto_Generic_Decrypt( session_.session_id(), encrypted.data(), encrypted.size(), iv_, OEMCrypto_AES_CBC_128_NO_PADDING, resultant.data()); - ASSERT_NO_FATAL_FAILURE( - session_.TestDecryptResult(OEMCrypto_ERROR_KEY_EXPIRED, status)); + ASSERT_EQ(OEMCrypto_ERROR_KEY_EXPIRED, status); ASSERT_NE(clear_buffer_, resultant); ASSERT_NO_FATAL_FAILURE(session_.TestSelectExpired(key_index)); } @@ -4668,8 +4685,7 @@ TEST_P(OEMCryptoGenericCryptoTest, KeyDurationSign) { OEMCryptoResult status = OEMCrypto_Generic_Sign( session_.session_id(), clear_buffer_.data(), clear_buffer_.size(), OEMCrypto_HMAC_SHA256, signature.data(), &signature_length); - ASSERT_NO_FATAL_FAILURE( - session_.TestDecryptResult(OEMCrypto_ERROR_KEY_EXPIRED, status)); + ASSERT_EQ(OEMCrypto_ERROR_KEY_EXPIRED, status); ASSERT_NE(expected_signature, signature); ASSERT_NO_FATAL_FAILURE(session_.TestSelectExpired(key_index)); } @@ -4702,8 +4718,7 @@ TEST_P(OEMCryptoGenericCryptoTest, KeyDurationVerify) { OEMCryptoResult status = OEMCrypto_Generic_Verify( session_.session_id(), clear_buffer_.data(), clear_buffer_.size(), OEMCrypto_HMAC_SHA256, signature.data(), signature.size()); - ASSERT_NO_FATAL_FAILURE( - session_.TestDecryptResult(OEMCrypto_ERROR_KEY_EXPIRED, status)); + ASSERT_EQ(OEMCrypto_ERROR_KEY_EXPIRED, status); ASSERT_NO_FATAL_FAILURE(session_.TestSelectExpired(key_index)); } @@ -4803,11 +4818,15 @@ INSTANTIATE_TEST_CASE_P(TestAll, OEMCryptoGenericCryptoKeyIdLengthTest, Range(kCurrentAPI - 1, kCurrentAPI + 1)); // Test usage table functionality. - class LicenseWithUsageEntry { public: LicenseWithUsageEntry(const std::string& pst = "my_pst") - : session_(), license_messages_(&session_), generic_crypto_(false) { + : session_(), + license_messages_(&session_), + generic_crypto_(false), + time_license_received_(0), + time_first_decrypt_(0), + time_last_decrypt_(0) { license_messages_.set_pst(pst); } @@ -4850,6 +4869,7 @@ class LicenseWithUsageEntry { ASSERT_NO_FATAL_FAILURE(session_.CreateNewUsageEntry(status)); if (status != nullptr && *status != OEMCrypto_SUCCESS) return; ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); + time_license_received_ = wvcdm::Clock().GetCurrentTime(); } void OpenAndReload(SessionUtil* util) { @@ -4859,15 +4879,20 @@ class LicenseWithUsageEntry { ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); } - void GenerateVerifyReport(OEMCrypto_Usage_Entry_Status status, - int64_t time_license_received = 0, - int64_t time_first_decrypt = 0, - int64_t time_last_decrypt = 0) { + // Test decrypt, and update the decrypt times for the pst report. + void TestDecryptCTR(bool select_key_first = true, + OEMCryptoResult expected_result = OEMCrypto_SUCCESS) { + session_.TestDecryptCTR(select_key_first, expected_result); + time_last_decrypt_ = wvcdm::Clock().GetCurrentTime(); + if (time_first_decrypt_ == 0) time_first_decrypt_ = time_last_decrypt_; + } + + void GenerateVerifyReport(OEMCrypto_Usage_Entry_Status status) { ASSERT_NO_FATAL_FAILURE(session_.GenerateReport(pst())); Test_PST_Report expected(pst(), status); ASSERT_NO_FATAL_FAILURE( - session_.VerifyReport(expected, time_license_received, - time_first_decrypt, time_last_decrypt)); + session_.VerifyReport(expected, time_license_received_, + time_first_decrypt_, time_last_decrypt_)); // The PST report was signed above. Below we verify that the entire message // that is sent to the server will be signed by the right mac keys. RenewalRoundTrip renewal_messages(&license_messages_); @@ -4892,6 +4917,9 @@ class LicenseWithUsageEntry { Session session_; LicenseRoundTrip license_messages_; bool generic_crypto_; + int64_t time_license_received_; + int64_t time_first_decrypt_; + int64_t time_last_decrypt_; }; class OEMCryptoUsageTableTest : public OEMCryptoGenericCryptoTest { @@ -4940,7 +4968,7 @@ TEST_P(OEMCryptoUsageTableTest, OnlineLicense) { ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kUnused)); ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kUnused)); ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kUnused)); - ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); + ASSERT_NO_FATAL_FAILURE(entry.TestDecryptCTR()); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kActive)); // Flag the entry as inactive. @@ -4950,7 +4978,7 @@ TEST_P(OEMCryptoUsageTableTest, OnlineLicense) { ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUsed)); // Decrypt should fail. ASSERT_NO_FATAL_FAILURE( - s.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE)); + entry.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE)); // We could call DeactivateUsageEntry multiple times. The state should not // change. ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(entry.pst())); @@ -4976,7 +5004,7 @@ TEST_P(OEMCryptoUsageTableTest, OnlineLicenseUnused) { ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUnused)); // Decrypt should fail. ASSERT_NO_FATAL_FAILURE( - s.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE)); + entry.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE)); // We could call DeactivateUsageEntry multiple times. The state should not // change. ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(entry.pst())); @@ -4994,7 +5022,7 @@ TEST_P(OEMCryptoUsageTableTest, ForbidReportWithNoUpdate) { Session& s = entry.session(); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kUnused)); - ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); + ASSERT_NO_FATAL_FAILURE(entry.TestDecryptCTR()); // Cannot generate a report without first updating the file. ASSERT_NO_FATAL_FAILURE( s.GenerateReport(entry.pst(), OEMCrypto_ERROR_ENTRY_NEEDS_UPDATE)); @@ -5008,7 +5036,7 @@ TEST_P(OEMCryptoUsageTableTest, ForbidReportWithNoUpdate) { s.GenerateReport(entry.pst(), OEMCrypto_ERROR_ENTRY_NEEDS_UPDATE)); // Decrypt should fail. ASSERT_NO_FATAL_FAILURE( - s.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE)); + entry.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE)); } // Test an online license with a license renewal. @@ -5017,18 +5045,13 @@ TEST_P(OEMCryptoUsageTableTest, OnlineLicenseWithRefreshAPI16) { entry.license_messages().set_api_version(license_api_version_); entry.MakeAndLoadOnline(this); Session& s = entry.session(); - const int64_t loaded = wvcdm::Clock().GetCurrentTime(); - ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); + ASSERT_NO_FATAL_FAILURE(entry.TestDecryptCTR()); RenewalRoundTrip renewal_messages(&entry.license_messages()); MakeRenewalRequest(&renewal_messages); LoadRenewal(&renewal_messages, OEMCrypto_SUCCESS); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); - ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport( - kActive, - loaded, // when license loaded. (not refreshed) - loaded, // first decrypt. - 0)); // last decrypt is now. + ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kActive)); } // Verify that a streaming license cannot be reloaded. @@ -5314,19 +5337,14 @@ TEST_P(OEMCryptoUsageTableTest, OfflineLicenseRefresh) { entry.MakeAndLoad(this, wvoec::kControlNonceOrEntry); Session& s = entry.session(); - const int64_t loaded = wvcdm::Clock().GetCurrentTime(); - ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); + ASSERT_NO_FATAL_FAILURE(entry.TestDecryptCTR()); // License renewal message is signed by client and verified by the server. RenewalRoundTrip renewal_messages(&entry.license_messages()); MakeRenewalRequest(&renewal_messages); LoadRenewal(&renewal_messages, OEMCrypto_SUCCESS); - ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); + ASSERT_NO_FATAL_FAILURE(entry.TestDecryptCTR()); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); - ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport( - kActive, - loaded, // license received. - loaded, // First decrypt when loaded, not refresh. - 0)); // last decrypt now. + ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kActive)); } // Test that an offline license can be reloaded in a new session. @@ -5338,7 +5356,7 @@ TEST_P(OEMCryptoUsageTableTest, ReloadOfflineLicense) { ASSERT_NO_FATAL_FAILURE(entry.OpenAndReload(this)); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kUnused)); - ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); + ASSERT_NO_FATAL_FAILURE(entry.TestDecryptCTR()); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kActive)); } @@ -5350,24 +5368,18 @@ TEST_P(OEMCryptoUsageTableTest, ReloadOfflineLicenseWithRefresh) { entry.license_messages().set_api_version(license_api_version_); entry.MakeOfflineAndClose(this); Session& s = entry.session(); - const int64_t loaded = wvcdm::Clock().GetCurrentTime(); ASSERT_NO_FATAL_FAILURE(entry.OpenAndReload(this)); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); - ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kUnused, loaded, 0, 0)); - ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); - const int64_t decrypt_time = wvcdm::Clock().GetCurrentTime(); + ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kUnused)); + ASSERT_NO_FATAL_FAILURE(entry.TestDecryptCTR()); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); RenewalRoundTrip renewal_messages(&entry.license_messages()); MakeRenewalRequest(&renewal_messages); LoadRenewal(&renewal_messages, OEMCrypto_SUCCESS); - ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); + ASSERT_NO_FATAL_FAILURE(entry.TestDecryptCTR()); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); - ASSERT_NO_FATAL_FAILURE( - entry.GenerateVerifyReport(kActive, - loaded, // license loaded. - decrypt_time, // first decrypt - 0)); // last decrypt + ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kActive)); } // Verify that we can still reload an offline license after OEMCrypto_Terminate @@ -5387,7 +5399,7 @@ TEST_P(OEMCryptoUsageTableTest, ReloadOfflineLicenseWithTerminate) { ASSERT_NO_FATAL_FAILURE(entry.OpenAndReload(this)); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kUnused)); - ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); + ASSERT_NO_FATAL_FAILURE(entry.TestDecryptCTR()); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kActive)); } @@ -5400,7 +5412,6 @@ TEST_P(OEMCryptoUsageTableTest, BadReloadOfflineLicense) { entry.license_messages().set_api_version(license_api_version_); entry.MakeOfflineAndClose(this); Session& s = entry.session(); - const int64_t loaded = wvcdm::Clock().GetCurrentTime(); // Offline license with new mac keys should fail. Session s2; @@ -5421,11 +5432,7 @@ TEST_P(OEMCryptoUsageTableTest, BadReloadOfflineLicense) { // Now we go back to the original license response. It should load OK. ASSERT_NO_FATAL_FAILURE(entry.OpenAndReload(this)); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); - ASSERT_NO_FATAL_FAILURE( - entry.GenerateVerifyReport(kUnused, - loaded, // license loaded. - 0, - 0)); // first and last decrypt + ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kUnused)); } // An offline license should not load on the first call if the nonce is bad. @@ -5495,12 +5502,13 @@ TEST_P(OEMCryptoUsageTableTest, DeactivateOfflineLicense) { Session& s = entry.session(); // Reload the offline license. ASSERT_NO_FATAL_FAILURE(entry.OpenAndReload(this)); - ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); // Should be able to decrypt. + ASSERT_NO_FATAL_FAILURE( + entry.TestDecryptCTR()); // Should be able to decrypt. ASSERT_NO_FATAL_FAILURE( s.DeactivateUsageEntry(entry.pst())); // Then deactivate. // After deactivate, should not be able to decrypt. ASSERT_NO_FATAL_FAILURE( - s.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE)); + entry.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE)); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUsed)); ASSERT_NO_FATAL_FAILURE(s.close()); @@ -5540,7 +5548,7 @@ TEST_P(OEMCryptoUsageTableTest, DeactivateOfflineLicenseUnused) { s.DeactivateUsageEntry(entry.pst())); // Then deactivate. // After deactivate, should not be able to decrypt. ASSERT_NO_FATAL_FAILURE( - s.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE)); + entry.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE)); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUnused)); ASSERT_NO_FATAL_FAILURE(s.close()); @@ -5591,14 +5599,13 @@ TEST_P(OEMCryptoUsageTableTest, UpdateFailsWithNullPtr) { // Class used to test usage table defragmentation. class OEMCryptoUsageTableDefragTest : public OEMCryptoUsageTableTest { protected: - void ReloadLicense(LicenseWithUsageEntry* entry, int64_t start) { + void ReloadLicense(LicenseWithUsageEntry* entry) { Session& s = entry->session(); ASSERT_NO_FATAL_FAILURE(entry->OpenAndReload(this)); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); - ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); + ASSERT_NO_FATAL_FAILURE(entry->TestDecryptCTR()); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); - ASSERT_NO_FATAL_FAILURE( - entry->GenerateVerifyReport(kActive, start, start, 0)); + ASSERT_NO_FATAL_FAILURE(entry->GenerateVerifyReport(kActive)); ASSERT_NO_FATAL_FAILURE(s.close()); } @@ -5640,13 +5647,11 @@ class OEMCryptoUsageTableDefragTest : public OEMCryptoUsageTableTest { TEST_P(OEMCryptoUsageTableDefragTest, MoveUsageEntries) { const size_t ENTRY_COUNT = 10; vector entries(ENTRY_COUNT); - vector start(ENTRY_COUNT); for (size_t i = 0; i < ENTRY_COUNT; i++) { entries[i].set_pst("pst " + std::to_string(i)); ASSERT_NO_FATAL_FAILURE(entries[i].MakeOfflineAndClose(this)) << "On license " << i << " pst=" << entries[i].pst(); wvcdm::TestSleep::SyncFakeClock(); - start[i] = wvcdm::Clock().GetCurrentTime(); } for (size_t i = 0; i < ENTRY_COUNT; i++) { ASSERT_NO_FATAL_FAILURE(entries[i].OpenAndReload(this)) @@ -5665,10 +5670,10 @@ TEST_P(OEMCryptoUsageTableDefragTest, MoveUsageEntries) { OEMCrypto_LoadUsageTableHeader(encrypted_usage_header_.data(), encrypted_usage_header_.size())); wvcdm::TestSleep::SyncFakeClock(); - ASSERT_NO_FATAL_FAILURE(ReloadLicense(&entries[0], start[0])); + ASSERT_NO_FATAL_FAILURE(ReloadLicense(&entries[0])); // Now has index 1. - ASSERT_NO_FATAL_FAILURE(ReloadLicense(&entries[4], start[4])); - ASSERT_NO_FATAL_FAILURE(ReloadLicense(&entries[2], start[2])); + ASSERT_NO_FATAL_FAILURE(ReloadLicense(&entries[4])); + ASSERT_NO_FATAL_FAILURE(ReloadLicense(&entries[2])); // When 4 was moved to 1, it increased the gen. number in the header. ASSERT_NO_FATAL_FAILURE( FailReloadLicense(&entries[1], OEMCrypto_ERROR_GENERATION_SKEW)); @@ -5853,11 +5858,12 @@ TEST_P(OEMCryptoUsageTableDefragTest, ManyUsageEntries) { } // Make sure that all of the licenses can be reloaded. for (size_t i = 0; i < entries.size(); i++) { + wvcdm::TestSleep::SyncFakeClock(); Session& s = entries[i]->session(); ASSERT_NO_FATAL_FAILURE(entries[i]->OpenAndReload(this)); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(entries[i]->GenerateVerifyReport(kUnused)); - ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); + ASSERT_NO_FATAL_FAILURE(entries[i]->TestDecryptCTR()); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(entries[i]->GenerateVerifyReport(kActive)); ASSERT_NO_FATAL_FAILURE(s.close()); @@ -5970,25 +5976,19 @@ TEST_P(OEMCryptoUsageTableTest, TimingTest) { ASSERT_NO_FATAL_FAILURE(entry3.MakeOfflineAndClose(this)); ASSERT_NO_FATAL_FAILURE(entry1.MakeOfflineAndClose(this)); - const int64_t loaded1 = wvcdm::Clock().GetCurrentTime(); ASSERT_NO_FATAL_FAILURE(entry2.MakeOfflineAndClose(this)); - const int64_t loaded2 = wvcdm::Clock().GetCurrentTime(); ASSERT_NO_FATAL_FAILURE(entry3.MakeOfflineAndClose(this)); - const int64_t loaded3 = wvcdm::Clock().GetCurrentTime(); wvcdm::TestSleep::Sleep(kLongSleep); ASSERT_NO_FATAL_FAILURE(entry1.OpenAndReload(this)); - const int64_t first_decrypt1 = wvcdm::Clock().GetCurrentTime(); - ASSERT_NO_FATAL_FAILURE(s1.TestDecryptCTR()); + ASSERT_NO_FATAL_FAILURE(entry1.TestDecryptCTR()); ASSERT_NO_FATAL_FAILURE(entry2.OpenAndReload(this)); - const int64_t first_decrypt2 = wvcdm::Clock().GetCurrentTime(); - ASSERT_NO_FATAL_FAILURE(s2.TestDecryptCTR()); + ASSERT_NO_FATAL_FAILURE(entry2.TestDecryptCTR()); wvcdm::TestSleep::Sleep(kLongSleep); - const int64_t second_decrypt = wvcdm::Clock().GetCurrentTime(); - ASSERT_NO_FATAL_FAILURE(s1.TestDecryptCTR()); - ASSERT_NO_FATAL_FAILURE(s2.TestDecryptCTR()); + ASSERT_NO_FATAL_FAILURE(entry1.TestDecryptCTR()); + ASSERT_NO_FATAL_FAILURE(entry2.TestDecryptCTR()); wvcdm::TestSleep::Sleep(kLongSleep); ASSERT_NO_FATAL_FAILURE(s1.DeactivateUsageEntry(entry1.pst())); @@ -6008,9 +6008,8 @@ TEST_P(OEMCryptoUsageTableTest, TimingTest) { // After a reboot, we should be able to reload keys, and generate reports. wvcdm::TestSleep::Sleep(kLongSleep); - const int64_t third_decrypt = wvcdm::Clock().GetCurrentTime(); ASSERT_NO_FATAL_FAILURE(entry2.OpenAndReload(this)); - ASSERT_NO_FATAL_FAILURE(s2.TestDecryptCTR()); + ASSERT_NO_FATAL_FAILURE(entry2.TestDecryptCTR()); ASSERT_NO_FATAL_FAILURE(s2.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(s2.close()); @@ -6028,13 +6027,11 @@ TEST_P(OEMCryptoUsageTableTest, TimingTest) { entry3.license_messages().set_api_version(global_features.api_version); wvcdm::TestSleep::Sleep(kLongSleep); ASSERT_NO_FATAL_FAILURE(s1.UpdateUsageEntry(&encrypted_usage_header_)); - ASSERT_NO_FATAL_FAILURE(entry1.GenerateVerifyReport( - kInactiveUsed, loaded1, first_decrypt1, second_decrypt)); + ASSERT_NO_FATAL_FAILURE(entry1.GenerateVerifyReport(kInactiveUsed)); ASSERT_NO_FATAL_FAILURE(s2.UpdateUsageEntry(&encrypted_usage_header_)); - ASSERT_NO_FATAL_FAILURE(entry2.GenerateVerifyReport( - kActive, loaded2, first_decrypt2, third_decrypt)); + ASSERT_NO_FATAL_FAILURE(entry2.GenerateVerifyReport(kActive)); ASSERT_NO_FATAL_FAILURE(s3.UpdateUsageEntry(&encrypted_usage_header_)); - ASSERT_NO_FATAL_FAILURE(entry3.GenerateVerifyReport(kUnused, loaded3)); + ASSERT_NO_FATAL_FAILURE(entry3.GenerateVerifyReport(kUnused)); } // Verify the times in the usage report. For performance reasons, we allow the @@ -6047,7 +6044,6 @@ TEST_P(OEMCryptoUsageTableTest, VerifyUsageTimes) { entry.license_messages().set_api_version(license_api_version_); entry.MakeAndLoadOnline(this); Session& s = entry.session(); - const int64_t load_time = wvcdm::Clock().GetCurrentTime(); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kUnused)); @@ -6067,18 +6063,16 @@ TEST_P(OEMCryptoUsageTableTest, VerifyUsageTimes) { PrintDotsWhileSleep(kIdleInSeconds, kDotIntervalInSeconds); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); - ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kUnused, load_time)); + ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kUnused)); cout << "Start simulated playback..." << endl; int64_t dot_time = kDotIntervalInSeconds; int64_t playback_time = 0; const int64_t start_time = wvcdm::Clock().GetCurrentTime(); do { - ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); + ASSERT_NO_FATAL_FAILURE(entry.TestDecryptCTR()); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); - ASSERT_NO_FATAL_FAILURE( - entry.GenerateVerifyReport(kActive, load_time, start_time, - 0)); // last decrypt = now. + ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kActive)); wvcdm::TestSleep::Sleep(kShortSleep); playback_time = wvcdm::Clock().GetCurrentTime() - start_time; ASSERT_LE(0, playback_time); @@ -6091,9 +6085,7 @@ TEST_P(OEMCryptoUsageTableTest, VerifyUsageTimes) { cout << "\nSimulated playback time = " << playback_time << " seconds.\n"; ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); - ASSERT_NO_FATAL_FAILURE( - entry.GenerateVerifyReport(kActive, load_time, start_time, - 0)); // last decrypt = now. + ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kActive)); EXPECT_NEAR(s.pst_report().seconds_since_first_decrypt() - s.pst_report().seconds_since_last_decrypt(), playback_time, kUsageTableTimeTolerance); @@ -6111,14 +6103,12 @@ TEST_P(OEMCryptoUsageTableTest, VerifyUsageTimes) { // |<----------------------------->| = seconds_since_first_decrypt // |<------------------------------------| = seconds_since_license_received ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); - ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport( - kActive, load_time, start_time, kIdleInSeconds)); + ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kActive)); ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(entry.pst())); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); - ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport( - kInactiveUsed, load_time, start_time, kIdleInSeconds)); + ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUsed)); ASSERT_NO_FATAL_FAILURE( - s.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE)); + entry.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE)); } // This test class is only used to roll back the wall clock. It is used to @@ -6190,7 +6180,7 @@ TEST_P(OEMCryptoUsageTableTestWallClock, TimeRollbackPrevention) { // Monotonic clock can't be changed. We use this since system clock will be // unreliable. const auto first_decrypt_monotonic = monotonic_clock.now(); - ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); + ASSERT_NO_FATAL_FAILURE(entry.TestDecryptCTR()); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(s.close()); @@ -6198,7 +6188,7 @@ TEST_P(OEMCryptoUsageTableTestWallClock, TimeRollbackPrevention) { wvcdm::TestSleep::Sleep(kLongDuration * 2); ASSERT_NO_FATAL_FAILURE(entry.OpenAndReload(this)); - ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); + ASSERT_NO_FATAL_FAILURE(entry.TestDecryptCTR()); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(s.close()); @@ -6210,7 +6200,7 @@ TEST_P(OEMCryptoUsageTableTestWallClock, TimeRollbackPrevention) { // Try to playback again. ASSERT_NO_FATAL_FAILURE(entry.OpenAndReload(this)); const auto third_decrypt_monotonic = monotonic_clock.now(); - ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); + ASSERT_NO_FATAL_FAILURE(entry.TestDecryptCTR()); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(s.close()); Test_PST_Report expected(entry.pst(), kActive); @@ -6239,12 +6229,13 @@ TEST_P(OEMCryptoUsageTableTest, PSTLargeBuffer) { Session& s = entry.session(); ASSERT_NO_FATAL_FAILURE(entry.OpenAndReload(this)); - ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); // Should be able to decrypt. + ASSERT_NO_FATAL_FAILURE( + entry.TestDecryptCTR()); // Should be able to decrypt. ASSERT_NO_FATAL_FAILURE( s.DeactivateUsageEntry(entry.pst())); // Then deactivate. // After deactivate, should not be able to decrypt. ASSERT_NO_FATAL_FAILURE( - s.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE)); + entry.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE)); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUsed)); ASSERT_NO_FATAL_FAILURE(s.close()); diff --git a/util/include/cdm_random.h b/util/include/cdm_random.h index dd3767c..bc97840 100644 --- a/util/include/cdm_random.h +++ b/util/include/cdm_random.h @@ -14,9 +14,18 @@ namespace wvcdm { // 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. +// actual value supplied may not be random. The generator is designed to +// meet the C++ named requirement UniformRandomBitGenerator to allow it to +// be used with standard library functions / class which are designed to +// work with the standard library generators. class CdmRandomGenerator { public: + // Result type of operator(). + using result_type = unsigned int; + // Inclusive boundaries of operator(). + static constexpr unsigned int min() { return 0; } + static constexpr unsigned int max() { return RAND_MAX; } + // The maximum number of bytes that can be generated at once for // `RandomData()`. static constexpr size_t kMaxRandomDataLength = 8192; // 8 kB @@ -44,12 +53,11 @@ class CdmRandomGenerator { // 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(); + // returned is in the range of [min(), max()]. + unsigned int Rand(); - // Allows for RNG to be callable, this is to make it similar to the - // C++11 generator interfaces. - int operator()() { return Rand(); } + // Allows for RNG to be callable. + unsigned int operator()() { return Rand(); } // Returns a pseudo-random integer within the provided inclusive range. uint64_t RandomInRange(uint64_t lower, uint64_t upper); @@ -79,7 +87,7 @@ class CdmRandomGenerator { // CdmRandomGenerator. class CdmRandom { public: - static int Rand() { return GetInstance()->Rand(); } + static unsigned int Rand() { return GetInstance()->Rand(); } static uint64_t RandomInRange(uint64_t lower, uint64_t upper) { return GetInstance()->RandomInRange(lower, upper); } diff --git a/util/src/cdm_random.cpp b/util/src/cdm_random.cpp index 591ac5f..751836e 100644 --- a/util/src/cdm_random.cpp +++ b/util/src/cdm_random.cpp @@ -46,9 +46,9 @@ void CdmRandomGenerator::Seed(unsigned int s) { generator_.seed(s); } -int CdmRandomGenerator::Rand() { +unsigned int CdmRandomGenerator::Rand() { CdmRandomLock lock(generator_lock_); - std::uniform_int_distribution dist(0, RAND_MAX); + std::uniform_int_distribution dist(0, RAND_MAX); return dist(generator_); } diff --git a/util/test/cdm_random_unittest.cpp b/util/test/cdm_random_unittest.cpp index 22706e5..8555524 100644 --- a/util/test/cdm_random_unittest.cpp +++ b/util/test/cdm_random_unittest.cpp @@ -4,10 +4,13 @@ #include +#include #include #include #include #include +#include +#include #include #include @@ -30,6 +33,43 @@ constexpr unsigned int kSeeds[] = {0, 1337, 1565904109, 776964657}; class CdmRandomGeneratorTest : public testing::TestWithParam {}; } // namespace +// Checks that the class CdmRandomGenerator meets the requirements of +// UniformRandomBitGenerator. +// ref: https://en.cppreference.com/w/cpp/named_req/UniformRandomBitGenerator +TEST(CdmRandomGeneratorTest, UniformRandomBitGeneratorRequirements) { + // Let G represent class CdmRandomGenerator, and g represent an instance + // of CdmRandomGenerator. + // 1) G::result_type is an unsigned integer (unspecified precision). + EXPECT_TRUE(std::is_integral::value); + EXPECT_TRUE(std::is_unsigned::value); + // 2&3 a) G::min() and G::max() have the result type of G::result_type. + EXPECT_TRUE((std::is_same::value)); + EXPECT_TRUE((std::is_same::value)); + // 2&3 b) G::min() is strictly less than G::max(). + EXPECT_LT(CdmRandomGenerator::min(), CdmRandomGenerator::max()); + // 4 a) g() have the result type of G::result_type. + CdmRandomGenerator g; + EXPECT_TRUE( + (std::is_same::value)); + // 4 b) g() is within [G::min() G::max()] + std::vector values; + for (size_t i = 0; i < kRandomTrialCount; ++i) { + CdmRandomGenerator::result_type x = g(); + EXPECT_LE(CdmRandomGenerator::min(), x); + EXPECT_GE(CdmRandomGenerator::max(), x); + values.push_back(x); + } + + // Verify compilation. + + // std::shuffle(RandomIt first, RandomIt last, URBG&& g) requires the + // class URBG to meet "UniformRandomBitGenerator" requirements. This + // will fail to compile if the requirements are not met. + std::shuffle(values.begin(), values.end(), CdmRandomGenerator()); +} + TEST_P(CdmRandomGeneratorTest, AllMethods) { const unsigned int seed = GetParam(); CdmRandomGenerator rng;