Manual Code Push

This update brings the partner repo in sync with the internal repo's
commit 040460be8b9556a699a6cd3813c88ce710f68146.
This commit is contained in:
Aaron Vaage
2020-07-23 14:42:42 -07:00
commit bee7997487
74 changed files with 7534 additions and 0 deletions

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

@@ -0,0 +1,88 @@
// Copyright 2019 Google LLC. All rights reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
// ----------------------------------------------------------------
// Builds libwv_odk.a, The ODK Library (libwv_odk) is used by
// 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/odk_util.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_test_helper.cpp",
"test/odk_timer_test.cpp",
],
}

10
oemcrypto/odk/README Normal file
View File

@@ -0,0 +1,10 @@
This ODK Library is used to generate and parse core OEMCrypto messages for
OEMCrypto v16 and above.
This library is used by both OEMCrypto on a device, and by Widevine license and
provisioning servers.
The source of truth for these files is in the server code base on piper. Do not
edit these files in the Android directory tree or in the Widevine Git
repository. If you need to edit these files and are not sure how to procede,
please ask for help from an engineer on the Widevine server or device teams.

View File

@@ -0,0 +1,171 @@
/* 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. */
/*********************************************************************
* OEMCryptoCENCCommon.h
*
* Common structures and error codes between WV servers and OEMCrypto.
*
*********************************************************************/
#ifndef WIDEVINE_ODK_INCLUDE_OEMCRYPTOCENCCOMMON_H_
#define WIDEVINE_ODK_INCLUDE_OEMCRYPTOCENCCOMMON_H_
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/* clang-format off */
typedef enum OEMCryptoResult {
OEMCrypto_SUCCESS = 0,
OEMCrypto_ERROR_INIT_FAILED = 1,
OEMCrypto_ERROR_TERMINATE_FAILED = 2,
OEMCrypto_ERROR_OPEN_FAILURE = 3,
OEMCrypto_ERROR_CLOSE_FAILURE = 4,
OEMCrypto_ERROR_ENTER_SECURE_PLAYBACK_FAILED = 5, /* deprecated */
OEMCrypto_ERROR_EXIT_SECURE_PLAYBACK_FAILED = 6, /* deprecated */
OEMCrypto_ERROR_SHORT_BUFFER = 7,
OEMCrypto_ERROR_NO_DEVICE_KEY = 8, /* no keybox device key. */
OEMCrypto_ERROR_NO_ASSET_KEY = 9,
OEMCrypto_ERROR_KEYBOX_INVALID = 10,
OEMCrypto_ERROR_NO_KEYDATA = 11,
OEMCrypto_ERROR_NO_CW = 12,
OEMCrypto_ERROR_DECRYPT_FAILED = 13,
OEMCrypto_ERROR_WRITE_KEYBOX = 14,
OEMCrypto_ERROR_WRAP_KEYBOX = 15,
OEMCrypto_ERROR_BAD_MAGIC = 16,
OEMCrypto_ERROR_BAD_CRC = 17,
OEMCrypto_ERROR_NO_DEVICEID = 18,
OEMCrypto_ERROR_RNG_FAILED = 19,
OEMCrypto_ERROR_RNG_NOT_SUPPORTED = 20,
OEMCrypto_ERROR_SETUP = 21,
OEMCrypto_ERROR_OPEN_SESSION_FAILED = 22,
OEMCrypto_ERROR_CLOSE_SESSION_FAILED = 23,
OEMCrypto_ERROR_INVALID_SESSION = 24,
OEMCrypto_ERROR_NOT_IMPLEMENTED = 25,
OEMCrypto_ERROR_NO_CONTENT_KEY = 26,
OEMCrypto_ERROR_CONTROL_INVALID = 27,
OEMCrypto_ERROR_UNKNOWN_FAILURE = 28,
OEMCrypto_ERROR_INVALID_CONTEXT = 29,
OEMCrypto_ERROR_SIGNATURE_FAILURE = 30,
OEMCrypto_ERROR_TOO_MANY_SESSIONS = 31,
OEMCrypto_ERROR_INVALID_NONCE = 32,
OEMCrypto_ERROR_TOO_MANY_KEYS = 33,
OEMCrypto_ERROR_DEVICE_NOT_RSA_PROVISIONED = 34,
OEMCrypto_ERROR_INVALID_RSA_KEY = 35,
OEMCrypto_ERROR_KEY_EXPIRED = 36,
OEMCrypto_ERROR_INSUFFICIENT_RESOURCES = 37,
OEMCrypto_ERROR_INSUFFICIENT_HDCP = 38,
OEMCrypto_ERROR_BUFFER_TOO_LARGE = 39,
OEMCrypto_WARNING_GENERATION_SKEW = 40, /* Warning, not error. */
OEMCrypto_ERROR_GENERATION_SKEW = 41,
OEMCrypto_LOCAL_DISPLAY_ONLY = 42, /* Info, not an error. */
OEMCrypto_ERROR_ANALOG_OUTPUT = 43,
OEMCrypto_ERROR_WRONG_PST = 44,
OEMCrypto_ERROR_WRONG_KEYS = 45,
OEMCrypto_ERROR_MISSING_MASTER = 46,
OEMCrypto_ERROR_LICENSE_INACTIVE = 47,
OEMCrypto_ERROR_ENTRY_NEEDS_UPDATE = 48,
OEMCrypto_ERROR_ENTRY_IN_USE = 49,
OEMCrypto_ERROR_USAGE_TABLE_UNRECOVERABLE = 50, /* Obsolete. Don't use. */
/* Use OEMCrypto_ERROR_NO_CONTENT_KEY instead of KEY_NOT_LOADED. */
OEMCrypto_KEY_NOT_LOADED = 51, /* Obsolete. */
OEMCrypto_KEY_NOT_ENTITLED = 52,
OEMCrypto_ERROR_BAD_HASH = 53,
OEMCrypto_ERROR_OUTPUT_TOO_LARGE = 54,
OEMCrypto_ERROR_SESSION_LOST_STATE = 55,
OEMCrypto_ERROR_SYSTEM_INVALIDATED = 56,
OEMCrypto_ERROR_LICENSE_RELOAD = 57,
OEMCrypto_ERROR_MULTIPLE_USAGE_ENTRIES = 58,
OEMCrypto_WARNING_MIXED_OUTPUT_PROTECTION = 59,
/* ODK return values */
ODK_ERROR_BASE = 1000,
ODK_ERROR_CORE_MESSAGE = ODK_ERROR_BASE,
ODK_SET_TIMER = ODK_ERROR_BASE + 1,
ODK_DISABLE_TIMER = ODK_ERROR_BASE + 2,
ODK_TIMER_EXPIRED = ODK_ERROR_BASE + 3,
ODK_UNSUPPORTED_API = ODK_ERROR_BASE + 4,
ODK_STALE_RENEWAL = ODK_ERROR_BASE + 5,
} OEMCryptoResult;
/* clang-format on */
/*
* OEMCrypto_Usage_Entry_Status.
* Valid values for status in the usage table.
*/
typedef enum OEMCrypto_Usage_Entry_Status {
kUnused = 0,
kActive = 1,
kInactive = 2, /* Deprecated. Use kInactiveUsed or kInactiveUnused. */
kInactiveUsed = 3,
kInactiveUnused = 4,
} OEMCrypto_Usage_Entry_Status;
/*
* OEMCrypto_LicenseType is used in the license message to indicate if the key
* objects are for content keys, or for entitlement keys.
*/
typedef enum OEMCrypto_LicenseType {
OEMCrypto_ContentLicense = 0,
OEMCrypto_EntitlementLicense = 1,
OEMCrypto_LicenstType_MaxValue = OEMCrypto_EntitlementLicense,
} OEMCrypto_LicenseType;
/* Private key type used in the provisioning response. */
typedef enum OEMCrypto_PrivateKeyType {
OEMCrypto_RSA_Private_Key = 0,
OEMCrypto_ECC_Private_Key = 1,
} OEMCrypto_PrivateKeyType;
/*
* OEMCrypto_Substring
*
* Used to indicate a substring of a signed message in OEMCrypto_LoadKeys and
* other functions which must verify that a parameter is contained within a
* signed message.
*/
typedef struct {
size_t offset;
size_t length;
} OEMCrypto_Substring;
/*
* OEMCrypto_KeyObject
* Points to the relevant fields for a content key. The fields are extracted
* from the License Response message offered to OEMCrypto_LoadKeys(). Each
* field points to one of the components of the key. Key data, key control,
* and both IV fields are 128 bits (16 bytes):
* key_id - the unique id of this key.
* key_id_length - the size of key_id. OEMCrypto may assume this is at
* most 16. However, OEMCrypto shall correctly handle key id lengths
* from 1 to 16 bytes.
* key_data_iv - the IV for performing AES-128-CBC decryption of the
* key_data field.
* key_data - the key data. It is encrypted (AES-128-CBC) with the
* session's derived encrypt key and the key_data_iv.
* key_control_iv - the IV for performing AES-128-CBC decryption of the
* key_control field.
* key_control - the key control block. It is encrypted (AES-128-CBC) with
* the content key from the key_data field.
*
* The memory for the OEMCrypto_KeyObject fields is allocated and freed
* by the caller of OEMCrypto_LoadKeys().
*/
typedef struct {
OEMCrypto_Substring key_id;
OEMCrypto_Substring key_data_iv;
OEMCrypto_Substring key_data;
OEMCrypto_Substring key_control_iv;
OEMCrypto_Substring key_control;
} OEMCrypto_KeyObject;
#ifdef __cplusplus
}
#endif
#endif /* WIDEVINE_ODK_INCLUDE_OEMCRYPTOCENCCOMMON_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. */
/*********************************************************************
* core_message_deserialize.h
*
* OEMCrypto v16 Core Message Serialization library counterpart (a.k.a. KDO)
*
* This file declares functions to deserialize request messages prepared by
* Widevine clients (OEMCrypto/ODK).
*
* Please refer to core_message_types.h for details.
*
*********************************************************************/
#ifndef WIDEVINE_ODK_INCLUDE_CORE_MESSAGE_DESERIALIZE_H_
#define WIDEVINE_ODK_INCLUDE_CORE_MESSAGE_DESERIALIZE_H_
#include "core_message_types.h"
namespace oemcrypto_core_message {
namespace deserialize {
/**
* Counterpart (deserializer) of ODK_PrepareCoreLicenseRequest (serializer)
*
* Parameters:
* [in] oemcrypto_core_message
* [out] core_license_request
*/
bool CoreLicenseRequestFromMessage(const std::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 CoreRenewalRequestFromMessage(const std::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 CoreProvisioningRequestFromMessage(
const std::string& oemcrypto_core_message,
ODK_ProvisioningRequest* core_provisioning_request);
} /* namespace deserialize */
} /* namespace oemcrypto_core_message */
#endif /* WIDEVINE_ODK_INCLUDE_CORE_MESSAGE_DESERIALIZE_H_ */

View File

@@ -0,0 +1,68 @@
/* 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. */
/*********************************************************************
* core_message_serialize.h
*
* OEMCrypto v16 Core Message Serialization library counterpart (a.k.a. KDO)
*
* This file declares functions to serialize response messages that will be
* parsed by Widevine clients (OEMCrypto/ODK).
*
* Please refer to core_message_types.h for details.
*
*********************************************************************/
#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"
namespace oemcrypto_core_message {
namespace serialize {
/**
* Counterpart (serializer) of ODK_ParseLicense (deserializer)
* struct-input variant
*
* Parameters:
* [in] parsed_lic
* [in] core_request
* [in] core_request_sha256
* [out] oemcrypto_core_message
*/
bool CreateCoreLicenseResponse(const ODK_ParsedLicense& parsed_lic,
const ODK_LicenseRequest& core_request,
const std::string& core_request_sha256,
std::string* oemcrypto_core_message);
/**
* Counterpart (serializer) of ODK_ParseRenewal (deserializer)
*
* Parameters:
* [in] core_request
* [in] renewal_duration_seconds
* [out] oemcrypto_core_message
*/
bool CreateCoreRenewalResponse(const ODK_RenewalRequest& core_request,
uint64_t renewal_duration_seconds,
std::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,
std::string* oemcrypto_core_message);
} /* namespace serialize */
} /* namespace oemcrypto_core_message */
#endif /* WIDEVINE_ODK_INCLUDE_CORE_MESSAGE_SERIALIZE_H_ */

View File

@@ -0,0 +1,62 @@
/* 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. */
/*********************************************************************
* core_message_serialize_proto.h
*
* These functions are an extension of those found in
* core_message_serialize.h. The difference is that these use the
* license and provisioning messages in protobuf format to create the core
* message.
*********************************************************************/
#ifndef WIDEVINE_ODK_INCLUDE_CORE_MESSAGE_SERIALIZE_PROTO_H_
#define WIDEVINE_ODK_INCLUDE_CORE_MESSAGE_SERIALIZE_PROTO_H_
#include <cstdint>
#include <string>
#include "core_message_types.h"
#include "license_protocol.pb.h"
namespace oemcrypto_core_message {
namespace serialize {
/* @ public create response (serializer) functions accepting proto input */
/**
* Counterpart (serializer) of ODK_ParseLicense (deserializer)
*
* Parameters:
* [in] serialized_license
serialized video_widevine::License
* [in] core_request oemcrypto core message from request.
* [in] core_request_sha256 - hash of serialized core request.
* [in] nonce_required - if the device should require a nonce match.
* [out] oemcrypto_core_message - the serialized oemcrypto core response.
*/
bool CreateCoreLicenseResponseFromProto(const std::string& serialized_license,
const ODK_LicenseRequest& core_request,
const std::string& core_request_sha256,
const bool nonce_required,
std::string* oemcrypto_core_message);
/**
* Counterpart (serializer) of ODK_ParseProvisioning (deserializer)
*
* Parameters:
* [in] serialized_provisioning_response
* serialized video_widevine::ProvisioningResponse
* [in] core_request
* [out] oemcrypto_core_message
*/
bool CreateCoreProvisioningResponseFromProto(
const std::string& serialized_provisioning_response,
const ODK_ProvisioningRequest& core_request,
std::string* oemcrypto_core_message);
} /* namespace serialize */
} /* namespace oemcrypto_core_message */
#endif /* WIDEVINE_ODK_INCLUDE_CORE_MESSAGE_SERIALIZE_PROTO_H_ */

View File

@@ -0,0 +1,99 @@
/* 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 */
/*********************************************************************
* core_message_types.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 | CoreLicenseRequestFromMessage |
* | +------------------------------------+ +-----------------------------------+
* | | ODK_PrepareCoreRenewalRequest | | CoreRenewalRequestFromMessage |
* | +------------------------------------+ +-----------------------------------+
* | | ODK_PrepareCoreProvisioningRequest | | CoreProvisioningRequestFromMessage|
* +---+------------------------------------+---+-----------------------------------+
* | d | ODK_ParseLicense | s | CreateCoreLicenseResponse |
* | +------------------------------------+ +-----------------------------------+
* | | ODK_ParseRenewal | | CreateCoreRenewalResponse |
* | +------------------------------------+ +-----------------------------------+
* | | ODK_ParseProvisioning | | CreateCoreProvisioningResponse |
* +---+------------------------------------+---+-----------------------------------+
*
*********************************************************************/
/* clang-format on */
#ifndef WIDEVINE_ODK_INCLUDE_CORE_MESSAGE_TYPES_H_
#define WIDEVINE_ODK_INCLUDE_CORE_MESSAGE_TYPES_H_
#include <cstdint>
#include <string>
namespace oemcrypto_core_message {
/* @ input/output structs */
/**
* Output structure for CoreLicenseRequestFromMessage
* Input structure for CreateCoreLicenseResponse
*/
struct ODK_LicenseRequest {
uint16_t api_minor_version;
uint16_t api_major_version;
uint32_t nonce;
uint32_t session_id;
};
/**
* Output structure for CoreRenewalRequestFromMessage
* Input structure for CreateCoreRenewalResponse
*/
struct ODK_RenewalRequest {
uint16_t api_minor_version;
uint16_t api_major_version;
uint32_t nonce;
uint32_t session_id;
uint64_t playback_time_seconds;
};
/**
* Output structure for CoreProvisioningRequestFromMessage
* Input structure for CreateCoreProvisioningResponse
*/
struct ODK_ProvisioningRequest {
uint16_t api_minor_version;
uint16_t api_major_version;
uint32_t nonce;
uint32_t session_id;
std::string device_id;
};
} /* namespace oemcrypto_core_message */
#endif /* WIDEVINE_ODK_INCLUDE_CORE_MESSAGE_TYPES_H_ */

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

@@ -0,0 +1,637 @@
/* Copyright 2019 Google LLC. All rights reserved. This file and proprietary */
/* source code may only be used and distributed under the Widevine Master */
/* License Agreement. */
/*********************************************************************
* odk.h
*
* OEMCrypto v16 Core Message Serialization library
*
* For Widevine Modular DRM, there are six message types between a server and
* a client device: license request and response, provisioning request and
* response, and renewal request and response.
*
* In OEMCrypto v15 and earlier, messages from the server were parsed by the
* CDM layer above OEMCrypto; the CDM in turn gave OEMCrypto a collection of
* pointers to protected data within the message. However, the pointers
* themselves were not signed by the server.
*
* Starting from OEMCrypto v16, all fields used by OEMCrypto in each of these
* messages have been identified in the document "Widevine Core Message
* Serialization". These fields are called the core of the message. Core
* message fields are (de)serialized using the ODK, a C library provided by
* Widevine. OEMCrypto will parse and verify the core of the message with
* help from the ODK.
*
* The ODK functions that parse code will fill out structs that have similar
* formats to the function parameters of the OEMCrypto v15 functions being
* replaced. The ODK will be provided in source code and it is Widevine's
* intention that partners can build and link ODK with their implementation
* of OEMCrypto with no or few code changes.
*
* OEMCrypto implementers shall build the ODK library as part of the Trusted
* Application (TA) running in the TEE. All memory and buffers used by the
* ODK library shall be sanitized by the OEMCrypto implementer to prevent
* modification by any process running the REE.
*
* See the documents "Widevine Core Message Serialization" and "License
* Duration and Renewal" for a detailed description of the ODK API. You can
* find these documents in the widevine repository as
* docs/Widevine_Core_Message_Serialization.pdf and
* docs/License_Duration_and_Renewal.pdf
*
*********************************************************************/
#ifndef WIDEVINE_ODK_INCLUDE_ODK_H_
#define WIDEVINE_ODK_INCLUDE_ODK_H_
#include <stdint.h>
#include "OEMCryptoCENCCommon.h"
#include "odk_structs.h"
#ifdef __cplusplus
extern "C" {
#endif
/*
* ODK_InitializeSessionValues
*
* Description:
* This function initializes the session's data structures. It shall be
* called from OEMCrypto_OpenSession.
*
* Parameters:
* [out] timer_limits: the session's timer limits.
* [out] clock_values: the session's clock values.
* [out] nonce_values: the session's ODK nonce values.
* [in] api_major_version: the API version of OEMCrypto.
* [in] session_id: the session id of the newly created session.
*
* Returns:
* OEMCrypto_SUCCESS
* OEMCrypto_ERROR_INVALID_CONTEXT
*
* Version:
* This method is new in version 16 of the API.
*/
OEMCryptoResult ODK_InitializeSessionValues(ODK_TimerLimits* timer_limits,
ODK_ClockValues* clock_values,
ODK_NonceValues* nonce_values,
uint32_t api_major_version,
uint32_t session_id);
/*
* ODK_SetNonceValues
*
* Description:
* This function sets the nonce value in the session's nonce structure. It
* shall be called from OEMCrypto_GenerateNonce.
*
* Parameters:
* [in/out] nonce_values: the session's nonce data.
* [in] nonce: the new nonce that was just generated.
*
* Returns:
* true on success
*
* Version:
* This method is new in version 16 of the API.
*/
OEMCryptoResult ODK_SetNonceValues(ODK_NonceValues* nonce_values,
uint32_t nonce);
/*
* ODK_InitializeClockValues
*
* Description:
* This function initializes the clock values in the session clock_values
* structure. It shall be called from OEMCrypto_PrepAndSignLicenseRequest.
*
* Parameters:
* [in/out] clock_values: the session's clock data.
* [in] system_time_seconds: the current time on OEMCrypto's monotonic clock.
*
* Returns:
* OEMCrypto_SUCCESS
* OEMCrypto_ERROR_INVALID_CONTEXT
*
* Version:
* This method is new in version 16 of the API.
*/
OEMCryptoResult ODK_InitializeClockValues(ODK_ClockValues* clock_values,
uint64_t system_time_seconds);
/*
* ODK_ReloadClockValues
*
* Description:
* This function sets the values in the clock_values structure. It shall be
* called from OEMCrypto_LoadUsageEntry. When a usage entry from a v15 or
* earlier license is loaded, the value time_of_license_loaded shall be used
* in place of time_of_license_signed.
*
* Parameters:
* [in/out] clock_values: the session's clock data.
* [in] time_of_license_signed: the value time_license_received from the
* loaded usage entry.
* [in] time_of_first_decrypt: the value time_of_first_decrypt from the
* loaded usage entry.
* [in] time_of_last_decrypt: the value time_of_last_decrypt from the loaded
* usage entry.
* [in] status: the value status from the loaded usage entry.
* [in] system_time_seconds: the current time on OEMCrypto's monotonic clock.
*
* Returns:
* OEMCrypto_SUCCESS
* OEMCrypto_ERROR_INVALID_CONTEXT
*
* Version:
* This method is new in version 16 of the API.
*/
OEMCryptoResult ODK_ReloadClockValues(ODK_ClockValues* clock_values,
uint64_t time_of_license_signed,
uint64_t time_of_first_decrypt,
uint64_t time_of_last_decrypt,
enum OEMCrypto_Usage_Entry_Status status,
uint64_t system_time_seconds);
/*
* ODK_AttemptFirstPlayback
*
* Description:
* This updates the clock values, and determines if playback may start based
* on the given system time. It uses the values in clock_values to determine
* if this is the first playback for the license or the first playback for
* just this session.
*
* This shall be called from the first call in a session to any of
* OEMCrypto_DecryptCENC or any of the OEMCrypto_Generic* functions.
*
* If OEMCrypto uses a hardware timer, and this function returns
* ODK_SET_TIMER, then the timer should be set to the value pointed to by
* timer_value.
*
* Parameters:
* [in] system_time_seconds: the current time on OEMCrypto's monotonic clock,
* in seconds.
* [in] timer_limits: timer limits specified in the license.
* [in/out] clock_values: the sessions clock values.
* [out] timer_value: set to the new timer value. Only used if the return
* value is ODK_SET_TIMER. This must be non-null if OEMCrypto uses a
* hardware timer.
*
* Returns:
* ODK_SET_TIMER: Success. The timer should be reset to the specified value
* and playback is allowed.
* ODK_DISABLE_TIMER: Success, but disable timer. Unlimited playback is
* allowed.
* ODK_TIMER_EXPIRED: Set timer as disabled. Playback is not allowed.
*
* Version:
* This method is new in version 16 of the API.
*/
OEMCryptoResult ODK_AttemptFirstPlayback(uint64_t system_time_seconds,
const ODK_TimerLimits* timer_limits,
ODK_ClockValues* clock_values,
uint64_t* timer_value);
/*
* ODK_UpdateLastPlaybackTime
*
* Description:
* Vendors that do not implement their own timer should call
* ODK_UpdateLastPlaybackTime regularly during playback. This updates the
* clock values, and determines if playback may continue based on the given
* system time. This shall be called from any of OEMCrypto_DecryptCENC or any
* of the OEMCrypto_Generic* functions.
*
* All Vendors (i.e. those that do or do not implement their own timer) shall
* call ODK_UpdateLastPlaybackTime from the function
* OEMCrypto_UpdateUsageEntry before updating the usage entry so that the
* clock values are accurate.
*
* Parameters:
* [in] system_time_seconds: the current time on OEMCrypto's monotonic clock,
* in seconds.
* [in] timer_limits: timer limits specified in the license.
* [in/out] clock_values: the sessions clock values.
*
* Returns:
* OEMCrypto_SUCCESS: Success. Playback is allowed.
* ODK_TIMER_EXPIRED: Set timer as disabled. Playback is not allowed.
*
* Version:
* This method is new in version 16 of the API.
*/
OEMCryptoResult ODK_UpdateLastPlaybackTime(uint64_t system_time_seconds,
const ODK_TimerLimits* timer_limits,
ODK_ClockValues* clock_values);
/*
* ODK_DeactivateUsageEntry
*
* Description:
* This function modifies the session's clock values to indicate that the
* license has been deactivated. It shall be called from
* OEMCrypto_DeactivateUsageEntry
*
* Parameters:
* [in/out] clock_values: the sessions clock values.
*
* Returns:
* OEMCrypto_SUCCESS
* OEMCrypto_ERROR_INVALID_CONTEXT
*
* Version:
* This method is new in version 16 of the API.
*/
OEMCryptoResult ODK_DeactivateUsageEntry(ODK_ClockValues* clock_values);
/*
* ODK_PrepareCoreLicenseRequest
*
* Description:
* Modifies the message to include a core license request at the beginning of
* the message buffer. The values in nonce_values are used to populate the
* message.
*
* This shall be called by OEMCrypto from OEMCrypto_PrepAndSignLicenseRequest.
*
* NOTE: if the message pointer is null and/or input core_message_size is
* zero, this function returns OEMCrypto_ERROR_SHORT_BUFFER and sets output
* core_message_size to the size needed.
*
* Parameters:
* [in/out] message: Pointer to memory for the entire message. Modified by
* the ODK library.
* [in] message_length: length of the entire message buffer.
* [in/out] core_message_size: length of the core message at the beginning of
* the message. (in) size of buffer reserved for the core message, in
* bytes. (out) actual length of the core message, in bytes.
* [in] nonce_values: pointer to the session's nonce data.
*
* Returns:
* OEMCrypto_SUCCESS
* OEMCrypto_ERROR_SHORT_BUFFER: core_message_size is too small
* OEMCrypto_ERROR_INVALID_CONTEXT
*
* Version:
* This method is new in version 16 of the API.
*/
OEMCryptoResult ODK_PrepareCoreLicenseRequest(
uint8_t* message, size_t message_length, size_t* core_message_size,
const ODK_NonceValues* nonce_values);
/*
* ODK_PrepareCoreRenewalRequest
*
* Description:
* Modifies the message to include a core renewal request at the beginning of
* the message buffer. The values in nonce_values, clock_values and
* system_time_seconds are used to populate the message. The nonce_values
* should match those from the license.
*
* This shall be called by OEMCrypto from OEMCrypto_PrepAndSignRenewalRequest.
*
* If status in clock_values indicates that a license has not been loaded,
* then this is a license release. The ODK library will change the value of
* nonce_values.api_major_version to 15. This will make
* OEMCrypto_PrepAndSignRenewalRequest sign just the message body, as it does
* for all legacy licenses.
*
* NOTE: if the message pointer is null and/or input core_message_size is
* zero, this function returns OEMCrypto_ERROR_SHORT_BUFFER and sets output
* core_message_size to the size needed.
*
* Parameters:
* [in/out] message: Pointer to memory for the entire message. Modified by
* the ODK library.
* [in] message_length: length of the entire message buffer.
* [in/out] core_message_size: length of the core message at the beginning of
* the message. (in) size of buffer reserved for the core message, in
* bytes. (out) actual length of the core message, in bytes.
* [in/out] nonce_values: pointer to the session's nonce data.
* [in/out] clock_values: the session's clock values.
* [in] system_time_seconds: the current time on OEMCrypto's clock, in
* seconds.
*
* Returns:
* OEMCrypto_SUCCESS
* OEMCrypto_ERROR_SHORT_BUFFER: core_message_size is too small
* OEMCrypto_ERROR_INVALID_CONTEXT
*
* Version:
* This method is new in version 16 of the API.
*/
OEMCryptoResult ODK_PrepareCoreRenewalRequest(uint8_t* message,
size_t message_length,
size_t* core_message_size,
ODK_NonceValues* nonce_values,
ODK_ClockValues* clock_values,
uint64_t system_time_seconds);
/*
* ODK_PrepareCoreProvisioningRequest
*
* Description:
* Modifies the message to include a core provisioning request at the
* beginning of the message buffer. The values in nonce_values are used to
* populate the message.
*
* This shall be called by OEMCrypto from
* OEMCrypto_PrepAndSignProvisioningRequest.
*
* The buffer device_id shall be the same string returned by
* OEMCrypto_GetDeviceID. The device ID shall be unique to the device, and
* stable across reboots and factory resets for an L1 device.
*
* NOTE: if the message pointer is null and/or input core_message_size is
* zero, this function returns OEMCrypto_ERROR_SHORT_BUFFER and sets output
* core_message_size to the size needed.
*
* Parameters:
* [in/out] message: Pointer to memory for the entire message. Modified by
* the ODK library.
* [in] message_length: length of the entire message buffer.
* [in/out] core_message_size: length of the core message at the beginning of
* the message. (in) size of buffer reserved for the core message, in
* bytes. (out) actual length of the core message, in bytes.
* [in] nonce_values: pointer to the session's nonce data.
* [in] device_id: For devices with a keybox, this is the device ID from the
* keybox. For devices with an OEM Certificate, this is a device unique
* id string.
* [in] device_id_length: length of device_id. The device ID can be at most
* 64 bytes.
*
* Returns:
* OEMCrypto_SUCCESS
* OEMCrypto_ERROR_SHORT_BUFFER: core_message_size is too small
* OEMCrypto_ERROR_INVALID_CONTEXT
*
* Version:
* This method is new in version 16 of the API.
*/
OEMCryptoResult ODK_PrepareCoreProvisioningRequest(
uint8_t* message, size_t message_length, size_t* core_message_size,
const ODK_NonceValues* nonce_values, const uint8_t* device_id,
size_t device_id_length);
/*
* ODK_InitializeV15Values
*
* Description:
* This function sets all limits in the timer_limits struct to the
* key_duration and initializes the other values. The field
* nonce_values.api_major_version will be set to 15. It shall be called from
* OEMCrypto_LoadKeys when loading a legacy license.
*
* Parameters:
* [out] timer_limits: The session's timer limits.
* [in/out] clock_values: The session's clock values.
* [in/out] nonce_values: The session's ODK nonce values.
* [in] key_duration: The duration from the first key's key control block. In
* practice, the key duration is the same for all keys and is the same
* as the license duration.
* [in] system_time_seconds: The current time on the system clock, as
* described in the document "License Duration and Renewal".
*
* Returns:
* OEMCrypto_SUCCESS
* OEMCrypto_ERROR_INVALID_CONTEXT
*
* Version:
* This method is new in version 16 of the API.
*/
OEMCryptoResult ODK_InitializeV15Values(ODK_TimerLimits* timer_limits,
ODK_ClockValues* clock_values,
ODK_NonceValues* nonce_values,
uint32_t key_duration,
uint64_t system_time_seconds);
/*
* ODK_RefreshV15Values
*
* Description:
* This function updates the clock_values as needed if a v15 renewal is
* accepted. The field nonce_values.api_major_version is verified to be 15.
*
* This is called from OEMCrypto_RefreshKeys for a valid license renewal.
* OEMCrypto shall pass in the current system time, and the key duration from
* the first object in the OEMCrypto_KeyRefreshObject.
*
* Parameters:
* [in] timer_limits: The session's timer limits.
* [in/out] clock_values: The session's clock values.
* [in] nonce_values: The session's ODK nonce values.
* [in] system_time_seconds: The current time on the system clock, as
* described in the document "License Duration and Renewal".
* [in] new_key_duration: The duration from the first
* OEMCrypto_KeyRefreshObject in key_array.
* [out] timer_value: set to the new timer value. Only used if the return
* value is ODK_SET_TIMER. This must be non-null if OEMCrypto uses a
* hardware timer.
*
* Returns:
* OEMCrypto_SUCCESS
* OEMCrypto_ERROR_UNKNOWN_FAILURE
* ODK_SET_TIMER: Success. The timer should be reset to the specified value
* and playback is allowed.
* ODK_DISABLE_TIMER: Success, but disable timer. Unlimited playback is
* allowed.
* ODK_TIMER_EXPIRED: Set timer as disabled. Playback is not allowed.
*
* Version:
* This method is new in version 16 of the API.
*/
OEMCryptoResult ODK_RefreshV15Values(const ODK_TimerLimits* timer_limits,
ODK_ClockValues* clock_values,
const ODK_NonceValues* nonce_values,
uint64_t system_time_seconds,
uint32_t new_key_duration,
uint64_t* timer_value);
/*
* ODK_ParseLicense
*
* Description:
* The function ODK_ParseLicense will parse the message and verify fields in
* the message.
*
* If the message does not parse correctly, ODK_VerifyAndParseLicense will
* return ODK_ERROR_CORE_MESSAGE that OEMCrypto should return to the CDM
* layer above.
*
* If the API in the message is not 16, then ODK_UNSUPPORTED_API is returned.
*
* If initial_license_load is true, and nonce_required in the license is
* true, then the ODK library shall verify that nonce_values->nonce and
* nonce_values->session_id are the same as those in the message. If
* verification fails, then it shall return OEMCrypto_ERROR_INVALID_NONCE.
*
* If initial_license_load is false, and nonce_required is true, then
* ODK_ParseLicense will set the values in nonce_values from those in the
* message.
*
* The function ODK_ParseLicense will verify that each substring points to a
* location in the message body. The message body is the buffer starting at
* message + core_message_length with size message_length -
* core_message_length.
*
* If initial_license_load is true, then ODK_ParseLicense shall verify that
* the parameter request_hash matches request_hash in the parsed license. If
* verification fails, then it shall return ODK_ERROR_CORE_MESSAGE. This was
* computed by OEMCrypto when the license was requested.
*
* If usage_entry_present is true, then ODK_ParseLicense shall verify that
* the pst in the license has a nonzero length.
*
* Parameters:
* [in] message: pointer to the message buffer.
* [in] message_length: length of the entire message buffer.
* [in] core_message_size: length of the core message, at the beginning of
* the message buffer.
* [in] initial_license_load: true when called for OEMCrypto_LoadLicense and
* false when called for OEMCrypto_ReloadLicense.
* [in] usage_entry_present: true if the session has a new usage entry
* associated with it created via OEMCrypto_CreateNewUsageEntry.
* [in] request_hash: the hash of the license request core message. This was
* computed by OEMCrypto when the license request was signed.
* [in/out] timer_limits: The session's timer limits. These will be updated.
* [in/out] clock_values: The session's clock values. These will be updated.
* [in/out] nonce_values: The session's nonce values. These will be updated.
* [out] parsed_license: the destination for the data.
*
* Returns:
* OEMCrypto_SUCCESS
* ODK_ERROR_CORE_MESSAGE: if the message did not parse correctly, or there
* were other incorrect values. An error should be returned to the CDM
* layer.
* ODK_UNSUPPORTED_API
* OEMCrypto_ERROR_INVALID_NONCE
*
* Version:
* This method is new in version 16 of the API.
*/
OEMCryptoResult ODK_ParseLicense(
const uint8_t* message, size_t message_length, size_t core_message_length,
bool initial_license_load, bool usage_entry_present,
const uint8_t request_hash[ODK_SHA256_HASH_SIZE],
ODK_TimerLimits* timer_limits, ODK_ClockValues* clock_values,
ODK_NonceValues* nonce_values, ODK_ParsedLicense* parsed_license);
/*
* ODK_ParseRenewal
*
* Description:
* The function ODK_ParseRenewal will parse the message and verify its
* contents. If the message does not parse correctly, an error of
* ODK_ERROR_CORE_MESSAGE is returned.
*
* ODK_ParseRenewal shall verify that all fields in nonce_values match those
* in the license. Otherwise it shall return OEMCrypto_ERROR_INVALID_NONCE.
*
* After parsing the message, this function updates the clock_values based on
* the timer_limits and the current system time. If playback may not
* continue, then ODK_TIMER_EXPIRED is returned.
*
* If playback may continue, a return value of ODK_SET_TIMER or
* ODK_TIMER_EXPIRED is returned. If the return value is ODK_SET_TIMER, then
* playback may continue until the timer expires. If the return value is
* ODK_DISABLE_TIMER, then playback time is not limited.
*
* If OEMCrypto uses a hardware timer, and this function returns
* ODK_SET_TIMER, then OEMCrypto shall set the timer to the value pointed to
* by timer_value.
*
* Parameters:
* [in] message: pointer to the message buffer.
* [in] message_length: length of the entire message buffer.
* [in] core_message_size: length of the core message, at the beginning of
* the message buffer.
* [in] nonce_values: pointer to the session's nonce data.
* [in] system_time_seconds: the current time on OEMCrypto's clock, in
* seconds.
* [in] timer_limits: timer limits specified in the license.
* [in/out] clock_values: the sessions clock values.
* [out] timer_value: set to the new timer value. Only used if the return
* value is ODK_SET_TIMER. This must be non-null if OEMCrypto uses a
* hardware timer.
*
* Returns:
* ODK_ERROR_CORE_MESSAGE: the message did not parse correctly, or there were
* other incorrect values. An error should be returned to the CDM layer.
* ODK_SET_TIMER: Success. The timer should be reset to the specified timer
* value.
* ODK_DISABLE_TIMER: Success, but disable timer. Unlimited playback is
* allowed.
* ODK_TIMER_EXPIRED: Set timer as disabled. Playback is not allowed.
* ODK_UNSUPPORTED_API
* ODK_STALE_RENEWAL: This renewal is not the most recently signed. It is
* rejected.
* OEMCrypto_ERROR_INVALID_NONCE
*
* Version:
* This method is new in version 16 of the API.
*/
OEMCryptoResult ODK_ParseRenewal(const uint8_t* message, size_t message_length,
size_t core_message_length,
const ODK_NonceValues* nonce_values,
uint64_t system_time_seconds,
const ODK_TimerLimits* timer_limits,
ODK_ClockValues* clock_values,
uint64_t* timer_value);
/*
* ODK_ParseProvisioning
*
* Description:
* The function ODK_ParseProvisioning will parse the message and verify the
* nonce values match those in the license.
*
* If the message does not parse correctly, ODK_ParseProvisioning will return
* an error that OEMCrypto should return to the CDM layer above.
*
* If the API in the message is larger than 16, then ODK_UNSUPPORTED_API is
* returned.
*
* ODK_ParseProvisioning shall verify that nonce_values->nonce and
* nonce_values->session_id are the same as those in the message. Otherwise
* it shall return OEMCrypto_ERROR_INVALID_NONCE.
*
* The function ODK_ParseProvisioning will verify that each substring points
* to a location in the message body. The message body is the buffer starting
* at message + core_message_length with size message_length -
* core_message_length.
*
* Parameters:
* [in] message: pointer to the message buffer.
* [in] message_length: length of the entire message buffer.
* [in] core_message_size: length of the core message, at the beginning of
* the message buffer.
* [in] nonce_values: pointer to the session's nonce data.
* [in] device_id: a pointer to a buffer containing the device ID of the
* device. The ODK function will verify it matches that in the message.
* [in] device_id_length: the length of the device ID.
* [out] parsed_response: destination for the parse data.
*
* Returns:
* OEMCrypto_SUCCESS
* ODK_ERROR_CORE_MESSAGE: the message did not parse correctly, or there were
* other incorrect values. An error should be returned to the CDM layer.
* ODK_UNSUPPORTED_API
* OEMCrypto_ERROR_INVALID_NONCE
*
* Version:
* This method is new in version 16 of the API.
*/
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);
#ifdef __cplusplus
}
#endif
#endif /* WIDEVINE_ODK_INCLUDE_ODK_H_ */

View File

@@ -0,0 +1,221 @@
/* Copyright 2019 Google LLC. All rights reserved. This file and proprietary */
/* source code may only be used and distributed under the Widevine Master */
/* License Agreement. */
#ifndef WIDEVINE_ODK_INCLUDE_ODK_STRUCTS_H_
#define WIDEVINE_ODK_INCLUDE_ODK_STRUCTS_H_
#include <stdint.h>
#include "OEMCryptoCENCCommon.h"
#include "odk_target.h"
/* The version of this library. */
#define ODK_MAJOR_VERSION 16
#define ODK_MINOR_VERSION 3
/* ODK Version string. Date changed automatically on each release. */
#define ODK_RELEASE_DATE "ODK v16.3 2020-07-19"
/* The lowest version number for an ODK message. */
#define ODK_FIRST_VERSION 16
/* Some useful constants. */
#define ODK_DEVICE_ID_LEN_MAX 64
#define ODK_SHA256_HASH_SIZE 32
/*
* ODK_TimerLimits Structure
*
* Description:
* Timer limits are specified in a license and are used to determine when
* playback is allowed. See the document "License Duration and Renewal" for a
* discussion on the time restrictions that may be placed on a license. The
* fields in this structure are directly related to the fields in the core
* license message. The fields are set when OEMCrypto calls the function
* ODK_ParseLicense or ODK_InitializeV15Values.
*
* Fields:
* soft_enforce_rental_duration: A boolean controlling the soft or hard
* enforcement of rental duration.
* soft_enforce_playback_duration: A boolean controlling the soft or hard
* enforcement of playback duration.
* earliest_playback_start_seconds: The earliest time that the first playback
* is allowed. Measured in seconds since the license request was signed. For
* most use cases, this is zero.
* rental_duration_seconds: Window of time for the allowed first playback.
* Measured in seconds since the earliest playback start. If
* soft_enforce_rental_duration is true, this applies only to the first
* playback. If soft_enforce_rental_duration is false, then this restricts
* any playback. A value of zero means no limit.
* total_playback_duration_seconds: Window of time for allowed playback.
* Measured in seconds since the first playback start. If
* soft_enforce_playback_duration is true, this applies only to the start of
* playback for any session. If soft_enforce_playback_duration is false, then
* this restricts any playback. A value of zero means no limit.
* initial_renewal_duration_seconds: Window of time for allowed playback.
* Measured in seconds since the first playback start. This value is only
* used to start the renewal timer. After a renewal message is loaded, the
* timer will be reset. A value of zero means no limit.
*
* Version:
* This struct changed in API version 16.2.
*/
typedef struct {
bool soft_enforce_rental_duration;
bool soft_enforce_playback_duration;
uint64_t earliest_playback_start_seconds;
uint64_t rental_duration_seconds;
uint64_t total_playback_duration_seconds;
uint64_t initial_renewal_duration_seconds;
} ODK_TimerLimits;
/*
* ODK_ClockValues Structure
*
* Description:
* Clock values are modified when decryption occurs or when a renewal is
* processed. They are used to track the current status of the license --
* i.e. has playback started? When does the timer expire? See the section
* "Complete ODK API" of the document "Widevine Core Message Serialization"
* for a complete list of all fields in this structure. Most of these values
* shall be saved with the usage entry.
*
* All times are in seconds. Most of the fields in this structure are saved
* in the usage entry. This structure should be initialized when a usage
* entry is created or loaded, and should be used to save a usage entry. It
* is updated using the ODK functions listed below. The time values are based
* on OEMCrypto's system clock, as described in the document "License
* Duration and Renewal".
*
* Fields:
* time_of_license_signed: Time that the license request was signed, based on
* OEMCrypto's system clock. This value shall be stored and reloaded with
* usage entry as time_of_license_received.
* time_of_first_decrypt: Time of the first decrypt or call select key, based
* on OEMCrypto's system clock. This is 0 if the license has not been used to
* decrypt any data. This value shall be stored and reloaded with usage entry.
* time_of_last_decrypt: Time of the most recent decrypt call, based on
* OEMCrypto's system clock. This value shall be stored and reloaded with
* usage entry.
* time_of_renewal_request: Time of the most recent renewal request, based on
* OEMCrypto's system clock. This is used to verify that a renewal is not
* stale.
* time_when_timer_expires: Time that the current timer expires, based on
* OEMCrypto's system clock. If the timer is active, this is used by the ODK
* library to determine if it has expired.
* timer_status: Used internally by the ODK library to indicate the current
* timer status.
* status: The license or usage entry status. This value shall be stored and
* reloaded with usage entry.
*
* Version:
* This struct changed in API version 16.2.
*/
typedef struct {
uint64_t time_of_license_signed;
uint64_t time_of_first_decrypt;
uint64_t time_of_last_decrypt;
uint64_t time_of_renewal_request;
uint64_t time_when_timer_expires;
uint32_t timer_status;
enum OEMCrypto_Usage_Entry_Status status;
} ODK_ClockValues;
/*
* ODK_NonceValues Structure
*
* Description:
* Nonce values are used to match a license or provisioning request to a
* license or provisioning response. They are also used to match a renewal
* request and response to a license. For this reason, the api_version might
* be lower than that supported by OEMCrypto. The api_version matches the
* version of the license. Similarly the nonce and session_id match the
* session that generated the license request. For an offline license, these
* might not match the session that is loading the license. We use the nonce
* to prevent a license from being replayed. By also including a session_id
* in the license request and license response, we prevent an attack using
* the birthday paradox to generate nonce collisions on a single device.
*
* Fields:
* api_major_version: the API version of the license. This is initialized to
* the API version of the ODK library, but may be lower.
* api_minor_version: the minor version of the ODK library. This is used by
* the server to verify that device is not using an obsolete version of the
* ODK library.
* nonce: a randomly generated number used to prevent replay attacks.
* session_id: the session id of the session which signed the license or
* provisioning request. It is used to prevent replay attacks from one
* session to another.
*
* Version:
* This struct changed in API version 16.2.
*/
typedef struct {
uint16_t api_minor_version;
uint16_t api_major_version;
uint32_t nonce;
uint32_t session_id;
} ODK_NonceValues;
/*
* ODK_ParsedLicense Structure
*
* Description:
* The parsed license structure contains information from the license
* message. The function ODK_ParseLicense will fill in the fields of this
* message. All substrings are contained within the message body.
*
* Fields:
* enc_mac_keys_iv: IV for decrypting new mac_key. Size is 128 bits.
* enc_mac_keys: encrypted mac_keys for generating new mac_keys. Size is 512
* bits.
* pst: the Provider Session Token.
* srm_restriction_data: optional data specifying the minimum SRM version.
* license_type: specifies if the license contains content keys or
* entitlement keys.
* nonce_required: indicates if the license requires a nonce.
* timer_limits: time limits of the for the license.
* key_array_length: number of keys present.
* key_array: set of keys to be installed.
*
* Version:
* This struct changed in API version 16.2.
*/
typedef struct {
OEMCrypto_Substring enc_mac_keys_iv;
OEMCrypto_Substring enc_mac_keys;
OEMCrypto_Substring pst;
OEMCrypto_Substring srm_restriction_data;
OEMCrypto_LicenseType license_type;
bool nonce_required;
ODK_TimerLimits timer_limits;
uint32_t key_array_length;
OEMCrypto_KeyObject key_array[ODK_MAX_NUM_KEYS];
} ODK_ParsedLicense;
/*
* ODK_ParsedProvisioning Structure
*
* Description:
* The parsed provisioning structure contains information from the license
* message. The function ODK_ParseProvisioning will fill in the fields of
* this message. All substrings are contained within the message body.
*
* Fields:
* key_type: indicates if this key is an RSA or ECC private key.
* enc_private_key: encrypted private key for the DRM certificate.
* enc_private_key_iv: IV for decrypting new private key. Size is 128 bits.
* encrypted_message_key: used for provisioning 3.0 to derive keys.
*
* Version:
* This struct changed in API version 16.2.
*/
typedef struct {
OEMCrypto_PrivateKeyType key_type;
OEMCrypto_Substring enc_private_key;
OEMCrypto_Substring enc_private_key_iv;
OEMCrypto_Substring encrypted_message_key; /* Used for Prov 3.0 */
} ODK_ParsedProvisioning;
#endif /* WIDEVINE_ODK_INCLUDE_ODK_STRUCTS_H_ */

View File

@@ -0,0 +1,13 @@
/* Copyright 2019 Google LLC. All rights reserved. This file is distributed */
/* under the Widevine Master License Agreement. */
/* Partners are expected to edit this file to support target specific code */
/* and limits. */
#ifndef WIDEVINE_ODK_INCLUDE_ODK_TARGET_H_
#define WIDEVINE_ODK_INCLUDE_ODK_TARGET_H_
/* Maximum number of keys can be modified to suit target's resource tier. */
#define ODK_MAX_NUM_KEYS 32
#endif /* WIDEVINE_ODK_INCLUDE_ODK_TARGET_H_ */

View File

@@ -0,0 +1,133 @@
// 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 "core_message_deserialize.h"
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <string>
#include "odk_serialize.h"
#include "odk_structs.h"
#include "odk_structs_priv.h"
#include "serialization_base.h"
namespace oemcrypto_core_message {
namespace deserialize {
namespace {
/**
* Template for parsing requests
*
* Template arguments:
* S: kdo output struct
* T: struct serialized by odk
* U: auto-generated deserializing function for |T|
*/
template <typename S, typename T, typename U>
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;
}
const uint8_t* buf =
reinterpret_cast<const uint8_t*>(oemcrypto_core_message.c_str());
const size_t buf_length = oemcrypto_core_message.size();
Message* msg = nullptr;
AllocateMessage(&msg, message_block);
InitMessage(msg, const_cast<uint8_t*>(buf), buf_length);
SetSize(msg, buf_length);
unpacker(msg, prepared);
if (!ValidMessage(msg)) {
return false;
}
const auto& core_message = prepared->core_message;
core_request->api_major_version = core_message.nonce_values.api_major_version;
core_request->api_minor_version = core_message.nonce_values.api_minor_version;
core_request->nonce = core_message.nonce_values.nonce;
core_request->session_id = core_message.nonce_values.session_id;
// Verify that the minor version matches the released version for the given
// major version.
if (core_request->api_major_version < ODK_FIRST_VERSION) {
// Non existing versions are not supported.
return false;
} else if (core_request->api_major_version == 16) {
// For version 16, we demand a minor version of at least 2.
// We accept 16.2, 16.3, or higher.
if (core_request->api_major_version < 2) return false;
} else {
// Other versions do not (yet) have a restriction on minor number.
// In particular, future versions are accepted for forward compatibility.
}
// For v16, a release and a renewal use the same message structure.
// However, for future API versions, the release might be a separate
// message. Otherwise, we expect an exact match of message types.
if (core_message.message_type != message_type &&
!(message_type == ODK_Renewal_Request_Type &&
core_message.message_type == ODK_Release_Request_Type)) {
return false;
}
// Verify that the amount of buffer we read, which is GetOffset, is not more
// than the total message size. We allow the total message size to be larger
// for forward compatibility because future messages might have extra fields
// that we can ignore.
if (core_message.message_length < GetOffset(msg)) return false;
return true;
}
} // namespace
bool CoreLicenseRequestFromMessage(const std::string& oemcrypto_core_message,
ODK_LicenseRequest* core_license_request) {
const auto unpacker = Unpack_ODK_PreparedLicenseRequest;
ODK_PreparedLicenseRequest prepared_license = {};
return ParseRequest(ODK_License_Request_Type, oemcrypto_core_message,
core_license_request, &prepared_license, unpacker);
}
bool CoreRenewalRequestFromMessage(const std::string& oemcrypto_core_message,
ODK_RenewalRequest* core_renewal_request) {
const auto unpacker = Unpack_ODK_PreparedRenewalRequest;
ODK_PreparedRenewalRequest prepared_renewal = {};
if (!ParseRequest(ODK_Renewal_Request_Type, oemcrypto_core_message,
core_renewal_request, &prepared_renewal, unpacker)) {
return false;
}
core_renewal_request->playback_time_seconds = prepared_renewal.playback_time;
return true;
}
bool CoreProvisioningRequestFromMessage(
const std::string& oemcrypto_core_message,
ODK_ProvisioningRequest* core_provisioning_request) {
const auto unpacker = Unpack_ODK_PreparedProvisioningRequest;
ODK_PreparedProvisioningRequest prepared_provision = {};
if (!ParseRequest(ODK_Provisioning_Request_Type, oemcrypto_core_message,
core_provisioning_request, &prepared_provision, unpacker)) {
return false;
}
const uint8_t* device_id = prepared_provision.device_id;
const uint32_t device_id_length = prepared_provision.device_id_length;
if (device_id_length > ODK_DEVICE_ID_LEN_MAX) {
return false;
}
uint8_t zero[ODK_DEVICE_ID_LEN_MAX] = {};
if (memcmp(zero, device_id + device_id_length,
ODK_DEVICE_ID_LEN_MAX - device_id_length)) {
return false;
}
core_provisioning_request->device_id.assign(
reinterpret_cast<const char*>(device_id), device_id_length);
return true;
}
} // namespace deserialize
} // namespace oemcrypto_core_message

View File

@@ -0,0 +1,125 @@
// 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 "core_message_serialize.h"
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <string>
#include <vector>
#include "odk_serialize.h"
#include "odk_structs.h"
#include "odk_structs_priv.h"
#include "serialization_base.h"
namespace oemcrypto_core_message {
namespace serialize {
namespace {
/**
* Template for parsing requests
*
* Template arguments:
* T: struct to be deserialized by odk
* S: kdo input struct
* P: auto-generated serializing function for |T|
*/
template <typename T, typename S, typename P>
bool CreateResponse(uint32_t message_type, const S& core_request,
std::string* oemcrypto_core_message, T& response,
const P& packer) {
if (!oemcrypto_core_message) {
return false;
}
auto* header = &response.request.core_message;
header->message_type = message_type;
header->nonce_values.api_major_version = core_request.api_major_version;
header->nonce_values.api_minor_version = core_request.api_minor_version;
header->nonce_values.nonce = core_request.nonce;
header->nonce_values.session_id = core_request.session_id;
// The message API version for the response is the minimum of our version and
// the request's version.
if (core_request.api_major_version > ODK_MAJOR_VERSION) {
header->nonce_values.api_major_version = ODK_MAJOR_VERSION;
header->nonce_values.api_minor_version = ODK_MINOR_VERSION;
}
static constexpr size_t BUF_CAPACITY = 2048;
std::vector<uint8_t> buf(BUF_CAPACITY, 0);
Message* msg = nullptr;
AllocateMessage(&msg, message_block);
InitMessage(msg, buf.data(), buf.capacity());
packer(msg, &response);
if (!ValidMessage(msg)) {
return false;
}
uint32_t message_length = GetSize(msg);
InitMessage(msg, buf.data() + sizeof(header->message_type),
sizeof(header->message_length));
Pack_uint32_t(msg, &message_length);
oemcrypto_core_message->assign(reinterpret_cast<const char*>(buf.data()),
message_length);
return true;
}
bool CopyDeviceId(const ODK_ProvisioningRequest& src,
ODK_ProvisioningResponse* dest) {
auto& request = dest->request;
const std::string& device_id = src.device_id;
if (request.device_id_length > sizeof(request.device_id)) {
return false;
}
request.device_id_length = device_id.size();
memset(request.device_id, 0, sizeof(request.device_id));
memcpy(request.device_id, device_id.data(), request.device_id_length);
return true;
}
} // namespace
bool CreateCoreLicenseResponse(const ODK_ParsedLicense& parsed_lic,
const ODK_LicenseRequest& core_request,
const std::string& core_request_sha256,
std::string* oemcrypto_core_message) {
ODK_LicenseResponse license_response{
{}, const_cast<ODK_ParsedLicense*>(&parsed_lic), {0}};
if (core_request_sha256.size() != sizeof(license_response.request_hash))
return false;
memcpy(license_response.request_hash, core_request_sha256.data(),
sizeof(license_response.request_hash));
return CreateResponse(ODK_License_Response_Type, core_request,
oemcrypto_core_message, license_response,
Pack_ODK_LicenseResponse);
}
bool CreateCoreRenewalResponse(const ODK_RenewalRequest& core_request,
uint64_t renewal_duration_seconds,
std::string* oemcrypto_core_message) {
ODK_RenewalResponse renewal_response{{}, core_request.playback_time_seconds};
renewal_response.request.playback_time = core_request.playback_time_seconds;
renewal_response.renewal_duration_seconds = renewal_duration_seconds;
return CreateResponse(ODK_Renewal_Response_Type, core_request,
oemcrypto_core_message, renewal_response,
Pack_ODK_RenewalResponse);
}
bool CreateCoreProvisioningResponse(const ODK_ParsedProvisioning& parsed_prov,
const ODK_ProvisioningRequest& core_request,
std::string* oemcrypto_core_message) {
ODK_ProvisioningResponse prov_response{
{}, const_cast<ODK_ParsedProvisioning*>(&parsed_prov)};
if (!CopyDeviceId(core_request, &prov_response)) {
return false;
}
return CreateResponse(ODK_Provisioning_Response_Type, core_request,
oemcrypto_core_message, prov_response,
Pack_ODK_ProvisioningResponse);
}
} // namespace serialize
} // namespace oemcrypto_core_message

View File

@@ -0,0 +1,183 @@
// 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 "core_message_serialize_proto.h"
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <string>
#include "core_message_serialize.h"
#include "license_protocol.pb.h"
#include "odk_serialize.h"
#include "odk_structs.h"
#include "odk_structs_priv.h"
#include "serialization_base.h"
namespace oemcrypto_core_message {
namespace serialize {
namespace {
/* @ private functions */
/**
* Extract OEMCrypto_Substring (offset, length) from serialized protobuf
*
* Parameters:
* message: serialized license protobuf
* field: substring value
*/
OEMCrypto_Substring GetOecSubstring(const std::string& message,
const std::string& field) {
OEMCrypto_Substring substring = {};
size_t pos = message.find(field);
if (pos != std::string::npos) {
substring = OEMCrypto_Substring{pos, field.length()};
}
return substring;
}
OEMCrypto_KeyObject KeyContainerToOecKey(
const std::string& proto, const video_widevine::License::KeyContainer& k) {
OEMCrypto_KeyObject obj = {};
obj.key_id = GetOecSubstring(proto, k.id());
obj.key_data_iv = GetOecSubstring(proto, k.iv());
// Strip off PKCS#5 padding - since we know the key is 16 or 32 bytes,
// the padding will always be 16 bytes.
const std::string& key_data = k.key();
const size_t PKCS5_PADDING_SIZE = 16;
obj.key_data = GetOecSubstring(
proto, key_data.substr(0, std::max(PKCS5_PADDING_SIZE, key_data.size()) -
PKCS5_PADDING_SIZE));
if (k.has_key_control()) {
const auto& key_control = k.key_control();
obj.key_control_iv = GetOecSubstring(proto, key_control.iv());
obj.key_control = GetOecSubstring(proto, key_control.key_control_block());
}
return obj;
}
} // namespace
// @ public create response functions
bool CreateCoreLicenseResponseFromProto(const std::string& serialized_license,
const ODK_LicenseRequest& core_request,
const std::string& core_request_sha256,
const bool nonce_required,
std::string* oemcrypto_core_message) {
video_widevine::License lic;
if (!lic.ParseFromString(serialized_license)) {
return false;
}
ODK_ParsedLicense parsed_lic{};
bool any_content = false;
bool any_entitlement = false;
for (int i = 0; i < lic.key_size(); ++i) {
const auto& k = lic.key(i);
switch (k.type()) {
case video_widevine::License_KeyContainer::SIGNING: {
if (!k.has_key()) {
continue;
}
parsed_lic.enc_mac_keys_iv =
GetOecSubstring(serialized_license, k.iv());
parsed_lic.enc_mac_keys = GetOecSubstring(serialized_license, k.key());
break;
}
case video_widevine::License_KeyContainer::CONTENT:
case video_widevine::License_KeyContainer::OPERATOR_SESSION:
case video_widevine::License_KeyContainer::ENTITLEMENT: {
if (k.type() == video_widevine::License_KeyContainer::ENTITLEMENT) {
any_entitlement = true;
} else {
any_content = true;
}
if (parsed_lic.key_array_length >= ODK_MAX_NUM_KEYS) {
return false;
}
uint32_t& n = parsed_lic.key_array_length;
parsed_lic.key_array[n++] = KeyContainerToOecKey(serialized_license, k);
break;
}
default: {
continue;
}
}
}
if (any_content && any_entitlement) {
// TODO(b/147513335): this should be logged -- both type of keys.
return false;
}
if (!any_content && !any_entitlement) {
// TODO(b/147513335): this should be logged -- no keys?
return false;
}
parsed_lic.license_type =
any_content ? OEMCrypto_ContentLicense : OEMCrypto_EntitlementLicense;
const auto& lid = lic.id();
if (lid.has_provider_session_token()) {
parsed_lic.pst =
GetOecSubstring(serialized_license, lid.provider_session_token());
}
if (lic.has_srm_requirement()) {
parsed_lic.srm_restriction_data =
GetOecSubstring(serialized_license, lic.srm_requirement());
}
parsed_lic.nonce_required = nonce_required;
const auto& policy = lic.policy();
ODK_TimerLimits& timer_limits = parsed_lic.timer_limits;
timer_limits.soft_enforce_rental_duration =
policy.soft_enforce_rental_duration();
timer_limits.soft_enforce_playback_duration =
policy.soft_enforce_playback_duration();
timer_limits.earliest_playback_start_seconds = 0;
timer_limits.rental_duration_seconds = policy.rental_duration_seconds();
timer_limits.total_playback_duration_seconds =
policy.playback_duration_seconds();
timer_limits.initial_renewal_duration_seconds =
policy.renewal_delay_seconds() +
policy.renewal_recovery_duration_seconds();
return CreateCoreLicenseResponse(parsed_lic, core_request,
core_request_sha256, oemcrypto_core_message);
}
bool CreateCoreProvisioningResponseFromProto(
const std::string& serialized_provisioning_resp,
const ODK_ProvisioningRequest& core_request,
std::string* oemcrypto_core_message) {
ODK_ParsedProvisioning parsed_prov{};
video_widevine::ProvisioningResponse prov;
if (!prov.ParseFromString(serialized_provisioning_resp)) {
return false;
}
parsed_prov.key_type =
OEMCrypto_RSA_Private_Key; // TODO(b/148404408): ECC or RSA
if (prov.has_device_rsa_key()) {
parsed_prov.enc_private_key =
GetOecSubstring(serialized_provisioning_resp, prov.device_rsa_key());
}
if (prov.has_device_rsa_key_iv()) {
parsed_prov.enc_private_key_iv =
GetOecSubstring(serialized_provisioning_resp, prov.device_rsa_key_iv());
}
if (prov.has_wrapping_key()) {
parsed_prov.encrypted_message_key =
GetOecSubstring(serialized_provisioning_resp, prov.wrapping_key());
}
return CreateCoreProvisioningResponse(parsed_prov, core_request,
oemcrypto_core_message);
}
} // namespace serialize
} // namespace oemcrypto_core_message

View File

@@ -0,0 +1,18 @@
# 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.
# These files are used by the server and by some ODK test code. These files are
# not built into the ODK library on the device.
{
'sources': [
'core_message_deserialize.cpp',
'core_message_serialize.cpp',
'core_message_serialize_proto.cpp',
],
'include_dirs': [
'src',
'../include',
],
}

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

@@ -0,0 +1,410 @@
/* Copyright 2019 Google LLC. All rights reserved. This file and proprietary */
/* source code may only be used and distributed under the Widevine Master */
/* License Agreement. */
#include "odk.h"
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include "odk_overflow.h"
#include "odk_serialize.h"
#include "odk_structs.h"
#include "odk_structs_priv.h"
#include "odk_util.h"
#include "serialization_base.h"
/* @ private odk functions */
static OEMCryptoResult ODK_PrepareRequest(
uint8_t* message, size_t message_length, size_t* core_message_length,
uint32_t message_type, const ODK_NonceValues* nonce_values,
void* prepared_request_buffer, size_t prepared_request_buffer_length) {
if (nonce_values == NULL || core_message_length == NULL ||
prepared_request_buffer == NULL ||
*core_message_length > message_length) {
return ODK_ERROR_CORE_MESSAGE;
}
Message* msg = NULL;
AllocateMessage(&msg, message_block);
InitMessage(msg, message, *core_message_length);
/* The core message should be at the beginning of the buffer, and with a
* shorter length. */
if (sizeof(ODK_CoreMessage) > prepared_request_buffer_length) {
return ODK_ERROR_CORE_MESSAGE;
}
ODK_CoreMessage* core_message = (ODK_CoreMessage*)prepared_request_buffer;
*core_message = (ODK_CoreMessage){
message_type,
0,
*nonce_values,
};
/* Set core message length, and pack prepared request into message if the
* message buffer has been correctly initialized by the caller. */
switch (message_type) {
case ODK_License_Request_Type: {
core_message->message_length = ODK_LICENSE_REQUEST_SIZE;
if (sizeof(ODK_PreparedLicenseRequest) > prepared_request_buffer_length) {
return ODK_ERROR_CORE_MESSAGE;
}
Pack_ODK_PreparedLicenseRequest(
msg, (ODK_PreparedLicenseRequest*)prepared_request_buffer);
break;
}
case ODK_Renewal_Request_Type: {
core_message->message_length = ODK_RENEWAL_REQUEST_SIZE;
if (sizeof(ODK_PreparedRenewalRequest) > prepared_request_buffer_length) {
return ODK_ERROR_CORE_MESSAGE;
}
Pack_ODK_PreparedRenewalRequest(
msg, (ODK_PreparedRenewalRequest*)prepared_request_buffer);
break;
}
case ODK_Provisioning_Request_Type: {
core_message->message_length = ODK_PROVISIONING_REQUEST_SIZE;
if (sizeof(ODK_PreparedProvisioningRequest) >
prepared_request_buffer_length) {
return ODK_ERROR_CORE_MESSAGE;
}
Pack_ODK_PreparedProvisioningRequest(
msg, (ODK_PreparedProvisioningRequest*)prepared_request_buffer);
break;
}
default: {
return ODK_ERROR_CORE_MESSAGE;
}
}
*core_message_length = core_message->message_length;
if (GetStatus(msg) != MESSAGE_STATUS_OK) {
/* This is to indicate the caller that the core_message_length has been
* appropriately set, but the message buffer is either empty or too small,
* which needs to be initialized and filled in the subsequent call. */
return OEMCrypto_ERROR_SHORT_BUFFER;
}
if (GetSize(msg) != *core_message_length) {
/* This should not happen. Something is wrong. */
return ODK_ERROR_CORE_MESSAGE;
}
return OEMCrypto_SUCCESS;
}
static OEMCryptoResult ODK_ParseResponse(
const uint8_t* message, size_t message_length, size_t core_message_length,
uint32_t message_type, const ODK_NonceValues* nonce_values,
void* response_buffer, uint32_t response_buffer_length) {
if (message == NULL || response_buffer == NULL ||
core_message_length > message_length) {
return ODK_ERROR_CORE_MESSAGE;
}
Message* msg = NULL;
AllocateMessage(&msg, message_block);
/* We initialize the message buffer with a size of the entire message
* length. */
InitMessage(msg, (uint8_t*)message, message_length);
/* The core message should be at the beginning of the buffer, and with a
* shorter length. The core message is the part we are parsing. */
SetSize(msg, core_message_length);
/* Parse message and unpack it into response buffer. */
switch (message_type) {
case ODK_License_Response_Type: {
if (sizeof(ODK_LicenseResponse) > response_buffer_length) {
return ODK_ERROR_CORE_MESSAGE;
}
Unpack_ODK_LicenseResponse(msg, (ODK_LicenseResponse*)response_buffer);
break;
}
case ODK_Renewal_Response_Type: {
if (sizeof(ODK_RenewalResponse) > response_buffer_length) {
return ODK_ERROR_CORE_MESSAGE;
}
Unpack_ODK_RenewalResponse(msg, (ODK_RenewalResponse*)response_buffer);
break;
}
case ODK_Provisioning_Response_Type: {
if (sizeof(ODK_ProvisioningResponse) > response_buffer_length) {
return ODK_ERROR_CORE_MESSAGE;
}
Unpack_ODK_ProvisioningResponse(
msg, (ODK_ProvisioningResponse*)response_buffer);
break;
}
default: {
return ODK_ERROR_CORE_MESSAGE;
}
}
ODK_CoreMessage* core_message = (ODK_CoreMessage*)response_buffer;
if (GetStatus(msg) != MESSAGE_STATUS_OK ||
message_type != core_message->message_type ||
GetOffset(msg) != core_message->message_length) {
return ODK_ERROR_CORE_MESSAGE;
}
if (nonce_values) {
/* always verify nonce_values for Renewal and Provisioning responses */
if (!ODK_NonceValuesEqual(nonce_values, &(core_message->nonce_values))) {
return OEMCrypto_ERROR_INVALID_NONCE;
}
}
return OEMCrypto_SUCCESS;
}
/* @ public odk functions */
/* @@ prepare request functions */
OEMCryptoResult ODK_PrepareCoreLicenseRequest(
uint8_t* message, size_t message_length, size_t* core_message_length,
const ODK_NonceValues* nonce_values) {
if (core_message_length == NULL || nonce_values == NULL) {
return ODK_ERROR_CORE_MESSAGE;
}
ODK_PreparedLicenseRequest license_request = {
{0},
};
return ODK_PrepareRequest(
message, message_length, core_message_length, ODK_License_Request_Type,
nonce_values, &license_request, sizeof(ODK_PreparedLicenseRequest));
}
OEMCryptoResult ODK_PrepareCoreRenewalRequest(uint8_t* message,
size_t message_length,
size_t* core_message_size,
ODK_NonceValues* nonce_values,
ODK_ClockValues* clock_values,
uint64_t system_time_seconds) {
if (core_message_size == NULL || nonce_values == NULL ||
clock_values == NULL) {
return ODK_ERROR_CORE_MESSAGE;
}
/* If the license has not been loaded, then this is release instead of a
* renewal. All releases use v15. */
if (clock_values->timer_status == ODK_CLOCK_TIMER_STATUS_LICENSE_NOT_LOADED ||
clock_values->timer_status == ODK_CLOCK_TIMER_STATUS_LICENSE_INACTIVE) {
nonce_values->api_major_version = ODK_FIRST_VERSION - 1;
}
if (nonce_values->api_major_version < ODK_FIRST_VERSION) {
*core_message_size = 0;
return OEMCrypto_SUCCESS;
}
ODK_PreparedRenewalRequest renewal_request = {{0}, 0};
/* First, we compute the time this request was made relative to the playback
* clock. */
if (clock_values->time_of_first_decrypt == 0) {
/* It is OK to preemptively request a renewal before playback starts.
* We'll treat this as asking for a renewal at playback time 0. */
renewal_request.playback_time = 0;
} else {
/* Otherwise, playback_time is relative to the first decrypt. */
if (odk_sub_overflow_u64(system_time_seconds,
clock_values->time_of_first_decrypt,
&renewal_request.playback_time)) {
return ODK_ERROR_CORE_MESSAGE;
}
}
/* Save time for this request so that we can verify the response. This makes
* all earlier requests invalid. If preparing this request fails, then all
* requests will be bad. */
clock_values->time_of_renewal_request = renewal_request.playback_time;
return ODK_PrepareRequest(
message, message_length, core_message_size, ODK_Renewal_Request_Type,
nonce_values, &renewal_request, sizeof(ODK_PreparedRenewalRequest));
}
OEMCryptoResult ODK_PrepareCoreProvisioningRequest(
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) {
if (core_message_length == NULL || nonce_values == NULL) {
return ODK_ERROR_CORE_MESSAGE;
}
ODK_PreparedProvisioningRequest provisioning_request = {
{0},
0,
{0},
};
if (device_id_length > sizeof(provisioning_request.device_id)) {
return ODK_ERROR_CORE_MESSAGE;
}
provisioning_request.device_id_length = device_id_length;
if (device_id) {
memcpy(provisioning_request.device_id, device_id, device_id_length);
}
return ODK_PrepareRequest(message, message_length, core_message_length,
ODK_Provisioning_Request_Type, nonce_values,
&provisioning_request,
sizeof(ODK_PreparedProvisioningRequest));
}
/* @@ parse response functions */
OEMCryptoResult ODK_ParseLicense(
const uint8_t* message, size_t message_length, size_t core_message_length,
bool initial_license_load, bool usage_entry_present,
const uint8_t* request_hash, ODK_TimerLimits* timer_limits,
ODK_ClockValues* clock_values, ODK_NonceValues* nonce_values,
ODK_ParsedLicense* parsed_license) {
if (message == NULL || request_hash == NULL || timer_limits == NULL ||
clock_values == NULL || nonce_values == NULL || parsed_license == NULL) {
return ODK_ERROR_CORE_MESSAGE;
}
ODK_LicenseResponse license_response = {{{0}}, parsed_license, {0}};
const OEMCryptoResult err = ODK_ParseResponse(
message, message_length, core_message_length, ODK_License_Response_Type,
NULL, &license_response, sizeof(ODK_LicenseResponse));
if (err != OEMCrypto_SUCCESS) {
return err;
}
/* We do not support future API version. Also, this function should not be
* used for legacy licenses. */
if (license_response.request.core_message.nonce_values.api_major_version >
ODK_MAJOR_VERSION ||
license_response.request.core_message.nonce_values.api_major_version <
ODK_FIRST_VERSION) {
return ODK_UNSUPPORTED_API;
}
/* If the server sent us an older format, record the license's API version. */
if (nonce_values->api_major_version >
license_response.request.core_message.nonce_values.api_major_version) {
nonce_values->api_major_version =
license_response.request.core_message.nonce_values.api_major_version;
nonce_values->api_minor_version =
license_response.request.core_message.nonce_values.api_minor_version;
} else if (nonce_values->api_minor_version >
license_response.request.core_message.nonce_values
.api_minor_version) {
nonce_values->api_minor_version =
license_response.request.core_message.nonce_values.api_minor_version;
}
/* If the license has a provider session token (pst), then OEMCrypto should
* have a usage entry loaded. The opposite is also an error. */
if ((usage_entry_present && parsed_license->pst.length == 0) ||
(!usage_entry_present && parsed_license->pst.length > 0)) {
return ODK_ERROR_CORE_MESSAGE;
}
if (parsed_license->nonce_required) {
if (initial_license_load) {
if (nonce_values->nonce !=
license_response.request.core_message.nonce_values.nonce ||
nonce_values->session_id !=
license_response.request.core_message.nonce_values.session_id) {
return OEMCrypto_ERROR_INVALID_NONCE;
}
} else { /* !initial_license_load */
nonce_values->nonce =
license_response.request.core_message.nonce_values.nonce;
nonce_values->session_id =
license_response.request.core_message.nonce_values.session_id;
}
}
/* For v16, in order to be backwards compatible with a v15 license server,
* OEMCrypto stores a hash of the core license request and only signs the
* message body. Here, when we process the license response, we verify that
* the server has the same hash of the core request. */
if (initial_license_load && parsed_license->nonce_required &&
crypto_memcmp(request_hash, license_response.request_hash,
ODK_SHA256_HASH_SIZE)) {
return ODK_ERROR_CORE_MESSAGE;
}
*timer_limits = parsed_license->timer_limits;
/* And update the clock values state. */
clock_values->timer_status = ODK_CLOCK_TIMER_STATUS_LICENSE_LOADED;
return OEMCrypto_SUCCESS;
}
OEMCryptoResult ODK_ParseRenewal(const uint8_t* message, size_t message_length,
size_t core_message_length,
const ODK_NonceValues* nonce_values,
uint64_t system_time,
const ODK_TimerLimits* timer_limits,
ODK_ClockValues* clock_values,
uint64_t* timer_value) {
if (message == NULL || nonce_values == NULL || timer_limits == NULL ||
clock_values == NULL) {
return ODK_ERROR_CORE_MESSAGE;
}
ODK_RenewalResponse renewal_response = {
{{0}, 0},
0,
};
const OEMCryptoResult err = ODK_ParseResponse(
message, message_length, core_message_length, ODK_Renewal_Response_Type,
nonce_values, &renewal_response, sizeof(ODK_RenewalResponse));
if (err != OEMCrypto_SUCCESS) {
return err;
}
/* Reference:
* Doc: License Duration and Renewal (Changes for OEMCrypto v16)
* Section: Renewal Message
*/
/* If a renewal request is lost in transit, we should throw it out and create
* a new one. We use the timestamp to make sure we have the latest request.
*/
if (clock_values->time_of_renewal_request <
renewal_response.request.playback_time) {
return ODK_STALE_RENEWAL;
}
return ODK_ComputeRenewalDuration(timer_limits, clock_values, system_time,
renewal_response.renewal_duration_seconds,
timer_value);
}
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 (message == NULL || nonce_values == NULL || device_id == NULL ||
parsed_response == NULL) {
return ODK_ERROR_CORE_MESSAGE;
}
ODK_ProvisioningResponse provisioning_response = {{{0}, 0, {0}},
parsed_response};
if (device_id_length > ODK_DEVICE_ID_LEN_MAX) {
return ODK_ERROR_CORE_MESSAGE;
}
const OEMCryptoResult err = ODK_ParseResponse(
message, message_length, core_message_length,
ODK_Provisioning_Response_Type, nonce_values, &provisioning_response,
sizeof(ODK_ProvisioningResponse));
if (err != OEMCrypto_SUCCESS) {
return err;
}
if (crypto_memcmp(device_id, provisioning_response.request.device_id,
device_id_length) != 0) {
return ODK_ERROR_CORE_MESSAGE;
}
const uint8_t zero[ODK_DEVICE_ID_LEN_MAX] = {0};
/* check bytes beyond device_id_length are 0 */
if (crypto_memcmp(zero,
provisioning_response.request.device_id + device_id_length,
ODK_DEVICE_ID_LEN_MAX - device_id_length) != 0) {
return ODK_ERROR_CORE_MESSAGE;
}
return OEMCrypto_SUCCESS;
}

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

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

View File

@@ -0,0 +1,17 @@
# 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.
# These files are built into the ODK library on the device. They are also used
# by the server and by test cocde. These files should compile on C99 compilers.
{
'sources': [
'odk.c',
'odk_overflow.c',
'odk_serialize.c',
'odk_timer.c',
'odk_util.c',
'serialization_base.c',
],
}

View File

@@ -0,0 +1,24 @@
/* Copyright 2019 Google LLC. All rights reserved. This file and proprietary */
/* source code may only be used and distributed under the Widevine Master */
/* License Agreement. */
#ifndef WIDEVINE_ODK_SRC_ODK_ASSERT_H_
#define WIDEVINE_ODK_SRC_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 /* WIDEVINE_ODK_SRC_ODK_ASSERT_H_ */

View File

@@ -0,0 +1,29 @@
/* Copyright 2019 Google LLC. All rights reserved. This file and proprietary */
/* source code may only be used and distributed under the Widevine Master */
/* License Agreement. */
#ifndef WIDEVINE_ODK_SRC_ODK_ENDIAN_H_
#define WIDEVINE_ODK_SRC_ODK_ENDIAN_H_
#ifdef __cplusplus
extern "C" {
#endif
#if defined(__linux__) || defined(__ANDROID__)
#include <endian.h>
#define oemcrypto_htobe32 htobe32
#define oemcrypto_be32toh be32toh
#define oemcrypto_htobe64 htobe64
#define oemcrypto_be64toh be64toh
#else /* defined(__linux__) || defined(__ANDROID__) */
uint32_t oemcrypto_htobe32(uint32_t u32);
uint32_t oemcrypto_be32toh(uint32_t u32);
uint64_t oemcrypto_htobe64(uint64_t u64);
uint64_t oemcrypto_be64toh(uint64_t u64);
#endif /* defined(__linux__) || defined(__ANDROID__) */
#ifdef __cplusplus
}
#endif
#endif /* WIDEVINE_ODK_SRC_ODK_ENDIAN_H_ */

View File

@@ -0,0 +1,36 @@
/* Copyright 2019 Google LLC. All rights reserved. This file and proprietary */
/* source code may only be used and distributed under the Widevine Master */
/* License Agreement. */
#include <stddef.h>
#include <stdint.h>
int odk_sub_overflow_u64(uint64_t a, uint64_t b, uint64_t* c) {
if (a >= b) {
if (c) {
*c = a - b;
}
return 0;
}
return 1;
}
int odk_add_overflow_u64(uint64_t a, uint64_t b, uint64_t* c) {
if (UINT64_MAX - a >= b) {
if (c) {
*c = a + b;
}
return 0;
}
return 1;
}
int odk_add_overflow_ux(size_t a, size_t b, size_t* c) {
if (SIZE_MAX - a >= b) {
if (c) {
*c = a + b;
}
return 0;
}
return 1;
}

View File

@@ -0,0 +1,23 @@
/* Copyright 2019 Google LLC. All rights reserved. This file and proprietary */
/* source code may only be used and distributed under the Widevine Master */
/* License Agreement. */
#ifndef WIDEVINE_ODK_SRC_ODK_OVERFLOW_H_
#define WIDEVINE_ODK_SRC_ODK_OVERFLOW_H_
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
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);
#ifdef __cplusplus
}
#endif
#endif /* WIDEVINE_ODK_SRC_ODK_OVERFLOW_H_ */

View File

@@ -0,0 +1,213 @@
/* Copyright 2019 Google LLC. All rights reserved. This file and proprietary */
/* source code may only be used and distributed under the Widevine Master */
/* License Agreement. */
/*
* This code is auto-generated, do not edit
*/
#include "odk_structs_priv.h"
#include "serialization_base.h"
/* @ serialize */
/* @@ private serialize */
static void Pack_ODK_NonceValues(Message* msg, ODK_NonceValues const* obj) {
Pack_uint16_t(msg, &obj->api_minor_version);
Pack_uint16_t(msg, &obj->api_major_version);
Pack_uint32_t(msg, &obj->nonce);
Pack_uint32_t(msg, &obj->session_id);
}
static void Pack_ODK_CoreMessage(Message* msg, ODK_CoreMessage const* obj) {
Pack_uint32_t(msg, &obj->message_type);
Pack_uint32_t(msg, &obj->message_length);
Pack_ODK_NonceValues(msg, &obj->nonce_values);
}
static void Pack_OEMCrypto_KeyObject(Message* msg,
OEMCrypto_KeyObject const* obj) {
Pack_OEMCrypto_Substring(msg, &obj->key_id);
Pack_OEMCrypto_Substring(msg, &obj->key_data_iv);
Pack_OEMCrypto_Substring(msg, &obj->key_data);
Pack_OEMCrypto_Substring(msg, &obj->key_control_iv);
Pack_OEMCrypto_Substring(msg, &obj->key_control);
}
static void Pack_ODK_TimerLimits(Message* msg, ODK_TimerLimits const* obj) {
Pack_bool(msg, &obj->soft_enforce_rental_duration);
Pack_bool(msg, &obj->soft_enforce_playback_duration);
Pack_uint64_t(msg, &obj->earliest_playback_start_seconds);
Pack_uint64_t(msg, &obj->rental_duration_seconds);
Pack_uint64_t(msg, &obj->total_playback_duration_seconds);
Pack_uint64_t(msg, &obj->initial_renewal_duration_seconds);
}
static void Pack_ODK_ParsedLicense(Message* msg, ODK_ParsedLicense const* obj) {
/* hand-coded */
if (obj->key_array_length > ODK_MAX_NUM_KEYS) {
SetStatus(msg, MESSAGE_STATUS_OVERFLOW_ERROR);
return;
}
Pack_OEMCrypto_Substring(msg, &obj->enc_mac_keys_iv);
Pack_OEMCrypto_Substring(msg, &obj->enc_mac_keys);
Pack_OEMCrypto_Substring(msg, &obj->pst);
Pack_OEMCrypto_Substring(msg, &obj->srm_restriction_data);
Pack_enum(msg, obj->license_type);
Pack_bool(msg, &obj->nonce_required);
Pack_ODK_TimerLimits(msg, &obj->timer_limits);
Pack_uint32_t(msg, &obj->key_array_length);
size_t i;
for (i = 0; i < (size_t)obj->key_array_length; i++) {
Pack_OEMCrypto_KeyObject(msg, &obj->key_array[i]);
}
}
static void Pack_ODK_ParsedProvisioning(Message* msg,
ODK_ParsedProvisioning const* obj) {
Pack_enum(msg, obj->key_type);
Pack_OEMCrypto_Substring(msg, &obj->enc_private_key);
Pack_OEMCrypto_Substring(msg, &obj->enc_private_key_iv);
Pack_OEMCrypto_Substring(msg, &obj->encrypted_message_key);
}
/* @@ odk serialize */
void Pack_ODK_PreparedLicenseRequest(Message* msg,
ODK_PreparedLicenseRequest const* obj) {
Pack_ODK_CoreMessage(msg, &obj->core_message);
}
void Pack_ODK_PreparedRenewalRequest(Message* msg,
ODK_PreparedRenewalRequest const* obj) {
Pack_ODK_CoreMessage(msg, &obj->core_message);
Pack_uint64_t(msg, &obj->playback_time);
}
void Pack_ODK_PreparedProvisioningRequest(
Message* msg, ODK_PreparedProvisioningRequest const* obj) {
Pack_ODK_CoreMessage(msg, &obj->core_message);
Pack_uint32_t(msg, &obj->device_id_length);
PackArray(msg, &obj->device_id[0], sizeof(obj->device_id));
}
/* @@ kdo serialize */
void Pack_ODK_LicenseResponse(Message* msg, ODK_LicenseResponse const* obj) {
Pack_ODK_PreparedLicenseRequest(msg, &obj->request);
Pack_ODK_ParsedLicense(msg, (const ODK_ParsedLicense*)obj->parsed_license);
PackArray(msg, &obj->request_hash[0], sizeof(obj->request_hash));
}
void Pack_ODK_RenewalResponse(Message* msg, ODK_RenewalResponse const* obj) {
Pack_ODK_PreparedRenewalRequest(msg, &obj->request);
Pack_uint64_t(msg, &obj->renewal_duration_seconds);
}
void Pack_ODK_ProvisioningResponse(Message* msg,
ODK_ProvisioningResponse const* obj) {
Pack_ODK_PreparedProvisioningRequest(msg, &obj->request);
Pack_ODK_ParsedProvisioning(
msg, (const ODK_ParsedProvisioning*)obj->parsed_provisioning);
}
/* @ deserialize */
/* @@ private deserialize */
static void Unpack_ODK_NonceValues(Message* msg, ODK_NonceValues* obj) {
Unpack_uint16_t(msg, &obj->api_minor_version);
Unpack_uint16_t(msg, &obj->api_major_version);
Unpack_uint32_t(msg, &obj->nonce);
Unpack_uint32_t(msg, &obj->session_id);
}
static void Unpack_ODK_CoreMessage(Message* msg, ODK_CoreMessage* obj) {
Unpack_uint32_t(msg, &obj->message_type);
Unpack_uint32_t(msg, &obj->message_length);
Unpack_ODK_NonceValues(msg, &obj->nonce_values);
}
static void Unpack_OEMCrypto_KeyObject(Message* msg, OEMCrypto_KeyObject* obj) {
Unpack_OEMCrypto_Substring(msg, &obj->key_id);
Unpack_OEMCrypto_Substring(msg, &obj->key_data_iv);
Unpack_OEMCrypto_Substring(msg, &obj->key_data);
Unpack_OEMCrypto_Substring(msg, &obj->key_control_iv);
Unpack_OEMCrypto_Substring(msg, &obj->key_control);
}
static void Unpack_ODK_TimerLimits(Message* msg, ODK_TimerLimits* obj) {
Unpack_bool(msg, &obj->soft_enforce_rental_duration);
Unpack_bool(msg, &obj->soft_enforce_playback_duration);
Unpack_uint64_t(msg, &obj->earliest_playback_start_seconds);
Unpack_uint64_t(msg, &obj->rental_duration_seconds);
Unpack_uint64_t(msg, &obj->total_playback_duration_seconds);
Unpack_uint64_t(msg, &obj->initial_renewal_duration_seconds);
}
static void Unpack_ODK_ParsedLicense(Message* msg, ODK_ParsedLicense* obj) {
Unpack_OEMCrypto_Substring(msg, &obj->enc_mac_keys_iv);
Unpack_OEMCrypto_Substring(msg, &obj->enc_mac_keys);
Unpack_OEMCrypto_Substring(msg, &obj->pst);
Unpack_OEMCrypto_Substring(msg, &obj->srm_restriction_data);
obj->license_type = (OEMCrypto_LicenseType)Unpack_enum(msg);
Unpack_bool(msg, &obj->nonce_required);
Unpack_ODK_TimerLimits(msg, &obj->timer_limits);
Unpack_uint32_t(msg, &obj->key_array_length);
if (obj->key_array_length > ODK_MAX_NUM_KEYS) {
SetStatus(msg, MESSAGE_STATUS_OVERFLOW_ERROR);
return;
}
uint32_t i;
for (i = 0; i < obj->key_array_length; i++) {
Unpack_OEMCrypto_KeyObject(msg, &obj->key_array[i]);
}
}
static void Unpack_ODK_ParsedProvisioning(Message* msg,
ODK_ParsedProvisioning* obj) {
obj->key_type = (OEMCrypto_PrivateKeyType)Unpack_enum(msg);
Unpack_OEMCrypto_Substring(msg, &obj->enc_private_key);
Unpack_OEMCrypto_Substring(msg, &obj->enc_private_key_iv);
Unpack_OEMCrypto_Substring(msg, &obj->encrypted_message_key);
}
/* @ kdo deserialize */
void Unpack_ODK_PreparedLicenseRequest(Message* msg,
ODK_PreparedLicenseRequest* obj) {
Unpack_ODK_CoreMessage(msg, &obj->core_message);
}
void Unpack_ODK_PreparedRenewalRequest(Message* msg,
ODK_PreparedRenewalRequest* obj) {
Unpack_ODK_CoreMessage(msg, &obj->core_message);
Unpack_uint64_t(msg, &obj->playback_time);
}
void Unpack_ODK_PreparedProvisioningRequest(
Message* msg, ODK_PreparedProvisioningRequest* obj) {
Unpack_ODK_CoreMessage(msg, &obj->core_message);
Unpack_uint32_t(msg, &obj->device_id_length);
UnpackArray(msg, &obj->device_id[0], sizeof(obj->device_id));
}
/* @@ odk deserialize */
void Unpack_ODK_LicenseResponse(Message* msg, ODK_LicenseResponse* obj) {
Unpack_ODK_PreparedLicenseRequest(msg, &obj->request);
Unpack_ODK_ParsedLicense(msg, obj->parsed_license);
UnpackArray(msg, &obj->request_hash[0], sizeof(obj->request_hash));
}
void Unpack_ODK_RenewalResponse(Message* msg, ODK_RenewalResponse* obj) {
Unpack_ODK_PreparedRenewalRequest(msg, &obj->request);
Unpack_uint64_t(msg, &obj->renewal_duration_seconds);
}
void Unpack_ODK_ProvisioningResponse(Message* msg,
ODK_ProvisioningResponse* obj) {
Unpack_ODK_PreparedProvisioningRequest(msg, &obj->request);
Unpack_ODK_ParsedProvisioning(msg, obj->parsed_provisioning);
}

View File

@@ -0,0 +1,49 @@
/* 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 WIDEVINE_ODK_SRC_ODK_SERIALIZE_H_
#define WIDEVINE_ODK_SRC_ODK_SERIALIZE_H_
#include "odk_structs_priv.h"
#include "serialization_base.h"
#ifdef __cplusplus
extern "C" {
#endif
/* odk pack */
void Pack_ODK_PreparedLicenseRequest(Message* msg,
const ODK_PreparedLicenseRequest* obj);
void Pack_ODK_PreparedRenewalRequest(Message* msg,
const ODK_PreparedRenewalRequest* obj);
void Pack_ODK_PreparedProvisioningRequest(
Message* msg, const ODK_PreparedProvisioningRequest* obj);
/* odk unpack */
void Unpack_ODK_LicenseResponse(Message* msg, ODK_LicenseResponse* obj);
void Unpack_ODK_RenewalResponse(Message* msg, ODK_RenewalResponse* obj);
void Unpack_ODK_ProvisioningResponse(Message* msg,
ODK_ProvisioningResponse* obj);
/* kdo pack */
void Pack_ODK_LicenseResponse(Message* msg, const ODK_LicenseResponse* obj);
void Pack_ODK_RenewalResponse(Message* msg, const ODK_RenewalResponse* obj);
void Pack_ODK_ProvisioningResponse(Message* msg,
const ODK_ProvisioningResponse* obj);
/* kdo unpack */
void Unpack_ODK_PreparedLicenseRequest(Message* msg,
ODK_PreparedLicenseRequest* obj);
void Unpack_ODK_PreparedRenewalRequest(Message* msg,
ODK_PreparedRenewalRequest* obj);
void Unpack_ODK_PreparedProvisioningRequest(
Message* msg, ODK_PreparedProvisioningRequest* obj);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* WIDEVINE_ODK_SRC_ODK_SERIALIZE_H_ */

View File

@@ -0,0 +1,104 @@
/* Copyright 2019 Google LLC. All rights reserved. This file and proprietary */
/* source code may only be used and distributed under the Widevine Master */
/* License Agreement. */
#ifndef WIDEVINE_ODK_SRC_ODK_STRUCTS_PRIV_H_
#define WIDEVINE_ODK_SRC_ODK_STRUCTS_PRIV_H_
#include <stdint.h>
#include "OEMCryptoCENCCommon.h"
#include "odk_structs.h"
#ifdef __cplusplus
extern "C" {
#endif
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,
/* Reserve future message types to support forward compatibility. */
ODK_Release_Request_Type = 7,
ODK_Release_Response_Type = 8,
} 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_PreparedLicenseRequest;
typedef struct {
ODK_CoreMessage core_message;
uint64_t playback_time;
} ODK_PreparedRenewalRequest;
typedef struct {
ODK_CoreMessage core_message;
uint32_t device_id_length;
uint8_t device_id[ODK_DEVICE_ID_LEN_MAX];
} ODK_PreparedProvisioningRequest;
typedef struct {
ODK_PreparedLicenseRequest request;
ODK_ParsedLicense* parsed_license;
uint8_t request_hash[ODK_SHA256_HASH_SIZE];
} ODK_LicenseResponse;
typedef struct {
ODK_PreparedRenewalRequest request;
uint64_t renewal_duration_seconds;
} ODK_RenewalResponse;
typedef struct {
ODK_PreparedProvisioningRequest request;
ODK_ParsedProvisioning* parsed_provisioning;
} ODK_ProvisioningResponse;
/* These are the sum of sizeof of each individual member of the request structs
*/
/* without any padding added by the compiler. Make sure they get updated when */
/* request structs change. Refer to test suite OdkSizeTest in */
/* ../test/odk_test.cpp for validations of each of the defined request sizes. */
#define ODK_LICENSE_REQUEST_SIZE 20
#define ODK_RENEWAL_REQUEST_SIZE 28
#define ODK_PROVISIONING_REQUEST_SIZE 88
/* These are the possible timer status values. */
#define ODK_CLOCK_TIMER_STATUS_UNDEFINED 0 /* Should not happen. */
/* When the structure has been initialized, but no license is loaded. */
#define ODK_CLOCK_TIMER_STATUS_LICENSE_NOT_LOADED 1
/* After the license is loaded, before a successful decrypt. */
#define ODK_CLOCK_TIMER_STATUS_LICENSE_LOADED 2
/* After the license is loaded, if a renewal has also been loaded. */
#define ODK_CLOCK_TIMER_STATUS_RENEWAL_LOADED 3
/* The first decrypt has occurred and the timer is active. */
#define ODK_CLOCK_TIMER_STATUS_ACTIVE 4
/* The first decrypt has occurred and the timer is unlimited. */
#define ODK_CLOCK_TIMER_STATUS_UNLIMITED 5
/* The timer has transitioned from active to expired. */
#define ODK_CLOCK_TIMER_STATUS_EXPIRED 6
/* The license has been marked as inactive. */
#define ODK_CLOCK_TIMER_STATUS_LICENSE_INACTIVE 7
/* A helper function for computing timer limits when a renewal is loaded. */
OEMCryptoResult ODK_ComputeRenewalDuration(const ODK_TimerLimits* timer_limits,
ODK_ClockValues* clock_values,
uint64_t system_time_seconds,
uint64_t new_renewal_duration,
uint64_t* timer_value);
#ifdef __cplusplus
}
#endif
#endif /* WIDEVINE_ODK_SRC_ODK_STRUCTS_PRIV_H_ */

View File

@@ -0,0 +1,502 @@
/* Copyright 2019 Google LLC. All rights reserved. This file and proprietary */
/* source code may only be used and distributed under the Widevine Master */
/* License Agreement. */
#include <stdint.h>
#include <string.h>
#include "odk.h"
#include "odk_overflow.h"
#include "odk_structs_priv.h"
/* Private function. Checks to see if the license is active. Returns
* ODK_TIMER_EXPIRED if the license is valid but inactive. Returns
* OEMCrypto_SUCCESS if the license is active. Returns
* OEMCrypto_ERROR_UNKNOWN_FAILURE on other errors. */
static OEMCryptoResult ODK_LicenseActive(const ODK_TimerLimits* timer_limits,
ODK_ClockValues* clock_values) {
/* Check some basic errors. */
if (clock_values == NULL || timer_limits == NULL) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
/* Check if the license has not been loaded yet. */
if (clock_values->timer_status == ODK_CLOCK_TIMER_STATUS_UNDEFINED ||
clock_values->timer_status == ODK_CLOCK_TIMER_STATUS_LICENSE_NOT_LOADED) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (clock_values->status > kActive) {
return ODK_TIMER_EXPIRED;
}
return OEMCrypto_SUCCESS;
}
/* Private function. Sets the timer_value to be the min(timer_value, new_value),
* with the convention that 0 means infinite. The convention that 0 means
* infinite is used for all Widevine license and duration values. */
static void ComputeMinimum(uint64_t* timer_value, uint64_t new_value) {
if (timer_value == NULL) return;
if (new_value > 0) {
if (*timer_value == 0 || *timer_value > new_value) {
*timer_value = new_value;
}
}
}
/* Private function. Check to see if the rental window restricts playback. If
* the rental enforcement is hard, or if this is the first playback, then we
* verify that system_time_seconds is within the rental window. If the
* enforcement is soft and we have already started playback, then there is no
* restriction.
* Return ODK_TIMER_EXPIRED if out of the window.
* Return ODK_TIMER_ACTIVE if within the window, and there is a hard limit.
* Return ODK_DISABLE_TIMER if no there should be no limit.
* Return other error on error.
* Also, if this function does compute a limit, the timer_value is reduced to
* obey that limit. If the limit is less restrictive than the current
* timer_value, then timer_value is not changed. */
static OEMCryptoResult ODK_CheckRentalWindow(
const ODK_TimerLimits* timer_limits, ODK_ClockValues* clock_values,
uint64_t system_time_seconds, uint64_t* timer_value) {
if (clock_values == NULL || timer_limits == NULL || timer_value == NULL) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
/* If playback has already started, and rental duration enforcement is soft,
* then there is no restriction. */
if (clock_values->time_of_first_decrypt > 0 &&
timer_limits->soft_enforce_rental_duration) {
return ODK_DISABLE_TIMER;
}
/* rental_clock = time since license signed. */
uint64_t rental_clock = 0;
if (odk_sub_overflow_u64(system_time_seconds,
clock_values->time_of_license_signed,
&rental_clock)) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
/* Check if it is before license is valid. This is an unusual case. First
* playback may still work if it occurs after the rental window opens. */
if (rental_clock < timer_limits->earliest_playback_start_seconds) {
return ODK_TIMER_EXPIRED;
}
/* If the rental duration is 0, there is no limit. */
if (timer_limits->rental_duration_seconds == 0) {
return ODK_DISABLE_TIMER;
}
/* End of rental window, based on rental clock (not system time). */
uint64_t end_of_rental_window = 0;
if (odk_add_overflow_u64(timer_limits->earliest_playback_start_seconds,
timer_limits->rental_duration_seconds,
&end_of_rental_window)) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
if (end_of_rental_window <= rental_clock) {
return ODK_TIMER_EXPIRED;
}
/* At this point system_time is within the rental window. */
if (timer_limits->soft_enforce_rental_duration) {
/* For soft enforcement, we allow playback, and do not adjust the timer. */
return ODK_DISABLE_TIMER;
}
uint64_t time_left = 0;
if (odk_sub_overflow_u64(end_of_rental_window, rental_clock, &time_left)) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
ComputeMinimum(timer_value, time_left);
return ODK_SET_TIMER;
}
/* Private function. Check to see if the playback window restricts
* playback. This should only be called if playback has started, so that
* clock_values->time_of_first_decrypt is nonzero.
* Return ODK_TIMER_EXPIRED if out of the window.
* Return ODK_SET_TIMER if within the window, and there is a hard limit.
* Return ODK_DISABLE_TIMER if no limit.
* Return other error on error.
* Also, if this function does compute a limit, the timer_value is reduced to
* obey that limit. If the limit is less restrictive than the current
* timer_value, then timer_value is not changed. */
static OEMCryptoResult ODK_CheckPlaybackWindow(
const ODK_TimerLimits* timer_limits, ODK_ClockValues* clock_values,
uint64_t system_time_seconds, uint64_t* timer_value) {
if (clock_values == NULL || timer_limits == NULL || timer_value == NULL) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
/* if the playback duration is 0, there is no limit. */
if (timer_limits->total_playback_duration_seconds == 0) {
return ODK_DISABLE_TIMER;
}
uint64_t end_of_playback_window = 0;
if (odk_add_overflow_u64(timer_limits->total_playback_duration_seconds,
clock_values->time_of_first_decrypt,
&end_of_playback_window)) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
if (end_of_playback_window <= system_time_seconds) {
return ODK_TIMER_EXPIRED;
}
/* At this point, system_time is within the total playback window. */
if (timer_limits->soft_enforce_playback_duration) {
/* For soft enforcement, we allow playback, and do not adjust the timer. */
return ODK_DISABLE_TIMER;
}
uint64_t time_left = 0;
if (odk_sub_overflow_u64(end_of_playback_window, system_time_seconds,
&time_left)) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
ComputeMinimum(timer_value, time_left);
return ODK_SET_TIMER;
}
/* Update the timer status. If playback has already started, we use the given
* status. However, if playback has not yet started, then we expect a call to
* ODK_AttemptFirstPlayback in the future, and we need to signal to it that we
* have already computed the timer limit. */
static void ODK_UpdateTimerStatusForRenewal(ODK_ClockValues* clock_values,
uint32_t new_status) {
if (clock_values == NULL) {
return; /* should not happen. */
}
if (clock_values->timer_status == ODK_CLOCK_TIMER_STATUS_LICENSE_LOADED) {
/* Signal that the timer is already set. */
clock_values->timer_status = ODK_CLOCK_TIMER_STATUS_RENEWAL_LOADED;
} else {
clock_values->timer_status = new_status;
}
}
/* Private function, but accessed from odk.c so cannot be static. This checks to
* see if a renewal message should restart the playback timer and sets the value
* appropriately. */
OEMCryptoResult ODK_ComputeRenewalDuration(const ODK_TimerLimits* timer_limits,
ODK_ClockValues* clock_values,
uint64_t system_time_seconds,
uint64_t new_renewal_duration,
uint64_t* timer_value) {
if (timer_limits == NULL || clock_values == NULL) {
return OEMCrypto_ERROR_INVALID_CONTEXT; /* should not happen. */
}
/* If this is before the license was signed, something is odd. Return an
* error. */
if (system_time_seconds < clock_values->time_of_license_signed) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
const OEMCryptoResult license_status =
ODK_LicenseActive(timer_limits, clock_values);
/* If the license is not active, then we cannot renew the license. */
if (license_status != OEMCrypto_SUCCESS) {
return license_status;
}
/* We start with the new renewal duration as the new timer limit. */
uint64_t new_timer_value = new_renewal_duration;
/* Then we factor in the rental window restrictions. This might decrease
* new_timer_value. */
const OEMCryptoResult rental_status = ODK_CheckRentalWindow(
timer_limits, clock_values, system_time_seconds, &new_timer_value);
/* If the rental status forbids playback, then we're done. */
if ((rental_status != ODK_DISABLE_TIMER) &&
(rental_status != ODK_SET_TIMER)) {
return rental_status;
}
/* If playback has already started and it has hard enforcement, then check
* total playback window. */
if (clock_values->time_of_first_decrypt > 0 &&
!timer_limits->soft_enforce_playback_duration) {
/* This might decrease new_timer_value. */
const OEMCryptoResult playback_status = ODK_CheckPlaybackWindow(
timer_limits, clock_values, system_time_seconds, &new_timer_value);
/* If the timer limits forbid playback in the playback window, then we're
* done. */
if ((playback_status != ODK_DISABLE_TIMER) &&
(playback_status != ODK_SET_TIMER)) {
return playback_status;
}
}
/* If new_timer_value is infinite (represented by 0), then there are no
* limits, so we can return now. */
if (new_timer_value == 0) {
clock_values->time_when_timer_expires = 0;
ODK_UpdateTimerStatusForRenewal(clock_values,
ODK_CLOCK_TIMER_STATUS_UNLIMITED);
return ODK_DISABLE_TIMER;
}
/* If the caller gave us a pointer to store the new timer value. Fill it. */
if (timer_value != NULL) {
*timer_value = new_timer_value;
}
if (odk_add_overflow_u64(system_time_seconds, new_timer_value,
&clock_values->time_when_timer_expires)) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
ODK_UpdateTimerStatusForRenewal(clock_values, ODK_CLOCK_TIMER_STATUS_ACTIVE);
return ODK_SET_TIMER;
}
/************************************************************************/
/************************************************************************/
/* Public functions, declared in odk.h. */
/* This is called when OEMCrypto opens a new session. */
OEMCryptoResult ODK_InitializeSessionValues(ODK_TimerLimits* timer_limits,
ODK_ClockValues* clock_values,
ODK_NonceValues* nonce_values,
uint32_t api_major_version,
uint32_t session_id) {
if (timer_limits == NULL || clock_values == NULL || nonce_values == NULL) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
/* Check that the API version passed in from OEMCrypto matches the version of
* this ODK library. */
if (api_major_version != ODK_MAJOR_VERSION) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
timer_limits->soft_enforce_rental_duration = false;
timer_limits->soft_enforce_playback_duration = false;
timer_limits->earliest_playback_start_seconds = 0;
timer_limits->rental_duration_seconds = 0;
timer_limits->total_playback_duration_seconds = 0;
timer_limits->initial_renewal_duration_seconds = 0;
ODK_InitializeClockValues(clock_values, 0);
nonce_values->api_major_version = ODK_MAJOR_VERSION;
nonce_values->api_minor_version = ODK_MINOR_VERSION;
nonce_values->nonce = 0;
nonce_values->session_id = session_id;
return OEMCrypto_SUCCESS;
}
/* This is called when OEMCrypto generates a new nonce in
* OEMCrypto_GenerateNonce. */
OEMCryptoResult ODK_SetNonceValues(ODK_NonceValues* nonce_values,
uint32_t nonce) {
if (nonce_values == NULL) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
/* Setting the nonce should only happen once per session. */
if (nonce_values->nonce != 0) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
nonce_values->nonce = nonce;
return OEMCrypto_SUCCESS;
}
/* This is called when OEMCrypto signs a license. */
OEMCryptoResult ODK_InitializeClockValues(ODK_ClockValues* clock_values,
uint64_t system_time_seconds) {
if (clock_values == NULL) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
clock_values->time_of_license_signed = system_time_seconds;
clock_values->time_of_first_decrypt = 0;
clock_values->time_of_last_decrypt = 0;
clock_values->time_when_timer_expires = 0;
clock_values->timer_status = ODK_CLOCK_TIMER_STATUS_LICENSE_NOT_LOADED;
clock_values->status = kUnused;
return OEMCrypto_SUCCESS;
}
/* This is called when OEMCrypto reloads a usage entry. */
OEMCryptoResult ODK_ReloadClockValues(ODK_ClockValues* clock_values,
uint64_t time_of_license_signed,
uint64_t time_of_first_decrypt,
uint64_t time_of_last_decrypt,
enum OEMCrypto_Usage_Entry_Status status,
uint64_t system_time_seconds) {
if (clock_values == NULL) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
clock_values->time_of_license_signed = time_of_license_signed;
clock_values->time_of_first_decrypt = time_of_first_decrypt;
clock_values->time_of_last_decrypt = time_of_last_decrypt;
clock_values->time_when_timer_expires = 0;
clock_values->timer_status = ODK_CLOCK_TIMER_STATUS_LICENSE_NOT_LOADED;
clock_values->status = status;
return OEMCrypto_SUCCESS;
}
/* This is called on the first playback for a session. */
OEMCryptoResult ODK_AttemptFirstPlayback(uint64_t system_time_seconds,
const ODK_TimerLimits* timer_limits,
ODK_ClockValues* clock_values,
uint64_t* timer_value) {
if (clock_values == NULL || timer_limits == NULL) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
/* All times are relative to when the license was signed. */
uint64_t rental_time = 0;
if (odk_sub_overflow_u64(system_time_seconds,
clock_values->time_of_license_signed,
&rental_time)) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
if (rental_time < timer_limits->earliest_playback_start_seconds) {
clock_values->timer_status = ODK_TIMER_EXPIRED;
return ODK_TIMER_EXPIRED;
}
/* If the license is inactive or not loaded, then playback is not allowed. */
OEMCryptoResult status = ODK_LicenseActive(timer_limits, clock_values);
if (status != OEMCrypto_SUCCESS) {
return status;
}
/* We start with the initial renewal duration as the timer limit. */
uint64_t new_timer_value = timer_limits->initial_renewal_duration_seconds;
/* However, if a renewal was loaded before this first playback, use the
* previously computed limit. */
if (clock_values->timer_status == ODK_CLOCK_TIMER_STATUS_RENEWAL_LOADED) {
if (clock_values->time_when_timer_expires <= system_time_seconds) {
return ODK_TIMER_EXPIRED;
}
if (odk_sub_overflow_u64(clock_values->time_when_timer_expires,
system_time_seconds, &new_timer_value)) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
}
/* Then we factor in the rental window restrictions. This might decrease
* new_timer_value. */
status = ODK_CheckRentalWindow(timer_limits, clock_values,
system_time_seconds, &new_timer_value);
if ((status != ODK_DISABLE_TIMER) && (status != ODK_SET_TIMER)) {
return status;
}
/* If playback has not already started, then this is the first playback. */
if (clock_values->time_of_first_decrypt == 0) {
clock_values->time_of_first_decrypt = system_time_seconds;
clock_values->status = kActive;
}
/* Similar to the rental window, we check the playback window
* restrictions. This might decrease new_timer_value. */
status = ODK_CheckPlaybackWindow(timer_limits, clock_values,
system_time_seconds, &new_timer_value);
if ((status != ODK_DISABLE_TIMER) && (status != ODK_SET_TIMER)) {
return status;
}
/* We know we are allowed to decrypt. The rest computes the timer duration. */
clock_values->time_of_last_decrypt = system_time_seconds;
/* If new_timer_value is infinite (represented by 0), then there are no
* limits, so we can return now. */
if (new_timer_value == 0) {
clock_values->time_when_timer_expires = 0;
clock_values->timer_status = ODK_CLOCK_TIMER_STATUS_UNLIMITED;
return ODK_DISABLE_TIMER;
}
/* If the caller gave us a pointer to store the new timer value. Fill it. */
if (timer_value) {
*timer_value = new_timer_value;
}
if (odk_add_overflow_u64(system_time_seconds, new_timer_value,
&clock_values->time_when_timer_expires)) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
clock_values->timer_status = ODK_CLOCK_TIMER_STATUS_ACTIVE;
return ODK_SET_TIMER;
}
/* This is called regularly during playback if OEMCrypto does not implement its
* own timer. */
OEMCryptoResult ODK_UpdateLastPlaybackTime(uint64_t system_time_seconds,
const ODK_TimerLimits* timer_limits,
ODK_ClockValues* clock_values) {
OEMCryptoResult status = ODK_LicenseActive(timer_limits, clock_values);
if (status != OEMCrypto_SUCCESS) {
return status;
}
switch (clock_values->timer_status) {
case ODK_CLOCK_TIMER_STATUS_UNLIMITED:
break;
case ODK_CLOCK_TIMER_STATUS_ACTIVE:
/* Note: we allow playback at the time when the timer expires, but not
* after. This is not important for business cases, but it makes it
* easier to write tests. */
if (clock_values->time_when_timer_expires > 0 &&
system_time_seconds > clock_values->time_when_timer_expires) {
clock_values->timer_status = ODK_CLOCK_TIMER_STATUS_EXPIRED;
return ODK_TIMER_EXPIRED;
}
break;
default: /* Expired, error state, or never started. */
return ODK_TIMER_EXPIRED;
}
clock_values->time_of_last_decrypt = system_time_seconds;
return OEMCrypto_SUCCESS;
}
/* This is called from OEMCrypto_DeactivateUsageEntry. */
OEMCryptoResult ODK_DeactivateUsageEntry(ODK_ClockValues* clock_values) {
if (clock_values == NULL) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (clock_values->status == kUnused) {
clock_values->status = kInactiveUnused;
} else if (clock_values->status == kActive) {
clock_values->status = kInactiveUsed;
}
clock_values->timer_status = ODK_CLOCK_TIMER_STATUS_LICENSE_INACTIVE;
return OEMCrypto_SUCCESS;
}
/* This is called when OEMCrypto loads a legacy v15 license, from
* OEMCrypto_LoadKeys. */
OEMCryptoResult ODK_InitializeV15Values(ODK_TimerLimits* timer_limits,
ODK_ClockValues* clock_values,
ODK_NonceValues* nonce_values,
uint32_t key_duration,
uint64_t system_time_seconds) {
if (timer_limits == NULL || clock_values == NULL || nonce_values == NULL) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
timer_limits->soft_enforce_playback_duration = false;
timer_limits->soft_enforce_rental_duration = false;
timer_limits->earliest_playback_start_seconds = 0;
timer_limits->rental_duration_seconds = 0;
timer_limits->total_playback_duration_seconds = 0;
timer_limits->initial_renewal_duration_seconds = key_duration;
nonce_values->api_major_version = 15;
nonce_values->api_minor_version = 0;
if (key_duration > 0) {
clock_values->time_when_timer_expires = system_time_seconds + key_duration;
} else {
clock_values->time_when_timer_expires = 0;
}
clock_values->timer_status = ODK_CLOCK_TIMER_STATUS_LICENSE_LOADED;
return OEMCrypto_SUCCESS;
}
/* This is called when OEMCrypto loads a legacy license renewal in
* OEMCrypto_RefreshKeys. */
OEMCryptoResult ODK_RefreshV15Values(const ODK_TimerLimits* timer_limits,
ODK_ClockValues* clock_values,
const ODK_NonceValues* nonce_values,
uint64_t system_time_seconds,
uint32_t new_key_duration,
uint64_t* timer_value) {
if (timer_limits == NULL || clock_values == NULL || nonce_values == NULL) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
if (nonce_values->api_major_version != 15) {
return OEMCrypto_ERROR_INVALID_NONCE;
}
if (clock_values->status > kActive) {
clock_values->timer_status = ODK_CLOCK_TIMER_STATUS_LICENSE_INACTIVE;
return ODK_TIMER_EXPIRED;
}
return ODK_ComputeRenewalDuration(timer_limits, clock_values,
system_time_seconds, new_key_duration,
timer_value);
}

View File

@@ -0,0 +1,34 @@
/* Copyright 2019 Google LLC. All rights reserved. This file and proprietary */
/* source code may only be used and distributed under the Widevine Master */
/* License Agreement. */
#include "odk_util.h"
int crypto_memcmp(const void* in_a, const void* in_b, size_t len) {
if (len == 0) {
return 0;
}
/* Only valid pointers are allowed. */
if (in_a == NULL || in_b == NULL) {
return -1;
}
const uint8_t* a = (const uint8_t*)in_a;
const uint8_t* b = (const uint8_t*)in_b;
uint8_t x = 0;
for (size_t i = 0; i < len; i++) {
x |= a[i] ^ b[i];
}
return x;
}
bool ODK_NonceValuesEqual(const ODK_NonceValues* a, const ODK_NonceValues* b) {
if (a == NULL || b == NULL) {
return (a == b);
}
return (a->api_major_version == b->api_major_version &&
a->api_minor_version == b->api_minor_version &&
a->nonce == b->nonce && a->session_id == b->session_id);
}

View File

@@ -0,0 +1,28 @@
/* Copyright 2019 Google LLC. All rights reserved. This file and proprietary */
/* source code may only be used and distributed under the Widevine Master */
/* License Agreement. */
#ifndef WIDEVINE_ODK_SRC_ODK_UTIL_H_
#define WIDEVINE_ODK_SRC_ODK_UTIL_H_
#include <stddef.h>
#include <stdint.h>
#include "odk_structs.h"
#ifdef __cplusplus
extern "C" {
#endif
/* crypto_memcmp returns zero iff the |len| bytes at |a| and |b| are equal. It
* takes an amount of time dependent on |len|, but independent of the contents
* of |a| and |b|. Unlike memcmp, it cannot be used to order elements as the
* return value when a != b is undefined, other than being non-zero. */
int crypto_memcmp(const void* a, const void* b, size_t len);
bool ODK_NonceValuesEqual(const ODK_NonceValues* a, const ODK_NonceValues* b);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* WIDEVINE_ODK_SRC_ODK_UTIL_H_ */

View File

@@ -0,0 +1,259 @@
/* Copyright 2019 Google LLC. All rights reserved. This file and proprietary */
/* source code may only be used and distributed under the Widevine Master */
/* License Agreement. */
#include "serialization_base.h"
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include "OEMCryptoCENCCommon.h"
#include "odk_assert.h"
#include "odk_overflow.h"
struct _Message {
uint8_t* base;
size_t capacity;
size_t size; /* bytes written */
size_t read_offset; /* bytes read */
MessageStatus status;
};
/* TODO(b/150776214): this can be removed once AllocateMessage gets cleaned up
*/
/*
* odk_static_assert(SIZE_OF_MESSAGE_STRUCT >= sizeof(struct _Message),
* "SIZE_OF_MESSAGE_STRUCT too small");
*/
bool ValidMessage(Message* message) {
if (message == NULL) {
return false;
}
if (message->status != MESSAGE_STATUS_OK) {
return false;
}
if (message->base == NULL) {
message->status = MESSAGE_STATUS_NULL_POINTER_ERROR;
return false;
}
if (message->size > message->capacity ||
message->read_offset > message->size) {
message->status = MESSAGE_STATUS_OVERFLOW_ERROR;
return false;
}
return true;
}
static void PackBytes(Message* message, const uint8_t* ptr, size_t count) {
if (count <= message->capacity - message->size) {
memcpy((void*)(message->base + message->size), (void*)ptr, count);
message->size += count;
} else {
message->status = MESSAGE_STATUS_OVERFLOW_ERROR;
}
}
void Pack_enum(Message* message, int value) {
uint32_t v32 = value;
Pack_uint32_t(message, &v32);
}
void Pack_bool(Message* message, const bool* value) {
if (!ValidMessage(message)) return;
uint8_t data[4] = {0};
data[3] = *value ? 1 : 0;
PackBytes(message, data, sizeof(data));
}
void Pack_uint16_t(Message* message, const uint16_t* value) {
if (!ValidMessage(message)) return;
uint8_t data[2] = {0};
data[0] = *value >> 8;
data[1] = *value >> 0;
PackBytes(message, data, sizeof(data));
}
void Pack_uint32_t(Message* message, const uint32_t* value) {
if (!ValidMessage(message)) return;
uint8_t data[4] = {0};
data[0] = *value >> 24;
data[1] = *value >> 16;
data[2] = *value >> 8;
data[3] = *value >> 0;
PackBytes(message, data, sizeof(data));
}
void Pack_uint64_t(Message* message, const uint64_t* value) {
if (!ValidMessage(message)) return;
uint32_t hi = *value >> 32;
uint32_t lo = *value;
Pack_uint32_t(message, &hi);
Pack_uint32_t(message, &lo);
}
void PackArray(Message* message, const uint8_t* base, size_t size) {
if (!ValidMessage(message)) return;
PackBytes(message, base, size);
}
void Pack_OEMCrypto_Substring(Message* msg, const OEMCrypto_Substring* obj) {
uint32_t offset = obj->offset;
uint32_t length = obj->length;
Pack_uint32_t(msg, &offset);
Pack_uint32_t(msg, &length);
}
static void UnpackBytes(Message* message, uint8_t* ptr, size_t count) {
if (count <= message->size - message->read_offset) {
memcpy((void*)ptr, (void*)(message->base + message->read_offset), count);
message->read_offset += count;
} else {
message->status = MESSAGE_STATUS_UNDERFLOW_ERROR;
}
}
int Unpack_enum(Message* message) {
uint32_t v32;
Unpack_uint32_t(message, &v32);
return v32;
}
void Unpack_bool(Message* message, bool* value) {
if (!ValidMessage(message)) return;
uint8_t data[4] = {0};
UnpackBytes(message, data, sizeof(data));
*value = (0 != data[3]);
}
void Unpack_uint16_t(Message* message, uint16_t* value) {
if (!ValidMessage(message)) return;
uint8_t data[2] = {0};
UnpackBytes(message, data, sizeof(data));
*value = data[0];
*value = *value << 8 | data[1];
}
void Unpack_uint32_t(Message* message, uint32_t* value) {
if (!ValidMessage(message)) return;
uint8_t data[4] = {0};
UnpackBytes(message, data, sizeof(data));
*value = data[0];
*value = *value << 8 | data[1];
*value = *value << 8 | data[2];
*value = *value << 8 | data[3];
}
void Unpack_uint64_t(Message* message, uint64_t* value) {
if (!ValidMessage(message)) return;
uint32_t hi = 0;
uint32_t lo = 0;
Unpack_uint32_t(message, &hi);
Unpack_uint32_t(message, &lo);
*value = hi;
*value = *value << 32 | lo;
}
void Unpack_OEMCrypto_Substring(Message* msg, OEMCrypto_Substring* obj) {
uint32_t offset = 0, length = 0;
Unpack_uint32_t(msg, &offset);
Unpack_uint32_t(msg, &length);
if (!ValidMessage(msg)) return;
/* Each substring should be contained within the message body, which is in the
* total message, just after the core message. The offset of a substring is
* relative to the message body. So we need to verify:
* 0 < offset and offset + length < message->capacity - message->size
* or offset + length + message->size < message->capacity
*/
size_t substring_end = 0; /* = offset + length; */
size_t end = 0; /* = substring_end + message->size; */
if (odk_add_overflow_ux(offset, length, &substring_end) ||
odk_add_overflow_ux(substring_end, msg->size, &end) ||
end > msg->capacity) {
msg->status = MESSAGE_STATUS_OVERFLOW_ERROR;
return;
}
obj->offset = offset;
obj->length = length;
}
/* copy out */
void UnpackArray(Message* message, uint8_t* address, size_t size) {
if (!ValidMessage(message)) return;
UnpackBytes(message, address, size);
}
/*
* The message structure, which is separate from the buffer,
* is initialized to reference the buffer
*/
void InitMessage(Message* message, uint8_t* buffer, size_t capacity) {
if (message == NULL) return;
memset(message, 0, sizeof(Message));
message->base = buffer;
message->capacity = capacity;
message->size = 0;
message->read_offset = 0;
message->status = MESSAGE_STATUS_OK;
}
/*
* The message structure is in the first sizeof(Memory) bytes
* of the buffer
*/
Message* CreateMessage(uint8_t* buffer, size_t buffer_size) {
if (buffer == NULL || buffer_size < sizeof(Message)) return NULL;
Message* message = (Message*)buffer;
message->base = buffer + sizeof(Message);
message->capacity = buffer_size - sizeof(Message);
message->size = 0;
message->read_offset = 0;
message->status = MESSAGE_STATUS_OK;
return message;
}
/*
* Set the message to an empty state
*/
void ResetMessage(Message* message) {
message->size = 0;
message->read_offset = 0;
message->status = MESSAGE_STATUS_OK;
}
uint8_t* GetBase(Message* message) {
if (message == NULL) return NULL;
return message->base;
}
size_t GetCapacity(Message* message) {
if (message == NULL) return 0;
return message->capacity;
}
size_t GetSize(Message* message) {
if (message == NULL) return 0;
return message->size;
}
void SetSize(Message* message, size_t size) {
if (message == NULL) return;
if (size > message->capacity)
message->status = MESSAGE_STATUS_OVERFLOW_ERROR;
else
message->size = size;
}
MessageStatus GetStatus(Message* message) { return message->status; }
void SetStatus(Message* message, MessageStatus status) {
message->status = status;
}
size_t GetOffset(Message* message) {
if (message == NULL) return 0;
return message->read_offset;
}
size_t SizeOfMessageStruct() { return sizeof(Message); }

View File

@@ -0,0 +1,96 @@
/* Copyright 2019 Google LLC. All rights reserved. This file and proprietary */
/* source code may only be used and distributed under the Widevine Master */
/* License Agreement. */
#ifndef WIDEVINE_ODK_SRC_SERIALIZATION_BASE_H_
#define WIDEVINE_ODK_SRC_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;
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;
bool ValidMessage(Message* message);
void Pack_enum(Message* message, int value);
void Pack_bool(Message* message, const bool* value);
void Pack_uint16_t(Message* message, const uint16_t* value);
void Pack_uint32_t(Message* message, const uint32_t* value);
void Pack_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);
int Unpack_enum(Message* message);
void Unpack_bool(Message* message, bool* value);
void Unpack_uint16_t(Message* message, uint16_t* value);
void Unpack_uint32_t(Message* message, uint32_t* value);
void Unpack_uint64_t(Message* message, uint64_t* value);
void UnpackArray(Message* message, uint8_t* address,
size_t size); /* copy out */
void Unpack_OEMCrypto_Substring(Message* msg, OEMCrypto_Substring* obj);
/*
* 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 /* WIDEVINE_ODK_SRC_SERIALIZATION_BASE_H_ */

View File

@@ -0,0 +1,168 @@
// Copyright 2020 Google LLC. All rights reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
cc_defaults {
name: "odk_fuzz_library_defaults",
srcs: [
"odk_fuzz_helper.cpp",
],
include_dirs: [
"vendor/widevine/libwvdrmengine/oemcrypto/odk/test",
"vendor/widevine/libwvdrmengine/oemcrypto/odk/include",
"vendor/widevine/libwvdrmengine/oemcrypto/odk/src",
],
}
cc_fuzz {
name: "odk_license_request_fuzz",
srcs: [
"odk_license_request_fuzz.cpp",
],
fuzz_config: {
componentid: 611718,
},
corpus: ["corpus/little_endian_64bit/license_request_corpus/*"],
static_libs: [
"libwv_kdo",
"libwv_odk",
],
defaults: ["odk_fuzz_library_defaults"],
proprietary: true,
}
cc_fuzz {
name: "odk_renewal_request_fuzz",
srcs: [
"odk_renewal_request_fuzz.cpp",
],
fuzz_config: {
componentid: 611718,
},
corpus: ["corpus/little_endian_64bit/renewal_request_corpus/*"],
static_libs: [
"libwv_kdo",
"libwv_odk",
],
defaults: ["odk_fuzz_library_defaults"],
proprietary: true,
}
cc_fuzz {
name: "odk_provisioning_request_fuzz",
srcs: [
"odk_provisioning_request_fuzz.cpp",
],
fuzz_config: {
componentid: 611718,
},
corpus: ["corpus/little_endian_64bit/provisioning_request_corpus/*"],
static_libs: [
"libwv_kdo",
"libwv_odk",
],
defaults: ["odk_fuzz_library_defaults"],
proprietary: true,
}
cc_fuzz {
name: "odk_license_response_fuzz",
srcs: [
"odk_license_response_fuzz.cpp",
],
fuzz_config: {
componentid: 611718,
},
corpus: ["corpus/little_endian_64bit/license_response_corpus/*"],
static_libs: [
"libwv_kdo",
"libwv_odk",
],
defaults: ["odk_fuzz_library_defaults"],
proprietary: true,
}
cc_fuzz {
name: "odk_renewal_response_fuzz",
srcs: [
"odk_renewal_response_fuzz.cpp",
],
fuzz_config: {
componentid: 611718,
},
corpus: ["corpus/little_endian_64bit/renewal_response_corpus/*"],
static_libs: [
"libwv_kdo",
"libwv_odk",
],
defaults: ["odk_fuzz_library_defaults"],
proprietary: true,
}
cc_fuzz {
name: "odk_provisioning_response_fuzz",
srcs: [
"odk_provisioning_response_fuzz.cpp",
],
fuzz_config: {
componentid: 611718,
},
corpus: ["corpus/little_endian_64bit/provisioning_response_corpus/*"],
static_libs: [
"libwv_kdo",
"libwv_odk",
],
defaults: ["odk_fuzz_library_defaults"],
proprietary: true,
}
cc_fuzz {
name: "odk_license_response_fuzz_with_mutator",
srcs: [
"odk_license_response_fuzz_with_mutator.cpp",
],
fuzz_config: {
componentid: 611718,
},
corpus: ["corpus/little_endian_64bit/license_response_corpus/*"],
static_libs: [
"libwv_kdo",
"libwv_odk",
],
defaults: ["odk_fuzz_library_defaults"],
proprietary: true,
}
cc_fuzz {
name: "odk_renewal_response_fuzz_with_mutator",
srcs: [
"odk_renewal_response_fuzz_with_mutator.cpp",
],
fuzz_config: {
componentid: 611718,
},
corpus: ["corpus/little_endian_64bit/renewal_response_corpus/*"],
static_libs: [
"libwv_kdo",
"libwv_odk",
],
defaults: ["odk_fuzz_library_defaults"],
proprietary: true,
}
cc_fuzz {
name: "odk_provisioning_response_fuzz_with_mutator",
srcs: [
"odk_provisioning_response_fuzz_with_mutator.cpp",
],
fuzz_config: {
componentid: 611718,
},
corpus: ["corpus/little_endian_64bit/provisioning_response_corpus/*"],
static_libs: [
"libwv_kdo",
"libwv_odk",
],
defaults: ["odk_fuzz_library_defaults"],
proprietary: true,
}

View File

@@ -0,0 +1,19 @@
# ODK Fuzzing
## Objective
* Run fuzzing on ODK and KDO serialize and deserialize APIs using google
supported fuzzer engines to find security vulnerabilities. Any issues found
by clusterfuzz will be reported to
[odk fuzz buganizer](https://b.corp.google.com/issues?q=componentid:425099%20status:open%20reporter:cluster-fuzz-googleplex@google.com).
## Run fuzz target on local machine
* In order to run fuzz target locally and see code coverage, save binary input
to be tested against fuzz target into a temporary corpus directory and
execute following commands
```shell
$ blaze build --config=asan-fuzzer //your:target
$ blaze-bin/your/target FULL_CORPUS_DIR
```

View File

@@ -0,0 +1,27 @@
// Copyright 2020 Google LLC. All rights reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
// ----------------------------------------------------------------
// Builds odk_corpus_generator shared library, which can be used with
// LD_PRELOAD command to generate corpus by intercepting oemcrypto
// unit tests.
// ----------------------------------------------------------------
// Builds libwv_odk.so, The ODK shared Library (libwv_odk) is used
// by the OEMCrypto unit tests to generate corpus for ODK fuzz scrips.
cc_library_shared {
name: "libwv_odk_corpus_generator",
include_dirs: [
"vendor/widevine/libwvdrmengine/oemcrypto/include",
"vendor/widevine/libwvdrmengine/oemcrypto/odk/include",
"vendor/widevine/libwvdrmengine/oemcrypto/odk/test",
],
host_ldlibs: ["-ldl"],
srcs: [
"odk_corpus_generator.c",
"odk_corpus_generator_helper.c",
],
proprietary: true,
owner: "widevine",
}

View File

@@ -0,0 +1,79 @@
# Objective
The Idea behind the corpus generator code is to intercept OEMCrypto unit test
calls to odk APIs using LD_PRELOAD and read the data into corpus files which can
be fed as corpus to fuzzer scripts.
LD_PRELOAD command needs to be run from cdm repository while running oemcrypto
unit tests.
## Get OEMCrypto and Build OEMCrypto unit tests:
* Install Pre-requisites
```shell
$ sudo apt-get install gyp ninja-build
```
* download cdm source code (including ODK & OEMCrypto unit tests):
```shell
$ git clone sso://widevine-internal/cdm
```
* We need to run odk as a dynamic library in order to use LD_PRELOAD, apply
patch from go/wvgerrit/95090 to locally cloned repo which has changes to run
odk as dynamic library:
```shell
$ cd /path/to/cdm/repo
$ git fetch origin 209721cc901745999e08e35466e74f708321267e
$ git cherry-pick FETCH_HEAD
```
* Build OEMCrypto unit tests:
```shell
$ cd /path/to/cdm/repo
$ export PATH_TO_CDM_DIR=..
$ gyp --format=ninja --depth=$(pwd) oemcrypto/oemcrypto_unittests.gyp
$ ninja -C out/Default/
```
## Capture corpus for odk fuzzer by intercepting OEMCrypto unit tests:
When we run LD_PRELOAD command odk_corpus_generator.so gets preloaded before
oemcrypto_unittests and odk_corpus_generator has functions to intercept calls to
ODK request and response APIs. Each call to odk API from oemcrypto_unittests
gets intercepted and input to ODK de serialize response APIs and output from ODK
serialize request APIs is captured in binary format and stored into corpus files
In order to run LD_PRELOAD command, we need to compile corpus generator shared
library and need to preload that before OEMCrypto unit tests run
* Compile shared library
```shell
$ cd /path/to/cdm/repo
$ gyp --format=ninja --depth=$(pwd) oemcrypto/odk/test/fuzzing/corpus_generator/odk_fuzz_corpus_generator.gyp
$ ninja -C out/Default/
```
* Preload the shared library before running OEMCrypto unit tests
```shell
$ cd oemcrypto/odk/test/fuzzing/corpus
$ mkdir license_request_corpus license_response_corpus renewal_request_corpus renewal_response_corpus provisioning_request_corpus provisioning_response_corpus
$ cd /path/to/cdm/repo
$ LD_PRELOAD=out/Default/lib/libodk_corpus_generator.so ./out/Default/oemcrypto_unittests
```
LD_PRELOAD command runs oemcrypto_unittests with odk_corpus_generator as
interceptor. We should see unit tests being executed. The corpus files in binary
format will be captured into `oemcrypto/odk/test/fuzzing/corpus` path. These
files can be used as input corpus for ODK request and response fuzzer scripts.
The generated corpus files can be minimized using go/testcorpus#minimize and
uploaded into google3 repository under following directory under respective
corpus types
`fuzzing/corpus`

View File

@@ -0,0 +1,160 @@
/* Copyright 2020 Google LLC. All rights reserved. This file and proprietary */
/* source code may only be used and distributed under the Widevine Master */
/* License Agreement. */
/* We must define this macro to get RTLD_NEXT definition from <dlfcn.h> */
#define _GNU_SOURCE
#include <dlfcn.h>
#include "fuzzing/corpus_generator/odk_corpus_generator_helper.h"
#include "fuzzing/odk_fuzz_structs.h"
#include "odk_structs.h"
OEMCryptoResult ODK_PrepareCoreLicenseRequest(
uint8_t* message, size_t message_length, size_t* core_message_length,
const ODK_NonceValues* nonce_values) {
OEMCryptoResult (*original_function)(uint8_t*, size_t, size_t*,
const ODK_NonceValues*);
original_function = dlsym(RTLD_NEXT, "ODK_PrepareCoreLicenseRequest");
OEMCryptoResult oem_crypto_result = (*original_function)(
message, message_length, core_message_length, nonce_values);
char* file_name = GetFileName("license_request_corpus");
/* License Request format expected by fuzzer - [Core License Request] */
AppendToFile(file_name, (const char*)message, *core_message_length);
free(file_name);
return oem_crypto_result;
}
OEMCryptoResult ODK_ParseLicense(
const uint8_t* message, size_t message_length, size_t core_message_length,
bool initial_license_load, bool usage_entry_present,
const uint8_t* request_hash, ODK_TimerLimits* timer_limits,
ODK_ClockValues* clock_values, ODK_NonceValues* nonce_values,
ODK_ParsedLicense* parsed_license) {
struct ODK_ParseLicense_Args parse_license_args;
parse_license_args.nonce_values = *nonce_values;
memcpy(parse_license_args.request_hash, request_hash, ODK_SHA256_HASH_SIZE);
parse_license_args.timer_limits = *timer_limits;
parse_license_args.clock_values = *clock_values;
parse_license_args.usage_entry_present = usage_entry_present;
parse_license_args.initial_license_load = initial_license_load;
OEMCryptoResult (*original_function)(
const uint8_t*, size_t, size_t, bool, bool, const uint8_t*,
ODK_TimerLimits*, ODK_ClockValues*, ODK_NonceValues*, ODK_ParsedLicense*);
original_function = dlsym(RTLD_NEXT, "ODK_ParseLicense");
OEMCryptoResult oem_crypto_result = (*original_function)(
message, message_length, core_message_length, initial_license_load,
usage_entry_present, request_hash, timer_limits, clock_values,
nonce_values, parsed_license);
char* file_name = GetFileName("license_response_corpus");
/* License Response format expected by fuzzer - [ODK_ParseLicense_Args][Core
*/
/* License Response] */
AppendToFile(file_name, (const char*)&parse_license_args,
sizeof(struct ODK_ParseLicense_Args));
AppendToFile(file_name, (const char*)message, core_message_length);
free(file_name);
return oem_crypto_result;
}
OEMCryptoResult ODK_PrepareCoreRenewalRequest(uint8_t* message,
size_t message_length,
size_t* core_message_size,
ODK_NonceValues* nonce_values,
ODK_ClockValues* clock_values,
uint64_t system_time_seconds) {
OEMCryptoResult (*original_function)(
uint8_t*, size_t, size_t*, ODK_NonceValues*, ODK_ClockValues*, uint64_t);
original_function = dlsym(RTLD_NEXT, "ODK_PrepareCoreRenewalRequest");
OEMCryptoResult oem_crypto_result =
(*original_function)(message, message_length, core_message_size,
nonce_values, clock_values, system_time_seconds);
char* file_name = GetFileName("renewal_request_corpus");
/* License Request format expected by fuzzer - [ODK_ClockValues][Core */
/* License Request] */
AppendToFile(file_name, (const char*)clock_values, sizeof(ODK_ClockValues));
AppendToFile(file_name, (const char*)message, *core_message_size);
free(file_name);
return oem_crypto_result;
}
OEMCryptoResult ODK_ParseRenewal(const uint8_t* message, size_t message_length,
size_t core_message_length,
const ODK_NonceValues* nonce_values,
uint64_t system_time,
const ODK_TimerLimits* timer_limits,
ODK_ClockValues* clock_values,
uint64_t* timer_value) {
struct ODK_ParseRenewal_Args parse_renewal_args;
parse_renewal_args.nonce_values = *nonce_values;
parse_renewal_args.clock_values = *clock_values;
parse_renewal_args.timer_limits = *timer_limits;
parse_renewal_args.system_time = system_time;
OEMCryptoResult (*original_function)(
const uint8_t*, size_t, size_t, const ODK_NonceValues*, uint64_t,
const ODK_TimerLimits*, ODK_ClockValues*, uint64_t*);
original_function = dlsym(RTLD_NEXT, "ODK_ParseRenewal");
OEMCryptoResult oem_crypto_result = (*original_function)(
message, message_length, core_message_length, nonce_values, system_time,
timer_limits, clock_values, timer_value);
char* file_name = GetFileName("renewal_response_corpus");
/* Renewal Response format expected by fuzzer - [ODK_ParseRenewal_Args][Core
*/
/* Renewal Response] */
AppendToFile(file_name, (const char*)&parse_renewal_args,
sizeof(struct ODK_ParseRenewal_Args));
AppendToFile(file_name, (const char*)message, core_message_length);
free(file_name);
return oem_crypto_result;
}
OEMCryptoResult ODK_PrepareCoreProvisioningRequest(
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) {
OEMCryptoResult (*original_function)(uint8_t*, size_t, size_t*,
const ODK_NonceValues*, const uint8_t*,
size_t);
original_function = dlsym(RTLD_NEXT, "ODK_PrepareCoreProvisioningRequest");
OEMCryptoResult oem_crypto_result =
(*original_function)(message, message_length, core_message_length,
nonce_values, device_id, device_id_length);
char* file_name = GetFileName("provisioning_request_corpus");
/* Provisioning Request format expected by fuzzer - [Core Provisioning */
/* Request] */
AppendToFile(file_name, (const char*)message, *core_message_length);
free(file_name);
return oem_crypto_result;
}
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) {
struct ODK_ParseProvisioning_Args parse_provisioning_args;
parse_provisioning_args.nonce_values = *nonce_values;
memcpy(parse_provisioning_args.device_id, device_id, device_id_length);
parse_provisioning_args.device_id_length = device_id_length;
OEMCryptoResult (*original_function)(const uint8_t*, size_t, size_t,
const ODK_NonceValues*, const uint8_t*,
size_t, ODK_ParsedProvisioning*);
original_function = dlsym(RTLD_NEXT, "ODK_ParseProvisioning");
OEMCryptoResult oem_crypto_result = (*original_function)(
message, message_length, core_message_length, nonce_values, device_id,
device_id_length, parsed_response);
char* file_name = GetFileName("provisioning_response_corpus");
/* Provisioning Response format expected by fuzzer - */
/* [ODK_ParseProvisioning_Args][Core Provisioning Response] */
AppendToFile(file_name, (const char*)&parse_provisioning_args,
sizeof(struct ODK_ParseProvisioning_Args));
AppendToFile(file_name, (const char*)message, core_message_length);
free(file_name);
return oem_crypto_result;
}

View File

@@ -0,0 +1,22 @@
/* Copyright 2020 Google LLC. All rights reserved. This file and proprietary */
/* source code may only be used and distributed under the Widevine Master */
/* License Agreement. */
#include "fuzzing/corpus_generator/odk_corpus_generator_helper.h"
void AppendToFile(const char* file_name, const char* message,
const size_t message_size) {
FILE* fptr;
if ((fptr = fopen(file_name, "ab")) == NULL) {
printf("Error! opening file %s", file_name);
return;
}
fwrite(message, message_size, 1, fptr);
fclose(fptr);
}
char* GetFileName(const char* directory) {
char* file_name;
file_name = malloc(150);
sprintf(file_name, "%s%s/%d", PATH_TO_CORPUS, directory, rand());
return file_name;
}

View File

@@ -0,0 +1,19 @@
/* Copyright 2020 Google LLC. All rights reserved. This file and proprietary */
/* source code may only be used and distributed under the Widevine Master */
/* License Agreement. */
#ifndef WIDEVINE_ODK_TEST_FUZZING_CORPUS_GENERATOR_ODK_CORPUS_GENERATOR_HELPER_H_
#define WIDEVINE_ODK_TEST_FUZZING_CORPUS_GENERATOR_ODK_CORPUS_GENERATOR_HELPER_H_
#define PATH_TO_CORPUS "./oemcrypto/odk/test/fuzzing/corpus/"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void AppendToFile(const char* file_name, const char* message,
const size_t message_size);
char* GetFileName(const char* directory);
#endif /* WIDEVINE_ODK_TEST_FUZZING_CORPUS_GENERATOR_ODK_CORPUS_GENERATOR_HELPER_H_ \
*/

View File

@@ -0,0 +1,33 @@
# Copyright 2020 Google LLC. All rights reserved. This file and proprietary
# source code may only be used and distributed under the Widevine Master License
# Agreement.
# Reference Link explaining flags for LD_PRELOAD: https://catonmat.net/simple-ld-preload-tutorial-part-two
{
'targets': [
{
'target_name': 'odk_corpus_generator',
'type': 'shared_library',
'cflags_cc': [
'-g3',
'-O0',
'-fno-omit-frame-pointer',
'-Wall',
],
'include_dirs': [
'../../../include',
'../../../test',
'../corpus_generator',
],
'ldflags': [
'-fPIC',
],
'libraries': [
'-ldl',
],
'sources': [
'odk_corpus_generator.c',
],
}
]
}

View File

@@ -0,0 +1,40 @@
# 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.
#TODO(b/151858867): Fix File paths
{
'targets': [
{
'target_name': 'odk_fuzz',
'type': 'executable',
'includes': [
'../src/odk.gypi',
'../kdo/oec_util.gypi',
],
'include_dirs': [
'../../include',
'../include',
'../src',
'../kdo/include',
],
'cflags_cc': [
'-std=c++11',
'-g3',
'-O0',
'-fsanitize=fuzzer,address,undefined',
'-fno-omit-frame-pointer',
],
'ldflags': [
'-fPIC',
'-fsanitize=fuzzer,address,undefined',
],
'sources': [
'odk_fuzz.cpp',
],
'dependencies': [
'../../../cdm/cdm.gyp:license_protocol'
],
}
]
}

View File

@@ -0,0 +1,159 @@
// Copyright 2020 Google LLC. All rights reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
#include "fuzzing/odk_fuzz_helper.h"
#include "odk.h"
namespace oemcrypto_core_message {
bool convert_byte_to_valid_boolean(const bool* in) {
const char* buf = reinterpret_cast<const char*>(in);
for (int i = 0; i < sizeof(bool); i++) {
if (buf[i]) {
return true;
}
}
return false;
}
void ConvertDataToValidBools(ODK_ParsedLicense* t) {
// Convert boolean flags in parsed_license to valid bytes to
// avoid errors from msan
t->nonce_required = convert_byte_to_valid_boolean(&t->nonce_required);
t->timer_limits.soft_enforce_playback_duration =
convert_byte_to_valid_boolean(
&t->timer_limits.soft_enforce_playback_duration);
t->timer_limits.soft_enforce_rental_duration = convert_byte_to_valid_boolean(
&t->timer_limits.soft_enforce_rental_duration);
}
void ConvertDataToValidBools(ODK_PreparedRenewalRequest* t) {}
void ConvertDataToValidBools(ODK_ParsedProvisioning* t) {}
OEMCryptoResult odk_serialize_LicenseRequest(
const void* in, uint8_t* out, size_t* size,
const ODK_LicenseRequest& core_license_request,
const ODK_NonceValues* nonce_values) {
return ODK_PrepareCoreLicenseRequest(out, SIZE_MAX, size, nonce_values);
}
OEMCryptoResult odk_serialize_RenewalRequest(
const void* in, uint8_t* out, size_t* size,
const ODK_RenewalRequest& core_renewal, ODK_NonceValues* nonce_values) {
ODK_ClockValues clock{};
memcpy(&clock, in, sizeof(ODK_ClockValues));
uint64_t system_time_seconds = core_renewal.playback_time_seconds;
return ODK_PrepareCoreRenewalRequest(out, SIZE_MAX, size, nonce_values,
&clock, system_time_seconds);
}
OEMCryptoResult odk_serialize_ProvisioningRequest(
const void* in, uint8_t* out, size_t* size,
const ODK_ProvisioningRequest& core_provisioning,
const ODK_NonceValues* nonce_values) {
const std::string& device_id = core_provisioning.device_id;
return ODK_PrepareCoreProvisioningRequest(
out, SIZE_MAX, size, nonce_values,
reinterpret_cast<const uint8_t*>(device_id.data()), device_id.size());
}
OEMCryptoResult odk_deserialize_LicenseResponse(const uint8_t* message,
size_t core_message_length,
ODK_ParseLicense_Args* a,
ODK_NonceValues* nonce_values,
ODK_ParsedLicense* parsed_lic) {
return ODK_ParseLicense(message, SIZE_MAX, core_message_length,
static_cast<bool>(a->initial_license_load),
static_cast<bool>(a->usage_entry_present),
a->request_hash, &a->timer_limits, &a->clock_values,
nonce_values, parsed_lic);
}
OEMCryptoResult odk_deserialize_RenewalResponse(
const uint8_t* buf, size_t len, ODK_ParseRenewal_Args* a,
ODK_NonceValues* nonce_values, ODK_PreparedRenewalRequest* renewal_msg) {
/* Address Sanitizer doesn't like values other than 0 OR 1 for boolean
* variables. Input from fuzzer can be parsed and any random bytes can be
* assigned to boolean variables. Using the workaround to mitigate sanitizer
* errors in fuzzer code and converting random bytes to 0 OR 1.
* This has no negative security impact*/
a->timer_limits.soft_enforce_playback_duration =
convert_byte_to_valid_boolean(
&a->timer_limits.soft_enforce_playback_duration);
a->timer_limits.soft_enforce_rental_duration = convert_byte_to_valid_boolean(
&a->timer_limits.soft_enforce_rental_duration);
uint64_t timer_value = 0;
OEMCryptoResult err =
ODK_ParseRenewal(buf, SIZE_MAX, len, nonce_values, a->system_time,
&a->timer_limits, &a->clock_values, &timer_value);
const bool is_parse_renewal_response_successful =
err == ODK_SET_TIMER || err == ODK_DISABLE_TIMER ||
err == ODK_TIMER_EXPIRED || err == ODK_STALE_RENEWAL;
if (!is_parse_renewal_response_successful) {
return err;
}
// In order to capture playback_time information which is part of
// renewal_msg and will be later used in kdo_serialize_RenewalResponse in
// odk_kdo method, we call Unpack_ODK_PreparedRenewalRequest private method.
// playback_time cannot be captured from publicly exposed API
// ODK_ParseRenewal.
Message* msg = nullptr;
AllocateMessage(&msg, message_block);
InitMessage(msg, const_cast<uint8_t*>(buf), len);
SetSize(msg, len);
Unpack_ODK_PreparedRenewalRequest(msg, renewal_msg);
return OEMCrypto_SUCCESS;
}
OEMCryptoResult odk_deserialize_ProvisioningResponse(
const uint8_t* buf, size_t len, ODK_ParseProvisioning_Args* a,
ODK_NonceValues* nonce_values, ODK_ParsedProvisioning* parsed_prov) {
return ODK_ParseProvisioning(buf, SIZE_MAX, len, nonce_values, a->device_id,
a->device_id_length, parsed_prov);
}
bool kdo_serialize_LicenseResponse(const ODK_ParseLicense_Args* args,
const ODK_ParsedLicense& parsed_lic,
std::string* oemcrypto_core_message) {
const auto& nonce_values = args->nonce_values;
ODK_LicenseRequest core_request{nonce_values.api_minor_version,
nonce_values.api_major_version,
nonce_values.nonce, nonce_values.session_id};
std::string core_request_sha_256(
reinterpret_cast<const char*>(args->request_hash), ODK_SHA256_HASH_SIZE);
return serialize::CreateCoreLicenseResponse(
parsed_lic, core_request, core_request_sha_256, oemcrypto_core_message);
}
bool kdo_serialize_RenewalResponse(
const ODK_ParseRenewal_Args* args,
const ODK_PreparedRenewalRequest& renewal_msg,
std::string* oemcrypto_core_message) {
const auto& nonce_values = args->nonce_values;
ODK_RenewalRequest core_request{
nonce_values.api_minor_version, nonce_values.api_major_version,
nonce_values.nonce, nonce_values.session_id, renewal_msg.playback_time};
return serialize::CreateCoreRenewalResponse(
core_request, args->timer_limits.initial_renewal_duration_seconds,
oemcrypto_core_message);
}
bool kdo_serialize_ProvisioningResponse(
const ODK_ParseProvisioning_Args* args,
const ODK_ParsedProvisioning& parsed_prov,
std::string* oemcrypto_core_message) {
const auto& nonce_values = args->nonce_values;
if (args->device_id_length > sizeof(args->device_id)) {
return false;
}
ODK_ProvisioningRequest core_request{
nonce_values.api_minor_version, nonce_values.api_major_version,
nonce_values.nonce, nonce_values.session_id,
std::string(reinterpret_cast<const char*>(args->device_id),
args->device_id_length)};
return serialize::CreateCoreProvisioningResponse(parsed_prov, core_request,
oemcrypto_core_message);
}
} // namespace oemcrypto_core_message

View File

@@ -0,0 +1,206 @@
/* Copyright 2020 Google LLC. All rights reserved. This file and proprietary */
/* source code may only be used and distributed under the Widevine Master */
/* License Agreement. */
#ifndef WIDEVINE_ODK_TEST_FUZZING_ODK_FUZZ_HELPER_H_
#define WIDEVINE_ODK_TEST_FUZZING_ODK_FUZZ_HELPER_H_
#include <memory>
#include <string>
#include "core_message_serialize.h"
#include "fuzzing/odk_fuzz_structs.h"
#include "odk_serialize.h"
namespace oemcrypto_core_message {
bool convert_byte_to_valid_boolean(const bool* in);
OEMCryptoResult odk_serialize_LicenseRequest(
const void* in, uint8_t* out, size_t* size,
const ODK_LicenseRequest& core_license_request,
const ODK_NonceValues* nonce_values);
OEMCryptoResult odk_serialize_RenewalRequest(
const void* in, uint8_t* out, size_t* size,
const ODK_RenewalRequest& core_renewal, ODK_NonceValues* nonce_values);
OEMCryptoResult odk_serialize_ProvisioningRequest(
const void* in, uint8_t* out, size_t* size,
const ODK_ProvisioningRequest& core_provisioning,
const ODK_NonceValues* nonce_values);
OEMCryptoResult odk_deserialize_LicenseResponse(const uint8_t* message,
size_t core_message_length,
ODK_ParseLicense_Args* a,
ODK_NonceValues* nonce_values,
ODK_ParsedLicense* parsed_lic);
OEMCryptoResult odk_deserialize_RenewalResponse(
const uint8_t* buf, size_t len, ODK_ParseRenewal_Args* a,
ODK_NonceValues* nonce_values, ODK_PreparedRenewalRequest* renewal_msg);
OEMCryptoResult odk_deserialize_ProvisioningResponse(
const uint8_t* buf, size_t len, ODK_ParseProvisioning_Args* a,
ODK_NonceValues* nonce_values, ODK_ParsedProvisioning* parsed_prov);
bool kdo_serialize_LicenseResponse(const ODK_ParseLicense_Args* args,
const ODK_ParsedLicense& parsed_lic,
std::string* oemcrypto_core_message);
bool kdo_serialize_RenewalResponse(
const ODK_ParseRenewal_Args* args,
const ODK_PreparedRenewalRequest& renewal_msg,
std::string* oemcrypto_core_message);
bool kdo_serialize_ProvisioningResponse(
const ODK_ParseProvisioning_Args* args,
const ODK_ParsedProvisioning& parsed_prov,
std::string* oemcrypto_core_message);
/* Idea behind having three different functions is: */
/* Only ODK_ParseLicense structure had fields which needed additional */
/* procession. Having a single function with templated parameter T was */
/* failing during compile time because other two structures doesn't have */
/* fields that need additional processing. Hence to reduce code redundance and
*/
/* make us of common FuzzerMutateResponse across three response fuzzers, */
/* three independent functions were defined and renewal and provisioning */
/* functions would be empty as no additional processing is needed for them. */
void ConvertDataToValidBools(ODK_ParsedLicense* t);
void ConvertDataToValidBools(ODK_PreparedRenewalRequest* t);
void ConvertDataToValidBools(ODK_ParsedProvisioning* t);
/* Forward-declare the libFuzzer's mutator callback. Mark it weak so that */
/* the program links successfully even outside of --config=asan-fuzzer */
/* (apparently the only config in which LLVM uses our custom mutator). */
extern "C" size_t LLVMFuzzerMutate(uint8_t* Data, size_t Size, size_t MaxSize)
__attribute__((weak));
template <typename A, typename T, typename F, typename G>
size_t FuzzerMutateResponse(uint8_t* data, size_t size, size_t max_size,
const F& odk_deserialize_fun,
const G& kdo_serialize_fun) {
const size_t kArgsSize = sizeof(A);
const size_t kCoreResponseSize = sizeof(T);
const size_t kTotalResponseSize = kArgsSize + kCoreResponseSize;
/* Deserializing data in order to make sure it deserializes properly. */
/* Input byte array format: [function arguments][data to parse]. */
std::shared_ptr<A> _args(new A());
A* args = _args.get();
memcpy(args, data, kArgsSize);
ODK_NonceValues nonce_values = args->nonce_values;
args->nonce_values.api_major_version = ODK_MAJOR_VERSION;
const uint8_t* buf = data + kArgsSize;
T t = {};
OEMCryptoResult result =
odk_deserialize_fun(buf, size - kArgsSize, args, &nonce_values, &t);
/* If data doesn't deserialize successfully, We copy random bytes into */
/* T and serialize using kdo function */
/* which will create a valid oemcrypto core message using */
/* nonce and request hash from function args. OEMCrypto core message acts as
*/
/* input to odk_kdo. */
if (result != OEMCrypto_SUCCESS) {
if (max_size < kTotalResponseSize) {
return 0;
}
/* Initialize remaining bytes needed in data to zero. */
if (size < kTotalResponseSize) {
memset(data + size, 0, kTotalResponseSize - size);
}
t = {};
memcpy(&t, buf, kCoreResponseSize);
}
/* Ask LLVM to run its usual mutations, hopefully giving us interesting */
/* inputs. We copy deserialized data into pointer data, run mutations */
/* and copy back the mutated data to args and t */
memcpy(data + kArgsSize, &t, kCoreResponseSize);
LLVMFuzzerMutate(data, kTotalResponseSize, kTotalResponseSize);
memcpy(args, data, kArgsSize);
memcpy(&t, data + kArgsSize, kCoreResponseSize);
/* Convert boolean flags in parsed message to valid bytes to */
/* avoid errors from msan. Only needed for parsed license. */
ConvertDataToValidBools(&t);
/* Serialize the data after mutation. */
std::string oemcrypto_core_message;
if (!kdo_serialize_fun(args, t, &oemcrypto_core_message)) {
return 0;
}
/* Copy mutated and serialized oemcrypto_core_message to data */
/* so that it acts as input to odk_kdo function. */
memcpy(data + kArgsSize, oemcrypto_core_message.data(),
oemcrypto_core_message.size());
return kArgsSize + oemcrypto_core_message.size();
}
/**
* Template arguments:
* A: struct holding function arguments
* T: odk deserialize output/kdo serialize input structure
* F: odk deserialize function
* G: kdo serialize function
*
* raw bytes -> F deserialize -> struct T -> G serialize -> raw bytes
*/
template <typename A, typename T, typename F, typename G>
void odk_kdo(const F& odk_fun, const G& kdo_fun, const uint8_t* in,
const size_t size, const size_t args_size, uint8_t* out) {
T t = {};
/* Input byte array format: [function arguments][data to parse] */
if (size < args_size) {
return;
}
const uint8_t* buf = in + args_size;
std::shared_ptr<A> _args(new A());
A* args = _args.get();
memcpy(args, in, args_size);
args->nonce_values.api_major_version = ODK_MAJOR_VERSION;
ODK_NonceValues nonce_values = args->nonce_values;
OEMCryptoResult result =
odk_fun(buf, size - args_size, args, &nonce_values, &t);
if (result != OEMCrypto_SUCCESS) {
return;
}
std::string oemcrypto_core_message;
if (!kdo_fun(args, t, &oemcrypto_core_message)) {
return;
}
}
/**
* Template arguments:
* T: kdo deserialize output/odk serialize input structure
* F: kdo deserialize function
* G: odk serialize function
*
* raw bytes -> F deserialize -> struct T -> G serialize -> raw bytes
*/
template <typename T, typename F, typename G>
static void kdo_odk(const F& kdo_fun, const G& odk_fun, const uint8_t* in,
size_t size, const size_t clock_value_size, uint8_t* out) {
if (size <= clock_value_size) {
return;
}
/* Input byte array format: [Clock Values][data to parse]. */
/* Only Renewal Request expects clock values to be present. */
std::string input(reinterpret_cast<const char*>(in) + clock_value_size,
size - clock_value_size);
T t = {};
if (!kdo_fun(input, &t)) {
return;
}
ODK_NonceValues nonce_values = {t.api_minor_version, t.api_major_version,
t.nonce, t.session_id};
OEMCryptoResult err = odk_fun(in, out, &size, t, &nonce_values);
if (OEMCrypto_SUCCESS != err) {
return;
}
}
} /* namespace oemcrypto_core_message */
#endif /* WIDEVINE_ODK_TEST_FUZZING_ODK_FUZZ_HELPER_H_ */

View File

@@ -0,0 +1,28 @@
/* Copyright 2020 Google LLC. All rights reserved. This file and proprietary */
/* source code may only be used and distributed under the Widevine Master */
/* License Agreement. */
#ifndef WIDEVINE_ODK_TEST_FUZZING_ODK_FUZZ_STRUCTS_H_
#define WIDEVINE_ODK_TEST_FUZZING_ODK_FUZZ_STRUCTS_H_
#include "odk_structs.h"
struct ODK_ParseLicense_Args {
ODK_NonceValues nonce_values;
uint8_t initial_license_load;
uint8_t usage_entry_present;
uint8_t request_hash[ODK_SHA256_HASH_SIZE];
ODK_TimerLimits timer_limits;
ODK_ClockValues clock_values;
};
struct ODK_ParseRenewal_Args {
ODK_NonceValues nonce_values;
uint64_t system_time;
ODK_TimerLimits timer_limits;
ODK_ClockValues clock_values;
};
struct ODK_ParseProvisioning_Args {
ODK_NonceValues nonce_values;
size_t device_id_length;
uint8_t device_id[64];
};
#endif /* WIDEVINE_ODK_TEST_FUZZING_ODK_FUZZ_STRUCTS_H_ */

View File

@@ -0,0 +1,22 @@
/* Copyright 2020 Google LLC. All rights reserved. This file and proprietary
* source code may only be used and distributed under the Widevine Master
* License Agreement.
*/
#include <vector>
#include "core_message_deserialize.h"
#include "fuzzing/odk_fuzz_helper.h"
namespace oemcrypto_core_message {
using oemcrypto_core_message::deserialize::CoreLicenseRequestFromMessage;
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
std::vector<uint8_t> out(size);
const size_t kClockValueSize = 0;
kdo_odk<ODK_LicenseRequest>(CoreLicenseRequestFromMessage,
odk_serialize_LicenseRequest, data, size,
kClockValueSize, out.data());
return 0;
}
} // namespace oemcrypto_core_message

View File

@@ -0,0 +1,20 @@
/* Copyright 2020 Google LLC. All rights reserved. This file and proprietary
* source code may only be used and distributed under the Widevine Master
* License Agreement.
*/
#include <vector>
#include "fuzzing/odk_fuzz_helper.h"
namespace oemcrypto_core_message {
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
const size_t kLicenseResponseArgsSize = sizeof(ODK_ParseLicense_Args);
std::vector<uint8_t> out(size);
odk_kdo<ODK_ParseLicense_Args, ODK_ParsedLicense>(
odk_deserialize_LicenseResponse, kdo_serialize_LicenseResponse, data,
size, kLicenseResponseArgsSize, out.data());
return 0;
}
} // namespace oemcrypto_core_message

View File

@@ -0,0 +1,35 @@
/* Copyright 2020 Google LLC. All rights reserved. This file and proprietary
* source code may only be used and distributed under the Widevine Master
* License Agreement.
*/
#include <vector>
#include "fuzzing/odk_fuzz_helper.h"
namespace oemcrypto_core_message {
// The custom mutator: Ensure that each input can be deserialized properly
// by ODK function after mutation.
extern "C" size_t LLVMFuzzerCustomMutator(uint8_t* data, size_t size,
size_t max_size, unsigned int seed) {
const size_t kLicenseResponseArgsSize = sizeof(ODK_ParseLicense_Args);
if (size < kLicenseResponseArgsSize) {
return 0;
}
// Mutate input data and return mutated input size.
return FuzzerMutateResponse<ODK_ParseLicense_Args, ODK_ParsedLicense>(
data, size, max_size, odk_deserialize_LicenseResponse,
kdo_serialize_LicenseResponse);
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
const size_t kLicenseResponseArgsSize = sizeof(ODK_ParseLicense_Args);
std::vector<uint8_t> out(size);
odk_kdo<ODK_ParseLicense_Args, ODK_ParsedLicense>(
odk_deserialize_LicenseResponse, kdo_serialize_LicenseResponse, data,
size, kLicenseResponseArgsSize, out.data());
return 0;
}
} // namespace oemcrypto_core_message

View File

@@ -0,0 +1,22 @@
/* Copyright 2020 Google LLC. All rights reserved. This file and proprietary
* source code may only be used and distributed under the Widevine Master
* License Agreement.
*/
#include <vector>
#include "core_message_deserialize.h"
#include "fuzzing/odk_fuzz_helper.h"
namespace oemcrypto_core_message {
using oemcrypto_core_message::deserialize::CoreProvisioningRequestFromMessage;
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
std::vector<uint8_t> out(size);
const size_t kClockValueSize = 0;
kdo_odk<ODK_ProvisioningRequest>(CoreProvisioningRequestFromMessage,
odk_serialize_ProvisioningRequest, data,
size, kClockValueSize, out.data());
return 0;
}
} // namespace oemcrypto_core_message

View File

@@ -0,0 +1,21 @@
/* Copyright 2020 Google LLC. All rights reserved. This file and proprietary
* source code may only be used and distributed under the Widevine Master
* License Agreement.
*/
#include <vector>
#include "fuzzing/odk_fuzz_helper.h"
namespace oemcrypto_core_message {
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
const size_t kProvisioningResponseArgsSize =
sizeof(ODK_ParseProvisioning_Args);
std::vector<uint8_t> out(size);
odk_kdo<ODK_ParseProvisioning_Args, ODK_ParsedProvisioning>(
odk_deserialize_ProvisioningResponse, kdo_serialize_ProvisioningResponse,
data, size, kProvisioningResponseArgsSize, out.data());
return 0;
}
} // namespace oemcrypto_core_message

View File

@@ -0,0 +1,38 @@
/* Copyright 2020 Google LLC. All rights reserved. This file and proprietary
* source code may only be used and distributed under the Widevine Master
* License Agreement.
*/
#include <vector>
#include "fuzzing/odk_fuzz_helper.h"
namespace oemcrypto_core_message {
// The custom mutator: Ensure that each input can be deserialized properly
// by ODK function after mutation.
extern "C" size_t LLVMFuzzerCustomMutator(uint8_t* data, size_t size,
size_t max_size, unsigned int seed) {
const size_t kProvisioningResponseArgsSize =
sizeof(ODK_ParseProvisioning_Args);
if (size < kProvisioningResponseArgsSize) {
return 0;
}
// Mutate input data and return mutated input size.
return FuzzerMutateResponse<ODK_ParseProvisioning_Args,
ODK_ParsedProvisioning>(
data, size, max_size, odk_deserialize_ProvisioningResponse,
kdo_serialize_ProvisioningResponse);
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
const size_t kProvisioningResponseArgsSize =
sizeof(ODK_ParseProvisioning_Args);
std::vector<uint8_t> out(size);
odk_kdo<ODK_ParseProvisioning_Args, ODK_ParsedProvisioning>(
odk_deserialize_ProvisioningResponse, kdo_serialize_ProvisioningResponse,
data, size, kProvisioningResponseArgsSize, out.data());
return 0;
}
} // namespace oemcrypto_core_message

View File

@@ -0,0 +1,22 @@
/* Copyright 2020 Google LLC. All rights reserved. This file and proprietary
* source code may only be used and distributed under the Widevine Master
* License Agreement.
*/
#include <vector>
#include "core_message_deserialize.h"
#include "fuzzing/odk_fuzz_helper.h"
namespace oemcrypto_core_message {
using oemcrypto_core_message::deserialize::CoreRenewalRequestFromMessage;
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
std::vector<uint8_t> out(size);
const size_t kClockValueSize = sizeof(ODK_ClockValues);
kdo_odk<ODK_RenewalRequest>(CoreRenewalRequestFromMessage,
odk_serialize_RenewalRequest, data, size,
kClockValueSize, out.data());
return 0;
}
} // namespace oemcrypto_core_message

View File

@@ -0,0 +1,20 @@
/* Copyright 2020 Google LLC. All rights reserved. This file and proprietary
* source code may only be used and distributed under the Widevine Master
* License Agreement.
*/
#include <vector>
#include "fuzzing/odk_fuzz_helper.h"
namespace oemcrypto_core_message {
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
const size_t kRenewalResponseArgsSize = sizeof(ODK_ParseRenewal_Args);
std::vector<uint8_t> out(size);
odk_kdo<ODK_ParseRenewal_Args, ODK_PreparedRenewalRequest>(
odk_deserialize_RenewalResponse, kdo_serialize_RenewalResponse, data,
size, kRenewalResponseArgsSize, out.data());
return 0;
}
} // namespace oemcrypto_core_message

View File

@@ -0,0 +1,36 @@
/* Copyright 2020 Google LLC. All rights reserved. This file and proprietary
* source code may only be used and distributed under the Widevine Master
* License Agreement.
*/
#include <vector>
#include "fuzzing/odk_fuzz_helper.h"
namespace oemcrypto_core_message {
// The custom mutator: Ensure that each input can be deserialized properly
// by ODK function after mutation.
extern "C" size_t LLVMFuzzerCustomMutator(uint8_t* data, size_t size,
size_t max_size, unsigned int seed) {
const size_t kRenewalResponseArgsSize = sizeof(ODK_ParseRenewal_Args);
if (size < kRenewalResponseArgsSize) {
return 0;
}
// Mutate input data and return mutated input size.
return FuzzerMutateResponse<ODK_ParseRenewal_Args,
ODK_PreparedRenewalRequest>(
data, size, max_size, odk_deserialize_RenewalResponse,
kdo_serialize_RenewalResponse);
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
const size_t kRenewalResponseArgsSize = sizeof(ODK_ParseRenewal_Args);
std::vector<uint8_t> out(size);
odk_kdo<ODK_ParseRenewal_Args, ODK_PreparedRenewalRequest>(
odk_deserialize_RenewalResponse, kdo_serialize_RenewalResponse, data,
size, kRenewalResponseArgsSize, out.data());
return 0;
}
} // namespace oemcrypto_core_message

View File

@@ -0,0 +1,37 @@
// Copyright 2020 Google LLC. All rights reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
#include "OEMCryptoCENCCommon.h"
#include "gtest/gtest.h"
#include "odk.h"
#include "third_party/absl/strings/escaping.h"
namespace wvodk_test {
TEST(CoreMessageTest, RenwalRequest) {
std::string oem =
"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrst"
"uvwxyzabcdefghijklmnopqrstuvwxyz";
const uint8_t* buf = reinterpret_cast<const uint8_t*>(oem.c_str());
uint8_t* message = const_cast<uint8_t*>(buf);
size_t message_length = 88;
size_t core_message_length = 88;
uint16_t api_minor_version = 16;
uint16_t api_major_version = 16;
uint32_t nonce = 0;
uint32_t timer_status = 2;
uint64_t time = 10;
enum OEMCrypto_Usage_Entry_Status status = kInactiveUsed;
ODK_NonceValues nonce_values{api_minor_version, api_major_version, nonce};
ODK_ClockValues clock_values{time, time, time, time,
time, timer_status, status};
uint64_t system_time_seconds = 100;
EXPECT_EQ(OEMCrypto_SUCCESS,
ODK_PrepareCoreRenewalRequest(message, message_length,
&core_message_length, &nonce_values,
&clock_values, system_time_seconds));
// All messages have at least a five 4-byte fields.
char* m = reinterpret_cast<char*>(message);
VLOG(0) << absl::BytesToHexString(std::string(m, core_message_length));
}
} // namespace wvodk_test

View File

@@ -0,0 +1,729 @@
// Copyright 2019 Google LLC. All rights reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
#include "odk.h"
#include <endian.h> // TODO(b/147944591): use this one? Or odk_endian.h?
#include <cstdlib>
#include <cstring>
#include "OEMCryptoCENCCommon.h"
#include "core_message_deserialize.h"
#include "core_message_serialize.h"
#include "core_message_types.h"
#include "gtest/gtest.h"
#include "odk_structs_priv.h"
#include "odk_test_helper.h"
namespace wvodk_test {
namespace {
using oemcrypto_core_message::ODK_LicenseRequest;
using oemcrypto_core_message::ODK_ProvisioningRequest;
using oemcrypto_core_message::ODK_RenewalRequest;
using oemcrypto_core_message::deserialize::CoreLicenseRequestFromMessage;
using oemcrypto_core_message::deserialize::CoreProvisioningRequestFromMessage;
using oemcrypto_core_message::deserialize::CoreRenewalRequestFromMessage;
using oemcrypto_core_message::serialize::CreateCoreLicenseResponse;
using oemcrypto_core_message::serialize::CreateCoreProvisioningResponse;
using oemcrypto_core_message::serialize::CreateCoreRenewalResponse;
constexpr uint32_t kExtraPayloadSize = 128u;
template <typename T, typename F, typename G>
void ValidateRequest(uint32_t message_type,
const std::vector<ODK_Field>& extra_fields,
const F& odk_prepare_func, const G& kdo_parse_func) {
uint32_t message_size = 0;
uint16_t api_major_version = ODK_MAJOR_VERSION;
uint16_t api_minor_version = ODK_MINOR_VERSION;
uint32_t nonce = 0xdeadbeef;
uint32_t session_id = 0xcafebabe;
ODK_NonceValues nonce_values{api_minor_version, api_major_version, nonce,
session_id};
std::vector<ODK_Field> total_fields = {
{ODK_UINT32, &message_type, "message_type"},
{ODK_UINT32, &message_size, "message_size"},
{ODK_UINT16, &api_minor_version, "api_minor_version"},
{ODK_UINT16, &api_major_version, "api_major_version"},
{ODK_UINT32, &nonce, "nonce"},
{ODK_UINT32, &session_id, "session_id"},
};
total_fields.insert(total_fields.end(), extra_fields.begin(),
extra_fields.end());
for (auto& field : total_fields) {
message_size += ODK_FieldLength(field.type);
}
// empty buf, expect core message length to be set correctly
size_t core_message_length = 0;
uint8_t* buf_empty = nullptr;
EXPECT_EQ(OEMCrypto_ERROR_SHORT_BUFFER,
odk_prepare_func(buf_empty, &core_message_length, &nonce_values));
EXPECT_EQ(core_message_length, message_size);
// non-empty buf, expect core message length to be set correctly, and buf is
// filled with ODK_Field values appropriately
uint8_t* buf = new uint8_t[message_size]{};
EXPECT_EQ(OEMCrypto_SUCCESS,
odk_prepare_func(buf, &core_message_length, &nonce_values));
EXPECT_EQ(core_message_length, message_size);
uint8_t* buf_expected = new uint8_t[message_size]{};
size_t buf_len_expected = 0;
EXPECT_EQ(OEMCrypto_SUCCESS, ODK_IterFields(ODK_WRITE, buf_expected, SIZE_MAX,
&buf_len_expected, total_fields));
EXPECT_EQ(buf_len_expected, message_size);
EXPECT_NO_FATAL_FAILURE(
ODK_ExpectEqualBuf(buf_expected, buf, message_size, total_fields));
// odk kdo round-trip: deserialize from buf, then serialize it to buf2
// expect them to be identical
T t = {};
std::string oemcrypto_core_message(reinterpret_cast<char*>(buf),
message_size);
EXPECT_TRUE(kdo_parse_func(oemcrypto_core_message, &t));
nonce_values.api_minor_version = t.api_minor_version;
nonce_values.api_major_version = t.api_major_version;
nonce_values.nonce = t.nonce;
nonce_values.session_id = t.session_id;
uint8_t* buf2 = new uint8_t[message_size]{};
EXPECT_EQ(OEMCrypto_SUCCESS,
odk_prepare_func(buf2, &core_message_length, &nonce_values));
EXPECT_EQ(core_message_length, message_size);
EXPECT_NO_FATAL_FAILURE(
ODK_ExpectEqualBuf(buf, buf2, message_size, total_fields));
delete[] buf;
delete[] buf_expected;
delete[] buf2;
}
/**
* Template arguments:
* T: kdo input struct
* F: odk deserializer
* G: kdo serializer
*/
template <typename T, typename F, typename G>
void ValidateResponse(ODK_CoreMessage* core_message,
const std::vector<ODK_Field>& extra_fields,
const F& odk_parse_func, const G& kdo_prepare_func) {
T t = {};
t.api_minor_version = core_message->nonce_values.api_minor_version;
t.api_major_version = core_message->nonce_values.api_major_version;
t.nonce = core_message->nonce_values.nonce;
t.session_id = core_message->nonce_values.session_id;
uint8_t* buf = nullptr;
uint32_t buf_size = 0;
ODK_BuildMessageBuffer(core_message, extra_fields, &buf, &buf_size);
uint8_t* zero = new uint8_t[buf_size]{};
size_t bytes_read = 0;
// zero-out input
EXPECT_EQ(OEMCrypto_SUCCESS, ODK_IterFields(ODK_READ, zero, buf_size,
&bytes_read, extra_fields));
// parse buf with odk
EXPECT_EQ(OEMCrypto_SUCCESS, odk_parse_func(buf, buf_size));
size_t size_out = 0;
ODK_IterFields(ODK_FieldMode::ODK_DUMP, buf, buf_size, &size_out,
extra_fields);
// serialize odk output to oemcrypto_core_message
std::string oemcrypto_core_message;
EXPECT_TRUE(kdo_prepare_func(t, &oemcrypto_core_message));
// verify round-trip works
EXPECT_NO_FATAL_FAILURE(ODK_ExpectEqualBuf(buf, oemcrypto_core_message.data(),
buf_size, extra_fields));
delete[] buf;
delete[] zero;
}
TEST(OdkTest, SerializeFields) {
uint32_t x[] = {0, 1, 2};
uint64_t y[] = {3LL << 32, 4LL << 32, 5LL << 32};
OEMCrypto_Substring s = {.offset = 6, .length = 7};
std::vector<ODK_Field> fields = {
{ODK_UINT32, &x[0], "x[0]"}, {ODK_UINT32, &x[1], "x[1]"},
{ODK_UINT32, &x[2], "x[2]"}, {ODK_UINT64, &y[0], "y[0]"},
{ODK_UINT64, &y[1], "y[1]"}, {ODK_UINT64, &y[2], "y[2]"},
{ODK_SUBSTRING, &s, "s"},
};
uint8_t buf[1024] = {0};
uint8_t buf2[1024] = {0};
size_t bytes_read = 0, bytes_written = 0;
ODK_IterFields(ODK_WRITE, buf, SIZE_MAX, &bytes_read, fields);
std::vector<ODK_Field> fields2(fields.size());
ODK_ResetOdkFields(&fields);
ODK_IterFields(ODK_READ, buf, bytes_read, &bytes_written, fields);
ODK_IterFields(ODK_WRITE, buf2, SIZE_MAX, &bytes_read, fields);
EXPECT_NO_FATAL_FAILURE(ODK_ExpectEqualBuf(buf, buf2, bytes_read, fields));
}
TEST(OdkTest, SerializeFieldsStress) {
const int n = 1024;
std::vector<ODK_Field> fields(n);
std::srand(0);
size_t total_size = 0;
for (int i = 0; i < n; i++) {
fields[i].type = static_cast<ODK_FieldType>(std::rand() %
static_cast<int>(ODK_NUMTYPES));
fields[i].value = malloc(ODK_AllocSize(fields[i].type));
fields[i].name = "stress";
total_size += ODK_FieldLength(fields[i].type);
}
uint8_t* buf = new uint8_t[total_size]{};
for (int i = 0; i < total_size; i++) {
buf[i] = std::rand() & 0xff;
}
size_t bytes_read = 0, bytes_written = 0;
uint8_t* buf2 = new uint8_t[total_size]{};
ODK_IterFields(ODK_READ, buf, total_size, &bytes_read, fields);
EXPECT_EQ(bytes_read, total_size);
ODK_IterFields(ODK_WRITE, buf2, total_size, &bytes_written, fields);
EXPECT_EQ(bytes_written, total_size);
EXPECT_NO_FATAL_FAILURE(ODK_ExpectEqualBuf(buf, buf2, total_size, fields));
// cleanup
for (int i = 0; i < n; i++) {
free(fields[i].value);
}
delete[] buf;
delete[] buf2;
}
TEST(OdkTest, NullRequestTest) {
size_t core_message_length = 0;
ODK_NonceValues nonce_values{0};
ODK_ClockValues clock_values{0};
// Assert that nullptr does not cause a core dump.
EXPECT_EQ(ODK_ERROR_CORE_MESSAGE, ODK_PrepareCoreLicenseRequest(
nullptr, 0uL, nullptr, &nonce_values));
EXPECT_EQ(ODK_ERROR_CORE_MESSAGE,
ODK_PrepareCoreLicenseRequest(nullptr, 0uL, &core_message_length,
nullptr));
EXPECT_EQ(ODK_ERROR_CORE_MESSAGE,
ODK_PrepareCoreRenewalRequest(nullptr, 0uL, nullptr, &nonce_values,
&clock_values, 0uL));
EXPECT_EQ(ODK_ERROR_CORE_MESSAGE,
ODK_PrepareCoreRenewalRequest(nullptr, 0uL, &core_message_length,
nullptr, &clock_values, 0uL));
EXPECT_EQ(ODK_ERROR_CORE_MESSAGE,
ODK_PrepareCoreRenewalRequest(nullptr, 0uL, &core_message_length,
&nonce_values, nullptr, 0uL));
EXPECT_EQ(ODK_ERROR_CORE_MESSAGE,
ODK_PrepareCoreProvisioningRequest(
nullptr, 0uL, &core_message_length, nullptr, nullptr, 0uL));
EXPECT_EQ(ODK_ERROR_CORE_MESSAGE,
ODK_PrepareCoreProvisioningRequest(nullptr, 0uL, nullptr,
&nonce_values, nullptr, 0uL));
// Null device id in provisioning request is ok
uint8_t message[ODK_PROVISIONING_REQUEST_SIZE] = {0};
core_message_length = ODK_PROVISIONING_REQUEST_SIZE;
EXPECT_EQ(OEMCrypto_SUCCESS,
ODK_PrepareCoreProvisioningRequest(
message, ODK_PROVISIONING_REQUEST_SIZE, &core_message_length,
&nonce_values, nullptr, 0uL));
}
TEST(OdkTest, NullResponseTest) {
constexpr size_t message_size = 64;
uint8_t message[message_size] = {0};
size_t core_message_length = message_size;
uint8_t request_hash[ODK_SHA256_HASH_SIZE] = {0};
ODK_TimerLimits timer_limits{0};
ODK_ParsedLicense parsed_license;
ODK_NonceValues nonce_values{0};
ODK_ClockValues clock_values{0};
// Assert that nullptr does not cause a core dump.
EXPECT_EQ(ODK_ERROR_CORE_MESSAGE,
ODK_ParseLicense(message, message_size, core_message_length, true,
true, request_hash, &timer_limits, &clock_values,
&nonce_values, nullptr));
EXPECT_EQ(ODK_ERROR_CORE_MESSAGE,
ODK_ParseLicense(message, message_size, core_message_length, true,
true, request_hash, &timer_limits, &clock_values,
nullptr, &parsed_license));
EXPECT_EQ(ODK_ERROR_CORE_MESSAGE,
ODK_ParseLicense(message, message_size, core_message_length, true,
true, request_hash, &timer_limits, nullptr,
&nonce_values, &parsed_license));
EXPECT_EQ(ODK_ERROR_CORE_MESSAGE,
ODK_ParseLicense(message, message_size, core_message_length, true,
true, request_hash, nullptr, &clock_values,
&nonce_values, &parsed_license));
EXPECT_EQ(ODK_ERROR_CORE_MESSAGE,
ODK_ParseLicense(message, message_size, core_message_length, true,
true, nullptr, &timer_limits, &clock_values,
&nonce_values, &parsed_license));
EXPECT_EQ(ODK_ERROR_CORE_MESSAGE,
ODK_ParseLicense(nullptr, message_size, core_message_length, true,
true, request_hash, &timer_limits, &clock_values,
&nonce_values, &parsed_license));
constexpr uint64_t system_time = 0;
uint64_t timer_value = 0;
EXPECT_EQ(ODK_ERROR_CORE_MESSAGE,
ODK_ParseRenewal(message, message_size, core_message_length,
&nonce_values, system_time, &timer_limits, nullptr,
&timer_value));
EXPECT_EQ(ODK_ERROR_CORE_MESSAGE,
ODK_ParseRenewal(message, message_size, core_message_length,
&nonce_values, system_time, nullptr, &clock_values,
&timer_value));
EXPECT_EQ(ODK_ERROR_CORE_MESSAGE,
ODK_ParseRenewal(message, message_size, core_message_length,
nullptr, system_time, &timer_limits, &clock_values,
&timer_value));
EXPECT_EQ(ODK_ERROR_CORE_MESSAGE,
ODK_ParseRenewal(nullptr, message_size, core_message_length,
&nonce_values, system_time, &timer_limits,
&clock_values, &timer_value));
uint8_t device_id[ODK_DEVICE_ID_LEN_MAX] = {0};
ODK_ParsedProvisioning parsed_response;
EXPECT_EQ(ODK_ERROR_CORE_MESSAGE,
ODK_ParseProvisioning(message, message_size, core_message_length,
&nonce_values, device_id,
ODK_DEVICE_ID_LEN_MAX, nullptr));
EXPECT_EQ(ODK_ERROR_CORE_MESSAGE,
ODK_ParseProvisioning(message, message_size, core_message_length,
&nonce_values, nullptr, 0, &parsed_response));
EXPECT_EQ(ODK_ERROR_CORE_MESSAGE,
ODK_ParseProvisioning(message, message_size, core_message_length,
nullptr, device_id, ODK_DEVICE_ID_LEN_MAX,
&parsed_response));
EXPECT_EQ(ODK_ERROR_CORE_MESSAGE,
ODK_ParseProvisioning(nullptr, message_size, core_message_length,
&nonce_values, device_id,
ODK_DEVICE_ID_LEN_MAX, &parsed_response));
}
TEST(OdkTest, PrepareCoreLicenseRequest) {
uint8_t license_message[ODK_LICENSE_REQUEST_SIZE] = {0};
size_t core_message_length = sizeof(license_message);
ODK_NonceValues nonce_values{0};
EXPECT_EQ(OEMCrypto_SUCCESS, ODK_PrepareCoreLicenseRequest(
license_message, sizeof(license_message),
&core_message_length, &nonce_values));
}
TEST(OdkTest, PrepareCoreLicenseRequestSize) {
uint8_t license_message[ODK_LICENSE_REQUEST_SIZE] = {0};
size_t core_message_length = sizeof(license_message);
ODK_NonceValues nonce_values{0};
// message length smaller than core message length
size_t core_message_length_invalid = core_message_length + 1;
EXPECT_EQ(ODK_ERROR_CORE_MESSAGE,
ODK_PrepareCoreLicenseRequest(
license_message, sizeof(license_message),
&core_message_length_invalid, &nonce_values));
// message length larger than core message length
uint8_t license_message_large[ODK_LICENSE_REQUEST_SIZE * 2] = {0};
EXPECT_EQ(OEMCrypto_SUCCESS,
ODK_PrepareCoreLicenseRequest(license_message_large,
sizeof(license_message_large),
&core_message_length, &nonce_values));
}
TEST(OdkTest, PrepareCoreRenewalRequest) {
uint8_t renewal_message[ODK_RENEWAL_REQUEST_SIZE] = {0};
size_t core_message_length = sizeof(renewal_message);
ODK_NonceValues nonce_values{0};
ODK_ClockValues clock_values{0};
constexpr uint64_t system_time_seconds = 10;
EXPECT_EQ(OEMCrypto_SUCCESS,
ODK_PrepareCoreRenewalRequest(
renewal_message, sizeof(renewal_message), &core_message_length,
&nonce_values, &clock_values, system_time_seconds));
}
TEST(OdkTest, PrepareCoreRenewalRequestTimer) {
uint8_t renewal_message[ODK_RENEWAL_REQUEST_SIZE] = {0};
size_t core_message_length = sizeof(renewal_message);
ODK_NonceValues nonce_values{2, 16, 0, 0};
constexpr uint64_t system_time_seconds = 10;
ODK_ClockValues clock_values_updated{0};
// system time smaller than first decrypt time
clock_values_updated.time_of_first_decrypt = system_time_seconds + 1;
EXPECT_EQ(ODK_ERROR_CORE_MESSAGE,
ODK_PrepareCoreRenewalRequest(
renewal_message, sizeof(renewal_message), &core_message_length,
&nonce_values, &clock_values_updated, system_time_seconds));
clock_values_updated.time_of_first_decrypt = system_time_seconds - 1;
EXPECT_EQ(OEMCrypto_SUCCESS,
ODK_PrepareCoreRenewalRequest(
renewal_message, sizeof(renewal_message), &core_message_length,
&nonce_values, &clock_values_updated, system_time_seconds));
// clock_values.time_of_renewal_request should get updated
EXPECT_EQ(system_time_seconds - clock_values_updated.time_of_first_decrypt,
clock_values_updated.time_of_renewal_request);
}
TEST(OdkTest, PrepareCoreProvisioningRequest) {
uint8_t provisioning_message[ODK_PROVISIONING_REQUEST_SIZE] = {0};
size_t core_message_length = sizeof(provisioning_message);
ODK_NonceValues nonce_values{0};
uint8_t device_id[ODK_DEVICE_ID_LEN_MAX] = {0};
EXPECT_EQ(
OEMCrypto_SUCCESS,
ODK_PrepareCoreProvisioningRequest(
provisioning_message, sizeof(provisioning_message),
&core_message_length, &nonce_values, device_id, sizeof(device_id)));
}
TEST(OdkTest, PrepareCoreProvisioningRequestDeviceId) {
uint8_t provisioning_message[ODK_PROVISIONING_REQUEST_SIZE] = {0};
size_t core_message_length = sizeof(provisioning_message);
ODK_NonceValues nonce_values{0};
uint8_t device_id_invalid[ODK_DEVICE_ID_LEN_MAX + 1] = {0};
EXPECT_EQ(ODK_ERROR_CORE_MESSAGE,
ODK_PrepareCoreProvisioningRequest(
provisioning_message, sizeof(provisioning_message),
&core_message_length, &nonce_values, device_id_invalid,
sizeof(device_id_invalid)));
}
// Serialize and de-serialize license request
TEST(OdkTest, LicenseRequestRoundtrip) {
std::vector<ODK_Field> empty;
auto odk_prepare_func = [&](uint8_t* const buf, size_t* size,
ODK_NonceValues* nonce_values) {
return ODK_PrepareCoreLicenseRequest(buf, SIZE_MAX, size, nonce_values);
};
auto kdo_parse_func = CoreLicenseRequestFromMessage;
ValidateRequest<ODK_LicenseRequest>(ODK_License_Request_Type, empty,
odk_prepare_func, kdo_parse_func);
}
TEST(OdkTest, RenewalRequestRoundtrip) {
constexpr uint64_t system_time_seconds = 0xBADDCAFE000FF1CE;
uint64_t playback_time = 0xCAFE00000000;
const uint64_t playback_start = system_time_seconds - playback_time;
const std::vector<ODK_Field> extra_fields = {
{ODK_UINT64, &playback_time, "playback_time"},
};
ODK_ClockValues clock_values = {0};
clock_values.time_of_first_decrypt = playback_start;
auto odk_prepare_func = [&](uint8_t* const buf, size_t* size,
ODK_NonceValues* nonce_values) {
return ODK_PrepareCoreRenewalRequest(buf, SIZE_MAX, size, nonce_values,
&clock_values, system_time_seconds);
};
auto kdo_parse_func = [&](const std::string& oemcrypto_core_message,
ODK_RenewalRequest* core_renewal_request) {
bool ok = CoreRenewalRequestFromMessage(oemcrypto_core_message,
core_renewal_request);
return ok;
};
ValidateRequest<ODK_RenewalRequest>(ODK_Renewal_Request_Type, extra_fields,
odk_prepare_func, kdo_parse_func);
}
TEST(OdkTest, ProvisionRequestRoundtrip) {
uint32_t device_id_length = ODK_DEVICE_ID_LEN_MAX / 2;
uint8_t device_id[ODK_DEVICE_ID_LEN_MAX] = {0};
memset(device_id, 0xff, device_id_length);
std::vector<ODK_Field> extra_fields = {
{ODK_UINT32, &device_id_length, "device_id_length"},
{ODK_DEVICEID, device_id, "device_id"},
};
auto odk_prepare_func = [&](uint8_t* const buf, size_t* size,
const ODK_NonceValues* nonce_values) {
return ODK_PrepareCoreProvisioningRequest(buf, SIZE_MAX, size, nonce_values,
device_id, device_id_length);
};
auto kdo_parse_func =
[&](const std::string& oemcrypto_core_message,
ODK_ProvisioningRequest* core_provisioning_request) {
bool ok = CoreProvisioningRequestFromMessage(oemcrypto_core_message,
core_provisioning_request);
return ok;
};
ValidateRequest<ODK_ProvisioningRequest>(ODK_Provisioning_Request_Type,
extra_fields, odk_prepare_func,
kdo_parse_func);
}
TEST(OdkTest, ParseLicenseErrorNonce) {
ODK_LicenseResponseParams params;
ODK_SetDefaultLicenseResponseParams(&params);
uint8_t* buf = nullptr;
uint32_t buf_size = 0;
ODK_BuildMessageBuffer(&(params.core_message), params.extra_fields, &buf,
&buf_size);
// temporarily mess up with nonce
params.core_message.nonce_values.nonce = 0;
OEMCryptoResult err = ODK_ParseLicense(
buf, buf_size + kExtraPayloadSize, buf_size, params.initial_license_load,
params.usage_entry_present, params.request_hash, &(params.timer_limits),
&(params.clock_values), &(params.core_message.nonce_values),
&(params.parsed_license));
EXPECT_EQ(OEMCrypto_ERROR_INVALID_NONCE, err);
delete[] buf;
}
TEST(OdkTest, ParseLicenseErrorUsageEntry) {
ODK_LicenseResponseParams params;
ODK_SetDefaultLicenseResponseParams(&params);
uint8_t* buf = nullptr;
uint32_t buf_size = 0;
ODK_BuildMessageBuffer(&(params.core_message), params.extra_fields, &buf,
&buf_size);
params.usage_entry_present = false;
OEMCryptoResult err = ODK_ParseLicense(
buf, buf_size + kExtraPayloadSize, buf_size, params.initial_license_load,
params.usage_entry_present, params.request_hash, &(params.timer_limits),
&(params.clock_values), &(params.core_message.nonce_values),
&(params.parsed_license));
EXPECT_EQ(ODK_ERROR_CORE_MESSAGE, err);
delete[] buf;
}
TEST(OdkTest, ParseLicenseErrorRequestHash) {
ODK_LicenseResponseParams params;
ODK_SetDefaultLicenseResponseParams(&params);
uint8_t* buf = nullptr;
uint32_t buf_size = 0;
ODK_BuildMessageBuffer(&(params.core_message), params.extra_fields, &buf,
&buf_size);
// temporarily mess up with request hash
params.request_hash[0] = 0xff;
OEMCryptoResult err = ODK_ParseLicense(
buf, buf_size + kExtraPayloadSize, buf_size, params.initial_license_load,
params.usage_entry_present, params.request_hash, &(params.timer_limits),
&(params.clock_values), &(params.core_message.nonce_values),
&(params.parsed_license));
EXPECT_EQ(ODK_ERROR_CORE_MESSAGE, err);
delete[] buf;
}
TEST(OdkTest, ParseRenewalErrorTimer) {
ODK_RenewalResponseParams params;
ODK_SetDefaultRenewalResponseParams(&params);
uint8_t* buf = nullptr;
uint32_t buf_size = 0;
ODK_BuildMessageBuffer(&(params.core_message), params.extra_fields, &buf,
&buf_size);
params.clock_values.time_of_renewal_request = 0;
OEMCryptoResult err = ODK_ParseRenewal(
buf, buf_size, buf_size, &(params.core_message.nonce_values),
params.system_time, &(params.timer_limits), &(params.clock_values),
&(params.playback_timer));
EXPECT_EQ(ODK_STALE_RENEWAL, err);
delete[] buf;
}
TEST(OdkTest, ParsePrivisioningErrorDeviceId) {
ODK_ProvisioningResponseParams params;
ODK_SetDefaultProvisioningResponseParams(&params);
uint8_t* buf = nullptr;
uint32_t buf_size = 0;
ODK_BuildMessageBuffer(&(params.core_message), params.extra_fields, &buf,
&buf_size);
// temporarily mess up with device_id
params.device_id[0] = 0;
OEMCryptoResult err = ODK_ParseProvisioning(
buf, buf_size + 16, buf_size, &(params.core_message.nonce_values),
params.device_id, params.device_id_length, &(params.parsed_provisioning));
EXPECT_EQ(ODK_ERROR_CORE_MESSAGE, err);
delete[] buf;
}
// Serialize and de-serialize license response
TEST(OdkTest, LicenseResponseRoundtrip) {
ODK_LicenseResponseParams params;
ODK_SetDefaultLicenseResponseParams(&params);
// save a copy of params.request_hash as it will be zero out during the test
uint8_t request_hash_read[ODK_SHA256_HASH_SIZE];
memcpy(request_hash_read, params.request_hash, sizeof(request_hash_read));
auto odk_parse_func = [&](const uint8_t* buf, size_t size) {
return ODK_ParseLicense(
buf, size + kExtraPayloadSize, size, params.initial_license_load,
params.usage_entry_present, request_hash_read, &(params.timer_limits),
&(params.clock_values), &(params.core_message.nonce_values),
&(params.parsed_license));
};
const std::string request_hash_string(
reinterpret_cast<const char*>(request_hash_read),
sizeof(request_hash_read));
auto kdo_prepare_func = [&](const ODK_LicenseRequest& core_request,
std::string* oemcrypto_core_message) {
return CreateCoreLicenseResponse(params.parsed_license, core_request,
request_hash_string,
oemcrypto_core_message);
};
ValidateResponse<ODK_LicenseRequest>(&(params.core_message),
params.extra_fields, odk_parse_func,
kdo_prepare_func);
}
TEST(OdkTest, RenewalResponseRoundtrip) {
ODK_RenewalResponseParams params;
ODK_SetDefaultRenewalResponseParams(&params);
const uint64_t playback_clock = params.playback_clock;
const uint64_t renewal_duration = params.renewal_duration;
auto odk_parse_func = [&](const uint8_t* buf, size_t size) {
OEMCryptoResult err =
ODK_ParseRenewal(buf, size, size, &(params.core_message.nonce_values),
params.system_time, &(params.timer_limits),
&(params.clock_values), &(params.playback_timer));
EXPECT_EQ(ODK_SET_TIMER, err);
EXPECT_EQ(renewal_duration, params.playback_timer);
EXPECT_EQ(params.clock_values.time_when_timer_expires,
params.system_time + params.playback_timer);
return OEMCrypto_SUCCESS;
};
auto kdo_prepare_func = [&](ODK_RenewalRequest& core_request,
std::string* oemcrypto_core_message) {
core_request.playback_time_seconds = playback_clock;
return CreateCoreRenewalResponse(core_request, renewal_duration,
oemcrypto_core_message);
};
ValidateResponse<ODK_RenewalRequest>(&(params.core_message),
params.extra_fields, odk_parse_func,
kdo_prepare_func);
}
TEST(OdkTest, ProvisionResponseRoundtrip) {
ODK_ProvisioningResponseParams params;
ODK_SetDefaultProvisioningResponseParams(&params);
// save a copy of params.device_id as it will be zero out during the test
const uint32_t device_id_length = params.device_id_length;
uint8_t device_id[ODK_DEVICE_ID_LEN_MAX] = {0};
memcpy(device_id, params.device_id, device_id_length);
auto odk_parse_func = [&](const uint8_t* buf, size_t size) {
OEMCryptoResult err = ODK_ParseProvisioning(
buf, size + 16, size, &(params.core_message.nonce_values), device_id,
device_id_length, &(params.parsed_provisioning));
return err;
};
auto kdo_prepare_func = [&](ODK_ProvisioningRequest& core_request,
std::string* oemcrypto_core_message) {
core_request.device_id.assign(reinterpret_cast<char*>(device_id),
device_id_length);
return CreateCoreProvisioningResponse(params.parsed_provisioning,
core_request, oemcrypto_core_message);
};
ValidateResponse<ODK_ProvisioningRequest>(&(params.core_message),
params.extra_fields, odk_parse_func,
kdo_prepare_func);
}
TEST(OdkSizeTest, LicenseRequest) {
uint8_t* message = nullptr;
size_t message_length = 0;
size_t core_message_length = 0;
uint16_t api_minor_version = ODK_MINOR_VERSION;
uint16_t api_major_version = 0;
uint32_t nonce = 0;
uint32_t session_id = 0;
ODK_NonceValues nonce_values{api_minor_version, api_major_version, nonce,
session_id};
EXPECT_EQ(OEMCrypto_ERROR_SHORT_BUFFER,
ODK_PrepareCoreLicenseRequest(message, message_length,
&core_message_length, &nonce_values));
// the core_message_length should be appropriately set
EXPECT_EQ(ODK_LICENSE_REQUEST_SIZE, core_message_length);
}
TEST(OdkSizeTest, RenewalRequest) {
uint8_t* message = nullptr;
size_t message_length = 0;
size_t core_message_length = 0;
uint16_t api_minor_version = ODK_MINOR_VERSION;
uint16_t api_major_version = ODK_MAJOR_VERSION;
uint32_t nonce = 0;
uint32_t session_id = 0;
ODK_ClockValues clock_values = {};
clock_values.time_of_first_decrypt = 10;
clock_values.timer_status = ODK_CLOCK_TIMER_STATUS_LICENSE_LOADED;
uint64_t system_time_seconds = 15;
ODK_NonceValues nonce_values{api_minor_version, api_major_version, nonce,
session_id};
EXPECT_EQ(OEMCrypto_ERROR_SHORT_BUFFER,
ODK_PrepareCoreRenewalRequest(message, message_length,
&core_message_length, &nonce_values,
&clock_values, system_time_seconds));
// the core_message_length should be appropriately set
EXPECT_EQ(ODK_RENEWAL_REQUEST_SIZE, core_message_length);
}
TEST(OdkSizeTest, ReleaseRequest) {
uint8_t* message = nullptr;
size_t message_length = 0;
size_t core_message_length = 0;
uint16_t api_minor_version = ODK_MINOR_VERSION;
uint16_t api_major_version = 0;
uint32_t nonce = 0;
uint32_t session_id = 0;
ODK_ClockValues clock_values = {};
clock_values.time_of_first_decrypt = 10;
clock_values.timer_status = ODK_CLOCK_TIMER_STATUS_LICENSE_INACTIVE;
uint64_t system_time_seconds = 15;
ODK_NonceValues nonce_values{api_minor_version, api_major_version, nonce,
session_id};
EXPECT_EQ(OEMCrypto_SUCCESS,
ODK_PrepareCoreRenewalRequest(message, message_length,
&core_message_length, &nonce_values,
&clock_values, system_time_seconds));
// Release requests do not have a core message.
EXPECT_GE(core_message_length, 0);
}
TEST(OdkSizeTest, ProvisioningRequest) {
uint8_t* message = nullptr;
size_t message_length = 0;
size_t core_message_length = 0;
uint16_t api_minor_version = ODK_MINOR_VERSION;
uint16_t api_major_version = 0;
uint32_t nonce = 0;
uint32_t session_id = 0;
uint32_t device_id_length = 0;
ODK_NonceValues nonce_values{api_minor_version, api_major_version, nonce,
session_id};
EXPECT_EQ(OEMCrypto_ERROR_SHORT_BUFFER,
ODK_PrepareCoreProvisioningRequest(
message, message_length, &core_message_length, &nonce_values,
nullptr, device_id_length));
// the core_message_length should be appropriately set
EXPECT_EQ(ODK_PROVISIONING_REQUEST_SIZE, core_message_length);
}
// Verify the version string contains the right version numbers.
TEST(OdkTest, CheckReleaseVersion) {
// Here are the version numbers.
std::string expected_version = std::to_string(ODK_MAJOR_VERSION) + "." +
std::to_string(ODK_MINOR_VERSION);
// Here is the version string.
EXPECT_NE(std::string(ODK_RELEASE_DATE).find(expected_version),
std::string::npos)
<< "Version mismatch in odk_structs.h";
}
} // namespace
} // namespace wvodk_test

View File

@@ -0,0 +1,13 @@
# Copyright 2019 Google LLC. All rights reserved. This file and proprietary
# source code may only be used and distributed under the Widevine Master License
# Agreement.
{
'sources': [
'odk_test.cpp',
'odk_test_helper.cpp',
'odk_test_helper.h',
'odk_timer_test.cpp',
],
}

View File

@@ -0,0 +1,488 @@
// Copyright 2019 Google LLC. All rights reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
#include "odk_test_helper.h"
#include <endian.h>
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iostream>
#include <vector>
#include "OEMCryptoCENCCommon.h"
#include "gtest/gtest.h"
#include "odk_structs.h"
#include "odk_structs_priv.h"
namespace wvodk_test {
void ODK_SetDefaultCoreFields(ODK_CoreMessage* core_message,
uint32_t message_type) {
ASSERT_TRUE(core_message != nullptr);
core_message->message_type = message_type;
core_message->message_length = 0;
core_message->nonce_values.api_minor_version = ODK_MINOR_VERSION;
core_message->nonce_values.api_major_version = ODK_MAJOR_VERSION;
core_message->nonce_values.nonce = 0xdeadbeef;
core_message->nonce_values.session_id = 0xcafebabe;
}
void ODK_SetDefaultLicenseResponseParams(ODK_LicenseResponseParams* params) {
ODK_SetDefaultCoreFields(&(params->core_message), ODK_License_Response_Type);
params->initial_license_load = true;
params->usage_entry_present = true;
params->parsed_license = {
.enc_mac_keys_iv = {.offset = 0, .length = 1},
.enc_mac_keys = {.offset = 2, .length = 3},
.pst = {.offset = 4, .length = 5},
.srm_restriction_data = {.offset = 6, .length = 7},
.license_type = OEMCrypto_EntitlementLicense,
.nonce_required = true,
.timer_limits =
{
.soft_enforce_rental_duration = true,
.soft_enforce_playback_duration = false,
.earliest_playback_start_seconds = 10,
.rental_duration_seconds = 11,
.total_playback_duration_seconds = 12,
.initial_renewal_duration_seconds = 13,
},
.key_array_length = 3,
.key_array =
{
{
.key_id = {.offset = 15, .length = 16},
.key_data_iv = {.offset = 17, .length = 18},
.key_data = {.offset = 19, .length = 20},
.key_control_iv = {.offset = 21, .length = 22},
.key_control = {.offset = 23, .length = 24},
},
{
.key_id = {.offset = 25, .length = 26},
.key_data_iv = {.offset = 27, .length = 28},
.key_data = {.offset = 29, .length = 30},
.key_control_iv = {.offset = 31, .length = 32},
.key_control = {.offset = 33, .length = 34},
},
{
.key_id = {.offset = 35, .length = 36},
.key_data_iv = {.offset = 37, .length = 38},
.key_data = {.offset = 39, .length = 40},
.key_control_iv = {.offset = 41, .length = 42},
.key_control = {.offset = 43, .length = 44},
},
},
};
memset(params->request_hash, 0xaa, sizeof(params->request_hash));
params->extra_fields = {
{ODK_SUBSTRING, &(params->parsed_license.enc_mac_keys_iv),
".enc_mac_keys_iv"},
{ODK_SUBSTRING, &(params->parsed_license.enc_mac_keys), ".enc_mac_keys"},
{ODK_SUBSTRING, &(params->parsed_license.pst), ".pst"},
{ODK_SUBSTRING, &(params->parsed_license.srm_restriction_data),
".srm_restriction_data"},
{ODK_UINT32, &(params->parsed_license.license_type), ".license_type"},
{ODK_UINT32, &(params->parsed_license.nonce_required), ".nonce_required"},
{ODK_UINT32,
&(params->parsed_license.timer_limits.soft_enforce_rental_duration),
".soft_enforce_rental_duration"},
{ODK_UINT32,
&(params->parsed_license.timer_limits.soft_enforce_playback_duration),
".soft_enforce_playback_duration"},
{ODK_UINT64,
&(params->parsed_license.timer_limits.earliest_playback_start_seconds),
".earliest_playback_start_seconds"},
{ODK_UINT64,
&(params->parsed_license.timer_limits.rental_duration_seconds),
".rental_duration_seconds"},
{ODK_UINT64,
&(params->parsed_license.timer_limits.total_playback_duration_seconds),
".total_playback_duration_seconds"},
{ODK_UINT64,
&(params->parsed_license.timer_limits.initial_renewal_duration_seconds),
".initial_renewal_duration_seconds"},
{ODK_UINT32, &(params->parsed_license.key_array_length),
".key_array_length"},
{ODK_SUBSTRING, &(params->parsed_license.key_array[0].key_id), ".key_id"},
{ODK_SUBSTRING, &(params->parsed_license.key_array[0].key_data_iv),
".key_data_iv"},
{ODK_SUBSTRING, &(params->parsed_license.key_array[0].key_data),
".key_data"},
{ODK_SUBSTRING, &(params->parsed_license.key_array[0].key_control_iv),
".key_control_iv"},
{ODK_SUBSTRING, &(params->parsed_license.key_array[0].key_control),
".key_control"},
{ODK_SUBSTRING, &(params->parsed_license.key_array[1].key_id), ".key_id"},
{ODK_SUBSTRING, &(params->parsed_license.key_array[1].key_data_iv),
".key_data_iv"},
{ODK_SUBSTRING, &(params->parsed_license.key_array[1].key_data),
".key_data"},
{ODK_SUBSTRING, &(params->parsed_license.key_array[1].key_control_iv),
".key_control_iv"},
{ODK_SUBSTRING, &(params->parsed_license.key_array[1].key_control),
".key_control"},
{ODK_SUBSTRING, &(params->parsed_license.key_array[2].key_id), ".key_id"},
{ODK_SUBSTRING, &(params->parsed_license.key_array[2].key_data_iv),
".key_data_iv"},
{ODK_SUBSTRING, &(params->parsed_license.key_array[2].key_data),
".key_data"},
{ODK_SUBSTRING, &(params->parsed_license.key_array[2].key_control_iv),
".key_control_iv"},
{ODK_SUBSTRING, &(params->parsed_license.key_array[2].key_control),
".key_control"},
{ODK_HASH, params->request_hash, ".request_hash"},
};
}
void ODK_SetDefaultRenewalResponseParams(ODK_RenewalResponseParams* params) {
ODK_SetDefaultCoreFields(&(params->core_message), ODK_Renewal_Response_Type);
params->system_time = 0xfaceb00c;
params->playback_clock = 10;
params->playback_timer = 20;
params->renewal_duration = 300;
params->extra_fields = {
{ODK_UINT64, &(params->playback_clock), "playback_clock"},
{ODK_UINT64, &(params->renewal_duration), "renewal_duration"},
};
params->timer_limits = {
.soft_enforce_rental_duration = false,
.soft_enforce_playback_duration = false,
.earliest_playback_start_seconds = 0,
.rental_duration_seconds = 1000,
.total_playback_duration_seconds = 2000,
.initial_renewal_duration_seconds = 300,
};
params->clock_values = {
.time_of_license_signed =
params->system_time - params->playback_clock - 42,
.time_of_first_decrypt = params->system_time - params->playback_clock,
.time_of_last_decrypt = params->system_time - params->playback_clock,
.time_of_renewal_request = params->playback_clock,
.time_when_timer_expires = params->system_time + params->playback_timer,
.timer_status = ODK_CLOCK_TIMER_STATUS_ACTIVE,
.status = kActive,
};
}
void ODK_SetDefaultProvisioningResponseParams(
ODK_ProvisioningResponseParams* params) {
ODK_SetDefaultCoreFields(&(params->core_message),
ODK_Provisioning_Response_Type);
params->device_id_length = ODK_DEVICE_ID_LEN_MAX / 2;
memset(params->device_id, 0xff, params->device_id_length);
memset(params->device_id + params->device_id_length, 0,
ODK_DEVICE_ID_LEN_MAX - params->device_id_length);
params->parsed_provisioning = {
.enc_private_key = {.offset = 0, .length = 1},
.enc_private_key_iv = {.offset = 2, .length = 3},
.encrypted_message_key = {.offset = 4, .length = 5},
};
params->extra_fields = {
{ODK_UINT32, &(params->device_id_length), "device_id_length"},
{ODK_DEVICEID, params->device_id, "device_id"},
{ODK_UINT32, &(params->parsed_provisioning).key_type, "key_type"},
{ODK_SUBSTRING, &(params->parsed_provisioning).enc_private_key,
"enc_private_key"},
{ODK_SUBSTRING, &(params->parsed_provisioning).enc_private_key_iv,
"enc_private_key_iv"},
{ODK_SUBSTRING, &(params->parsed_provisioning).encrypted_message_key,
"encrypted_message_key"},
};
}
size_t ODK_FieldLength(ODK_FieldType type) {
switch (type) {
case ODK_UINT16:
return sizeof(uint16_t);
case ODK_UINT32:
return sizeof(uint32_t);
case ODK_UINT64:
return sizeof(uint64_t);
case ODK_SUBSTRING:
return sizeof(uint32_t) + sizeof(uint32_t);
case ODK_DEVICEID:
return ODK_DEVICE_ID_LEN_MAX;
case ODK_HASH:
return ODK_SHA256_HASH_SIZE;
default:
return SIZE_MAX;
}
}
size_t ODK_AllocSize(ODK_FieldType type) {
if (type == ODK_SUBSTRING) {
return sizeof(OEMCrypto_Substring);
}
return ODK_FieldLength(type);
}
OEMCryptoResult ODK_WriteSingleField(uint8_t* buf, const ODK_Field* field) {
if (buf == nullptr || field == nullptr || field->value == nullptr) {
return ODK_ERROR_CORE_MESSAGE;
}
switch (field->type) {
case ODK_UINT16: {
const uint16_t u16 = htobe16(*static_cast<uint16_t*>(field->value));
memcpy(buf, &u16, sizeof(u16));
break;
}
case ODK_UINT32: {
const uint32_t u32 = htobe32(*static_cast<uint32_t*>(field->value));
memcpy(buf, &u32, sizeof(u32));
break;
}
case ODK_UINT64: {
const uint64_t u64 = htobe64(*static_cast<uint64_t*>(field->value));
memcpy(buf, &u64, sizeof(u64));
break;
}
case ODK_SUBSTRING: {
OEMCrypto_Substring* s = static_cast<OEMCrypto_Substring*>(field->value);
const uint32_t off = htobe32(s->offset);
const uint32_t len = htobe32(s->length);
memcpy(buf, &off, sizeof(off));
memcpy(buf + sizeof(off), &len, sizeof(len));
break;
}
case ODK_DEVICEID:
case ODK_HASH: {
const size_t field_len = ODK_FieldLength(field->type);
const uint8_t* const id = static_cast<uint8_t*>(field->value);
memcpy(buf, id, field_len);
break;
}
default:
return ODK_ERROR_CORE_MESSAGE;
}
return OEMCrypto_SUCCESS;
}
OEMCryptoResult ODK_ReadSingleField(const uint8_t* buf,
const ODK_Field* field) {
if (buf == nullptr || field == nullptr || field->value == nullptr) {
return ODK_ERROR_CORE_MESSAGE;
}
switch (field->type) {
case ODK_UINT16: {
memcpy(field->value, buf, sizeof(uint16_t));
uint16_t* u16p = static_cast<uint16_t*>(field->value);
*u16p = be16toh(*u16p);
break;
}
case ODK_UINT32: {
memcpy(field->value, buf, sizeof(uint32_t));
uint32_t* u32p = static_cast<uint32_t*>(field->value);
*u32p = be32toh(*u32p);
break;
}
case ODK_UINT64: {
memcpy(field->value, buf, sizeof(uint64_t));
uint64_t* u64p = static_cast<uint64_t*>(field->value);
*u64p = be64toh(*u64p);
break;
}
case ODK_SUBSTRING: {
OEMCrypto_Substring* s = static_cast<OEMCrypto_Substring*>(field->value);
uint32_t off = 0;
uint32_t len = 0;
memcpy(&off, buf, sizeof(off));
memcpy(&len, buf + sizeof(off), sizeof(len));
s->offset = be32toh(off);
s->length = be32toh(len);
break;
}
case ODK_DEVICEID:
case ODK_HASH: {
const size_t field_len = ODK_FieldLength(field->type);
uint8_t* const id = static_cast<uint8_t*>(field->value);
memcpy(id, buf, field_len);
break;
}
default:
return ODK_ERROR_CORE_MESSAGE;
}
return OEMCrypto_SUCCESS;
}
OEMCryptoResult ODK_DumpSingleField(const uint8_t* buf,
const ODK_Field* field) {
if (buf == nullptr || field == nullptr || field->value == nullptr) {
return ODK_ERROR_CORE_MESSAGE;
}
switch (field->type) {
case ODK_UINT16: {
uint16_t val;
memcpy(&val, buf, sizeof(uint16_t));
val = be16toh(val);
std::cerr << field->name << ": " << val << " = 0x" << std::hex << val
<< "\n";
break;
}
case ODK_UINT32: {
uint32_t val;
memcpy(&val, buf, sizeof(uint32_t));
val = be32toh(val);
std::cerr << field->name << ": " << val << " = 0x" << std::hex << val
<< "\n";
break;
}
case ODK_UINT64: {
uint64_t val;
memcpy(&val, buf, sizeof(uint64_t));
val = be64toh(val);
std::cerr << field->name << ": " << val << " = 0x" << std::hex << val
<< "\n";
break;
}
case ODK_SUBSTRING: {
uint32_t off = 0;
uint32_t len = 0;
memcpy(&off, buf, sizeof(off));
memcpy(&len, buf + sizeof(off), sizeof(len));
std::cerr << field->name << ": (off=" << off << ", len=" << len << ")\n";
break;
}
case ODK_DEVICEID:
case ODK_HASH: {
const size_t field_len = ODK_FieldLength(field->type);
std::cerr << field->name << ": ";
for (size_t i = 0; i < field_len; i++) {
std::cerr << std::hex << std::setfill('0') << std::setw(2)
<< static_cast<unsigned int>(buf[i]);
}
std::cerr << "\n";
break;
}
default:
return ODK_ERROR_CORE_MESSAGE;
}
std::cerr << std::dec; // Return to normal.
return OEMCrypto_SUCCESS;
}
/*
* Parameters:
* [in] size_in: buffer size
* [out] size_out: bytes processed
*/
OEMCryptoResult ODK_IterFields(ODK_FieldMode mode, uint8_t* buf,
const size_t size_in, size_t* size_out,
const std::vector<ODK_Field>& fields) {
if (buf == nullptr || size_out == nullptr) {
return ODK_ERROR_CORE_MESSAGE;
}
size_t off = 0, off2 = 0;
for (size_t i = 0; i < fields.size(); i++) {
if (__builtin_add_overflow(off, ODK_FieldLength(fields[i].type), &off2) ||
off2 > size_in) {
return ODK_ERROR_CORE_MESSAGE;
}
uintptr_t base = reinterpret_cast<uintptr_t>(buf);
if (__builtin_add_overflow(base, off, &base)) {
return ODK_ERROR_CORE_MESSAGE;
}
uint8_t* const buf_off = buf + off;
switch (mode) {
case ODK_WRITE:
ODK_WriteSingleField(buf_off, &fields[i]);
break;
case ODK_READ:
ODK_ReadSingleField(buf_off, &fields[i]);
break;
case ODK_DUMP:
ODK_DumpSingleField(buf_off, &fields[i]);
break;
default:
return ODK_ERROR_CORE_MESSAGE;
}
off = off2;
}
*size_out = off;
if (*size_out > size_in) {
return ODK_ERROR_CORE_MESSAGE;
}
return OEMCrypto_SUCCESS;
}
void ODK_ExpectEqualBuf(const void* s1, const void* s2, size_t n,
const std::vector<ODK_Field>& fields) {
if (memcmp(s1, s2, n) != 0) {
const void* buffers[] = {s1, s2};
for (int i = 0; i < 2; i++) {
char _tmp[] = "/tmp/fileXXXXXX";
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);
out.close();
std::cerr << "buffer " << i << " dumped to " << tmp << std::endl;
size_t bytes_written;
uint8_t* buf =
const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(buffers[i]));
ODK_IterFields(ODK_DUMP, buf, n, &bytes_written, fields);
}
FAIL();
}
}
void ODK_ResetOdkFields(std::vector<ODK_Field>* fields) {
if (fields == nullptr) {
return;
}
for (auto& field : *fields) {
if (field.value != nullptr) {
const size_t size = ODK_AllocSize(field.type);
memset(field.value, 0, size);
}
}
}
void ODK_BuildMessageBuffer(ODK_CoreMessage* core_message,
const std::vector<ODK_Field>& extra_fields,
uint8_t** buf, uint32_t* buf_size) {
ASSERT_TRUE(core_message != nullptr);
ASSERT_TRUE(buf_size != nullptr);
std::vector<ODK_Field> total_fields = {
{ODK_UINT32, &(core_message->message_type), "message_type"},
{ODK_UINT32, &(core_message->message_length), "message_size"},
{ODK_UINT16, &(core_message->nonce_values.api_minor_version),
"api_minor_version"},
{ODK_UINT16, &(core_message->nonce_values.api_major_version),
"api_major_version"},
{ODK_UINT32, &(core_message->nonce_values.nonce), "nonce"},
{ODK_UINT32, &(core_message->nonce_values.session_id), "session_id"},
};
uint32_t header_size = 0;
for (auto& field : total_fields) {
header_size += ODK_FieldLength(field.type);
}
total_fields.insert(total_fields.end(), extra_fields.begin(),
extra_fields.end());
for (auto& field : total_fields) {
*buf_size += ODK_FieldLength(field.type);
}
// update message_size
*(reinterpret_cast<uint32_t*>(total_fields[1].value)) = *buf_size;
*buf = new uint8_t[*buf_size]{};
size_t bytes_written = 0;
// serialize ODK fields to message buffer
EXPECT_EQ(OEMCrypto_SUCCESS, ODK_IterFields(ODK_WRITE, *buf, SIZE_MAX,
&bytes_written, total_fields));
EXPECT_EQ(bytes_written, *buf_size);
}
} // namespace wvodk_test

View File

@@ -0,0 +1,99 @@
/* Copyright 2019 Google LLC. All rights reserved. This file and proprietary */
/* source code may only be used and distributed under the Widevine Master */
/* License Agreement. */
#ifndef WIDEVINE_ODK_TEST_ODK_TEST_HELPER_H_
#define WIDEVINE_ODK_TEST_ODK_TEST_HELPER_H_
#include <cstdint>
#include <string>
#include <vector>
#include "odk_structs.h"
#include "odk_structs_priv.h"
namespace wvodk_test {
enum ODK_FieldType {
ODK_UINT16,
ODK_UINT32,
ODK_UINT64,
ODK_SUBSTRING,
ODK_DEVICEID,
ODK_HASH,
ODK_NUMTYPES,
};
enum ODK_FieldMode {
ODK_READ,
ODK_WRITE,
ODK_DUMP,
};
struct ODK_Field {
ODK_FieldType type;
void* value;
std::string name;
};
struct ODK_LicenseResponseParams {
ODK_CoreMessage core_message;
bool initial_license_load;
bool usage_entry_present;
uint8_t request_hash[ODK_SHA256_HASH_SIZE];
ODK_TimerLimits timer_limits;
ODK_ClockValues clock_values;
ODK_ParsedLicense parsed_license;
std::vector<ODK_Field> extra_fields;
};
struct ODK_RenewalResponseParams {
ODK_CoreMessage core_message;
uint64_t system_time;
uint64_t playback_clock;
uint64_t renewal_duration;
ODK_TimerLimits timer_limits;
ODK_ClockValues clock_values;
uint64_t playback_timer;
std::vector<ODK_Field> extra_fields;
};
struct ODK_ProvisioningResponseParams {
ODK_CoreMessage core_message;
uint8_t device_id[ODK_DEVICE_ID_LEN_MAX];
uint32_t device_id_length;
ODK_ParsedProvisioning parsed_provisioning;
std::vector<ODK_Field> extra_fields;
};
/* Default values in core_message for testing */
void ODK_SetDefaultCoreFields(ODK_CoreMessage* core_message,
uint32_t message_type);
void ODK_SetDefaultLicenseResponseParams(ODK_LicenseResponseParams* params);
void ODK_SetDefaultRenewalResponseParams(ODK_RenewalResponseParams* params);
void ODK_SetDefaultProvisioningResponseParams(
ODK_ProvisioningResponseParams* params);
size_t ODK_FieldLength(ODK_FieldType type);
size_t ODK_AllocSize(ODK_FieldType type);
/* Copy ODK_Field to buf */
OEMCryptoResult ODK_WriteSingleField(uint8_t* buf, const ODK_Field* field);
/* Load buf to ODK_Field */
OEMCryptoResult ODK_ReadSingleField(const uint8_t* buf, const ODK_Field* field);
OEMCryptoResult ODK_DumpSingleField(const uint8_t* buf, const ODK_Field* field);
OEMCryptoResult ODK_IterFields(ODK_FieldMode mode, uint8_t* buf,
const size_t size_in, size_t* size_out,
const std::vector<ODK_Field>& fields);
void ODK_ExpectEqualBuf(const void* s1, const void* s2, size_t n,
const std::vector<ODK_Field>& fields);
void ODK_ResetOdkFields(std::vector<ODK_Field>* fields);
/* Serialize core_message and extra_fields into buf */
void ODK_BuildMessageBuffer(ODK_CoreMessage* core_message,
const std::vector<ODK_Field>& extra_fields,
uint8_t** buf, uint32_t* buf_size);
} /* namespace wvodk_test */
#endif /* WIDEVINE_ODK_TEST_ODK_TEST_HELPER_H_ */

File diff suppressed because it is too large Load Diff