OEMCrypto v16.1

Merge of http://go/wvgerrit/93404

This CL updates the Widevine CDM to support OEMCrypto v16.1

Test: Tested in 16.2 CL
Bug: 141247171
Change-Id: I69bd993500f6fb63bf6010c8b0250dc7acc3d71b
This commit is contained in:
Fred Gylys-Colwell
2020-01-18 10:11:24 -08:00
parent 7e2619e379
commit 7665614b2e
132 changed files with 12331 additions and 9341 deletions

View File

@@ -8,6 +8,7 @@ cc_library_static {
include_dirs: [
"vendor/widevine/libwvdrmengine/oemcrypto/include",
"vendor/widevine/libwvdrmengine/oemcrypto/odk/include",
"vendor/widevine/libwvdrmengine/oemcrypto/odk/src",
],
srcs: [
@@ -22,6 +23,33 @@ cc_library_static {
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 {
@@ -29,7 +57,7 @@ cc_test {
include_dirs: [
"vendor/widevine/libwvdrmengine/oemcrypto/include",
"vendor/widevine/libwvdrmengine/oemcrypto/odk/include",
"vendor/widevine/libwvdrmengine/oemcrypto/odk/kdo/include",
"vendor/widevine/libwvdrmengine/oemcrypto/odk/src",
],
// WARNING: Module tags are not supported in Soong.
@@ -45,10 +73,10 @@ cc_test {
"libcdm_protos",
"libcdm",
"libwv_odk",
"libwv_kdo",
],
srcs: [
"kdo/src/oec_util.cpp",
"test/odk_test.cpp",
"test/odk_timer_test.cpp",
],

View File

@@ -0,0 +1,9 @@
The ODK Library is used to generate and parse core OEMCrypto messages.
This library is used by both OEMcrypto on a device, and by Widvine 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

@@ -1,6 +1,6 @@
// 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.
/* 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
@@ -9,8 +9,8 @@
*
*********************************************************************/
#ifndef OEMCRYPTO_CENC_COMMON_H_
#define OEMCRYPTO_CENC_COMMON_H_
#ifndef WIDEVINE_ODK_INCLUDE_OEMCRYPTOCENCCOMMON_H_
#define WIDEVINE_ODK_INCLUDE_OEMCRYPTOCENCCOMMON_H_
#include <stdbool.h>
#include <stddef.h>
@@ -20,17 +20,17 @@
extern "C" {
#endif
// clang-format off
/* 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_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_DEVICE_KEY = 8, /* no keybox device key. */
OEMCrypto_ERROR_NO_ASSET_KEY = 9,
OEMCrypto_ERROR_KEYBOX_INVALID = 10,
OEMCrypto_ERROR_NO_KEYDATA = 11,
@@ -62,9 +62,9 @@ typedef enum OEMCryptoResult {
OEMCrypto_ERROR_INSUFFICIENT_RESOURCES = 37,
OEMCrypto_ERROR_INSUFFICIENT_HDCP = 38,
OEMCrypto_ERROR_BUFFER_TOO_LARGE = 39,
OEMCrypto_WARNING_GENERATION_SKEW = 40, // Warning, not an error.
OEMCrypto_WARNING_GENERATION_SKEW = 40, /* Warning, not error. */
OEMCrypto_ERROR_GENERATION_SKEW = 41,
OEMCrypto_LOCAL_DISPLAY_ONLY = 42, // Info, not an error.
OEMCrypto_LOCAL_DISPLAY_ONLY = 42, /* Info, not an error. */
OEMCrypto_ERROR_ANALOG_OUTPUT = 43,
OEMCrypto_ERROR_WRONG_PST = 44,
OEMCrypto_ERROR_WRONG_KEYS = 45,
@@ -72,8 +72,9 @@ typedef enum OEMCryptoResult {
OEMCrypto_ERROR_LICENSE_INACTIVE = 47,
OEMCrypto_ERROR_ENTRY_NEEDS_UPDATE = 48,
OEMCrypto_ERROR_ENTRY_IN_USE = 49,
OEMCrypto_ERROR_USAGE_TABLE_UNRECOVERABLE = 50, // Reserved. Do not use.
OEMCrypto_KEY_NOT_LOADED = 51, // obsolete. use error 26.
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,
@@ -89,8 +90,9 @@ typedef enum OEMCryptoResult {
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
/* clang-format on */
/*
* OEMCrypto_Usage_Entry_Status.
@@ -99,7 +101,7 @@ typedef enum OEMCryptoResult {
typedef enum OEMCrypto_Usage_Entry_Status {
kUnused = 0,
kActive = 1,
kInactive = 2, // Deprecated. Used kInactiveUsed or kInactiveUnused.
kInactive = 2, /* Deprecated. Use kInactiveUsed or kInactiveUnused. */
kInactiveUsed = 3,
kInactiveUnused = 4,
} OEMCrypto_Usage_Entry_Status;
@@ -150,4 +152,4 @@ typedef struct {
}
#endif
#endif // OEMCRYPTO_CENC_COMMON_H_
#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,64 @@
/* Copyright 2019 Google LLC. All rights reserved. This file and proprietary */
/* source code may only be used and distributed under the Widevine Master */
/* License Agreement. */
/*********************************************************************
* 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
* [out] oemcrypto_core_message
*/
bool CreateCoreLicenseResponse(const ODK_ParsedLicense& parsed_lic,
const ODK_LicenseRequest& core_request,
std::string* oemcrypto_core_message);
/**
* Counterpart (serializer) of ODK_ParseRenewal (deserializer)
*
* Parameters:
* [in] core_request
* [out] oemcrypto_core_message
*/
bool CreateCoreRenewalResponse(const ODK_RenewalRequest& core_request,
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,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_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
* [out] oemcrypto_core_message
*/
bool CreateCoreLicenseResponseFromProto(const std::string& serialized_license,
const ODK_LicenseRequest& core_request,
const std::string& core_request_sha256,
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,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. */
/* 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 {
uint32_t api_version;
uint32_t nonce;
uint32_t session_id;
};
/**
* Output structure for CoreRenewalRequestFromMessage
* Input structure for CreateCoreRenewalResponse
*/
struct ODK_RenewalRequest {
uint32_t api_version;
uint32_t nonce;
uint32_t session_id;
uint64_t playback_time_seconds;
};
/**
* Output structure for CoreProvisioningRequestFromMessage
* Input structure for CreateCoreProvisioningResponse
*/
struct ODK_ProvisioningRequest {
uint32_t api_version;
uint32_t nonce;
uint32_t session_id;
std::string device_id;
};
} /* namespace oemcrypto_core_message */
#endif /* WIDEVINE_ODK_INCLUDE_CORE_MESSAGE_TYPES_H_ */

View File

@@ -1,8 +1,6 @@
/*
* 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.
*/
/* 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
@@ -44,10 +42,11 @@
*
*********************************************************************/
#ifndef ODK_H_
#define ODK_H_
#ifndef WIDEVINE_ODK_INCLUDE_ODK_H_
#define WIDEVINE_ODK_INCLUDE_ODK_H_
#include <stdint.h>
#include "OEMCryptoCENCCommon.h"
#include "odk_structs.h"
@@ -319,7 +318,7 @@ OEMCryptoResult ODK_PrepareCoreLicenseRequest(
*/
OEMCryptoResult ODK_PrepareCoreRenewalRequest(
uint8_t* message, size_t message_length, size_t* core_message_size,
const ODK_NonceValues* nonce_values, const ODK_ClockValues* clock_values,
const ODK_NonceValues* nonce_values, ODK_ClockValues* clock_values,
uint64_t system_time_seconds);
/*
@@ -327,8 +326,8 @@ OEMCryptoResult ODK_PrepareCoreRenewalRequest(
*
* 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.
* beginning of the message buffer. The values in nonce_values, clock_values
* and system_time_seconds are used to populate the message.
*
* This shall be called by OEMCrypto from
* OEMCrypto_PrepAndSignProvisioningRequest.
@@ -400,39 +399,60 @@ OEMCryptoResult ODK_InitializeV15Values(ODK_TimerLimits* timer_limits,
uint32_t key_duration,
uint64_t system_time_seconds);
/*
* ODK_RefreshV15Values
*
* Description:
* This function updates the clock_values as needed if the renewal is
* accepted. The field nonce_values.api_level is verified to be 15.
*
* 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".
* [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,
uint64_t* timer_value);
/*
* ODK_ParseLicense
*
* Description:
* The function ODK_ParseLicense will parse the message and verify fields in
* the message.
* The function ODK_ParseLicense will parse the message and verify
*
* 1. Either the nonce matches the one passed in or the license does not
* require a nonce.
* 2. The API version of the message matches.
* 3. The session id matches.
* The function ODK_ParseLicense will parse the message and set each
* substring pointer to point 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 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 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
* hash matches request_hash in the parsed license. If verification fails,
* then it shall return ODK_ERROR_CORE_MESSAGE.
*
* If usage_entry_present is true, then ODK_ParseLicense shall verify that
* the pst in the license has a nonzero length.
* layer above. If the api in the message is larger than 16, then
* ODK_UNSUPPORTED_API is returned.
*
* Parameters:
* [in] message: pointer to the message buffer.
@@ -470,12 +490,9 @@ OEMCryptoResult ODK_ParseLicense(
* ODK_ParseRenewal
*
* Description:
* The function ODK_ParseRenewal will parse the message and verify. 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.
* The function ODK_ParseRenewal will parse the message and verify that the
* nonce values match those in the license. If the message does not parse
* correctly, an error of ODK_ERROR_CORE_MESSAGE is returned.
*
* After parsing the message, this function updates the clock_values based on
* the timer_limits and the current system time. If playback may not
@@ -534,21 +551,14 @@ OEMCryptoResult ODK_ParseRenewal(const uint8_t* message, size_t message_length,
* The function ODK_ParseProvisioning will parse the message and verify the
* nonce values match those in the license.
*
* The function ODK_ParseProvisioning will parse the message and set each
* substring pointer to point 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 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 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.
@@ -580,4 +590,4 @@ OEMCryptoResult ODK_ParseProvisioning(
}
#endif
#endif // ODK_H_
#endif /* WIDEVINE_ODK_INCLUDE_ODK_H_ */

View File

@@ -1,13 +1,12 @@
/*
* 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.
*/
/* Copyright 2019 Google LLC. All rights reserved. This file and proprietary */
/* source code may only be used and distributed under the Widevine Master */
/* License Agreement. */
#ifndef ODK_STRUCTS_H_
#define ODK_STRUCTS_H_
#ifndef WIDEVINE_ODK_INCLUDE_ODK_STRUCTS_H_
#define WIDEVINE_ODK_INCLUDE_ODK_STRUCTS_H_
#include <stdint.h>
#include "OEMCryptoCENCCommon.h"
#define ODK_MAX_NUM_KEYS 32
@@ -24,11 +23,11 @@
*/
typedef struct {
uint32_t /*boolean*/ soft_expiry;
uint64_t earliest_playback_start_seconds; // seconds since license signed.
uint64_t latest_playback_start_seconds; // seconds since license signed.
uint64_t initial_playback_duration_seconds; // seconds since playback start.
uint64_t renewal_playback_duration_seconds; // seconds since renewal signed.
uint64_t license_duration_seconds; // seconds since license signed.
uint64_t earliest_playback_start_seconds; /* since license signed. */
uint64_t latest_playback_start_seconds; /* since license signed. */
uint64_t initial_playback_duration_seconds; /* since playback start. */
uint64_t renewal_playback_duration_seconds; /* since renewal signed. */
uint64_t license_duration_seconds; /* since license signed. */
} ODK_TimerLimits;
/*
@@ -71,6 +70,7 @@ 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;
@@ -93,4 +93,4 @@ typedef struct {
uint32_t session_id;
} ODK_NonceValues;
#endif // ODK_STRUCTS_H_
#endif /* WIDEVINE_ODK_INCLUDE_ODK_STRUCTS_H_ */

View File

@@ -1,209 +0,0 @@
/*
* 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 "oec_util.h"
#include <stdint.h>
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <string>
#include "odk_overflow.h"
#include "odk_serialize.h"
#include "odk_structs.h"
#include "odk_structs_priv.h"
#include "oemcrypto_types.h"
#include "serialization_base.h"
using namespace oec_util;
namespace oec_util {
namespace {
/* @ private functions */
const int CURRENT_OEC_VERSION = 16;
/**
* 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 string& oemcrypto_core_message,
S* core_request, T* prepared, const U unpacker) {
if (!core_request) {
return false;
}
const uint8_t* buf =
reinterpret_cast<const uint8_t*>(oemcrypto_core_message.c_str());
size_t buf_length = oemcrypto_core_message.size();
Message* msg = NULL;
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_version = core_message.nonce_values.api_version;
core_request->nonce = core_message.nonce_values.nonce;
core_request->session_id = core_message.nonce_values.session_id;
return core_message.message_type == message_type &&
core_message.message_length == GetOffset(msg) &&
core_request->api_version == CURRENT_OEC_VERSION;
}
/**
* 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,
string* oemcrypto_core_message, T& response,
const P& packer) {
if (!oemcrypto_core_message) {
return false;
}
auto* header = reinterpret_cast<ODK_CoreMessage*>(&response);
header->message_type = message_type;
header->nonce_values.api_version = core_request.api_version;
header->nonce_values.nonce = core_request.nonce;
header->nonce_values.session_id = core_request.session_id;
uint8_t buf[2048] = {0};
Message* msg = NULL;
AllocateMessage(&msg, message_block);
InitMessage(msg, buf, sizeof(buf));
packer(msg, &response);
if (!ValidMessage(msg)) {
return false;
}
uint32_t message_length = GetSize(msg);
InitMessage(msg, buf + sizeof(header->message_type),
sizeof(header->message_length));
Pack_uint32_t(msg, &message_length);
oemcrypto_core_message->assign(reinterpret_cast<const char*>(buf),
message_length);
return true;
}
bool CopyDeviceId(ODK_ProvisioningResponse& dest,
const ODK_ProvisioningRequest& src) {
auto& core_provisioning = dest.core_provisioning;
const string& device_id = src.device_id;
core_provisioning.device_id_length = device_id.size();
if (core_provisioning.device_id_length >
sizeof(core_provisioning.device_id)) {
return false;
}
memset(core_provisioning.device_id, 0, sizeof(core_provisioning.device_id));
memcpy(core_provisioning.device_id, device_id.data(),
core_provisioning.device_id_length);
return true;
}
} // namespace
// @ public parse request (deserializer) functions
bool ParseLicenseRequest(const string& oemcrypto_core_message,
ODK_LicenseRequest* core_license_request) {
const auto unpacker = Unpack_ODK_PreparedLicense;
ODK_PreparedLicense prepared_license = {};
return ParseRequest(ODK_License_Request_Type, oemcrypto_core_message,
core_license_request, &prepared_license, unpacker);
}
bool ParseRenewalRequest(const string& oemcrypto_core_message,
ODK_RenewalRequest* core_renewal_request) {
const auto unpacker = Unpack_ODK_RenewalMessage;
ODK_RenewalMessage prepared_renewal = {};
if (!ParseRequest(ODK_Renewal_Request_Type, oemcrypto_core_message,
core_renewal_request, &prepared_renewal, unpacker)) {
return false;
}
core_renewal_request->playback_time = prepared_renewal.playback_time;
return true;
}
bool ParseProvisioningRequest(
const string& oemcrypto_core_message,
ODK_ProvisioningRequest* core_provisioning_request) {
const auto unpacker = Unpack_ODK_ProvisioningMessage;
ODK_ProvisioningMessage 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;
}
// @ public create response functions
bool CreateCoreLicenseResponse(const ODK_ParsedLicense& parsed_lic,
const ODK_LicenseRequest& core_request,
string* oemcrypto_core_message) {
ODK_LicenseResponse license_response{
{}, const_cast<ODK_ParsedLicense*>(&parsed_lic)};
return CreateResponse(ODK_License_Response_Type, core_request,
oemcrypto_core_message, license_response,
Pack_ODK_LicenseResponse);
}
bool CreateCoreRenewalResponse(const ODK_RenewalRequest& core_request,
string* oemcrypto_core_message) {
ODK_RenewalMessage renewal{{}, core_request.playback_time};
renewal.playback_time = core_request.playback_time;
return CreateResponse(ODK_Renewal_Response_Type, core_request,
oemcrypto_core_message, renewal,
Pack_ODK_RenewalMessage);
}
bool CreateCoreProvisioningResponse(const ODK_ParsedProvisioning& parsed_prov,
const ODK_ProvisioningRequest& core_request,
string* oemcrypto_core_message) {
ODK_ProvisioningResponse prov_response{
{}, const_cast<ODK_ParsedProvisioning*>(&parsed_prov)};
if (!CopyDeviceId(prov_response, core_request)) {
return false;
}
return CreateResponse(ODK_Provisioning_Response_Type, core_request,
oemcrypto_core_message, prov_response,
Pack_ODK_ProvisioningResponse);
}
} // namespace oec_util

View File

@@ -0,0 +1,112 @@
// 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 {
const int EARLIEST_OEMCRYPTO_VERSION_WITH_ODK = 16;
const int LATEST_OEMCRYPTO_VERSION = 16;
/**
* 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_version = core_message.nonce_values.api_version;
core_request->nonce = core_message.nonce_values.nonce;
core_request->session_id = core_message.nonce_values.session_id;
return core_message.message_type == message_type &&
core_message.message_length == GetOffset(msg) &&
core_request->api_version >= EARLIEST_OEMCRYPTO_VERSION_WITH_ODK &&
core_request->api_version <= LATEST_OEMCRYPTO_VERSION;
}
} // namespace
bool CoreLicenseRequestFromMessage(const std::string& oemcrypto_core_message,
ODK_LicenseRequest* core_license_request) {
const auto unpacker = Unpack_ODK_PreparedLicense;
ODK_PreparedLicense 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_RenewalMessage;
ODK_RenewalMessage 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_ProvisioningMessage;
ODK_ProvisioningMessage 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,114 @@
// 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 <algorithm>
#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 = reinterpret_cast<ODK_CoreMessage*>(&response);
header->message_type = message_type;
header->nonce_values.api_version = core_request.api_version;
header->nonce_values.nonce = core_request.nonce;
header->nonce_values.session_id = core_request.session_id;
const 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& core_provisioning = dest->core_provisioning;
const std::string& device_id = src.device_id;
core_provisioning.device_id_length = device_id.size();
if (core_provisioning.device_id_length >
sizeof(core_provisioning.device_id)) {
return false;
}
memset(core_provisioning.device_id, 0, sizeof(core_provisioning.device_id));
memcpy(core_provisioning.device_id, device_id.data(),
core_provisioning.device_id_length);
return true;
}
} // namespace
bool CreateCoreLicenseResponse(const ODK_ParsedLicense& parsed_lic,
const ODK_LicenseRequest& core_request,
std::string* oemcrypto_core_message) {
ODK_LicenseResponse license_response{
{}, const_cast<ODK_ParsedLicense*>(&parsed_lic)};
return CreateResponse(ODK_License_Response_Type, core_request,
oemcrypto_core_message, license_response,
Pack_ODK_LicenseResponse);
}
bool CreateCoreRenewalResponse(const ODK_RenewalRequest& core_request,
std::string* oemcrypto_core_message) {
ODK_RenewalMessage renewal{{}, core_request.playback_time_seconds};
renewal.playback_time = core_request.playback_time_seconds;
return CreateResponse(ODK_Renewal_Response_Type, core_request,
oemcrypto_core_message, renewal,
Pack_ODK_RenewalMessage);
}
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

@@ -1,29 +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.
*/
// 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 "oec_util_proto.h"
#include "core_message_serialize_proto.h"
#include <stdint.h>
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <string>
#include "odk_overflow.h"
#include "core_message_serialize.h"
#include "license_protocol.pb.h"
#include "odk_serialize.h"
#include "odk_structs.h"
#include "odk_structs_priv.h"
#include "oemcrypto_types.h"
#include "serialization_base.h"
using namespace oec_util;
namespace oec_util {
namespace oemcrypto_core_message {
namespace serialize {
namespace {
/* @ private functions */
@@ -45,14 +40,14 @@ OEMCrypto_Substring GetOecSubstring(const std::string& message,
return substring;
}
OEMCrypto_KeyObject KeyContainerToOecKey(const string& proto,
const License::KeyContainer& k) {
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 string& key_data = k.key();
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()) -
@@ -69,31 +64,40 @@ OEMCrypto_KeyObject KeyContainerToOecKey(const string& proto,
// @ public create response functions
bool CreateCoreLicenseResponse(const video_widevine::License& lic,
const ODK_LicenseRequest& core_request,
string* oemcrypto_core_message) {
string proto;
if (!lic.SerializeToString(&proto)) {
bool CreateCoreLicenseResponseFromProto(const std::string& serialized_license,
const ODK_LicenseRequest& core_request,
const std::string& core_request_sha256,
std::string* oemcrypto_core_message) {
video_widevine::License lic;
if (!lic.ParseFromString(serialized_license)) {
return false;
}
ODK_ParsedLicense parsed_lic{};
if (core_request_sha256.size() != ODK_SHA256_HASH_SIZE) {
return false;
}
std::memcpy(parsed_lic.request_hash, core_request_sha256.data(),
ODK_SHA256_HASH_SIZE);
for (int i = 0; i < lic.key_size(); ++i) {
const auto& k = lic.key(i);
switch (k.type()) {
case License_KeyContainer::SIGNING: {
parsed_lic.enc_mac_keys_iv = GetOecSubstring(proto, k.iv());
case video_widevine::License_KeyContainer::SIGNING: {
parsed_lic.enc_mac_keys_iv =
GetOecSubstring(serialized_license, k.iv());
// Strip off PKCS#5 padding
string mac_keys(k.key(), 2 * wvoec::MAC_KEY_SIZE);
parsed_lic.enc_mac_keys = GetOecSubstring(proto, mac_keys);
const size_t MAC_KEY_SIZE = 32;
std::string mac_keys(k.key(), 2 * MAC_KEY_SIZE);
parsed_lic.enc_mac_keys = GetOecSubstring(serialized_license, mac_keys);
break;
}
case License_KeyContainer::CONTENT: {
case video_widevine::License_KeyContainer::CONTENT: {
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(proto, k);
parsed_lic.key_array[n++] = KeyContainerToOecKey(serialized_license, k);
break;
}
default: {
@@ -102,19 +106,19 @@ bool CreateCoreLicenseResponse(const video_widevine::License& lic,
}
}
const auto& license_id = lic.id();
if (license_id.has_provider_session_token()) {
const auto& lid = lic.id();
if (lid.has_provider_session_token()) {
parsed_lic.pst =
GetOecSubstring(proto, license_id.provider_session_token());
GetOecSubstring(serialized_license, lid.provider_session_token());
}
if (lic.has_srm_requirement()) {
parsed_lic.srm_restriction_data =
GetOecSubstring(proto, lic.srm_requirement());
GetOecSubstring(serialized_license, lic.srm_requirement());
}
parsed_lic.license_type = license_id.type();
// todo: nonce_required
parsed_lic.license_type = lid.type();
// todo(robertshih): nonce_required
const auto& policy = lic.policy();
ODK_TimerLimits& timer_limits = parsed_lic.timer_limits;
timer_limits.soft_expiry = policy.soft_enforce_playback_duration();
@@ -131,31 +135,33 @@ bool CreateCoreLicenseResponse(const video_widevine::License& lic,
oemcrypto_core_message);
}
bool CreateCoreProvisioningResponse(
const video_widevine::ProvisioningResponse& prov,
bool CreateCoreProvisioningResponseFromProto(
const std::string& serialized_provisioning_resp,
const ODK_ProvisioningRequest& core_request,
string* oemcrypto_core_message) {
std::string* oemcrypto_core_message) {
ODK_ParsedProvisioning parsed_prov{};
string proto;
if (!prov.SerializeToString(&proto)) {
video_widevine::ProvisioningResponse prov;
if (!prov.ParseFromString(serialized_provisioning_resp)) {
return false;
}
parsed_prov.key_type = 0; // todo: ECC or RSA
parsed_prov.key_type = 0; // todo(robertshih): ECC or RSA
if (prov.has_device_rsa_key()) {
parsed_prov.enc_private_key = GetOecSubstring(proto, prov.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(proto, prov.device_rsa_key_iv());
GetOecSubstring(serialized_provisioning_resp, prov.device_rsa_key_iv());
}
if (prov.has_wrapping_key()) {
parsed_prov.encrypted_message_key =
GetOecSubstring(proto, prov.wrapping_key());
GetOecSubstring(serialized_provisioning_resp, prov.wrapping_key());
}
return CreateCoreProvisioningResponse(parsed_prov, core_request,
oemcrypto_core_message);
}
} // namespace oec_util
} // namespace serialize
} // namespace oemcrypto_core_message

View File

@@ -1,14 +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.
*/
/* 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.h"
#include "odk_overflow.h"
#include "odk_serialize.h"
#include "odk_structs.h"
@@ -18,6 +17,7 @@
#define ODK_LICENSE_REQUEST_SIZE 20
#define ODK_RENEWAL_REQUEST_SIZE 28
#define ODK_PROVISIONING_REQUEST_SIZE 88
#define OEC_API_VERSION 16
/* @ private odk functions */
@@ -35,7 +35,9 @@ static OEMCryptoResult ODK_PrepareRequest(uint8_t* buffer, size_t buffer_length,
AllocateMessage(&msg, message_block);
InitMessage(msg, buffer, *core_message_length);
*core_message = (ODK_CoreMessage){
message_type, 0, *nonce_values,
message_type,
0,
*nonce_values,
};
switch (message_type) {
@@ -69,13 +71,21 @@ static OEMCryptoResult ODK_PrepareRequest(uint8_t* buffer, size_t buffer_length,
static OEMCryptoResult ODK_ParseResponse(const uint8_t* buf,
size_t message_length,
size_t core_message_length,
uint32_t message_type,
const ODK_NonceValues* nonce_values,
ODK_CoreMessage* const core_message) {
if (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*)buf, message_length);
SetSize(msg, 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);
switch (message_type) {
case ODK_License_Response_Type: {
@@ -121,33 +131,44 @@ static OEMCryptoResult ODK_ParseResponse(const uint8_t* buf,
OEMCryptoResult ODK_PrepareCoreLicenseRequest(
uint8_t* message, size_t message_length, size_t* core_message_length,
const ODK_NonceValues* nonce_values) {
ODK_PreparedLicense license_request = {0};
ODK_PreparedLicense license_request = {
{0},
};
return ODK_PrepareRequest(message, message_length, core_message_length,
ODK_License_Request_Type, nonce_values,
&license_request.core_message);
}
OEMCryptoResult ODK_PrepareCoreRenewalRequest(
uint8_t* message, size_t message_length, size_t* core_message_length,
const ODK_NonceValues* nonce_values,
const ODK_ClockValues* clock_values, uint64_t system_time_seconds) {
uint8_t* message, size_t message_length, size_t* core_message_size,
const ODK_NonceValues* nonce_values, ODK_ClockValues* clock_values,
uint64_t system_time_seconds) {
ODK_RenewalMessage renewal_request = {
{0},
};
if (odk_sub_overflow_u64(system_time_seconds,
clock_values->time_of_first_decrypt,
&renewal_request.playback_time)) {
return ODK_ERROR_CORE_MESSAGE;
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;
}
}
return ODK_PrepareRequest(message, message_length, core_message_length,
/* Save time for this request so that we can verify the response. */
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.core_message);
}
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) {
const ODK_NonceValues* nonce_values, const uint8_t* device_id,
size_t device_id_length) {
ODK_ProvisioningMessage provisioning_request = {
{0},
};
@@ -165,54 +186,60 @@ OEMCryptoResult ODK_PrepareCoreProvisioningRequest(
/* @@ parse request 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 (!nonce_values || !parsed_license) {
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 || !request_hash || !timer_limits || !clock_values ||
!nonce_values || !parsed_license) {
return ODK_ERROR_CORE_MESSAGE;
}
ODK_LicenseResponse license_response = {{0}, parsed_license};
OEMCryptoResult err = ODK_ParseResponse(
message, message_length, ODK_License_Response_Type, NULL,
&license_response.core_message);
message, message_length, core_message_length, ODK_License_Response_Type,
NULL, &license_response.core_message);
if (err != OEMCrypto_SUCCESS) {
return err;
}
if (license_response.core_message.nonce_values.api_version != 16) {
/* This function should not be used for legacy licenses. */
if (license_response.core_message.nonce_values.api_version !=
OEC_API_VERSION) {
return ODK_UNSUPPORTED_API;
}
if (parsed_license->nonce_required) {
if (initial_license_load) {
if (nonce_values->nonce != license_response.core_message.nonce_values.nonce ||
nonce_values->session_id != license_response.core_message.nonce_values.session_id) {
if (nonce_values->nonce !=
license_response.core_message.nonce_values.nonce ||
nonce_values->session_id !=
license_response.core_message.nonce_values.session_id) {
return OEMCrypto_ERROR_INVALID_NONCE;
}
} else { /* !initial_license_load */
nonce_values->nonce = license_response.core_message.nonce_values.nonce;
nonce_values->session_id = license_response.core_message.nonce_values.session_id;
nonce_values->session_id =
license_response.core_message.nonce_values.session_id;
}
}
if (initial_license_load &&
memcmp(request_hash, parsed_license->request_hash, ODK_SHA256_HASH_SIZE)) {
/* 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 && memcmp(request_hash, parsed_license->request_hash,
ODK_SHA256_HASH_SIZE)) {
return ODK_ERROR_CORE_MESSAGE;
}
/* If the license has a provider session token (pst), then OEMCrypto should
* have a usage entry loaded. */
if (usage_entry_present && parsed_license->pst.length == 0) {
return ODK_ERROR_CORE_MESSAGE;
}
*timer_limits = parsed_license->timer_limits;
return err;
}
@@ -223,7 +250,7 @@ OEMCryptoResult ODK_ParseRenewal(const uint8_t* message, size_t message_length,
const ODK_TimerLimits* timer_limits,
ODK_ClockValues* clock_values,
uint64_t* timer_value) {
if (!nonce_values || !timer_limits || !clock_values || !timer_value) {
if (!message || !nonce_values || !timer_limits || !clock_values) {
return ODK_ERROR_CORE_MESSAGE;
}
@@ -231,8 +258,8 @@ OEMCryptoResult ODK_ParseRenewal(const uint8_t* message, size_t message_length,
{0},
};
OEMCryptoResult err = ODK_ParseResponse(
message, message_length, ODK_Renewal_Response_Type, nonce_values,
&renewal_response.core_message);
message, message_length, core_message_length, ODK_Renewal_Response_Type,
nonce_values, &renewal_response.core_message);
if (err) {
return err;
@@ -243,42 +270,37 @@ OEMCryptoResult ODK_ParseRenewal(const uint8_t* message, size_t message_length,
* Section: Renewal Message
*/
uint64_t playback_timer = 0;
if (odk_sub_overflow_u64(clock_values->time_when_timer_expires, system_time,
&playback_timer)) {
return ODK_TIMER_EXPIRED;
/* 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.playback_time) {
return ODK_STALE_RENEWAL;
}
uint64_t time_since_playback_began = 0;
uint64_t time_since_reset = 0;
uint64_t time_since_message_signed = 0;
/* ... or use clock_values->time_of_license_signed ? */
if (odk_sub_overflow_u64(system_time, clock_values->time_of_first_decrypt,
&time_since_playback_began) ||
odk_sub_overflow_u64(timer_limits->renewal_playback_duration_seconds,
playback_timer, &time_since_reset) ||
odk_sub_overflow_u64(time_since_playback_began,
renewal_response.playback_time,
&time_since_message_signed) ||
time_since_message_signed >= time_since_reset ||
odk_add_overflow_u64(system_time,
/* The timer value should be set to the renewal duration. */
if (timer_value) {
*timer_value = timer_limits->renewal_playback_duration_seconds;
}
if (timer_limits->renewal_playback_duration_seconds == 0) {
clock_values->time_when_timer_expires = 0;
clock_values->timer_status = ODK_DISABLE_TIMER;
return ODK_DISABLE_TIMER;
}
if (odk_add_overflow_u64(system_time,
timer_limits->renewal_playback_duration_seconds,
&clock_values->time_when_timer_expires)) {
return ODK_ERROR_CORE_MESSAGE;
}
/* todo: when to return ODK_DISABLE_TIMER */
*timer_value = timer_limits->renewal_playback_duration_seconds;
clock_values->timer_status = ODK_SET_TIMER;
return ODK_SET_TIMER;
}
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,
const uint8_t* message, size_t message_length, size_t core_message_length,
const ODK_NonceValues* nonce_values, const uint8_t* device_id,
size_t device_id_length, ODK_ParsedProvisioning* parsed_response) {
if (!nonce_values || !device_id || !parsed_response) {
if (!message || !nonce_values || !device_id || !parsed_response) {
return ODK_ERROR_CORE_MESSAGE;
}
@@ -290,9 +312,10 @@ OEMCryptoResult ODK_ParseProvisioning(
return ODK_ERROR_CORE_MESSAGE;
}
OEMCryptoResult err = ODK_ParseResponse(
message, message_length, ODK_Provisioning_Response_Type,
nonce_values, &provisioning_response.core_provisioning.core_message);
const OEMCryptoResult err =
ODK_ParseResponse(message, message_length, core_message_length,
ODK_Provisioning_Response_Type, nonce_values,
&provisioning_response.core_provisioning.core_message);
if (err) {
return err;

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

@@ -1,8 +1,6 @@
/*
* 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.
*/
/* 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>

View File

@@ -0,0 +1,31 @@
/* 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_
#ifdef __cplusplus
extern "C" {
#endif
#ifndef __has_builtin
# define __has_builtin(x) 0
#endif
#if (defined(__GNUC__) && __GNUC__ >= 5) || \
__has_builtin(__builtin_add_overflow)
# define odk_sub_overflow_u64 __builtin_sub_overflow
# define odk_add_overflow_u64 __builtin_add_overflow
# define odk_add_overflow_ux __builtin_add_overflow
#else
int odk_sub_overflow_u64(uint64_t a, uint64_t b, uint64_t* c);
int odk_add_overflow_u64(uint64_t a, uint64_t b, uint64_t* c);
int odk_add_overflow_ux(size_t a, size_t b, size_t* c);
#endif
#ifdef __cplusplus
}
#endif
#endif /* WIDEVINE_ODK_SRC_ODK_OVERFLOW_H_ */

View File

@@ -1,8 +1,6 @@
/*
* 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.
*/
/* 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
@@ -29,21 +27,20 @@ static void Pack_ODK_CoreMessage(Message* msg, ODK_CoreMessage const* obj) {
static void Pack_OEMCrypto_KeyObject(Message* msg,
OEMCrypto_KeyObject const* obj) {
Pack_OEMCrypto_Substring(msg, (const OEMCrypto_Substring*)&obj->key_id);
Pack_OEMCrypto_Substring(msg, (const OEMCrypto_Substring*)&obj->key_data_iv);
Pack_OEMCrypto_Substring(msg, (const OEMCrypto_Substring*)&obj->key_data);
Pack_OEMCrypto_Substring(msg,
(const OEMCrypto_Substring*)&obj->key_control_iv);
Pack_OEMCrypto_Substring(msg, (const OEMCrypto_Substring*)&obj->key_control);
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_uint32_t(msg, (const uint32_t*)&obj->soft_expiry);
Pack_uint64_t(msg, (const uint64_t*)&obj->earliest_playback_start_seconds);
Pack_uint64_t(msg, (const uint64_t*)&obj->latest_playback_start_seconds);
Pack_uint64_t(msg, (const uint64_t*)&obj->initial_playback_duration_seconds);
Pack_uint64_t(msg, (const uint64_t*)&obj->renewal_playback_duration_seconds);
Pack_uint64_t(msg, (const uint64_t*)&obj->license_duration_seconds);
Pack_uint32_t(msg, &obj->soft_expiry);
Pack_uint64_t(msg, &obj->earliest_playback_start_seconds);
Pack_uint64_t(msg, &obj->latest_playback_start_seconds);
Pack_uint64_t(msg, &obj->initial_playback_duration_seconds);
Pack_uint64_t(msg, &obj->renewal_playback_duration_seconds);
Pack_uint64_t(msg, &obj->license_duration_seconds);
}
static void Pack_ODK_ParsedLicense(Message* msg, ODK_ParsedLicense const* obj) {
@@ -52,62 +49,57 @@ static void Pack_ODK_ParsedLicense(Message* msg, ODK_ParsedLicense const* obj) {
SetStatus(msg, MESSAGE_STATUS_OVERFLOW_ERROR);
return;
}
Pack_OEMCrypto_Substring(msg,
(const OEMCrypto_Substring*)&obj->enc_mac_keys_iv);
Pack_OEMCrypto_Substring(msg, (const OEMCrypto_Substring*)&obj->enc_mac_keys);
Pack_OEMCrypto_Substring(msg, (const OEMCrypto_Substring*)&obj->pst);
Pack_OEMCrypto_Substring(
msg, (const OEMCrypto_Substring*)&obj->srm_restriction_data);
Pack_uint32_t(msg, (const uint32_t*)&obj->license_type);
Pack_uint32_t(msg, (const uint32_t*)&obj->nonce_required);
Pack_ODK_TimerLimits(msg, (const ODK_TimerLimits*)&obj->timer_limits);
PackArray(msg, (const uint8_t*)&obj->request_hash[0], sizeof(obj->request_hash));
Pack_uint32_t(msg, (const uint32_t*)&obj->key_array_length);
for (size_t i = 0; i < (size_t)obj->key_array_length; i++) {
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_uint32_t(msg, &obj->license_type);
Pack_uint32_t(msg, &obj->nonce_required);
Pack_ODK_TimerLimits(msg, &obj->timer_limits);
PackArray(msg, &obj->request_hash[0], sizeof(obj->request_hash));
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_uint32_t(msg, (const uint32_t*)&obj->key_type);
Pack_OEMCrypto_Substring(msg,
(const OEMCrypto_Substring*)&obj->enc_private_key);
Pack_OEMCrypto_Substring(
msg, (const OEMCrypto_Substring*)&obj->enc_private_key_iv);
Pack_OEMCrypto_Substring(
msg, (const OEMCrypto_Substring*)&obj->encrypted_message_key);
Pack_uint32_t(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_PreparedLicense(Message* msg, ODK_PreparedLicense const* obj) {
Pack_ODK_CoreMessage(msg, (const ODK_CoreMessage*)&obj->core_message);
Pack_ODK_CoreMessage(msg, &obj->core_message);
}
void Pack_ODK_RenewalMessage(Message* msg, ODK_RenewalMessage const* obj) {
Pack_ODK_CoreMessage(msg, (const ODK_CoreMessage*)&obj->core_message);
Pack_uint64_t(msg, (const uint64_t*)&obj->playback_time);
Pack_ODK_CoreMessage(msg, &obj->core_message);
Pack_uint64_t(msg, &obj->playback_time);
}
void Pack_ODK_ProvisioningMessage(Message* msg,
ODK_ProvisioningMessage const* obj) {
Pack_ODK_CoreMessage(msg, (const ODK_CoreMessage*)&obj->core_message);
Pack_uint32_t(msg, (const uint32_t*)&obj->device_id_length);
PackArray(msg, (const uint8_t*)&obj->device_id[0], sizeof(obj->device_id));
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_CoreMessage(msg, (const ODK_CoreMessage*)&obj->core_message);
Pack_ODK_CoreMessage(msg, &obj->core_message);
Pack_ODK_ParsedLicense(msg, (const ODK_ParsedLicense*)obj->parsed_license);
}
void Pack_ODK_ProvisioningResponse(Message* msg,
ODK_ProvisioningResponse const* obj) {
Pack_ODK_ProvisioningMessage(
msg, (const ODK_ProvisioningMessage*)&obj->core_provisioning);
Pack_ODK_ProvisioningMessage(msg, &obj->core_provisioning);
Pack_ODK_ParsedProvisioning(
msg, (const ODK_ParsedProvisioning*)obj->parsed_provisioning);
}
@@ -129,81 +121,77 @@ static void Unpack_ODK_CoreMessage(Message* msg, ODK_CoreMessage* obj) {
}
static void Unpack_OEMCrypto_KeyObject(Message* msg, OEMCrypto_KeyObject* obj) {
Unpack_OEMCrypto_Substring(msg, (OEMCrypto_Substring*)&obj->key_id);
Unpack_OEMCrypto_Substring(msg, (OEMCrypto_Substring*)&obj->key_data_iv);
Unpack_OEMCrypto_Substring(msg, (OEMCrypto_Substring*)&obj->key_data);
Unpack_OEMCrypto_Substring(msg, (OEMCrypto_Substring*)&obj->key_control_iv);
Unpack_OEMCrypto_Substring(msg, (OEMCrypto_Substring*)&obj->key_control);
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_uint32_t(msg, (uint32_t*)&obj->soft_expiry);
Unpack_uint64_t(msg, (uint64_t*)&obj->earliest_playback_start_seconds);
Unpack_uint64_t(msg, (uint64_t*)&obj->latest_playback_start_seconds);
Unpack_uint64_t(msg, (uint64_t*)&obj->initial_playback_duration_seconds);
Unpack_uint64_t(msg, (uint64_t*)&obj->renewal_playback_duration_seconds);
Unpack_uint64_t(msg, (uint64_t*)&obj->license_duration_seconds);
Unpack_uint32_t(msg, &obj->soft_expiry);
Unpack_uint64_t(msg, &obj->earliest_playback_start_seconds);
Unpack_uint64_t(msg, &obj->latest_playback_start_seconds);
Unpack_uint64_t(msg, &obj->initial_playback_duration_seconds);
Unpack_uint64_t(msg, &obj->renewal_playback_duration_seconds);
Unpack_uint64_t(msg, &obj->license_duration_seconds);
}
static void Unpack_ODK_ParsedLicense(Message* msg, ODK_ParsedLicense* obj) {
Unpack_OEMCrypto_Substring(msg, (OEMCrypto_Substring*)&obj->enc_mac_keys_iv);
Unpack_OEMCrypto_Substring(msg, (OEMCrypto_Substring*)&obj->enc_mac_keys);
Unpack_OEMCrypto_Substring(msg, (OEMCrypto_Substring*)&obj->pst);
Unpack_OEMCrypto_Substring(msg,
(OEMCrypto_Substring*)&obj->srm_restriction_data);
Unpack_uint32_t(msg, (uint32_t*)&obj->license_type);
Unpack_uint32_t(msg, (uint32_t*)&obj->nonce_required);
Unpack_ODK_TimerLimits(msg, (ODK_TimerLimits*)&obj->timer_limits);
UnpackArray(msg, (uint8_t*)&obj->request_hash[0], sizeof(obj->request_hash));
Unpack_uint32_t(msg, (uint32_t*)&obj->key_array_length);
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);
Unpack_uint32_t(msg, &obj->license_type);
Unpack_uint32_t(msg, &obj->nonce_required);
Unpack_ODK_TimerLimits(msg, &obj->timer_limits);
UnpackArray(msg, &obj->request_hash[0], sizeof(obj->request_hash));
Unpack_uint32_t(msg, &obj->key_array_length);
if (obj->key_array_length > ODK_MAX_NUM_KEYS) {
SetStatus(msg, MESSAGE_STATUS_OVERFLOW_ERROR);
return;
}
for (uint32_t i = 0; i < obj->key_array_length; i++) {
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) {
Unpack_uint32_t(msg, (uint32_t*)&obj->key_type);
Unpack_OEMCrypto_Substring(msg, (OEMCrypto_Substring*)&obj->enc_private_key);
Unpack_OEMCrypto_Substring(msg,
(OEMCrypto_Substring*)&obj->enc_private_key_iv);
Unpack_OEMCrypto_Substring(msg,
(OEMCrypto_Substring*)&obj->encrypted_message_key);
Unpack_uint32_t(msg, &obj->key_type);
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_PreparedLicense(Message* msg, ODK_PreparedLicense* obj) {
Unpack_ODK_CoreMessage(msg, (ODK_CoreMessage*)&obj->core_message);
Unpack_ODK_CoreMessage(msg, &obj->core_message);
}
void Unpack_ODK_ProvisioningMessage(Message* msg,
ODK_ProvisioningMessage* obj) {
Unpack_ODK_CoreMessage(msg, (ODK_CoreMessage*)&obj->core_message);
Unpack_uint32_t(msg, (uint32_t*)&obj->device_id_length);
UnpackArray(msg, (uint8_t*)&obj->device_id[0], sizeof(obj->device_id));
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_CoreMessage(msg, (ODK_CoreMessage*)&obj->core_message);
Unpack_ODK_ParsedLicense(msg, (ODK_ParsedLicense*)obj->parsed_license);
Unpack_ODK_CoreMessage(msg, &obj->core_message);
Unpack_ODK_ParsedLicense(msg, obj->parsed_license);
}
void Unpack_ODK_RenewalMessage(Message* msg, ODK_RenewalMessage* obj) {
Unpack_ODK_CoreMessage(msg, (ODK_CoreMessage*)&obj->core_message);
Unpack_uint64_t(msg, (uint64_t*)&obj->playback_time);
Unpack_ODK_CoreMessage(msg, &obj->core_message);
Unpack_uint64_t(msg, &obj->playback_time);
}
void Unpack_ODK_ProvisioningResponse(Message* msg,
ODK_ProvisioningResponse* obj) {
Unpack_ODK_ProvisioningMessage(
msg, (ODK_ProvisioningMessage*)&obj->core_provisioning);
Unpack_ODK_ParsedProvisioning(
msg, (ODK_ParsedProvisioning*)obj->parsed_provisioning);
Unpack_ODK_ProvisioningMessage(msg, &obj->core_provisioning);
Unpack_ODK_ParsedProvisioning(msg, obj->parsed_provisioning);
}

View File

@@ -0,0 +1,42 @@
/* 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_PreparedLicense(Message* msg, ODK_PreparedLicense const* obj);
void Pack_ODK_RenewalMessage(Message* msg, ODK_RenewalMessage const* obj);
void Pack_ODK_ProvisioningMessage(Message* msg,
ODK_ProvisioningMessage const* obj);
/* odk unpack */
void Unpack_ODK_LicenseResponse(Message* msg, ODK_LicenseResponse* obj);
void Unpack_ODK_RenewalMessage(Message* msg, ODK_RenewalMessage* obj);
void Unpack_ODK_ProvisioningResponse(Message* msg,
ODK_ProvisioningResponse* obj);
/* kdo pack */
void Pack_ODK_LicenseResponse(Message* msg, ODK_LicenseResponse const* obj);
void Pack_ODK_ProvisioningResponse(Message* msg,
ODK_ProvisioningResponse const* obj);
/* kdo unpack */
void Unpack_ODK_PreparedLicense(Message* msg, ODK_PreparedLicense* obj);
void Unpack_ODK_ProvisioningMessage(Message* msg, ODK_ProvisioningMessage* obj);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* WIDEVINE_ODK_SRC_ODK_SERIALIZE_H_ */

View File

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

View File

@@ -1,8 +1,6 @@
/*
* 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.
*/
/* 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>
@@ -57,11 +55,11 @@ OEMCryptoResult ODK_InitializeClockValues(ODK_ClockValues* clock_values,
}
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) {
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;
@@ -98,7 +96,8 @@ uint32_t ODK_AttemptFirstPlayback(uint64_t system_time_seconds,
* session. */
if (clock_values->status == kUnused) {
/* If the rental clock has expired, the license has expired. */
if (rental_time > timer_limits->latest_playback_start_seconds) {
if (rental_time > timer_limits->latest_playback_start_seconds &&
timer_limits->latest_playback_start_seconds > 0) {
clock_values->timer_status = ODK_TIMER_EXPIRED;
return ODK_TIMER_EXPIRED;
}
@@ -187,6 +186,13 @@ OEMCryptoResult ODK_UpdateLastPlaybackTime(uint64_t system_time_seconds,
if (clock_values->timer_status == ODK_TIMER_EXPIRED) {
return ODK_TIMER_EXPIRED;
}
/* If the clock status is already marked as inactive, then playback is
* not allowed. */
/* TODO(b/142415188): add helper function. */
if (clock_values->status > kActive) {
clock_values->timer_status = ODK_TIMER_EXPIRED;
return ODK_TIMER_EXPIRED;
}
if (clock_values->time_when_timer_expires > 0 &&
system_time_seconds > clock_values->time_when_timer_expires) {
clock_values->timer_status = ODK_TIMER_EXPIRED;
@@ -200,7 +206,7 @@ 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 {
} else if (clock_values->status == kActive) {
clock_values->status = kInactiveUsed;
}
return OEMCrypto_SUCCESS;
@@ -227,3 +233,53 @@ OEMCryptoResult ODK_InitializeV15Values(ODK_TimerLimits* timer_limits,
}
return OEMCrypto_SUCCESS;
}
OEMCryptoResult ODK_RefreshV15Values(const ODK_TimerLimits* timer_limits,
ODK_ClockValues* clock_values,
const ODK_NonceValues* nonce_values,
uint64_t system_time_seconds,
uint64_t* timer_value) {
if (timer_limits == NULL || clock_values == NULL || nonce_values == NULL)
return OEMCrypto_ERROR_INVALID_CONTEXT;
if (nonce_values->api_version != 15) return OEMCrypto_ERROR_INVALID_NONCE;
if (clock_values->status > kActive) {
clock_values->timer_status = ODK_TIMER_EXPIRED;
return ODK_TIMER_EXPIRED;
}
/* 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;
/* All times are relative to when the license was signed. */
const uint64_t rental_time =
system_time_seconds - clock_values->time_of_license_signed;
/* The timer should be limited by the renewal playback duration. This is
* similar to code in AttemptFirstPlayback, above, except we use the
* renewal_playback_duration here, and we do not change clock_values->status.
*/
uint64_t time_left = timer_limits->renewal_playback_duration_seconds;
/* If there is a license duration, it also limits the timer. Remember, a
* limit of 0 means no limit, or infinite. */
if (timer_limits->license_duration_seconds > 0) {
if (timer_limits->license_duration_seconds < rental_time) {
clock_values->timer_status = ODK_TIMER_EXPIRED;
return ODK_TIMER_EXPIRED;
}
if (timer_limits->license_duration_seconds - rental_time < time_left ||
time_left == 0) {
time_left = timer_limits->license_duration_seconds - rental_time;
}
}
if (time_left == 0 || timer_limits->soft_expiry) { /* Unlimited. */
clock_values->time_when_timer_expires = 0;
clock_values->timer_status = ODK_DISABLE_TIMER;
return ODK_DISABLE_TIMER;
}
/* Set timer to limit playback. */
if (timer_value) *timer_value = time_left;
clock_values->time_when_timer_expires = system_time_seconds + time_left;
clock_values->timer_status = ODK_SET_TIMER;
return ODK_SET_TIMER;
}

View File

@@ -1,8 +1,6 @@
/*
* 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.
*/
/* 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"
@@ -117,8 +115,16 @@ void Unpack_OEMCrypto_Substring(Message* msg, OEMCrypto_Substring* obj) {
Unpack_uint32_t(msg, &offset);
Unpack_uint32_t(msg, &length);
if (!ValidMessage(msg)) return;
size_t end = 0;
if (offset > msg->capacity || odk_add_overflow_ux(offset, length, &end) ||
/* 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;
@@ -188,6 +194,7 @@ size_t GetSize(Message* message) {
void SetSize(Message* message, size_t size) {
if (message == NULL) return;
if (size > message->capacity) message->status = MESSAGE_STATUS_OVERFLOW_ERROR;
message->size = size;
}

View File

@@ -0,0 +1,90 @@
/* Copyright 2019 Google LLC. All rights reserved. This file and proprietary */
/* source code may only be used and distributed under the Widevine Master */
/* License Agreement. */
#ifndef 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;
bool ValidMessage(Message* message);
void Pack_uint32_t(Message* message, const uint32_t* value);
void Pack_uint64_t(Message* message, const uint64_t* value);
void PackArray(Message* message, const uint8_t* base, size_t size);
void Pack_OEMCrypto_Substring(Message* msg, const OEMCrypto_Substring* obj);
void Unpack_uint32_t(Message* message, uint32_t* value);
void Unpack_uint64_t(Message* message, uint64_t* value);
void UnpackArray(Message* message, uint8_t* address,
size_t size); /* copy out */
void Unpack_OEMCrypto_Substring(Message* msg, OEMCrypto_Substring* obj);
typedef enum {
MESSAGE_STATUS_OK,
MESSAGE_STATUS_UNKNOWN_ERROR,
MESSAGE_STATUS_OVERFLOW_ERROR,
MESSAGE_STATUS_UNDERFLOW_ERROR,
MESSAGE_STATUS_PARSE_ERROR,
MESSAGE_STATUS_NULL_POINTER_ERROR,
MESSAGE_STATUS_API_VALUE_ERROR
} MessageStatus;
/*
* Create a message from a buffer. The message structure consumes the first
* sizeof(Message) bytes of the buffer. The caller is responsible for ensuring
* that the buffer remains allocated for the lifetime of the message.
*/
Message* CreateMessage(uint8_t* buffer, size_t buffer_size);
/*
* Initialize a message structure to reference a separate buffer. The caller
* is responsible for ensuring that the buffer remains allocated for the
* lifetime of the message.
*/
void InitMessage(Message* message, uint8_t* buffer, size_t capacity);
/*
* Reset an existing the message to an empty state
*/
void ResetMessage(Message* message);
uint8_t* GetBase(Message* message);
size_t GetCapacity(Message* message);
size_t GetSize(Message* message);
void SetSize(Message* message, size_t size);
MessageStatus GetStatus(Message* message);
void SetStatus(Message* message, MessageStatus status);
size_t GetOffset(Message* message);
size_t SizeOfMessageStruct();
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* WIDEVINE_ODK_SRC_SERIALIZATION_BASE_H_ */

View File

@@ -1,5 +1,4 @@
/*
* Copyright 2019 Google LLC. All Rights Reserved. This file and proprietary
/* 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.
*/
@@ -13,12 +12,16 @@
#include <string>
#include "OEMCryptoCENCCommon.h"
#include "core_message_deserialize.h"
#include "core_message_serialize.h"
#include "core_message_types.h"
#include "odk.h"
#include "odk_serialize.h"
#include "oec_util.h"
#include "odk_structs.h"
#include "odk_structs_priv.h"
using namespace std;
using namespace oec_util;
// TODO(b/147297226): remove this: using namespace std;
// TODO(b/147297226): remove this: using namespace oec_util;
typedef std::function<size_t(const uint8_t*, uint8_t*, size_t)> roundtrip_fun;
@@ -43,7 +46,7 @@ static OEMCryptoResult odk_fun_RenewalRequest(
static OEMCryptoResult odk_fun_ProvisioningRequest(
uint8_t* out, size_t* size, uint32_t api_version, uint32_t nonce,
uint32_t session_id, const ODK_ProvisioningRequest& core_provisioning) {
const string& device_id = core_provisioning.device_id;
const std::string& device_id = core_provisioning.device_id;
return ODK_PrepareCoreProvisioningRequest(
out, SIZE_MAX, size, api_version, nonce, session_id,
reinterpret_cast<const uint8_t*>(device_id.data()), device_id.size());
@@ -52,7 +55,7 @@ static OEMCryptoResult odk_fun_ProvisioningRequest(
template <typename T, typename F, typename G>
static roundtrip_fun kdo_odk(const F& kdo_fun, const G& odk_fun) {
auto roundtrip = [&](const uint8_t* in, uint8_t* out, size_t size) -> size_t {
string input(reinterpret_cast<const char*>(in), size);
std::string input(reinterpret_cast<const char*>(in), size);
T t = {};
if (!kdo_fun(input, &t)) {
return 0;
@@ -93,14 +96,15 @@ static OEMCryptoResult odk_fun_LicenseResponse(
const uint8_t* message, size_t message_length, uint32_t api_version,
uint32_t nonce, uint32_t session_id, const ODK_ParseLicense_Args* a,
ODK_ParsedLicense& parsed_lic) {
return ODK_ParseLicense(message, message_length, api_version, nonce,
session_id, bool(a->initial_license_load),
bool(a->usage_entry_present), &parsed_lic);
return ODK_ParseLicense(
message, message_length, api_version, nonce, session_id,
static_cast<bool>(a->initial_license_load),
static_cast<bool>(a->usage_entry_present), &parsed_lic);
}
static bool kdo_fun_LicenseResponse(const ODK_ParseLicense_Args* args,
const ODK_ParsedLicense& parsed_lic,
string* oemcrypto_core_message) {
std::string* oemcrypto_core_message) {
const auto& common = args->common;
ODK_LicenseRequest core_request{common.api_version, common.nonce,
common.session_id};
@@ -129,7 +133,7 @@ static OEMCryptoResult odk_fun_RenewalResponse(
static bool kdo_fun_RenewalResponse(const ODK_ParseRenewal_Args* args,
const ODK_RenewalMessage& renewal_msg,
string* oemcrypto_core_message) {
std::string* oemcrypto_core_message) {
const auto& common = args->common;
ODK_RenewalRequest core_request{common.api_version, common.nonce,
common.session_id, renewal_msg.playback_time};
@@ -146,13 +150,14 @@ static OEMCryptoResult odk_fun_ProvisioningResponse(
static bool kdo_fun_ProvisioningResponse(
const ODK_ParseProvisioning_Args* args,
const ODK_ParsedProvisioning& parsed_prov, string* oemcrypto_core_message) {
const ODK_ParsedProvisioning& parsed_prov,
std::string* oemcrypto_core_message) {
const auto& common = args->common;
assert(args->device_id_length <= sizeof(args->device_id));
ODK_ProvisioningRequest core_request{
common.api_version, common.nonce, common.session_id,
string(reinterpret_cast<const char*>(args->device_id),
args->device_id_length)};
std::string(reinterpret_cast<const char*>(args->device_id),
args->device_id_length)};
return CreateCoreProvisioningResponse(parsed_prov, core_request,
oemcrypto_core_message);
}
@@ -177,7 +182,7 @@ static roundtrip_fun odk_kdo(const F& odk_fun, const G& kdo_fun) {
return 0;
}
string oemcrypto_core_message;
std::string oemcrypto_core_message;
if (!kdo_fun(args, t, &oemcrypto_core_message)) {
return 0;
}

View File

@@ -1,31 +1,61 @@
/*
* 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.
*/
// 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 <algorithm>
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <fstream>
#include <functional>
#include <iostream>
#include <string>
#include <vector>
#include <endian.h>
#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.h"
#include "odk_structs_priv.h"
#include <gtest/gtest.h>
namespace {
#include "odk.h"
#include "odk_test.h"
#include "oec_util.h"
using oemcrypto_core_message::ODK_LicenseRequest;
using oemcrypto_core_message::ODK_ProvisioningRequest;
using oemcrypto_core_message::ODK_RenewalRequest;
using namespace oec_util;
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;
enum ODK_FieldType {
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;
};
size_t ODK_FieldLength(ODK_FieldType type) {
switch (type) {
@@ -130,6 +160,54 @@ OEMCryptoResult ODK_ReadSingleField(const uint8_t* const buf,
return OEMCrypto_SUCCESS;
}
OEMCryptoResult ODK_DumpSingleField(const uint8_t* const buf,
const ODK_Field* const field) {
if (!field || !field->value) {
return ODK_ERROR_CORE_MESSAGE;
}
switch (field->type) {
case ODK_UINT32: {
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) << buf[i]
<< "\n";
}
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
@@ -137,7 +215,7 @@ OEMCryptoResult ODK_ReadSingleField(const uint8_t* const buf,
*/
OEMCryptoResult ODK_IterFields(ODK_FieldMode mode, uint8_t* const buf,
const size_t size_in, size_t* size_out,
std::vector<ODK_Field>& fields) {
const std::vector<ODK_Field>& fields) {
if (!buf || !size_out) {
return ODK_ERROR_CORE_MESSAGE;
}
@@ -156,6 +234,8 @@ OEMCryptoResult ODK_IterFields(ODK_FieldMode mode, uint8_t* const buf,
ODK_WriteSingleField(buf_off, &fields[i]);
} else if (mode == ODK_READ) {
ODK_ReadSingleField(buf_off, &fields[i]);
} else if (mode == ODK_DUMP) {
ODK_DumpSingleField(buf_off, &fields[i]);
} else {
return ODK_ERROR_CORE_MESSAGE;
}
@@ -168,31 +248,28 @@ OEMCryptoResult ODK_IterFields(ODK_FieldMode mode, uint8_t* const buf,
return OEMCrypto_SUCCESS;
}
OEMCryptoResult ODK_ReadFields(const uint8_t* const buf, const size_t size_in,
size_t* size_out,
std::vector<ODK_Field>& fields) {
return ODK_IterFields(ODK_READ, const_cast<uint8_t*>(buf), size_in, size_out,
fields);
}
OEMCryptoResult ODK_WriteFields(uint8_t* const buf, const size_t size_in,
size_t* size_out,
std::vector<ODK_Field>& fields) {
return ODK_IterFields(ODK_WRITE, buf, size_in, size_out, fields);
}
void expect_eq_buf(const void* s1, const void* s2, size_t n) {
void expect_eq_buf(const void* s1, const void* s2, size_t n,
const std::vector<ODK_Field>& fields) {
if (memcmp(s1, s2, n)) {
const void* buffers[] = {s1, s2};
for (int i = 0; i < 2; i++) {
char _tmp[] = "/tmp/fileXXXXXX";
mkstemp(_tmp);
const int temp_fd = mkstemp(_tmp);
if (temp_fd >= 0) {
close(temp_fd);
} else {
std::cerr << "Failed to open temp file." << std::endl;
break;
}
std::string tmp(_tmp);
std::fstream out(tmp, std::ios::out | std::ios::binary);
out.write((char*)buffers[i], n);
out.write(static_cast<const char*>(buffers[i]), n);
out.close();
std:
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();
}
@@ -200,7 +277,7 @@ void expect_eq_buf(const void* s1, const void* s2, size_t n) {
template <typename T, typename F, typename G>
void ValidateRequest(uint32_t message_type,
std::vector<ODK_Field>& extra_fields,
const std::vector<ODK_Field>& extra_fields,
const F& odk_prepare_func, const G& kdo_parse_func) {
uint32_t message_size = 0;
uint32_t api_version = 16;
@@ -208,9 +285,11 @@ void ValidateRequest(uint32_t message_type,
uint32_t session_id = 0xcafebabe;
ODK_NonceValues nonce_values{api_version, nonce, session_id};
std::vector<ODK_Field> total_fields = {
{ODK_UINT32, &message_type}, {ODK_UINT32, &message_size},
{ODK_UINT32, &api_version}, {ODK_UINT32, &nonce},
{ODK_UINT32, &session_id},
{ODK_UINT32, &message_type, "message_type"},
{ODK_UINT32, &message_size, "message_size"},
{ODK_UINT32, &api_version, "api_version"},
{ODK_UINT32, &nonce, "nonce"},
{ODK_UINT32, &session_id, "session_id"},
};
total_fields.insert(total_fields.end(), extra_fields.begin(),
@@ -231,7 +310,7 @@ void ValidateRequest(uint32_t message_type,
&bytes_written, total_fields));
EXPECT_EQ(bytes_written, message_size);
expect_eq_buf(buf, buf2, message_size);
EXPECT_NO_FATAL_FAILURE(expect_eq_buf(buf, buf2, message_size, total_fields));
// odk kdo roundtrip
T t = {};
@@ -244,7 +323,7 @@ void ValidateRequest(uint32_t message_type,
EXPECT_EQ(OEMCrypto_SUCCESS,
odk_prepare_func(buf2, &bytes_written, &nonce_values));
EXPECT_EQ(bytes_written, message_size);
expect_eq_buf(buf, buf2, message_size);
EXPECT_NO_FATAL_FAILURE(expect_eq_buf(buf, buf2, message_size, total_fields));
delete[] buf;
delete[] buf2;
@@ -258,16 +337,18 @@ void ValidateRequest(uint32_t message_type,
*/
template <typename T, typename F, typename G>
void ValidateResponse(uint32_t message_type,
std::vector<ODK_Field>& extra_fields,
const std::vector<ODK_Field>& extra_fields,
const F& odk_parse_func, const G& kdo_prepare_func) {
uint32_t message_size = 0;
uint32_t api_version = 16;
uint32_t nonce = 0xdeadbeef;
uint32_t session_id = 0xcafebabe;
std::vector<ODK_Field> total_fields = {
{ODK_UINT32, &message_type}, {ODK_UINT32, &message_size},
{ODK_UINT32, &api_version}, {ODK_UINT32, &nonce},
{ODK_UINT32, &session_id},
{ODK_UINT32, &message_type, "message_type"},
{ODK_UINT32, &message_size, "message_size"},
{ODK_UINT32, &api_version, "api_version"},
{ODK_UINT32, &nonce, "nonce"},
{ODK_UINT32, &session_id, "session_id"},
};
uint32_t header_size = 0;
@@ -311,7 +392,8 @@ void ValidateResponse(uint32_t message_type,
EXPECT_TRUE(kdo_prepare_func(t, &oemcrypto_core_message));
EXPECT_EQ(bytes_written, message_size);
expect_eq_buf(buf, oemcrypto_core_message.data(), message_size);
EXPECT_NO_FATAL_FAILURE(expect_eq_buf(buf, oemcrypto_core_message.data(),
message_size, total_fields));
delete[] buf;
delete[] zero;
}
@@ -321,9 +403,10 @@ TEST(OdkTest, SerializeFields) {
uint64_t y[] = {3ll << 32, 4ll << 32, 5ll << 32};
OEMCrypto_Substring s = {.offset = 6, .length = 7};
std::vector<ODK_Field> fields = {
{ODK_UINT32, &x[0]}, {ODK_UINT32, &x[1]}, {ODK_UINT32, &x[2]},
{ODK_UINT64, &y[0]}, {ODK_UINT64, &y[1]}, {ODK_UINT64, &y[2]},
{ODK_SUBSTRING, &s},
{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};
@@ -332,7 +415,7 @@ TEST(OdkTest, SerializeFields) {
ODK_IterFields(ODK_READ, buf, bytes_read, &bytes_written, fields);
ODK_IterFields(ODK_WRITE, buf2, SIZE_MAX, &bytes_read, fields);
expect_eq_buf(buf, buf2, bytes_read);
EXPECT_NO_FATAL_FAILURE(expect_eq_buf(buf, buf2, bytes_read, fields));
}
TEST(OdkTest, SerializeFieldsStress) {
@@ -343,8 +426,8 @@ TEST(OdkTest, SerializeFieldsStress) {
for (int i = 0; i < n; i++) {
fields[i].type = static_cast<ODK_FieldType>(std::rand() %
static_cast<int>(ODK_NUMTYPES));
size_t field_size = ODK_AllocSize(fields[i].type);
fields[i].value = malloc(ODK_AllocSize(fields[i].type));
fields[i].name = "stress";
total_size += ODK_FieldLength(fields[i].type);
}
@@ -360,7 +443,7 @@ TEST(OdkTest, SerializeFieldsStress) {
ODK_IterFields(ODK_WRITE, buf2, total_size, &bytes_written, fields);
EXPECT_EQ(bytes_written, total_size);
expect_eq_buf(buf, buf2, total_size);
EXPECT_NO_FATAL_FAILURE(expect_eq_buf(buf, buf2, total_size, fields));
// cleanup
for (int i = 0; i < n; i++) {
@@ -376,17 +459,20 @@ TEST(OdkTest, LicenseRequest) {
ODK_NonceValues* nonce_values) {
return ODK_PrepareCoreLicenseRequest(buf, SIZE_MAX, size, nonce_values);
};
auto kdo_parse_func = ParseLicenseRequest;
auto kdo_parse_func = CoreLicenseRequestFromMessage;
ValidateRequest<ODK_LicenseRequest>(ODK_License_Request_Type, empty,
odk_prepare_func, kdo_parse_func);
}
TEST(OdkTest, RenewalRequest) {
uint64_t system_time_seconds = 0xBADDCAFE000FF1CE;
const uint64_t system_time_seconds = 0xBADDCAFE000FF1CE;
uint64_t playback_time = 0xCAFE00000000;
const uint64_t playback_start = system_time_seconds - playback_time;
std::vector<ODK_Field> extra_fields = {
{ODK_UINT64, &system_time_seconds},
{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,
const ODK_NonceValues* nonce_values) {
return ODK_PrepareCoreRenewalRequest(buf, SIZE_MAX, size, nonce_values,
@@ -394,9 +480,10 @@ TEST(OdkTest, RenewalRequest) {
};
auto kdo_parse_func = [&](const std::string& oemcrypto_core_message,
ODK_RenewalRequest* core_renewal_request) {
bool ok = ParseRenewalRequest(oemcrypto_core_message, core_renewal_request);
bool ok = CoreRenewalRequestFromMessage(oemcrypto_core_message,
core_renewal_request);
if (ok) {
system_time_seconds = core_renewal_request->playback_time;
playback_time = core_renewal_request->playback_time_seconds;
}
return ok;
};
@@ -405,12 +492,12 @@ TEST(OdkTest, RenewalRequest) {
}
TEST(OdkTest, ProvisionRequest) {
uint32_t device_id_length = DEVICE_ID_MAX / 2;
uint8_t device_id[DEVICE_ID_MAX] = {0};
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},
{ODK_DEVICEID, device_id},
{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) {
@@ -420,8 +507,8 @@ TEST(OdkTest, ProvisionRequest) {
auto kdo_parse_func =
[&](const std::string& oemcrypto_core_message,
ODK_ProvisioningRequest* core_provisioning_request) {
bool ok = ParseProvisioningRequest(oemcrypto_core_message,
core_provisioning_request);
bool ok = CoreProvisioningRequestFromMessage(oemcrypto_core_message,
core_provisioning_request);
if (ok) {
const std::string& device_id_str =
core_provisioning_request->device_id;
@@ -482,48 +569,58 @@ TEST(OdkTest, LicenseResponse) {
},
};
uint32_t message_type = ODK_License_Response_Type;
std::vector<ODK_Field> extra_fields = {
{ODK_SUBSTRING, &parsed_license.enc_mac_keys_iv},
{ODK_SUBSTRING, &parsed_license.enc_mac_keys},
{ODK_SUBSTRING, &parsed_license.pst},
{ODK_SUBSTRING, &parsed_license.srm_restriction_data},
{ODK_UINT32, &parsed_license.license_type},
{ODK_UINT32, &parsed_license.nonce_required},
{ODK_UINT32, &parsed_license.timer_limits.soft_expiry},
{ODK_SUBSTRING, &parsed_license.enc_mac_keys_iv, ".enc_mac_keys_iv"},
{ODK_SUBSTRING, &parsed_license.enc_mac_keys, ".enc_mac_keys"},
{ODK_SUBSTRING, &parsed_license.pst, ".pst"},
{ODK_SUBSTRING, &parsed_license.srm_restriction_data,
".srm_restriction_data"},
{ODK_UINT32, &parsed_license.license_type, ".license_type"},
{ODK_UINT32, &parsed_license.nonce_required, ".nonce_required"},
{ODK_UINT32, &parsed_license.timer_limits.soft_expiry, ".soft_expiry"},
{ODK_UINT64, &parsed_license.timer_limits.earliest_playback_start_seconds,
".earliest_playback_start_seconds"},
{ODK_UINT64, &parsed_license.timer_limits.latest_playback_start_seconds,
".latest_playback_start_seconds"},
{ODK_UINT64,
&parsed_license.timer_limits.earliest_playback_start_seconds},
{ODK_UINT64, &parsed_license.timer_limits.latest_playback_start_seconds},
&parsed_license.timer_limits.initial_playback_duration_seconds,
".initial_playback_duration_seconds"},
{ODK_UINT64,
&parsed_license.timer_limits.initial_playback_duration_seconds},
{ODK_UINT64,
&parsed_license.timer_limits.renewal_playback_duration_seconds},
{ODK_UINT64, &parsed_license.timer_limits.license_duration_seconds},
{ODK_HASH, &parsed_license.request_hash},
{ODK_UINT32, &parsed_license.key_array_length},
{ODK_SUBSTRING, &parsed_license.key_array[0].key_id},
{ODK_SUBSTRING, &parsed_license.key_array[0].key_data_iv},
{ODK_SUBSTRING, &parsed_license.key_array[0].key_data},
{ODK_SUBSTRING, &parsed_license.key_array[0].key_control_iv},
{ODK_SUBSTRING, &parsed_license.key_array[0].key_control},
{ODK_SUBSTRING, &parsed_license.key_array[1].key_id},
{ODK_SUBSTRING, &parsed_license.key_array[1].key_data_iv},
{ODK_SUBSTRING, &parsed_license.key_array[1].key_data},
{ODK_SUBSTRING, &parsed_license.key_array[1].key_control_iv},
{ODK_SUBSTRING, &parsed_license.key_array[1].key_control},
{ODK_SUBSTRING, &parsed_license.key_array[2].key_id},
{ODK_SUBSTRING, &parsed_license.key_array[2].key_data_iv},
{ODK_SUBSTRING, &parsed_license.key_array[2].key_data},
{ODK_SUBSTRING, &parsed_license.key_array[2].key_control_iv},
{ODK_SUBSTRING, &parsed_license.key_array[2].key_control},
&parsed_license.timer_limits.renewal_playback_duration_seconds,
".renewal_playback_duration_seconds"},
{ODK_UINT64, &parsed_license.timer_limits.license_duration_seconds,
".license_duration_seconds"},
{ODK_HASH, &parsed_license.request_hash, ".request_hash"},
{ODK_UINT32, &parsed_license.key_array_length, ".key_array_length"},
{ODK_SUBSTRING, &parsed_license.key_array[0].key_id, ".key_id"},
{ODK_SUBSTRING, &parsed_license.key_array[0].key_data_iv, ".key_data_iv"},
{ODK_SUBSTRING, &parsed_license.key_array[0].key_data, ".key_data"},
{ODK_SUBSTRING, &parsed_license.key_array[0].key_control_iv,
".key_control_iv"},
{ODK_SUBSTRING, &parsed_license.key_array[0].key_control, ".key_control"},
{ODK_SUBSTRING, &parsed_license.key_array[1].key_id, ".key_id"},
{ODK_SUBSTRING, &parsed_license.key_array[1].key_data_iv, ".key_data_iv"},
{ODK_SUBSTRING, &parsed_license.key_array[1].key_data, ".key_data"},
{ODK_SUBSTRING, &parsed_license.key_array[1].key_control_iv,
".key_control_iv"},
{ODK_SUBSTRING, &parsed_license.key_array[1].key_control, ".key_control"},
{ODK_SUBSTRING, &parsed_license.key_array[2].key_id, ".key_id"},
{ODK_SUBSTRING, &parsed_license.key_array[2].key_data_iv, ".key_data_iv"},
{ODK_SUBSTRING, &parsed_license.key_array[2].key_data, ".key_data"},
{ODK_SUBSTRING, &parsed_license.key_array[2].key_control_iv,
".key_control_iv"},
{ODK_SUBSTRING, &parsed_license.key_array[2].key_control, ".key_control"},
};
uint8_t request_hash[ODK_SHA256_HASH_SIZE] = {};
memcpy(request_hash, parsed_license.request_hash, ODK_SHA256_HASH_SIZE);
auto odk_parse_func = [&](const uint8_t* buf, size_t size,
ODK_NonceValues* nonce_values) {
return ODK_ParseLicense(buf, size + 128, size, 1, 0, request_hash, nullptr,
nullptr, nonce_values, &parsed_license);
ODK_TimerLimits timer_limits;
ODK_ClockValues clock_values;
return ODK_ParseLicense(buf, size + 128, size, true, false, request_hash,
&timer_limits, &clock_values, nonce_values,
&parsed_license);
};
auto kdo_prepare_func = [&](const ODK_LicenseRequest& core_request,
std::string* oemcrypto_core_message) {
@@ -540,7 +637,7 @@ TEST(OdkTest, RenewalResponse) {
uint64_t playback_timer = 12;
uint64_t message_playback_clock = 10;
std::vector<ODK_Field> extra_fields = {
{ODK_UINT64, &message_playback_clock},
{ODK_UINT64, &message_playback_clock, "message_playback_clock"},
};
ODK_TimerLimits timer_limits = {
@@ -556,6 +653,7 @@ TEST(OdkTest, RenewalResponse) {
.time_of_license_signed = 0,
.time_of_first_decrypt = system_time - playback_clock,
.time_of_last_decrypt = 0,
.time_of_renewal_request = message_playback_clock,
.time_when_timer_expires = system_time + playback_timer,
.timer_status = 0,
.status = kUnused,
@@ -579,7 +677,7 @@ TEST(OdkTest, RenewalResponse) {
};
auto kdo_prepare_func = [&](ODK_RenewalRequest& core_request,
std::string* oemcrypto_core_message) {
core_request.playback_time = message_playback_clock;
core_request.playback_time_seconds = message_playback_clock;
return CreateCoreRenewalResponse(core_request, oemcrypto_core_message);
};
ValidateResponse<ODK_RenewalRequest>(ODK_Renewal_Response_Type, extra_fields,
@@ -587,8 +685,8 @@ TEST(OdkTest, RenewalResponse) {
}
TEST(OdkTest, ProvisionResponse) {
uint32_t device_id_length = DEVICE_ID_MAX / 2;
uint8_t device_id[DEVICE_ID_MAX] = {0};
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);
ODK_ParsedProvisioning parsed_response = {
@@ -598,18 +696,20 @@ TEST(OdkTest, ProvisionResponse) {
};
std::vector<ODK_Field> extra_fields = {
{ODK_UINT32, &device_id_length},
{ODK_DEVICEID, device_id},
{ODK_UINT32, &parsed_response.key_type},
{ODK_SUBSTRING, &parsed_response.enc_private_key},
{ODK_SUBSTRING, &parsed_response.enc_private_key_iv},
{ODK_SUBSTRING, &parsed_response.encrypted_message_key},
{ODK_UINT32, &device_id_length, "device_id_length"},
{ODK_DEVICEID, device_id, "device_id"},
{ODK_UINT32, &parsed_response.key_type, "key_type"},
{ODK_SUBSTRING, &parsed_response.enc_private_key, "enc_private_key"},
{ODK_SUBSTRING, &parsed_response.enc_private_key_iv,
"enc_private_key_iv"},
{ODK_SUBSTRING, &parsed_response.encrypted_message_key,
"encrypted_message_key"},
};
auto odk_parse_func = [&](const uint8_t* buf, size_t size,
ODK_NonceValues* nonce_values) {
// restore device id because it is not part of parsed_response
device_id_length = DEVICE_ID_MAX / 2;
device_id_length = ODK_DEVICE_ID_LEN_MAX / 2;
memset(device_id, 0xff, device_id_length);
OEMCryptoResult err =
ODK_ParseProvisioning(buf, size + 16, size, nonce_values, device_id,
@@ -671,7 +771,6 @@ TEST(OdkSizeTest, ProvisioningRequest) {
uint32_t api_version = 0;
uint32_t nonce = 0;
uint32_t session_id = 0;
uint8_t* device_id = nullptr;
uint32_t device_id_length = 0;
ODK_NonceValues nonce_values{api_version, nonce, session_id};
EXPECT_EQ(OEMCrypto_ERROR_SHORT_BUFFER,
@@ -682,3 +781,5 @@ TEST(OdkSizeTest, ProvisioningRequest) {
size_t minimum_message_size = 5 * 4;
EXPECT_GE(core_message_length, minimum_message_size);
}
} // namespace

View File

@@ -1,22 +1,18 @@
/*
* Copyright 2019 Google LLC. All Rights Reserved. This file and proprietary
/* Copyright 2019 Google LLC. All rights reserved. This file and proprietary
* source code may only be used and distributed under the Widevine Master
* License Agreement.
*/
#include <gtest/gtest.h>
#include "OEMCryptoCENCCommon.h"
#include "gtest/gtest.h"
#include "odk.h"
using ::testing::tuple;
using ::testing::Values;
using ::testing::WithParamInterface;
namespace {
constexpr uint64_t kTolerance = 1; // Allow 1 second of roundoff.
} // namespace
namespace odk_test {
struct ServerExpiry {
bool soft_rental;
bool soft_playback;
@@ -391,8 +387,6 @@ TEST_F(OdkTimerSoftTest, EarlyTest) {
// Starting playback within the window should work.
const uint64_t start_time =
system_time(timer_limits_.earliest_playback_start_seconds);
const uint64_t cutoff_time =
system_time(timer_limits_.license_duration_seconds);
// For a soft_expiry = true, we should not set a timer.
EXPECT_EQ(ODK_DISABLE_TIMER,
ODK_AttemptFirstPlayback(start_time, &timer_limits_, &clock_values_,
@@ -863,7 +857,6 @@ TEST_P(Odk7DayTest, StartDay6ReloadDay7) {
uint64_t timer_value = 0;
// Starting playback within the window should work.
const uint64_t six_days = 600u;
const uint64_t seven_days = 700u;
const uint64_t start_time = system_time(rental_window_start_ + six_days);
EXPECT_NE(ODK_TIMER_EXPIRED,
ODK_AttemptFirstPlayback(start_time, &timer_limits_, &clock_values_,
@@ -924,13 +917,4 @@ INSTANTIATE_TEST_CASE_P(OdkSoftHard, Odk7DayTest,
ServerExpiry({false, true}),
ServerExpiry({false, false})));
// ************************************************************************
// ************************************************************************
// TODO(b/140765031): Cover all tests in Use Cases document.
// Limited Duration License
// 7 day with renewal.
// Streaming with renewal
// Persistent with renewal
} // namespace odk_test
} // namespace