OEMCrypto v16.1 -- update ODK

This CL updates the ODK library to address review comments.
This commit is contained in:
Fred Gylys-Colwell
2020-02-01 11:56:34 -08:00
parent 858fa33cd7
commit 9fa2cbdb67
38 changed files with 1121 additions and 268 deletions

View File

@@ -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

84
oemcrypto/odk/Android.bp Normal file
View File

@@ -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]/<name>/<name>
// - 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",
],
}

View File

@@ -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 <stdbool.h>
#include <stddef.h>
@@ -152,4 +152,4 @@ typedef struct {
}
#endif
#endif /* ...ODK_INCLUDE_OEMCRYPTOCENCCOMMON_H_ */
#endif /* WIDEVINE_ODK_INCLUDE_OEMCRYPTOCENCCOMMON_H_ */

View File

@@ -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_ */

View File

@@ -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_ */

View File

@@ -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 <cstdint>
#include <string>
@@ -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_ */

View File

@@ -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 <cstdint>
#include <string>
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_ */

View File

@@ -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 <stdint.h>
@@ -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_ */

View File

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

View File

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

View File

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

View File

@@ -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 <stdint.h>
@@ -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_ */

View File

@@ -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 <stdint.h>
#include "OEMCryptoCENCCommon.h"
#include "odk_structs.h"
typedef enum {
ODK_License_Request_Type = 1,
ODK_License_Response_Type = 2,
ODK_Renewal_Request_Type = 3,
ODK_Renewal_Response_Type = 4,
ODK_Provisioning_Request_Type = 5,
ODK_Provisioning_Response_Type = 6,
} ODK_MessageType;
typedef struct {
uint32_t message_type;
uint32_t message_length;
ODK_NonceValues nonce_values;
} ODK_CoreMessage;
typedef struct {
ODK_CoreMessage core_message;
} ODK_PreparedLicense;
typedef struct {
ODK_CoreMessage core_message;
uint64_t playback_time;
} ODK_RenewalMessage;
typedef struct {
ODK_CoreMessage core_message;
uint32_t device_id_length;
uint8_t device_id[ODK_DEVICE_ID_LEN_MAX];
} ODK_ProvisioningMessage;
typedef struct {
ODK_CoreMessage core_message;
ODK_ParsedLicense* parsed_license;
} ODK_LicenseResponse;
typedef struct {
ODK_ProvisioningMessage core_provisioning;
ODK_ParsedProvisioning* parsed_provisioning;
} ODK_ProvisioningResponse;
#endif // ODK_STRUCTS_PRIV_H_

View File

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

View File

@@ -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 <cstdint>
#include <string>
#include "odk_structs.h"
using namespace std;
namespace oec_util {
// @ input/output structs
/**
* Output structure for ParseLicenseRequest
* Input structure for CreateCoreLicenseResponse
*/
struct ODK_LicenseRequest {
uint32_t api_version;
uint32_t nonce;
uint32_t session_id;
};
/**
* Output structure for ParseRenewalRequest
* Input structure for CreateCoreRenewalResponse
*/
struct ODK_RenewalRequest {
uint32_t api_version;
uint32_t nonce;
uint32_t session_id;
uint64_t playback_time;
};
/**
* Output structure for ParseProvisioningRequest
* Input structure for CreateCoreProvisioningResponse
*/
struct ODK_ProvisioningRequest {
uint32_t api_version;
uint32_t nonce;
uint32_t session_id;
string device_id;
};
// @ public parse request (deserializer) functions
/**
* Counterpart (deserializer) of ODK_PrepareCoreLicenseRequest (serializer)
*
* Parameters:
* [in] oemcrypto_core_message
* [out] core_license_request
*/
bool ParseLicenseRequest(const string& oemcrypto_core_message,
ODK_LicenseRequest* core_license_request);
/**
* Counterpart (deserializer) of ODK_PrepareCoreRenewalRequest (serializer)
*
* Parameters:
* [in] oemcrypto_core_message
* [out] core_renewal_request
*/
bool ParseRenewalRequest(const string& oemcrypto_core_message,
ODK_RenewalRequest* core_renewal_request);
/**
* Counterpart (deserializer) of ODK_PrepareCoreProvisioningRequest (serializer)
*
* Parameters:
* [in] oemcrypto_core_message
* [out] core_provisioning_request
*/
bool ParseProvisioningRequest(
const string& oemcrypto_core_message,
ODK_ProvisioningRequest* core_provisioning_request);
// @ public create response (serializer) functions
/**
* Counterpart (serializer) of ODK_ParseLicense (deserializer)
* struct-input variant
*
* Parameters:
* [in] parsed_lic
* [in] core_request
* [out] oemcrypto_core_message
*/
bool CreateCoreLicenseResponse(const ODK_ParsedLicense& parsed_lic,
const ODK_LicenseRequest& core_request,
string* oemcrypto_core_message);
/**
* Counterpart (serializer) of ODK_ParseRenewal (deserializer)
*
* Parameters:
* [in] core_request
* [out] oemcrypto_core_message
*/
bool CreateCoreRenewalResponse(const ODK_RenewalRequest& core_request,
string* oemcrypto_core_message);
/**
* Counterpart (serializer) of ODK_ParseProvisioning (deserializer)
* struct-input variant
*
* Parameters:
* [in] parsed_prov
* [in] core_request
* [out] oemcrypto_core_message
*/
bool CreateCoreProvisioningResponse(const ODK_ParsedProvisioning& parsed_prov,
const ODK_ProvisioningRequest& core_request,
string* oemcrypto_core_message);
} // namespace oec_util
#endif // OEC_UTIL_H_

View File

@@ -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 <cstdint>
#include <string>
#include "license_protocol.pb.h"
#include "oec_util.h"
using namespace std;
using video_widevine::License;
using video_widevine::License_KeyContainer;
namespace oec_util {
// @ public create response (serializer) functions
/**
* Counterpart (serializer) of ODK_ParseLicense (deserializer)
*
* Parameters:
* [in] license
* [in] core_request
* [out] oemcrypto_core_message
*/
bool CreateCoreLicenseResponse(const video_widevine::License& license,
const ODK_LicenseRequest& core_request,
string* oemcrypto_core_message);
/**
* Counterpart (serializer) of ODK_ParseProvisioning (deserializer)
*
* Parameters:
* [in] provisioning_response
* [in] core_request
* [out] oemcrypto_core_message
*/
bool CreateCoreProvisioningResponse(
const video_widevine::ProvisioningResponse& provisioning_response,
const ODK_ProvisioningRequest& core_request,
string* oemcrypto_core_message);
} // namespace oec_util
#endif // OEC_UTIL_PROTO_H_

View File

@@ -31,8 +31,9 @@ const int LATEST_OEMCRYPTO_VERSION = 16;
* U: auto-generated deserializing function for |T|
*/
template <typename S, typename T, typename U>
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 = {};

View File

@@ -30,7 +30,7 @@ namespace {
*/
template <typename T, typename S, typename P>
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<ODK_ParsedLicense*>(&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<ODK_ParsedProvisioning*>(&parsed_prov)};
if (!CopyDeviceId(core_request, &prov_response)) {

View File

@@ -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);

View File

@@ -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_ */

View File

@@ -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_ */

View File

@@ -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_ */

View File

@@ -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_ */

View File

@@ -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 <stdint.h>
@@ -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_ */

View File

@@ -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_ */

View File

@@ -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<size_t(const uint8_t*, uint8_t*, size_t)> 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<const uint8_t*>(device_id.data()), device_id.size());
@@ -55,7 +55,7 @@ static OEMCryptoResult odk_fun_ProvisioningRequest(
template <typename T, typename F, typename G>
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<const char*>(in), size);
std::string input(reinterpret_cast<const char*>(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<const char*>(args->device_id),
args->device_id_length)};
std::string(reinterpret_cast<const char*>(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;
}

View File

@@ -4,18 +4,12 @@
#include "odk.h"
#include <endian.h>
#include <unistd.h>
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <fstream>
#include <functional>
#include <iostream>
#include <string>
#include <vector>
@@ -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<const char*>(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<ODK_Field> 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,

View File

@@ -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_

View File

@@ -10,6 +10,7 @@
#include <openssl/cmac.h>
#include <openssl/evp.h>
#include <openssl/hmac.h>
#include <openssl/mem.h>
#include <openssl/rand.h>
#include <openssl/sha.h>
#include <stddef.h>
@@ -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;

View File

@@ -18,6 +18,7 @@
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/hmac.h>
#include <openssl/mem.h>
#include <openssl/rand.h>
#include <openssl/rsa.h>
#include <openssl/sha.h>
@@ -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;

View File

@@ -14,6 +14,7 @@
#include <openssl/aes.h>
#include <openssl/hmac.h>
#include <openssl/mem.h>
#include <openssl/rand.h>
#include <openssl/sha.h>
@@ -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<uint8_t>& server,
const std::vector<uint8_t>& 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<uint8_t>& server,
@@ -117,7 +120,7 @@ OEMCryptoResult UsageTableEntry::ReportUsage(const std::vector<uint8_t>& 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());

View File

@@ -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 <assert.h>
#include <string.h>
#include <algorithm>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <iterator>
#include <sstream>
#include <vector>
#include <openssl/cmac.h>
#include <openssl/err.h>
#include <openssl/evp.h>
// clang-format off
// This is a test keybox. It will not be accepted by production systems.
static std::vector<uint8_t> 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<uint8_t> 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<uint8_t> new_system_id = {0x00, 0x00, 0x23, 0x45};
static const std::vector<uint8_t> 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<uint8_t>& key,
const std::vector<uint8_t>& context,
std::vector<uint8_t>* out) {
if (key.empty() || context.empty() || out == NULL) {
std::cerr << "DeriveKey(): Invalid inputs" << std::endl;
return false;
}
std::vector<uint8_t> 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] << " <new_key_filename>" << 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<uint8_t> context(test_keybox.begin() + DEVICE_KEY_OFFSET,
test_keybox.begin() + CA_TOKEN_END);
// derive the new device key
std::vector<uint8_t> 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<uint8_t> 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;
;
}
}

View File

@@ -981,15 +981,12 @@ void Session::EncryptCTR(const vector<uint8_t>& 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

View File

@@ -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_;

View File

@@ -816,7 +816,7 @@ class OEMCryptoLicenseTest : public OEMCryptoLicenseTestAPI16,
public WithParamInterface<uint32_t> {
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<int32_t>(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<int32_t>(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<uint32_t>(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<LicenseWithUsageEntry> entries(ENTRY_COUNT);
vector<int64_t> 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());

View File

@@ -14,9 +14,18 @@ namespace wvcdm {
// It's purpose is to simplified interface for C++11's <random> library.
// Some of the methods use a "device specific" random seed, if the
// compiler/device does not support device specific randomizers, then the
// actual value supplied may not be random.
// 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);
}

View File

@@ -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<int> dist(0, RAND_MAX);
std::uniform_int_distribution<unsigned int> dist(0, RAND_MAX);
return dist(generator_);
}

View File

@@ -4,10 +4,13 @@
#include <stdlib.h>
#include <algorithm>
#include <chrono>
#include <limits>
#include <string>
#include <thread>
#include <type_traits>
#include <vector>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
@@ -30,6 +33,43 @@ constexpr unsigned int kSeeds[] = {0, 1337, 1565904109, 776964657};
class CdmRandomGeneratorTest : public testing::TestWithParam<unsigned int> {};
} // 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<CdmRandomGenerator::result_type>::value);
EXPECT_TRUE(std::is_unsigned<CdmRandomGenerator::result_type>::value);
// 2&3 a) G::min() and G::max() have the result type of G::result_type.
EXPECT_TRUE((std::is_same<CdmRandomGenerator::result_type,
decltype(CdmRandomGenerator::min())>::value));
EXPECT_TRUE((std::is_same<CdmRandomGenerator::result_type,
decltype(CdmRandomGenerator::max())>::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<CdmRandomGenerator::result_type, decltype(g())>::value));
// 4 b) g() is within [G::min() G::max()]
std::vector<CdmRandomGenerator::result_type> 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;