Updates for 16.1 unit tests and code
This patch has the full v16.1 reference code and unit tests.
This commit is contained in:
@@ -499,6 +499,7 @@ typedef enum OEMCrypto_PrivateKeyType {
|
||||
#define OEMCrypto_GetOEMPublicCertificate _oecc104
|
||||
#define OEMCrypto_DecryptCENC _oecc105
|
||||
#define OEMCrypto_LoadDRMPrivateKey _oecc107
|
||||
#define OEMCrypto_MinorAPIVersion _oecc108
|
||||
// clang-format on
|
||||
|
||||
/*
|
||||
|
||||
@@ -66,16 +66,16 @@ const uint32_t kControlCGMSCopyNever = (0x03);
|
||||
// clang-format on
|
||||
|
||||
// Various constants and sizes:
|
||||
static const size_t KEY_CONTROL_SIZE = 16;
|
||||
static const size_t KEY_ID_SIZE = 16;
|
||||
static const size_t KEY_IV_SIZE = 16;
|
||||
static const size_t KEY_PAD_SIZE = 16;
|
||||
static const size_t KEY_SIZE = 16;
|
||||
static const size_t AES_128_BLOCK_SIZE = 16;
|
||||
static const size_t MAC_KEY_SIZE = 32;
|
||||
static const size_t KEYBOX_KEY_DATA_SIZE = 72;
|
||||
static const size_t SRM_REQUIREMENT_SIZE = 12;
|
||||
static const size_t HMAC_SHA256_SIGNATURE_SIZE = 32;
|
||||
constexpr size_t KEY_CONTROL_SIZE = 16;
|
||||
constexpr size_t KEY_ID_SIZE = 16;
|
||||
constexpr size_t KEY_IV_SIZE = 16;
|
||||
constexpr size_t KEY_PAD_SIZE = 16;
|
||||
constexpr size_t KEY_SIZE = 16;
|
||||
constexpr size_t AES_128_BLOCK_SIZE = 16;
|
||||
constexpr size_t MAC_KEY_SIZE = 32;
|
||||
constexpr size_t KEYBOX_KEY_DATA_SIZE = 72;
|
||||
constexpr size_t SRM_REQUIREMENT_SIZE = 12;
|
||||
constexpr size_t HMAC_SHA256_SIGNATURE_SIZE = 32;
|
||||
|
||||
} // namespace wvoec
|
||||
|
||||
|
||||
9
oemcrypto/odk/README
Normal file
9
oemcrypto/odk/README
Normal 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.
|
||||
@@ -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 VIDEO_WIDEVINE_EXPORT_COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_INCLUDE_OEMCRYPTOCENCCOMMON_H_
|
||||
#define VIDEO_WIDEVINE_EXPORT_COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_INCLUDE_OEMCRYPTOCENCCOMMON_H_
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
@@ -20,19 +20,17 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef uint32_t OEMCrypto_SESSION;
|
||||
|
||||
// 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,
|
||||
@@ -64,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,
|
||||
@@ -74,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,
|
||||
@@ -93,7 +92,7 @@ typedef enum OEMCryptoResult {
|
||||
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.
|
||||
@@ -102,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;
|
||||
@@ -153,4 +152,4 @@ typedef struct {
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // OEMCRYPTO_CENC_COMMON_H_
|
||||
#endif /* ...ODK_INCLUDE_OEMCRYPTOCENCCOMMON_H_ */
|
||||
|
||||
59
oemcrypto/odk/include/core_message_deserialize.h
Normal file
59
oemcrypto/odk/include/core_message_deserialize.h
Normal 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 VIDEO_WIDEVINE_EXPORT_COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_INCLUDE_CORE_MESSAGE_DESERIALIZE_H_
|
||||
#define VIDEO_WIDEVINE_EXPORT_COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_INCLUDE_CORE_MESSAGE_DESERIALIZE_H_
|
||||
|
||||
#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 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 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 string& oemcrypto_core_message,
|
||||
ODK_ProvisioningRequest* core_provisioning_request);
|
||||
|
||||
} /* namespace deserialize */
|
||||
} /* namespace oemcrypto_core_message */
|
||||
|
||||
#endif /* ...ODK_INCLUDE_CORE_MESSAGE_DESERIALIZE_H_ */
|
||||
64
oemcrypto/odk/include/core_message_serialize.h
Normal file
64
oemcrypto/odk/include/core_message_serialize.h
Normal 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 VIDEO_WIDEVINE_EXPORT_COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_INCLUDE_CORE_MESSAGE_SERIALIZE_H_
|
||||
#define VIDEO_WIDEVINE_EXPORT_COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_INCLUDE_CORE_MESSAGE_SERIALIZE_H_
|
||||
|
||||
#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,
|
||||
string* oemcrypto_core_message);
|
||||
|
||||
/**
|
||||
* Counterpart (serializer) of ODK_ParseRenewal (deserializer)
|
||||
*
|
||||
* Parameters:
|
||||
* [in] core_request
|
||||
* [out] oemcrypto_core_message
|
||||
*/
|
||||
bool CreateCoreRenewalResponse(const ODK_RenewalRequest& core_request,
|
||||
string* oemcrypto_core_message);
|
||||
|
||||
/**
|
||||
* Counterpart (serializer) of ODK_ParseProvisioning (deserializer)
|
||||
* struct-input variant
|
||||
*
|
||||
* Parameters:
|
||||
* [in] parsed_prov
|
||||
* [in] core_request
|
||||
* [out] oemcrypto_core_message
|
||||
*/
|
||||
bool CreateCoreProvisioningResponse(const ODK_ParsedProvisioning& parsed_prov,
|
||||
const ODK_ProvisioningRequest& core_request,
|
||||
string* oemcrypto_core_message);
|
||||
} /* namespace serialize */
|
||||
} /* namespace oemcrypto_core_message */
|
||||
|
||||
#endif /* ...ODK_INCLUDE_CORE_MESSAGE_SERIALIZE_H_ */
|
||||
59
oemcrypto/odk/include/core_message_serialize_proto.h
Normal file
59
oemcrypto/odk/include/core_message_serialize_proto.h
Normal 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 VIDEO_WIDEVINE_EXPORT_COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_INCLUDE_CORE_MESSAGE_SERIALIZE_PROTO_H_
|
||||
#define VIDEO_WIDEVINE_EXPORT_COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_INCLUDE_CORE_MESSAGE_SERIALIZE_PROTO_H_
|
||||
|
||||
#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 /* ...ODK_INCLUDE_CORE_MESSAGE_SERIALIZE_PROTO_H_ */
|
||||
98
oemcrypto/odk/include/core_message_types.h
Normal file
98
oemcrypto/odk/include/core_message_types.h
Normal file
@@ -0,0 +1,98 @@
|
||||
/* 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 VIDEO_WIDEVINE_EXPORT_COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_INCLUDE_CORE_MESSAGE_TYPES_H_
|
||||
#define VIDEO_WIDEVINE_EXPORT_COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_INCLUDE_CORE_MESSAGE_TYPES_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
namespace oemcrypto_core_message {
|
||||
|
||||
using std::string;
|
||||
|
||||
/* @ 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;
|
||||
string device_id;
|
||||
};
|
||||
|
||||
} /* namespace oemcrypto_core_message */
|
||||
|
||||
#endif /* ...ODK_INCLUDE_CORE_MESSAGE_TYPES_H_ */
|
||||
@@ -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 VIDEO_WIDEVINE_EXPORT_COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_INCLUDE_ODK_H_
|
||||
#define VIDEO_WIDEVINE_EXPORT_COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_INCLUDE_ODK_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "OEMCryptoCENCCommon.h"
|
||||
#include "odk_structs.h"
|
||||
|
||||
@@ -305,7 +304,7 @@ OEMCryptoResult ODK_PrepareCoreLicenseRequest(
|
||||
* the message. (in) size of buffer reserved for the core message, in
|
||||
* bytes. (out) actual length of the core message, in bytes.
|
||||
* [in] nonce_values: pointer to the session's nonce data.
|
||||
* [in] clock_values: the session's clock values.
|
||||
* [in/out] clock_values: the session's clock values.
|
||||
* [in] system_time_seconds: the current time on OEMCrypto's clock, in
|
||||
* 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.
|
||||
@@ -439,35 +438,21 @@ OEMCryptoResult ODK_RefreshV15Values(const ODK_TimerLimits* timer_limits,
|
||||
* 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.
|
||||
@@ -505,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
|
||||
@@ -547,10 +529,8 @@ OEMCryptoResult ODK_ParseLicense(
|
||||
* value.
|
||||
* ODK_DISABLE_TIMER: Success, but disable timer. Unlimited playback is
|
||||
* allowed.
|
||||
* ODK_TIMER_EXPIRED: Set timer as diabled. Playback is not allowed.
|
||||
* ODK_TIMER_EXPIRED: Set timer as disabled. Playback is not allowed.
|
||||
* ODK_UNSUPPORTED_API
|
||||
* ODK_STALE_RENEWAL: This renewal is not the most recently signed. It is
|
||||
* rejected.
|
||||
* OEMCrypto_ERROR_INVALID_NONCE
|
||||
*
|
||||
* Version:
|
||||
@@ -571,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.
|
||||
@@ -617,4 +590,4 @@ OEMCryptoResult ODK_ParseProvisioning(
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // ODK_H_
|
||||
#endif /* ...ODK_INCLUDE_ODK_H_ */
|
||||
|
||||
@@ -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 VIDEO_WIDEVINE_EXPORT_COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_INCLUDE_ODK_STRUCTS_H_
|
||||
#define VIDEO_WIDEVINE_EXPORT_COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_INCLUDE_ODK_STRUCTS_H_
|
||||
|
||||
#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;
|
||||
|
||||
/*
|
||||
@@ -94,4 +93,4 @@ typedef struct {
|
||||
uint32_t session_id;
|
||||
} ODK_NonceValues;
|
||||
|
||||
#endif // ODK_STRUCTS_H_
|
||||
#endif /* ...ODK_INCLUDE_ODK_STRUCTS_H_ */
|
||||
|
||||
@@ -1,172 +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.
|
||||
*/
|
||||
|
||||
// clang-format off
|
||||
/*********************************************************************
|
||||
* oec_util.h
|
||||
*
|
||||
* OEMCrypto v16 Core Message Serialization library counterpart (a.k.a. KDO)
|
||||
*
|
||||
* For Widevine Modular DRM, there are six message types between a server and
|
||||
* a client device: license request and response, provisioning request and
|
||||
* response, and renewal request and response.
|
||||
*
|
||||
* In OEMCrypto v15 and earlier, messages from the server were parsed by the
|
||||
* CDM layer above OEMCrypto; the CDM in turn gave OEMCrypto a collection of
|
||||
* pointers to protected data within the message. However, the pointers
|
||||
* themselves were not signed by the server.
|
||||
*
|
||||
* Starting from OEMCrypto v16, all fields used by OEMCrypto in each of these
|
||||
* messages have been identified in the document "Widevine Core Message
|
||||
* Serialization". These fields are called the core of the message. Core
|
||||
* message fields are (de)serialized using the ODK, a C library provided by
|
||||
* Widevine. OEMCrypto will parse and verify the core of the message with
|
||||
* help from the ODK.
|
||||
*
|
||||
* The KDO library is the counterpart of ODK used in the CDM & Widevine
|
||||
* servers. For each message type generated by the ODK, KDO provides a
|
||||
* corresponding parser. For each message type to be parsed by the ODK,
|
||||
* KDO provides a corresponding writer.
|
||||
*
|
||||
* Table: ODK vs KDO (s: serialize; d: deserialize)
|
||||
* +----------------------------------------+------------------------------------+
|
||||
* | ODK | KDO |
|
||||
* +---+------------------------------------+---+--------------------------------+
|
||||
* | s | ODK_PrepareCoreLicenseRequest | d | ParseLicenseRequest |
|
||||
* | +------------------------------------+ +--------------------------------+
|
||||
* | | ODK_PrepareCoreRenewalRequest | | ParseRenewalRequest |
|
||||
* | +------------------------------------+ +--------------------------------+
|
||||
* | | ODK_PrepareCoreProvisioningRequest | | ParseProvisioningRequest |
|
||||
* +---+------------------------------------+---+--------------------------------+
|
||||
* | d | ODK_ParseLicense | s | CreateCoreLicenseResponse |
|
||||
* | +------------------------------------+ +--------------------------------+
|
||||
* | | ODK_ParseRenewal | | CreateCoreRenewalResponse |
|
||||
* | +------------------------------------+ +--------------------------------+
|
||||
* | | ODK_ParseProvisioning | | CreateCoreProvisioningResponse |
|
||||
* +---+------------------------------------+---+--------------------------------+
|
||||
*
|
||||
*********************************************************************/
|
||||
// clang-format on
|
||||
|
||||
#ifndef OEC_UTIL_H_
|
||||
#define OEC_UTIL_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include "odk_structs.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace oec_util {
|
||||
|
||||
// @ input/output structs
|
||||
|
||||
/**
|
||||
* Output structure for ParseLicenseRequest
|
||||
* Input structure for CreateCoreLicenseResponse
|
||||
*/
|
||||
struct ODK_LicenseRequest {
|
||||
uint32_t api_version;
|
||||
uint32_t nonce;
|
||||
uint32_t session_id;
|
||||
};
|
||||
|
||||
/**
|
||||
* Output structure for ParseRenewalRequest
|
||||
* Input structure for CreateCoreRenewalResponse
|
||||
*/
|
||||
struct ODK_RenewalRequest {
|
||||
uint32_t api_version;
|
||||
uint32_t nonce;
|
||||
uint32_t session_id;
|
||||
uint64_t playback_time;
|
||||
};
|
||||
|
||||
/**
|
||||
* Output structure for ParseProvisioningRequest
|
||||
* Input structure for CreateCoreProvisioningResponse
|
||||
*/
|
||||
struct ODK_ProvisioningRequest {
|
||||
uint32_t api_version;
|
||||
uint32_t nonce;
|
||||
uint32_t session_id;
|
||||
string device_id;
|
||||
};
|
||||
|
||||
// @ public parse request (deserializer) functions
|
||||
|
||||
/**
|
||||
* Counterpart (deserializer) of ODK_PrepareCoreLicenseRequest (serializer)
|
||||
*
|
||||
* Parameters:
|
||||
* [in] oemcrypto_core_message
|
||||
* [out] core_license_request
|
||||
*/
|
||||
bool ParseLicenseRequest(const string& oemcrypto_core_message,
|
||||
ODK_LicenseRequest* core_license_request);
|
||||
|
||||
/**
|
||||
* Counterpart (deserializer) of ODK_PrepareCoreRenewalRequest (serializer)
|
||||
*
|
||||
* Parameters:
|
||||
* [in] oemcrypto_core_message
|
||||
* [out] core_renewal_request
|
||||
*/
|
||||
bool ParseRenewalRequest(const string& oemcrypto_core_message,
|
||||
ODK_RenewalRequest* core_renewal_request);
|
||||
|
||||
/**
|
||||
* Counterpart (deserializer) of ODK_PrepareCoreProvisioningRequest (serializer)
|
||||
*
|
||||
* Parameters:
|
||||
* [in] oemcrypto_core_message
|
||||
* [out] core_provisioning_request
|
||||
*/
|
||||
bool ParseProvisioningRequest(
|
||||
const string& oemcrypto_core_message,
|
||||
ODK_ProvisioningRequest* core_provisioning_request);
|
||||
|
||||
// @ public create response (serializer) functions
|
||||
|
||||
/**
|
||||
* Counterpart (serializer) of ODK_ParseLicense (deserializer)
|
||||
* struct-input variant
|
||||
*
|
||||
* Parameters:
|
||||
* [in] parsed_lic
|
||||
* [in] core_request
|
||||
* [out] oemcrypto_core_message
|
||||
*/
|
||||
bool CreateCoreLicenseResponse(const ODK_ParsedLicense& parsed_lic,
|
||||
const ODK_LicenseRequest& core_request,
|
||||
string* oemcrypto_core_message);
|
||||
|
||||
/**
|
||||
* Counterpart (serializer) of ODK_ParseRenewal (deserializer)
|
||||
*
|
||||
* Parameters:
|
||||
* [in] core_request
|
||||
* [out] oemcrypto_core_message
|
||||
*/
|
||||
bool CreateCoreRenewalResponse(const ODK_RenewalRequest& core_request,
|
||||
string* oemcrypto_core_message);
|
||||
|
||||
/**
|
||||
* Counterpart (serializer) of ODK_ParseProvisioning (deserializer)
|
||||
* struct-input variant
|
||||
*
|
||||
* Parameters:
|
||||
* [in] parsed_prov
|
||||
* [in] core_request
|
||||
* [out] oemcrypto_core_message
|
||||
*/
|
||||
bool CreateCoreProvisioningResponse(const ODK_ParsedProvisioning& parsed_prov,
|
||||
const ODK_ProvisioningRequest& core_request,
|
||||
string* oemcrypto_core_message);
|
||||
} // namespace oec_util
|
||||
|
||||
#endif // OEC_UTIL_H_
|
||||
@@ -1,59 +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.
|
||||
*/
|
||||
|
||||
/*********************************************************************
|
||||
* oec_util_proto.h
|
||||
*
|
||||
* These functions are an extension of those found in oec_util.h. The
|
||||
* difference is that these use the license and provisioning messages
|
||||
* in protobuf format to create the core message.
|
||||
*********************************************************************/
|
||||
|
||||
#ifndef OEC_UTIL_PROTO_H_
|
||||
#define OEC_UTIL_PROTO_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include "license_protocol.pb.h"
|
||||
#include "oec_util.h"
|
||||
|
||||
using namespace std;
|
||||
using video_widevine::License;
|
||||
using video_widevine::License_KeyContainer;
|
||||
|
||||
namespace oec_util {
|
||||
|
||||
// @ public create response (serializer) functions
|
||||
|
||||
/**
|
||||
* Counterpart (serializer) of ODK_ParseLicense (deserializer)
|
||||
*
|
||||
* Parameters:
|
||||
* [in] license
|
||||
* [in] core_request
|
||||
* [out] oemcrypto_core_message
|
||||
*/
|
||||
bool CreateCoreLicenseResponse(const video_widevine::License& license,
|
||||
const ODK_LicenseRequest& core_request,
|
||||
string* oemcrypto_core_message);
|
||||
|
||||
/**
|
||||
* Counterpart (serializer) of ODK_ParseProvisioning (deserializer)
|
||||
*
|
||||
* Parameters:
|
||||
* [in] provisioning_response
|
||||
* [in] core_request
|
||||
* [out] oemcrypto_core_message
|
||||
*/
|
||||
bool CreateCoreProvisioningResponse(
|
||||
const video_widevine::ProvisioningResponse& provisioning_response,
|
||||
const ODK_ProvisioningRequest& core_request,
|
||||
string* oemcrypto_core_message);
|
||||
|
||||
} // namespace oec_util
|
||||
|
||||
#endif // OEC_UTIL_PROTO_H_
|
||||
@@ -1,16 +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.
|
||||
|
||||
{
|
||||
'sources': [
|
||||
'src/oec_util.cpp',
|
||||
'src/oec_util_proto.cpp',
|
||||
],
|
||||
'include_dirs': [
|
||||
'../src',
|
||||
'../include',
|
||||
'include',
|
||||
],
|
||||
}
|
||||
|
||||
@@ -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
|
||||
111
oemcrypto/odk/src/core_message_deserialize.cpp
Normal file
111
oemcrypto/odk/src/core_message_deserialize.cpp
Normal file
@@ -0,0 +1,111 @@
|
||||
// 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 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 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 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 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
|
||||
114
oemcrypto/odk/src/core_message_serialize.cpp
Normal file
114
oemcrypto/odk/src/core_message_serialize.cpp
Normal 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,
|
||||
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 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,
|
||||
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_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,
|
||||
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
|
||||
@@ -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
|
||||
18
oemcrypto/odk/src/kdo.gypi
Normal file
18
oemcrypto/odk/src/kdo.gypi
Normal file
@@ -0,0 +1,18 @@
|
||||
# Copyright 2019 Google LLC. All rights reserved. This file and proprietary
|
||||
# source code may only be used and distributed under the Widevine Master License
|
||||
# Agreement.
|
||||
|
||||
# These files are used by the server and by some ODK test code. These files are
|
||||
# not built into the ODK library on the device.
|
||||
{
|
||||
'sources': [
|
||||
'core_message_deserialize.cpp',
|
||||
'core_message_serialize.cpp',
|
||||
'core_message_serialize_proto.cpp',
|
||||
],
|
||||
'include_dirs': [
|
||||
'src',
|
||||
'../include',
|
||||
],
|
||||
}
|
||||
|
||||
@@ -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,16 +131,18 @@ 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,
|
||||
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},
|
||||
};
|
||||
@@ -148,15 +160,15 @@ OEMCryptoResult ODK_PrepareCoreRenewalRequest(
|
||||
}
|
||||
/* 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_length,
|
||||
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},
|
||||
};
|
||||
@@ -174,30 +186,28 @@ 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) {
|
||||
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
|
||||
/* This function should not be used for legacy licenses. */
|
||||
if (license_response.core_message.nonce_values.api_version != 16) {
|
||||
if (license_response.core_message.nonce_values.api_version !=
|
||||
OEC_API_VERSION) {
|
||||
return ODK_UNSUPPORTED_API;
|
||||
}
|
||||
|
||||
@@ -249,8 +259,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;
|
||||
@@ -268,41 +278,28 @@ OEMCryptoResult ODK_ParseRenewal(const uint8_t* message, size_t message_length,
|
||||
return ODK_STALE_RENEWAL;
|
||||
}
|
||||
|
||||
uint64_t playback_timer = 0;
|
||||
if (odk_sub_overflow_u64(clock_values->time_when_timer_expires, system_time,
|
||||
&playback_timer)) {
|
||||
return ODK_TIMER_EXPIRED;
|
||||
/* The timer value should be set to the renewal duration. */
|
||||
if (timer_value) {
|
||||
*timer_value = timer_limits->renewal_playback_duration_seconds;
|
||||
}
|
||||
|
||||
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,
|
||||
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 */
|
||||
if (timer_value)
|
||||
*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) {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
@@ -316,9 +313,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);
|
||||
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;
|
||||
|
||||
@@ -1,4 +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.
|
||||
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
# 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.
|
||||
|
||||
# These files are built into the ODK library on the device. They are also used
|
||||
# by the server and by test cocde. These files should compile on C98 compilers.
|
||||
{
|
||||
'sources': [
|
||||
'odk.c',
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
/* 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_ASSERT_H_
|
||||
#define ODK_ASSERT_H_
|
||||
#ifndef VIDEO_WIDEVINE_EXPORT_COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_SRC_ODK_ASSERT_H_
|
||||
#define VIDEO_WIDEVINE_EXPORT_COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_SRC_ODK_ASSERT_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -24,4 +21,4 @@ extern "C" {
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ODK_ASSERT_H_ */
|
||||
#endif /* ...ODK_SRC_ODK_ASSERT_H_ */
|
||||
|
||||
29
oemcrypto/odk/src/odk_endian.h
Normal file
29
oemcrypto/odk/src/odk_endian.h
Normal 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 VIDEO_WIDEVINE_EXPORT_COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_SRC_ODK_ENDIAN_H_
|
||||
#define VIDEO_WIDEVINE_EXPORT_COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_SRC_ODK_ENDIAN_H_
|
||||
|
||||
#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 /* ...ODK_SRC_ODK_ENDIAN_H_ */
|
||||
@@ -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>
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
/*
|
||||
* 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_OVERFLOW_H_
|
||||
#define ODK_OVERFLOW_H_
|
||||
#ifndef VIDEO_WIDEVINE_EXPORT_COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_SRC_ODK_OVERFLOW_H_
|
||||
#define VIDEO_WIDEVINE_EXPORT_COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_SRC_ODK_OVERFLOW_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -30,4 +28,4 @@ int odk_add_overflow_ux(size_t a, size_t b, size_t* c);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ODK_OVERFLOW_H_ */
|
||||
#endif /* ...ODK_SRC_ODK_OVERFLOW_H_ */
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -1,14 +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. */
|
||||
|
||||
/*
|
||||
* This code is auto-generated, do not edit
|
||||
*/
|
||||
#ifndef ODKITEE_SERIALIZER_H_
|
||||
#define ODKITEE_SERIALIZER_H_
|
||||
#ifndef VIDEO_WIDEVINE_EXPORT_COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_SRC_ODK_SERIALIZE_H_
|
||||
#define VIDEO_WIDEVINE_EXPORT_COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_SRC_ODK_SERIALIZE_H_
|
||||
|
||||
#include "odk_structs_priv.h"
|
||||
#include "serialization_base.h"
|
||||
@@ -39,6 +37,6 @@ void Unpack_ODK_PreparedLicense(Message* msg, ODK_PreparedLicense* obj);
|
||||
void Unpack_ODK_ProvisioningMessage(Message* msg, ODK_ProvisioningMessage* obj);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
#endif /* ODKITEE_SERIALIZER_H_ */
|
||||
#endif /* ...ODK_SRC_ODK_SERIALIZE_H_ */
|
||||
|
||||
@@ -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_PRIV_H_
|
||||
#define ODK_STRUCTS_PRIV_H_
|
||||
#ifndef VIDEO_WIDEVINE_EXPORT_COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_SRC_ODK_STRUCTS_PRIV_H_
|
||||
#define VIDEO_WIDEVINE_EXPORT_COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_SRC_ODK_STRUCTS_PRIV_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "OEMCryptoCENCCommon.h"
|
||||
#include "odk_structs.h"
|
||||
|
||||
@@ -51,4 +50,4 @@ typedef struct {
|
||||
ODK_ParsedProvisioning* parsed_provisioning;
|
||||
} ODK_ProvisioningResponse;
|
||||
|
||||
#endif // ODK_STRUCTS_PRIV_H_
|
||||
#endif /* ...ODK_SRC_ODK_STRUCTS_PRIV_H_ */
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
/*
|
||||
* 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 ODKITEE_SERIALIZATION_BASE_H_
|
||||
#define ODKITEE_SERIALIZATION_BASE_H_
|
||||
#ifndef VIDEO_WIDEVINE_EXPORT_COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_SRC_SERIALIZATION_BASE_H_
|
||||
#define VIDEO_WIDEVINE_EXPORT_COMMON_OEMCRYPTO_CORE_MESSAGE_ODK_SRC_SERIALIZATION_BASE_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -43,7 +41,8 @@ void Pack_OEMCrypto_Substring(Message* msg, const OEMCrypto_Substring* obj);
|
||||
|
||||
void Unpack_uint32_t(Message* message, uint32_t* value);
|
||||
void Unpack_uint64_t(Message* message, uint64_t* value);
|
||||
void UnpackArray(Message* message, uint8_t* base, size_t size); /* copy out */
|
||||
void UnpackArray(Message* message, uint8_t* address,
|
||||
size_t size); /* copy out */
|
||||
void Unpack_OEMCrypto_Substring(Message* msg, OEMCrypto_Substring* obj);
|
||||
|
||||
typedef enum {
|
||||
@@ -85,7 +84,7 @@ size_t GetOffset(Message* message);
|
||||
size_t SizeOfMessageStruct();
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif // ODKITEE_SERIALIZATION_BASE_H_
|
||||
#endif /* ...ODK_SRC_SERIALIZATION_BASE_H_ */
|
||||
|
||||
@@ -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: remove this: using namespace std;
|
||||
// TODO: remove this: using namespace oec_util;
|
||||
|
||||
typedef std::function<size_t(const uint8_t*, uint8_t*, size_t)> roundtrip_fun;
|
||||
|
||||
@@ -93,9 +96,10 @@ 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,
|
||||
|
||||
@@ -1,4 +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.
|
||||
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
/*
|
||||
* Copyright 2019 Google LLC. All Rights Reserved. This file and proprietary
|
||||
* source code may only be used and distributed under the Widevine Master
|
||||
* License Agreement.
|
||||
*/
|
||||
// Copyright 2019 Google LLC. All rights reserved. This file and proprietary
|
||||
// source code may only be used and distributed under the Widevine Master
|
||||
// License Agreement.
|
||||
|
||||
#include "odk.h"
|
||||
|
||||
#include <endian.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
@@ -17,15 +20,48 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <unistd.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 +166,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 +221,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 +240,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,20 +254,8 @@ 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++) {
|
||||
@@ -189,9 +263,13 @@ void expect_eq_buf(const void* s1, const void* s2, size_t n) {
|
||||
mkstemp(_tmp);
|
||||
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::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();
|
||||
}
|
||||
@@ -199,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;
|
||||
@@ -207,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(),
|
||||
@@ -230,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 = {};
|
||||
@@ -243,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;
|
||||
@@ -257,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;
|
||||
@@ -310,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;
|
||||
}
|
||||
@@ -320,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};
|
||||
@@ -331,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) {
|
||||
@@ -342,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);
|
||||
}
|
||||
|
||||
@@ -359,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++) {
|
||||
@@ -375,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;
|
||||
uint64_t playback_time = 0xCAFE00000000;
|
||||
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,
|
||||
@@ -393,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;
|
||||
};
|
||||
@@ -404,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) {
|
||||
@@ -419,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;
|
||||
@@ -481,48 +569,57 @@ 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;
|
||||
return ODK_ParseLicense(buf, size + 128, size, true, false, request_hash,
|
||||
&timer_limits, nullptr, nonce_values,
|
||||
&parsed_license);
|
||||
};
|
||||
auto kdo_prepare_func = [&](const ODK_LicenseRequest& core_request,
|
||||
std::string* oemcrypto_core_message) {
|
||||
@@ -539,7 +636,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 = {
|
||||
@@ -555,6 +652,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,
|
||||
@@ -578,7 +676,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,
|
||||
@@ -586,8 +684,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 = {
|
||||
@@ -597,18 +695,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,
|
||||
@@ -670,7 +770,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,
|
||||
@@ -681,3 +780,5 @@ TEST(OdkSizeTest, ProvisioningRequest) {
|
||||
size_t minimum_message_size = 5 * 4;
|
||||
EXPECT_GE(core_message_length, minimum_message_size);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -1,4 +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.
|
||||
|
||||
|
||||
@@ -1,64 +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.
|
||||
*/
|
||||
|
||||
#ifndef ODK_TEST_H_
|
||||
#define ODK_TEST_H_
|
||||
|
||||
#include "OEMCryptoCENCCommon.h"
|
||||
|
||||
typedef enum {
|
||||
ODK_License_Request_Type = 1,
|
||||
ODK_License_Response_Type = 2,
|
||||
ODK_Renewal_Request_Type = 3,
|
||||
ODK_Renewal_Response_Type = 4,
|
||||
ODK_Provisioning_Request_Type = 5,
|
||||
ODK_Provisioning_Response_Type = 6,
|
||||
} ODK_MessageType;
|
||||
|
||||
typedef enum {
|
||||
ODK_UINT32,
|
||||
ODK_UINT64,
|
||||
ODK_SUBSTRING,
|
||||
ODK_DEVICEID,
|
||||
ODK_HASH,
|
||||
ODK_NUMTYPES,
|
||||
} ODK_FieldType;
|
||||
|
||||
typedef enum {
|
||||
ODK_READ,
|
||||
ODK_WRITE,
|
||||
} ODK_FieldMode;
|
||||
|
||||
typedef struct {
|
||||
ODK_FieldType type;
|
||||
void* value;
|
||||
} ODK_Field;
|
||||
|
||||
#define DEVICE_ID_MAX (64)
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
size_t ODK_FieldLength(ODK_FieldType type);
|
||||
OEMCryptoResult ODK_WriteSingleField(uint8_t* const buf,
|
||||
const ODK_Field* const field);
|
||||
OEMCryptoResult ODK_ReadSingleField(const uint8_t* const buf,
|
||||
const ODK_Field* const field);
|
||||
|
||||
OEMCryptoResult ODK_ReadFields(const uint8_t* const buf, const size_t size_in,
|
||||
size_t* size_out, const size_t n,
|
||||
const ODK_Field* const fields);
|
||||
|
||||
OEMCryptoResult ODK_WriteFields(uint8_t* const buf, const size_t size_in,
|
||||
size_t* size_out, const size_t n,
|
||||
const ODK_Field* const fields);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // ODK_TEST_H_
|
||||
@@ -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_,
|
||||
@@ -550,7 +544,7 @@ TEST_F(OdkTimerSoftTest, ReloadLate) {
|
||||
// OEMCrypto's point of view, there is only playback duration. A soft or hard
|
||||
// rental duration is translated into different rental and license durations.
|
||||
// The four test classes below all have a 700 rental window and a 200 playback
|
||||
// duration. We'll use the server descritpion, and then set the OEMCrypto
|
||||
// duration. We'll use the server description, and then set the OEMCrypto
|
||||
// restraints in the test SetUp() function. Note, it's easier to use the word
|
||||
// "day" but really the rental window is 700 seconds, not 7 days. These tests
|
||||
// have some coverage overlap with the ones above, but it is better to have
|
||||
@@ -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
|
||||
|
||||
@@ -19,7 +19,8 @@
|
||||
'type': 'executable',
|
||||
'sources': [
|
||||
'test/oemcrypto_test_main.cpp',
|
||||
'odk/kdo/src/oec_util.cpp',
|
||||
'odk/src/core_message_deserialize.cpp',
|
||||
'odk/src/core_message_serialize.cpp',
|
||||
'<(platform_specific_dir)/file_store.cpp',
|
||||
'<(platform_specific_dir)/log.cpp',
|
||||
'<(util_dir)/src/platform.cpp',
|
||||
|
||||
@@ -226,7 +226,7 @@ OEMCryptoResult CryptoEngine::SetDestination(
|
||||
default:
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
size_t max_allowed = max_sample_size();
|
||||
const size_t max_allowed = max_sample_size();
|
||||
if (max_allowed > 0 &&
|
||||
(max_allowed < max_length || max_allowed < data_length)) {
|
||||
LOGE("Output too large (or buffer too small).");
|
||||
|
||||
@@ -32,6 +32,7 @@ typedef std::map<SessionId, SessionContext*> ActiveSessions;
|
||||
class CryptoEngine {
|
||||
public:
|
||||
static const uint32_t kApiVersion = 16;
|
||||
static const uint32_t kMinorApiVersion = 0;
|
||||
|
||||
// This is like a factory method, except we choose which version to use at
|
||||
// compile time. It is defined in several source files. The build system
|
||||
|
||||
@@ -52,41 +52,6 @@ uint32_t unaligned_dereference_uint32(const void* unaligned_ptr) {
|
||||
return value;
|
||||
}
|
||||
|
||||
void advance_dest_buffer(OEMCrypto_DestBufferDesc* dest_buffer, size_t bytes) {
|
||||
switch (dest_buffer->type) {
|
||||
case OEMCrypto_BufferType_Clear:
|
||||
dest_buffer->buffer.clear.address += bytes;
|
||||
dest_buffer->buffer.clear.address_length -= bytes;
|
||||
break;
|
||||
|
||||
case OEMCrypto_BufferType_Secure:
|
||||
dest_buffer->buffer.secure.offset += bytes;
|
||||
break;
|
||||
|
||||
case OEMCrypto_BufferType_Direct:
|
||||
// Nothing to do for this buffer type.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Advance an IV according to ISO-CENC's CTR modes. The lower half of the IV is
|
||||
// split off and treated as an unsigned 64-bit integer, then incremented by the
|
||||
// number of complete crypto blocks decrypted. The resulting value is then
|
||||
// copied back into the IV over the previous lower half.
|
||||
void advance_iv_ctr(uint8_t (*subsample_iv)[wvoec::KEY_IV_SIZE], size_t bytes) {
|
||||
uint64_t counter;
|
||||
assert(sizeof(*subsample_iv) == wvoec::KEY_IV_SIZE);
|
||||
assert(sizeof(counter) * 2 == sizeof(*subsample_iv));
|
||||
static const size_t half_iv_size = wvoec::KEY_IV_SIZE / 2;
|
||||
memcpy(&counter, &(*subsample_iv)[half_iv_size], half_iv_size);
|
||||
|
||||
size_t increment =
|
||||
bytes / wvoec::AES_128_BLOCK_SIZE; // The truncation here is intentional
|
||||
counter = wvcdm::htonll64(wvcdm::ntohll64(counter) + increment);
|
||||
|
||||
memcpy(&(*subsample_iv)[half_iv_size], &counter, half_iv_size);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace wvoec_ref {
|
||||
@@ -637,6 +602,13 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_DecryptCENC(
|
||||
return OEMCrypto_ERROR_INVALID_SESSION;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
if (!crypto_engine->ValidRootOfTrust()) {
|
||||
LOGE("[OEMCrypto_DecryptCENC(): ERROR_KEYBOX_INVALID]");
|
||||
return OEMCrypto_ERROR_KEYBOX_INVALID;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Iterate through all the samples and validate them before doing any decrypt
|
||||
for (size_t sample_index = 0; sample_index < samples_length; ++sample_index) {
|
||||
const OEMCrypto_SampleDescription& sample = samples[sample_index];
|
||||
@@ -679,65 +651,7 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_DecryptCENC(
|
||||
}
|
||||
}
|
||||
|
||||
// Iterate through all the samples and decrypt each one
|
||||
for (size_t sample_index = 0; sample_index < samples_length; ++sample_index) {
|
||||
const OEMCrypto_SampleDescription& sample = samples[sample_index];
|
||||
|
||||
// Iterate through all the subsamples and decrypt each one
|
||||
const uint8_t* subsample_source = sample.buffers.input_data;
|
||||
OEMCrypto_DestBufferDesc subsample_dest = sample.buffers.output_descriptor;
|
||||
uint8_t subsample_iv[wvoec::KEY_IV_SIZE];
|
||||
assert(sizeof(sample.iv) == wvoec::KEY_IV_SIZE);
|
||||
assert(sizeof(subsample_iv) == wvoec::KEY_IV_SIZE);
|
||||
memcpy(&subsample_iv[0], &sample.iv[0], wvoec::KEY_IV_SIZE);
|
||||
for (size_t subsample_index = 0; subsample_index < sample.subsamples_length;
|
||||
++subsample_index) {
|
||||
const OEMCrypto_SubSampleDescription& subsample =
|
||||
sample.subsamples[subsample_index];
|
||||
const size_t subsample_length =
|
||||
subsample.num_bytes_clear + subsample.num_bytes_encrypted;
|
||||
|
||||
OEMCryptoResult result = crypto_engine->SetDestination(
|
||||
subsample_dest, subsample_length, subsample.subsample_flags);
|
||||
if (result != OEMCrypto_SUCCESS) {
|
||||
LOGE("[OEMCrypto_DecryptCENC(): SetDestination status: %d]", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
if (!crypto_engine->ValidRootOfTrust()) {
|
||||
LOGE("[OEMCrypto_DecryptCENC(): ERROR_KEYBOX_INVALID]");
|
||||
return OEMCrypto_ERROR_KEYBOX_INVALID;
|
||||
}
|
||||
#endif
|
||||
|
||||
result = session_ctx->DecryptSubsample(
|
||||
subsample, subsample_source, crypto_engine->destination(),
|
||||
subsample_dest.type, subsample_iv, pattern);
|
||||
if (result != OEMCrypto_SUCCESS) {
|
||||
LOGE("[OEMCrypto_DecryptCENC(): DecryptSubsample status: %d]", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
result = crypto_engine->PushDestination(subsample_dest,
|
||||
subsample.subsample_flags);
|
||||
if (result != OEMCrypto_SUCCESS) {
|
||||
LOGE("[OEMCrypto_DecryptCENC(): PushDestination status: %d]", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Advance the source buffer, the dest buffer, and (if necessary) the IV
|
||||
subsample_source += subsample_length;
|
||||
advance_dest_buffer(&subsample_dest, subsample_length);
|
||||
if (subsample.num_bytes_encrypted > 0 &&
|
||||
session_ctx->current_content_key()->ctr_mode()) {
|
||||
advance_iv_ctr(&subsample_iv,
|
||||
subsample.block_offset + subsample.num_bytes_encrypted);
|
||||
}
|
||||
} // Subsample loop
|
||||
} // Sample loop
|
||||
|
||||
return OEMCrypto_SUCCESS;
|
||||
return session_ctx->DecryptSamples(samples, samples_length, pattern);
|
||||
}
|
||||
|
||||
OEMCRYPTO_API OEMCryptoResult OEMCrypto_CopyBuffer(
|
||||
@@ -752,8 +666,8 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_CopyBuffer(
|
||||
LOGE("[OEMCrypto_CopyBuffer(): OEMCrypto_ERROR_INVALID_CONTEXT]");
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
if (crypto_engine->max_sample_size() > 0 &&
|
||||
data_length > crypto_engine->max_sample_size()) {
|
||||
if (crypto_engine->max_subsample_size() > 0 &&
|
||||
data_length > crypto_engine->max_subsample_size()) {
|
||||
// For testing reasons only, pretend that this integration only supports
|
||||
// the minimum possible buffer size.
|
||||
LOGE("[OEMCrypto_CopyBuffer(): OEMCrypto_ERROR_BUFFER_TOO_LARGE]");
|
||||
@@ -1415,6 +1329,10 @@ OEMCRYPTO_API uint32_t OEMCrypto_APIVersion() {
|
||||
return CryptoEngine::kApiVersion;
|
||||
}
|
||||
|
||||
OEMCRYPTO_API uint32_t OEMCrypto_MinorAPIVersion() {
|
||||
return CryptoEngine::kMinorApiVersion;
|
||||
}
|
||||
|
||||
OEMCRYPTO_API uint8_t OEMCrypto_Security_Patch_Level() {
|
||||
uint8_t security_patch_level = crypto_engine->config_security_patch_level();
|
||||
return security_patch_level;
|
||||
@@ -1468,12 +1386,11 @@ OEMCRYPTO_API bool OEMCrypto_SupportsUsageTable() {
|
||||
}
|
||||
|
||||
OEMCRYPTO_API size_t OEMCrypto_MaximumUsageTableHeaderSize() {
|
||||
// TOOD(b/140080305): fill in a real value.
|
||||
if (crypto_engine == nullptr) {
|
||||
LOGE("OEMCrypto_MaximumUsageTableHeaderSize: OEMCrypto Not Initialized.");
|
||||
return 0;
|
||||
}
|
||||
return 200;
|
||||
return crypto_engine->max_usage_table_size();
|
||||
}
|
||||
|
||||
OEMCRYPTO_API OEMCryptoResult OEMCrypto_GetNumberOfOpenSessions(size_t* count) {
|
||||
|
||||
@@ -47,6 +47,43 @@ void ctr128_inc64(uint8_t* counter) {
|
||||
if (++counter[--n] != 0) return;
|
||||
} while (n > 8);
|
||||
}
|
||||
|
||||
void advance_dest_buffer(OEMCrypto_DestBufferDesc* dest_buffer, size_t bytes) {
|
||||
switch (dest_buffer->type) {
|
||||
case OEMCrypto_BufferType_Clear:
|
||||
dest_buffer->buffer.clear.address += bytes;
|
||||
dest_buffer->buffer.clear.address_length -= bytes;
|
||||
break;
|
||||
|
||||
case OEMCrypto_BufferType_Secure:
|
||||
dest_buffer->buffer.secure.offset += bytes;
|
||||
break;
|
||||
|
||||
case OEMCrypto_BufferType_Direct:
|
||||
// Nothing to do for this buffer type.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Advance an IV according to ISO-CENC's CTR modes. The lower half of the IV is
|
||||
// split off and treated as an unsigned 64-bit integer, then incremented by the
|
||||
// number of complete crypto blocks decrypted. The resulting value is then
|
||||
// copied back into the IV over the previous lower half.
|
||||
void advance_iv_ctr(uint8_t (*subsample_iv)[wvoec::KEY_IV_SIZE], size_t bytes) {
|
||||
uint64_t counter;
|
||||
// Per its type, sizeof(*subsample_iv) == wvoec::KEY_IV_SIZE
|
||||
static_assert(sizeof(counter) * 2 == wvoec::KEY_IV_SIZE,
|
||||
"A uint64_t failed to be half the size of an AES-128 IV.");
|
||||
constexpr size_t half_iv_size = wvoec::KEY_IV_SIZE / 2;
|
||||
memcpy(&counter, &(*subsample_iv)[half_iv_size], half_iv_size);
|
||||
|
||||
const size_t increment =
|
||||
bytes / wvoec::AES_128_BLOCK_SIZE; // The truncation here is intentional
|
||||
counter = wvcdm::htonll64(wvcdm::ntohll64(counter) + increment);
|
||||
|
||||
memcpy(&(*subsample_iv)[half_iv_size], &counter, half_iv_size);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace wvoec_ref {
|
||||
@@ -366,7 +403,7 @@ OEMCryptoResult SessionContext::PrepAndSignRenewalRequest(
|
||||
}
|
||||
// If we have signed a request, but have not loaded it, something is wrong.
|
||||
// On the other hand, we can sign a license release using the mac keys from
|
||||
// the usage table.
|
||||
// the usage table. So it is OK if we have never signed a license request.
|
||||
if (state_request_signed_ && !state_response_loaded_) {
|
||||
LOGE("Attempt to sign renewal before load");
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
@@ -1418,7 +1455,9 @@ OEMCryptoResult SessionContext::LoadUsageEntry(
|
||||
}
|
||||
const OEMCryptoResult result = ce_->usage_table().LoadUsageEntry(
|
||||
this, &usage_entry_, index, buffer, &clock_values_);
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
if ((result != OEMCrypto_SUCCESS) &&
|
||||
(result != OEMCrypto_WARNING_GENERATION_SKEW))
|
||||
return result;
|
||||
if (!usage_entry_) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
|
||||
usage_entry_status_ = kUsageEntryLoaded;
|
||||
@@ -1429,7 +1468,7 @@ OEMCryptoResult SessionContext::LoadUsageEntry(
|
||||
mac_key_client_ = std::vector<uint8_t>(
|
||||
usage_entry_->mac_key_client(),
|
||||
usage_entry_->mac_key_client() + wvoec::MAC_KEY_SIZE);
|
||||
return OEMCrypto_SUCCESS;
|
||||
return result;
|
||||
}
|
||||
|
||||
OEMCryptoResult SessionContext::UpdateUsageEntry(uint8_t* header_buffer,
|
||||
@@ -1484,10 +1523,68 @@ bool SessionContext::DecryptMessage(const std::vector<uint8_t>& key,
|
||||
return true;
|
||||
}
|
||||
|
||||
OEMCryptoResult SessionContext::DecryptSamples(
|
||||
const OEMCrypto_SampleDescription* samples, size_t samples_length,
|
||||
const OEMCrypto_CENCEncryptPatternDesc* pattern) {
|
||||
// Iterate through all the samples and decrypt each one
|
||||
for (size_t sample_index = 0; sample_index < samples_length; ++sample_index) {
|
||||
const OEMCrypto_SampleDescription& sample = samples[sample_index];
|
||||
|
||||
// Iterate through all the subsamples and decrypt each one. A production
|
||||
// implementation may be able to do something more efficient, like
|
||||
// decrypting all the encrypted portions in one pass.
|
||||
const uint8_t* subsample_source = sample.buffers.input_data;
|
||||
OEMCrypto_DestBufferDesc subsample_dest = sample.buffers.output_descriptor;
|
||||
uint8_t subsample_iv[wvoec::KEY_IV_SIZE];
|
||||
static_assert(sizeof(sample.iv) == wvoec::KEY_IV_SIZE,
|
||||
"The IV in OEMCrypto_SampleDescription is the wrong length.");
|
||||
// Per its type, sizeof(subsample_iv) == wvoec::KEY_IV_SIZE
|
||||
memcpy(subsample_iv, sample.iv, wvoec::KEY_IV_SIZE);
|
||||
for (size_t subsample_index = 0; subsample_index < sample.subsamples_length;
|
||||
++subsample_index) {
|
||||
const OEMCrypto_SubSampleDescription& subsample =
|
||||
sample.subsamples[subsample_index];
|
||||
const size_t subsample_length =
|
||||
subsample.num_bytes_clear + subsample.num_bytes_encrypted;
|
||||
|
||||
OEMCryptoResult result = ce_->SetDestination(
|
||||
subsample_dest, subsample_length, subsample.subsample_flags);
|
||||
if (result != OEMCrypto_SUCCESS) {
|
||||
LOGE("SetDestination status: %d", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
result = DecryptSubsample(subsample, subsample_source, ce_->destination(),
|
||||
subsample_dest.type, subsample_iv, pattern);
|
||||
if (result != OEMCrypto_SUCCESS) {
|
||||
LOGE("DecryptSubsample status: %d", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
result = ce_->PushDestination(subsample_dest, subsample.subsample_flags);
|
||||
if (result != OEMCrypto_SUCCESS) {
|
||||
LOGE("PushDestination status: %d", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Advance the source buffer, the dest buffer, and (if necessary) the IV
|
||||
subsample_source += subsample_length;
|
||||
advance_dest_buffer(&subsample_dest, subsample_length);
|
||||
if (subsample.num_bytes_encrypted > 0 &&
|
||||
current_content_key()->ctr_mode()) {
|
||||
advance_iv_ctr(&subsample_iv,
|
||||
subsample.block_offset + subsample.num_bytes_encrypted);
|
||||
}
|
||||
} // Subsample loop
|
||||
} // Sample loop
|
||||
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
OEMCryptoResult SessionContext::DecryptSubsample(
|
||||
const OEMCrypto_SubSampleDescription& subsample, const uint8_t* cipher_data,
|
||||
uint8_t* clear_data, OEMCryptoBufferType buffer_type,
|
||||
const uint8_t iv[wvoec::KEY_IV_SIZE],
|
||||
const uint8_t (&iv)[wvoec::KEY_IV_SIZE],
|
||||
const OEMCrypto_CENCEncryptPatternDesc* pattern) {
|
||||
// Handle the clear portion of the subsample.
|
||||
if (subsample.num_bytes_clear > 0) {
|
||||
@@ -1602,14 +1699,16 @@ OEMCryptoResult SessionContext::PatternDecryptCBC(
|
||||
size_t l = 0;
|
||||
size_t pattern_offset = 0;
|
||||
while (l < cipher_data_length) {
|
||||
size_t size =
|
||||
const size_t size =
|
||||
std::min(cipher_data_length - l, static_cast<size_t>(AES_BLOCK_SIZE));
|
||||
bool skip_block = (pattern_offset >= pattern->encrypt);
|
||||
const bool skip_block = (pattern_offset >= pattern->encrypt);
|
||||
pattern_offset = (pattern_offset + 1) % pattern_length;
|
||||
// TODO(b/140503351): The (size < AES_BLOCK_SIZE) check is not correct for
|
||||
// patterns where (pattern.encrypt > 1).
|
||||
if (skip_block || (size < AES_BLOCK_SIZE)) {
|
||||
memmove(&clear_data[l], &cipher_data[l], size);
|
||||
// If we are decrypting in-place, then this byte is already correct and
|
||||
// can be skipped.
|
||||
if (clear_data != cipher_data) {
|
||||
memcpy(&clear_data[l], &cipher_data[l], size);
|
||||
}
|
||||
} else {
|
||||
uint8_t aes_output[AES_BLOCK_SIZE];
|
||||
// Save the iv for the next block, in case cipher_data is in the same
|
||||
|
||||
@@ -96,10 +96,8 @@ class SessionContext {
|
||||
virtual bool ValidateMessage(const uint8_t* message, size_t message_length,
|
||||
const uint8_t* signature,
|
||||
size_t signature_length);
|
||||
OEMCryptoResult DecryptSubsample(
|
||||
const OEMCrypto_SubSampleDescription& subsample,
|
||||
const uint8_t* cipher_data, uint8_t* clear_data,
|
||||
OEMCryptoBufferType buffer_type, const uint8_t iv[wvoec::KEY_IV_SIZE],
|
||||
OEMCryptoResult DecryptSamples(
|
||||
const OEMCrypto_SampleDescription* samples, size_t samples_length,
|
||||
const OEMCrypto_CENCEncryptPatternDesc* pattern);
|
||||
|
||||
OEMCryptoResult Generic_Encrypt(const uint8_t* in_buffer,
|
||||
@@ -236,6 +234,12 @@ class SessionContext {
|
||||
OEMCryptoResult CheckStatusOnline(uint32_t nonce, uint32_t control);
|
||||
// Check that the usage entry status is valid for offline use.
|
||||
OEMCryptoResult CheckStatusOffline(uint32_t nonce, uint32_t control);
|
||||
|
||||
OEMCryptoResult DecryptSubsample(
|
||||
const OEMCrypto_SubSampleDescription& subsample,
|
||||
const uint8_t* cipher_data, uint8_t* clear_data,
|
||||
OEMCryptoBufferType buffer_type, const uint8_t (&iv)[wvoec::KEY_IV_SIZE],
|
||||
const OEMCrypto_CENCEncryptPatternDesc* pattern);
|
||||
OEMCryptoResult ChooseDecrypt(const uint8_t* iv, size_t block_offset,
|
||||
const OEMCrypto_CENCEncryptPatternDesc* pattern,
|
||||
const uint8_t* cipher_data,
|
||||
|
||||
@@ -34,7 +34,7 @@ void advance_iv_ctr(uint8_t (*subsample_iv)[wvoec::KEY_IV_SIZE], size_t bytes) {
|
||||
constexpr size_t half_iv_size = wvoec::KEY_IV_SIZE / 2;
|
||||
memcpy(&counter, &(*subsample_iv)[half_iv_size], half_iv_size);
|
||||
|
||||
size_t increment =
|
||||
const size_t increment =
|
||||
bytes / wvoec::AES_128_BLOCK_SIZE; // The truncation here is intentional
|
||||
counter = wvcdm::htonll64(wvcdm::ntohll64(counter) + increment);
|
||||
|
||||
@@ -82,7 +82,8 @@ OEMCryptoResult DecryptFallbackChain::DecryptSample(
|
||||
for (size_t i = 0; i < sample.subsamples_length; ++i) {
|
||||
const OEMCrypto_SubSampleDescription& subsample = sample.subsamples[i];
|
||||
|
||||
size_t length = subsample.num_bytes_clear + subsample.num_bytes_encrypted;
|
||||
const size_t length =
|
||||
subsample.num_bytes_clear + subsample.num_bytes_encrypted;
|
||||
fake_sample.buffers.input_data_length = length;
|
||||
fake_sample.subsamples = &subsample;
|
||||
fake_sample.subsamples_length = 1;
|
||||
|
||||
@@ -26,11 +26,12 @@
|
||||
|
||||
#include "OEMCryptoCENC.h"
|
||||
#include "clock.h"
|
||||
#include "core_message_deserialize.h"
|
||||
#include "core_message_serialize.h"
|
||||
#include "disallow_copy_and_assign.h"
|
||||
#include "log.h"
|
||||
#include "oec_device_features.h"
|
||||
#include "oec_test_data.h"
|
||||
#include "oec_util.h"
|
||||
#include "oemcrypto_types.h"
|
||||
#include "platform.h"
|
||||
#include "string_conversions.h"
|
||||
@@ -67,20 +68,20 @@ int GetRandBytes(unsigned char* buf, int num) {
|
||||
// is a common operation for tests. Generates a random IV which can be used to
|
||||
// encrypt the input buffer.
|
||||
void GenerateSimpleSampleDescription(
|
||||
const uint8_t* input_data, size_t input_data_length, uint8_t* output_buffer,
|
||||
size_t output_buffer_length, OEMCrypto_SampleDescription* sample,
|
||||
const std::vector<uint8_t>& in, std::vector<uint8_t>& out,
|
||||
OEMCrypto_SampleDescription* sample,
|
||||
OEMCrypto_SubSampleDescription* subsample) {
|
||||
ASSERT_NE(nullptr, sample);
|
||||
ASSERT_NE(nullptr, subsample);
|
||||
ASSERT_NE(sample, nullptr);
|
||||
ASSERT_NE(subsample, nullptr);
|
||||
|
||||
// Generate test data
|
||||
EXPECT_EQ(1, GetRandBytes(&sample->iv[0], KEY_IV_SIZE));
|
||||
EXPECT_EQ(GetRandBytes(&sample->iv[0], KEY_IV_SIZE), 1);
|
||||
|
||||
// Describe the test data
|
||||
sample->buffers.input_data = input_data;
|
||||
sample->buffers.input_data_length = input_data_length;
|
||||
sample->buffers.input_data = in.data();
|
||||
sample->buffers.input_data_length = in.size();
|
||||
subsample->num_bytes_clear = 0;
|
||||
subsample->num_bytes_encrypted = input_data_length;
|
||||
subsample->num_bytes_encrypted = sample->buffers.input_data_length;
|
||||
subsample->subsample_flags =
|
||||
OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample;
|
||||
subsample->block_offset = 0;
|
||||
@@ -91,8 +92,8 @@ void GenerateSimpleSampleDescription(
|
||||
OEMCrypto_DestBufferDesc& out_buffer_descriptor =
|
||||
sample->buffers.output_descriptor;
|
||||
out_buffer_descriptor.type = OEMCrypto_BufferType_Clear;
|
||||
out_buffer_descriptor.buffer.clear.address = output_buffer;
|
||||
out_buffer_descriptor.buffer.clear.address_length = output_buffer_length;
|
||||
out_buffer_descriptor.buffer.clear.address = out.data();
|
||||
out_buffer_descriptor.buffer.clear.address_length = out.size();
|
||||
}
|
||||
|
||||
// Increment counter for AES-CTR. The CENC spec specifies we increment only
|
||||
@@ -244,7 +245,8 @@ void ProvisioningRoundTrip::VerifyRequestSignature(
|
||||
void ProvisioningRoundTrip::FillAndVerifyCoreRequest(
|
||||
const std::string& core_message_string) {
|
||||
EXPECT_TRUE(
|
||||
oec_util::ParseProvisioningRequest(core_message_string, &core_request_));
|
||||
oemcrypto_core_message::deserialize::CoreProvisioningRequestFromMessage(
|
||||
core_message_string, &core_request_));
|
||||
EXPECT_EQ(global_features.api_version, core_request_.api_version);
|
||||
EXPECT_EQ(session()->nonce(), core_request_.nonce);
|
||||
EXPECT_EQ(session()->session_id(), core_request_.session_id);
|
||||
@@ -294,8 +296,8 @@ void ProvisioningRoundTrip::EncryptAndSignResponse() {
|
||||
&encrypted_response_data_);
|
||||
core_response_.enc_private_key.length =
|
||||
encrypted_response_data_.rsa_key_length;
|
||||
ASSERT_TRUE(CreateCoreProvisioningResponse(core_response_, core_request_,
|
||||
&serialized_core_message_));
|
||||
ASSERT_TRUE(oemcrypto_core_message::serialize::CreateCoreProvisioningResponse(
|
||||
core_response_, core_request_, &serialized_core_message_));
|
||||
// Stripe the encrypted message.
|
||||
encrypted_response_.resize(message_size_);
|
||||
for (size_t i = 0; i < encrypted_response_.size(); i++) {
|
||||
@@ -318,6 +320,7 @@ void ProvisioningRoundTrip::EncryptAndSignResponse() {
|
||||
}
|
||||
|
||||
OEMCryptoResult ProvisioningRoundTrip::LoadResponse(Session* session) {
|
||||
EXPECT_NE(session, nullptr);
|
||||
size_t wrapped_key_length = 0;
|
||||
const OEMCryptoResult sts = OEMCrypto_LoadProvisioning(
|
||||
session->session_id(), encrypted_response_.data(),
|
||||
@@ -352,7 +355,8 @@ void LicenseRoundTrip::VerifyRequestSignature(
|
||||
void LicenseRoundTrip::FillAndVerifyCoreRequest(
|
||||
const std::string& core_message_string) {
|
||||
EXPECT_TRUE(
|
||||
oec_util::ParseLicenseRequest(core_message_string, &core_request_));
|
||||
oemcrypto_core_message::deserialize::CoreLicenseRequestFromMessage(
|
||||
core_message_string, &core_request_));
|
||||
EXPECT_EQ(global_features.api_version, core_request_.api_version);
|
||||
if (expect_request_has_correct_nonce_) {
|
||||
EXPECT_EQ(session()->nonce(), core_request_.nonce);
|
||||
@@ -489,8 +493,8 @@ void LicenseRoundTrip::EncryptAndSignResponse() {
|
||||
if (api_version_ < kCoreMessagesAPI) {
|
||||
serialized_core_message_.resize(0);
|
||||
} else {
|
||||
ASSERT_TRUE(CreateCoreLicenseResponse(core_response_, core_request_,
|
||||
&serialized_core_message_));
|
||||
ASSERT_TRUE(oemcrypto_core_message::serialize::CreateCoreLicenseResponse(
|
||||
core_response_, core_request_, &serialized_core_message_));
|
||||
}
|
||||
|
||||
// Stripe the encrypted message.
|
||||
@@ -516,6 +520,7 @@ void LicenseRoundTrip::EncryptAndSignResponse() {
|
||||
}
|
||||
|
||||
OEMCryptoResult LicenseRoundTrip::LoadResponse(Session* session) {
|
||||
EXPECT_NE(session, nullptr);
|
||||
// Some tests adjust the offset to be beyond the length of the message. Here,
|
||||
// we create a duplicate of the message buffer so that these offsets do not
|
||||
// point to garbage data. The goal is to make sure OEMCrypto is verifying that
|
||||
@@ -747,14 +752,16 @@ void RenewalRoundTrip::FillAndVerifyCoreRequest(
|
||||
if (license_messages_->api_version() < kCoreMessagesAPI) {
|
||||
// For v15, we expect that no core request was created.
|
||||
EXPECT_FALSE(
|
||||
oec_util::ParseRenewalRequest(core_message_string, &core_request_));
|
||||
oemcrypto_core_message::deserialize::CoreRenewalRequestFromMessage(
|
||||
core_message_string, &core_request_));
|
||||
} else {
|
||||
EXPECT_TRUE(
|
||||
oec_util::ParseRenewalRequest(core_message_string, &core_request_));
|
||||
oemcrypto_core_message::deserialize::CoreRenewalRequestFromMessage(
|
||||
core_message_string, &core_request_));
|
||||
EXPECT_EQ(license_messages_->core_request().api_version,
|
||||
core_request_.api_version);
|
||||
if (!is_release_) {
|
||||
// For a license release, we don not expect the nonce to be correct. That
|
||||
// For a license release, we do not expect the nonce to be correct. That
|
||||
// is because a release might be sent without loading the license first.
|
||||
EXPECT_EQ(license_messages_->core_request().nonce, core_request_.nonce);
|
||||
EXPECT_EQ(license_messages_->core_request().session_id,
|
||||
@@ -810,8 +817,8 @@ void RenewalRoundTrip::EncryptAndSignResponse() {
|
||||
sizeof(response_data_.keys[0].control));
|
||||
serialized_core_message_.resize(0);
|
||||
} else {
|
||||
ASSERT_TRUE(
|
||||
CreateCoreRenewalResponse(core_request_, &serialized_core_message_));
|
||||
ASSERT_TRUE(oemcrypto_core_message::serialize::CreateCoreRenewalResponse(
|
||||
core_request_, &serialized_core_message_));
|
||||
}
|
||||
// Concatenate the core message and the response.
|
||||
ASSERT_GE(kMaxCoreMessage, serialized_core_message_.size());
|
||||
@@ -847,12 +854,7 @@ Session::Session()
|
||||
forced_session_id_(false),
|
||||
session_id_(0),
|
||||
nonce_(0),
|
||||
public_rsa_(0),
|
||||
message_size_(sizeof(MessageData)),
|
||||
// Most tests only use 4 keys. Other tests will explicitly call
|
||||
// set_num_keys.
|
||||
num_keys_(4) {
|
||||
}
|
||||
public_rsa_(0) {}
|
||||
|
||||
Session::~Session() {
|
||||
if (!forced_session_id_ && open_) close();
|
||||
@@ -953,524 +955,6 @@ void Session::GenerateDerivedKeysFromSessionKey() {
|
||||
key_deriver_.DeriveKeys(session_key.data(), mac_context, enc_context);
|
||||
}
|
||||
|
||||
void Session::LoadTestKeys(const std::string& provider_session_token,
|
||||
bool new_mac_keys) {
|
||||
std::string message =
|
||||
wvcdm::BytesToString(message_ptr(), sizeof(MessageData));
|
||||
OEMCrypto_Substring pst = GetSubstring(message, provider_session_token);
|
||||
OEMCrypto_Substring enc_mac_keys_iv = GetSubstring(
|
||||
message, wvcdm::BytesToString(encrypted_license().mac_key_iv,
|
||||
sizeof(encrypted_license().mac_key_iv)));
|
||||
OEMCrypto_Substring enc_mac_keys = GetSubstring(
|
||||
message, wvcdm::BytesToString(encrypted_license().mac_keys,
|
||||
sizeof(encrypted_license().mac_keys)));
|
||||
if (new_mac_keys) {
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS,
|
||||
OEMCrypto_LoadKeys(
|
||||
session_id(), message_ptr(), message_size_, signature_.data(),
|
||||
signature_.size(), enc_mac_keys_iv, enc_mac_keys, num_keys_,
|
||||
key_array_, pst, GetSubstring(), OEMCrypto_ContentLicense));
|
||||
// Update new generated keys.
|
||||
key_deriver_.set_mac_keys(license_.mac_keys);
|
||||
} else {
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS,
|
||||
OEMCrypto_LoadKeys(
|
||||
session_id(), message_ptr(), message_size_, signature_.data(),
|
||||
signature_.size(), GetSubstring(), GetSubstring(), num_keys_,
|
||||
key_array_, pst, GetSubstring(), OEMCrypto_ContentLicense));
|
||||
}
|
||||
VerifyTestKeys();
|
||||
}
|
||||
|
||||
void Session::LoadEntitlementTestKeys(const std::string& provider_session_token,
|
||||
bool new_mac_keys,
|
||||
OEMCryptoResult expected_sts) {
|
||||
std::string message =
|
||||
wvcdm::BytesToString(message_ptr(), sizeof(MessageData));
|
||||
OEMCrypto_Substring pst = GetSubstring(message, provider_session_token);
|
||||
OEMCrypto_Substring enc_mac_keys_iv = GetSubstring(
|
||||
message, wvcdm::BytesToString(encrypted_license().mac_key_iv,
|
||||
sizeof(encrypted_license().mac_key_iv)));
|
||||
OEMCrypto_Substring enc_mac_keys = GetSubstring(
|
||||
message, wvcdm::BytesToString(encrypted_license().mac_keys,
|
||||
sizeof(encrypted_license().mac_keys)));
|
||||
if (new_mac_keys) {
|
||||
ASSERT_EQ(
|
||||
expected_sts,
|
||||
OEMCrypto_LoadKeys(session_id(), message_ptr(), message_size_,
|
||||
signature_.data(), signature_.size(),
|
||||
enc_mac_keys_iv, enc_mac_keys, num_keys_,
|
||||
key_array_, pst, GetSubstring(),
|
||||
OEMCrypto_EntitlementLicense));
|
||||
// Update new generated keys.
|
||||
key_deriver_.set_mac_keys(license_.mac_keys);
|
||||
} else {
|
||||
ASSERT_EQ(
|
||||
expected_sts,
|
||||
OEMCrypto_LoadKeys(session_id(), message_ptr(), message_size_,
|
||||
signature_.data(), signature_.size(), GetSubstring(),
|
||||
GetSubstring(), num_keys_, key_array_, pst,
|
||||
GetSubstring(), OEMCrypto_EntitlementLicense));
|
||||
}
|
||||
}
|
||||
|
||||
void Session::FillEntitledKeyArray() {
|
||||
int offset = 0;
|
||||
entitled_message_.clear();
|
||||
for (size_t i = 0; i < num_keys_; ++i) {
|
||||
EntitledContentKeyData* key_data = &entitled_key_data_[i];
|
||||
|
||||
entitled_key_array_[i].entitlement_key_id.offset = offset;
|
||||
entitled_key_array_[i].entitlement_key_id.length =
|
||||
key_array_[i].key_id.length;
|
||||
offset += key_array_[i].key_id.length;
|
||||
entitled_message_ +=
|
||||
wvcdm::BytesToString(message_ptr() + key_array_[i].key_id.offset,
|
||||
key_array_[i].key_id.length);
|
||||
|
||||
EXPECT_EQ(1, GetRandBytes(key_data->content_key_id,
|
||||
sizeof(key_data->content_key_id)));
|
||||
entitled_key_array_[i].content_key_id.offset = offset;
|
||||
entitled_key_array_[i].content_key_id.length =
|
||||
sizeof(key_data->content_key_id);
|
||||
offset += sizeof(key_data->content_key_id);
|
||||
entitled_message_ += wvcdm::BytesToString(key_data->content_key_id,
|
||||
sizeof(key_data->content_key_id));
|
||||
|
||||
EXPECT_EQ(1, GetRandBytes(key_data->content_key_data,
|
||||
sizeof(key_data->content_key_data)));
|
||||
entitled_key_array_[i].content_key_data.offset = offset;
|
||||
entitled_key_array_[i].content_key_data.length =
|
||||
sizeof(key_data->content_key_data);
|
||||
offset += sizeof(key_data->content_key_data);
|
||||
entitled_message_ += wvcdm::BytesToString(
|
||||
key_data->content_key_data, sizeof(key_data->content_key_data));
|
||||
|
||||
EXPECT_EQ(1, GetRandBytes(key_data[i].content_key_data_iv,
|
||||
sizeof(key_data[i].content_key_data_iv)));
|
||||
entitled_key_array_[i].content_key_data_iv.offset = offset;
|
||||
entitled_key_array_[i].content_key_data_iv.length =
|
||||
sizeof(key_data->content_key_data_iv);
|
||||
offset += sizeof(key_data->content_key_data_iv);
|
||||
entitled_message_ += wvcdm::BytesToString(
|
||||
key_data->content_key_data_iv, sizeof(key_data->content_key_data_iv));
|
||||
}
|
||||
}
|
||||
|
||||
void Session::LoadEntitledContentKeys(OEMCryptoResult expected_sts) {
|
||||
encrypted_entitled_message_ = entitled_message_;
|
||||
std::vector<OEMCrypto_EntitledContentKeyObject> encrypted_entitled_key_array(
|
||||
entitled_key_array_, entitled_key_array_ + num_keys_);
|
||||
|
||||
for (size_t i = 0; i < num_keys_; ++i) {
|
||||
// Load the entitlement key from |key_array_|.
|
||||
AES_KEY aes_key;
|
||||
AES_set_encrypt_key(message_ptr() + key_array_[i].key_data.offset, 256,
|
||||
&aes_key);
|
||||
|
||||
// Encrypt the content key with the entitlement key.
|
||||
uint8_t iv[16];
|
||||
const uint8_t* content_key_data = reinterpret_cast<const uint8_t*>(
|
||||
entitled_message_.data() +
|
||||
entitled_key_array_[i].content_key_data.offset);
|
||||
const uint8_t* encrypted_content_key_data =
|
||||
reinterpret_cast<const uint8_t*>(
|
||||
encrypted_entitled_message_.data() +
|
||||
encrypted_entitled_key_array[i].content_key_data.offset);
|
||||
memcpy(&iv[0],
|
||||
message_ptr() +
|
||||
encrypted_entitled_key_array[i].content_key_data_iv.offset,
|
||||
16);
|
||||
AES_cbc_encrypt(content_key_data,
|
||||
const_cast<uint8_t*>(encrypted_content_key_data),
|
||||
encrypted_entitled_key_array[i].content_key_data.length,
|
||||
&aes_key, iv, AES_ENCRYPT);
|
||||
}
|
||||
ASSERT_EQ(
|
||||
expected_sts,
|
||||
OEMCrypto_LoadEntitledContentKeys(
|
||||
session_id(),
|
||||
reinterpret_cast<const uint8_t*>(encrypted_entitled_message_.data()),
|
||||
encrypted_entitled_message_.size(),
|
||||
encrypted_entitled_key_array.size(),
|
||||
encrypted_entitled_key_array.data()));
|
||||
if (expected_sts != OEMCrypto_SUCCESS) {
|
||||
return;
|
||||
}
|
||||
VerifyEntitlementTestKeys();
|
||||
}
|
||||
|
||||
// This function verifies that the key control block reported by OEMCrypto agree
|
||||
// with the truth key control block. Failures in this function probably
|
||||
// indicate the OEMCrypto_LoadKeys did not correctly process the key control
|
||||
// block.
|
||||
void Session::VerifyTestKeys() {
|
||||
for (unsigned int i = 0; i < num_keys_; i++) {
|
||||
KeyControlBlock block;
|
||||
size_t size = sizeof(block);
|
||||
OEMCryptoResult sts = OEMCrypto_QueryKeyControl(
|
||||
session_id(), license_.keys[i].key_id, license_.keys[i].key_id_length,
|
||||
reinterpret_cast<uint8_t*>(&block), &size);
|
||||
if (sts != OEMCrypto_ERROR_NOT_IMPLEMENTED) {
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
|
||||
ASSERT_EQ(sizeof(block), size);
|
||||
// control duration and bits stored in network byte order. For printing
|
||||
// we change to host byte order.
|
||||
ASSERT_EQ((htonl_fnc(license_.keys[i].control.duration)),
|
||||
(htonl_fnc(block.duration)))
|
||||
<< "For key " << i;
|
||||
ASSERT_EQ(htonl_fnc(license_.keys[i].control.control_bits),
|
||||
htonl_fnc(block.control_bits))
|
||||
<< "For key " << i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This function verifies that the key control block reported by OEMCrypto agree
|
||||
// with the truth key control block. Failures in this function probably
|
||||
// indicate the OEMCrypto_LoadEntitledKeys did not correctly process the key
|
||||
// control block.
|
||||
void Session::VerifyEntitlementTestKeys() {
|
||||
for (unsigned int i = 0; i < num_keys_; i++) {
|
||||
KeyControlBlock block;
|
||||
size_t size = sizeof(block);
|
||||
const uint8_t* content_key_id =
|
||||
reinterpret_cast<const uint8_t*>(entitled_message_.data());
|
||||
OEMCryptoResult sts = OEMCrypto_QueryKeyControl(
|
||||
session_id(),
|
||||
content_key_id + entitled_key_array_[i].content_key_id.offset,
|
||||
entitled_key_array_[i].content_key_id.length,
|
||||
reinterpret_cast<uint8_t*>(&block), &size);
|
||||
if (sts != OEMCrypto_ERROR_NOT_IMPLEMENTED) {
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
|
||||
ASSERT_EQ(sizeof(block), size);
|
||||
// control duration and bits stored in network byte order. For printing
|
||||
// we change to host byte order.
|
||||
ASSERT_EQ((htonl_fnc(license_.keys[i].control.duration)),
|
||||
(htonl_fnc(block.duration)))
|
||||
<< "For key " << i;
|
||||
ASSERT_EQ(htonl_fnc(license_.keys[i].control.control_bits),
|
||||
htonl_fnc(block.control_bits))
|
||||
<< "For key " << i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Session::RefreshTestKeys(const size_t key_count, uint32_t control_bits,
|
||||
uint32_t nonce, OEMCryptoResult expected_result) {
|
||||
// Note: we store the message in encrypted_license_, but the refresh key
|
||||
// message is not actually encrypted. It is, however, signed.
|
||||
// FillRefreshMessage fills the message with a duration of kLongDuration.
|
||||
FillRefreshMessage(key_count, control_bits, nonce);
|
||||
key_deriver_.ServerSignBuffer(
|
||||
reinterpret_cast<const uint8_t*>(&padded_message_), message_size_,
|
||||
&signature_);
|
||||
std::vector<OEMCrypto_KeyRefreshObject> key_array(key_count);
|
||||
FillRefreshArray(key_array.data(), key_count);
|
||||
OEMCryptoResult sts = OEMCrypto_RefreshKeys(
|
||||
session_id(), message_ptr(), message_size_, signature_.data(),
|
||||
signature_.size(), key_count, key_array.data());
|
||||
ASSERT_EQ(expected_result, sts);
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(TestDecryptCTR());
|
||||
// This should still be valid key, even if the refresh failed, because this
|
||||
// is before the original license duration.
|
||||
wvcdm::TestSleep::Sleep(kShortSleep);
|
||||
ASSERT_NO_FATAL_FAILURE(TestDecryptCTR(false));
|
||||
// This should be after duration of the original license, but before the
|
||||
// expiration of the refresh message. This should succeed if and only if the
|
||||
// refresh succeeded.
|
||||
wvcdm::TestSleep::Sleep(kShortSleep + kLongSleep);
|
||||
if (expected_result == OEMCrypto_SUCCESS) {
|
||||
ASSERT_NO_FATAL_FAILURE(TestDecryptCTR(false, OEMCrypto_SUCCESS));
|
||||
} else {
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE));
|
||||
}
|
||||
}
|
||||
|
||||
void Session::FillSimpleMessage(uint32_t duration, uint32_t control,
|
||||
uint32_t nonce, const std::string& pst) {
|
||||
EXPECT_EQ(
|
||||
1, GetRandBytes(license_.mac_key_iv, sizeof(license_.mac_key_iv)));
|
||||
memset(license_.padding, 0, sizeof(license_.padding));
|
||||
EXPECT_EQ(1, GetRandBytes(license_.mac_keys, sizeof(license_.mac_keys)));
|
||||
for (unsigned int i = 0; i < num_keys_; i++) {
|
||||
memset(license_.keys[i].key_id, 0, kTestKeyIdMaxLength);
|
||||
license_.keys[i].key_id_length = kDefaultKeyIdLength;
|
||||
memset(license_.keys[i].key_id, i, license_.keys[i].key_id_length);
|
||||
EXPECT_EQ(1, GetRandBytes(license_.keys[i].key_data,
|
||||
sizeof(license_.keys[i].key_data)));
|
||||
license_.keys[i].key_data_length = KEY_SIZE;
|
||||
EXPECT_EQ(1, GetRandBytes(license_.keys[i].key_iv,
|
||||
sizeof(license_.keys[i].key_iv)));
|
||||
EXPECT_EQ(1, GetRandBytes(license_.keys[i].control_iv,
|
||||
sizeof(license_.keys[i].control_iv)));
|
||||
if (global_features.api_version >= 12) {
|
||||
// For version 12 and above, we require OEMCrypto to handle kcNN for all
|
||||
// licenses.
|
||||
std::string kcVersion =
|
||||
"kc" + std::to_string(global_features.api_version);
|
||||
memcpy(license_.keys[i].control.verification, kcVersion.c_str(), 4);
|
||||
} else if (control & wvoec::kControlSecurityPatchLevelMask) {
|
||||
// For versions before 12, we require the special key control block only
|
||||
// when there are newer features present.
|
||||
memcpy(license_.keys[i].control.verification, "kc11", 4);
|
||||
} else if (control & wvoec::kControlRequireAntiRollbackHardware) {
|
||||
memcpy(license_.keys[i].control.verification, "kc10", 4);
|
||||
} else if (control & (wvoec::kControlHDCPVersionMask |
|
||||
wvoec::kControlReplayMask)) {
|
||||
memcpy(license_.keys[i].control.verification, "kc09", 4);
|
||||
} else {
|
||||
memcpy(license_.keys[i].control.verification, "kctl", 4);
|
||||
}
|
||||
license_.keys[i].control.duration = htonl(duration);
|
||||
license_.keys[i].control.nonce = htonl(nonce);
|
||||
license_.keys[i].control.control_bits = htonl(control);
|
||||
license_.keys[i].cipher_mode = OEMCrypto_CipherMode_CTR;
|
||||
}
|
||||
memcpy(license_.pst, pst.c_str(), min(sizeof(license_.pst), pst.length()));
|
||||
pst_ = pst;
|
||||
}
|
||||
|
||||
void Session::FillSimpleEntitlementMessage(
|
||||
uint32_t duration, uint32_t control, uint32_t nonce,
|
||||
const std::string& pst) {
|
||||
EXPECT_EQ(
|
||||
1, GetRandBytes(license_.mac_key_iv, sizeof(license_.mac_key_iv)));
|
||||
EXPECT_EQ(1, GetRandBytes(license_.mac_keys, sizeof(license_.mac_keys)));
|
||||
for (unsigned int i = 0; i < num_keys_; i++) {
|
||||
memset(license_.keys[i].key_id, 0, kTestKeyIdMaxLength);
|
||||
license_.keys[i].key_id_length = kDefaultKeyIdLength;
|
||||
memset(license_.keys[i].key_id, i, license_.keys[i].key_id_length);
|
||||
EXPECT_EQ(1, GetRandBytes(license_.keys[i].key_data,
|
||||
sizeof(license_.keys[i].key_data)));
|
||||
license_.keys[i].key_data_length = KEY_SIZE * 2; // AES-256 keys
|
||||
EXPECT_EQ(1, GetRandBytes(license_.keys[i].key_iv,
|
||||
sizeof(license_.keys[i].key_iv)));
|
||||
EXPECT_EQ(1, GetRandBytes(license_.keys[i].control_iv,
|
||||
sizeof(license_.keys[i].control_iv)));
|
||||
if (global_features.api_version >= 12) {
|
||||
// For version 12 and above, we require OEMCrypto to handle kcNN for all
|
||||
// licenses.
|
||||
std::string kcVersion =
|
||||
"kc" + std::to_string(global_features.api_version);
|
||||
memcpy(license_.keys[i].control.verification, kcVersion.c_str(), 4);
|
||||
} else if (control & wvoec::kControlSecurityPatchLevelMask) {
|
||||
// For versions before 12, we require the special key control block only
|
||||
// when there are newer features present.
|
||||
memcpy(license_.keys[i].control.verification, "kc11", 4);
|
||||
} else if (control & wvoec::kControlRequireAntiRollbackHardware) {
|
||||
memcpy(license_.keys[i].control.verification, "kc10", 4);
|
||||
} else if (control & (wvoec::kControlHDCPVersionMask |
|
||||
wvoec::kControlReplayMask)) {
|
||||
memcpy(license_.keys[i].control.verification, "kc09", 4);
|
||||
} else {
|
||||
memcpy(license_.keys[i].control.verification, "kctl", 4);
|
||||
}
|
||||
license_.keys[i].control.duration = htonl(duration);
|
||||
license_.keys[i].control.nonce = htonl(nonce);
|
||||
license_.keys[i].control.control_bits = htonl(control);
|
||||
license_.keys[i].cipher_mode = OEMCrypto_CipherMode_CTR;
|
||||
}
|
||||
memcpy(license_.pst, pst.c_str(), min(sizeof(license_.pst), pst.length()));
|
||||
pst_ = pst;
|
||||
}
|
||||
|
||||
void Session::FillRefreshMessage(size_t key_count, uint32_t control_bits,
|
||||
uint32_t nonce) {
|
||||
for (unsigned int i = 0; i < key_count; i++) {
|
||||
encrypted_license().keys[i].key_id_length = license_.keys[i].key_id_length;
|
||||
memcpy(encrypted_license().keys[i].key_id, license_.keys[i].key_id,
|
||||
encrypted_license().keys[i].key_id_length);
|
||||
if (global_features.api_version >= 12) {
|
||||
// For version 12 and above, we require OEMCrypto to handle kcNN for all
|
||||
// licenses.
|
||||
std::string kcVersion =
|
||||
"kc" + std::to_string(global_features.api_version);
|
||||
memcpy(encrypted_license().keys[i].control.verification,
|
||||
kcVersion.c_str(), 4);
|
||||
} else {
|
||||
// For versions before 12, we require the special key control block only
|
||||
// when there are newer features present.
|
||||
memcpy(encrypted_license().keys[i].control.verification, "kctl", 4);
|
||||
}
|
||||
encrypted_license().keys[i].control.duration = htonl(kLongDuration);
|
||||
encrypted_license().keys[i].control.nonce = htonl(nonce);
|
||||
encrypted_license().keys[i].control.control_bits = htonl(control_bits);
|
||||
}
|
||||
}
|
||||
|
||||
void Session::SetLoadKeysSubstringParams() {
|
||||
load_keys_params_.resize(4);
|
||||
std::string message =
|
||||
wvcdm::BytesToString(message_ptr(), sizeof(MessageData));
|
||||
OEMCrypto_Substring* enc_mac_keys_iv = load_keys_params_.data();
|
||||
*enc_mac_keys_iv = GetSubstring(
|
||||
message, wvcdm::BytesToString(encrypted_license().mac_key_iv,
|
||||
sizeof(encrypted_license().mac_key_iv)));
|
||||
OEMCrypto_Substring* enc_mac_keys = &load_keys_params_[1];
|
||||
*enc_mac_keys = GetSubstring(
|
||||
message, wvcdm::BytesToString(encrypted_license().mac_keys,
|
||||
sizeof(encrypted_license().mac_keys)));
|
||||
OEMCrypto_Substring* pst = &load_keys_params_[2];
|
||||
size_t pst_length =
|
||||
strlen(reinterpret_cast<const char*>(encrypted_license().pst));
|
||||
*pst = GetSubstring(
|
||||
message, wvcdm::BytesToString(encrypted_license().pst, pst_length));
|
||||
OEMCrypto_Substring* srm_req = &load_keys_params_[3];
|
||||
*srm_req = GetSubstring();
|
||||
}
|
||||
|
||||
void Session::EncryptAndSign() {
|
||||
ASSERT_NO_FATAL_FAILURE(GenerateDerivedKeysFromSessionKey());
|
||||
encrypted_license() = license_;
|
||||
|
||||
uint8_t iv_buffer[16];
|
||||
memcpy(iv_buffer, &license_.mac_key_iv[0], KEY_IV_SIZE);
|
||||
key_deriver_.CBCEncrypt(&license_.mac_keys[0],
|
||||
&encrypted_license().mac_keys[0], 2 * MAC_KEY_SIZE,
|
||||
license_.mac_key_iv);
|
||||
|
||||
for (unsigned int i = 0; i < num_keys_; i++) {
|
||||
memcpy(iv_buffer, &license_.keys[i].control_iv[0], KEY_IV_SIZE);
|
||||
AES_KEY aes_key;
|
||||
AES_set_encrypt_key(&license_.keys[i].key_data[0], 128, &aes_key);
|
||||
AES_cbc_encrypt(
|
||||
reinterpret_cast<const uint8_t*>(&license_.keys[i].control),
|
||||
reinterpret_cast<uint8_t*>(&encrypted_license().keys[i].control),
|
||||
KEY_SIZE, &aes_key, iv_buffer, AES_ENCRYPT);
|
||||
key_deriver_.CBCEncrypt(
|
||||
&license_.keys[i].key_data[0], &encrypted_license().keys[i].key_data[0],
|
||||
license_.keys[i].key_data_length, license_.keys[i].key_iv);
|
||||
}
|
||||
memcpy(encrypted_license().pst, license_.pst, sizeof(license_.pst));
|
||||
key_deriver_.ServerSignBuffer(
|
||||
reinterpret_cast<const uint8_t*>(&padded_message_), message_size_,
|
||||
&signature_);
|
||||
FillKeyArray(encrypted_license(), key_array_);
|
||||
SetLoadKeysSubstringParams();
|
||||
}
|
||||
|
||||
void Session::VerifyLicenseRequestSignature(size_t data_length) {
|
||||
// In the real world, a message should be signed by the client and
|
||||
// verified by the server. This simulates that.
|
||||
vector<uint8_t> data(data_length);
|
||||
for (size_t i = 0; i < data.size(); i++) data[i] = i & 0xFF;
|
||||
OEMCryptoResult sts;
|
||||
size_t gen_signature_length = 0;
|
||||
size_t core_message_length = 0;
|
||||
sts = OEMCrypto_PrepAndSignLicenseRequest(session_id(), data.data(),
|
||||
data.size(), &core_message_length,
|
||||
nullptr, &gen_signature_length);
|
||||
ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts);
|
||||
vector<uint8_t> gen_signature(gen_signature_length);
|
||||
sts = OEMCrypto_PrepAndSignLicenseRequest(
|
||||
session_id(), data.data(), data.size(), &core_message_length,
|
||||
gen_signature.data(), &gen_signature_length);
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
|
||||
if (global_features.api_version >= kCoreMessagesAPI) {
|
||||
ASSERT_GT(data.size(), core_message_length);
|
||||
std::string core_message(reinterpret_cast<char*>(data.data()),
|
||||
core_message_length);
|
||||
VerifyCoreLicenseRequest(core_message);
|
||||
}
|
||||
VerifyRSASignature(data, gen_signature.data(), gen_signature.size(),
|
||||
kSign_RSASSA_PSS);
|
||||
}
|
||||
|
||||
void Session::VerifyRenewalRequestSignature(size_t data_length) {
|
||||
// In the real world, a message should be signed by the client and
|
||||
// verified by the server. This simulates that.
|
||||
vector<uint8_t> data(data_length);
|
||||
for (size_t i = 0; i < data.size(); i++) data[i] = i & 0xFF;
|
||||
OEMCryptoResult sts;
|
||||
size_t gen_signature_length = 0;
|
||||
size_t core_message_length = 0;
|
||||
sts = OEMCrypto_PrepAndSignRenewalRequest(session_id(), data.data(),
|
||||
data.size(), &core_message_length,
|
||||
nullptr, &gen_signature_length);
|
||||
ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts);
|
||||
ASSERT_EQ(HMAC_SHA256_SIGNATURE_SIZE, gen_signature_length);
|
||||
vector<uint8_t> gen_signature(gen_signature_length);
|
||||
sts = OEMCrypto_PrepAndSignRenewalRequest(
|
||||
session_id(), data.data(), data.size(), &core_message_length,
|
||||
gen_signature.data(), &gen_signature_length);
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
|
||||
if (global_features.api_version >= kCoreMessagesAPI) {
|
||||
ASSERT_GT(data.size(), core_message_length);
|
||||
std::string core_message(reinterpret_cast<char*>(data.data()),
|
||||
core_message_length);
|
||||
VerifyCoreRenewalRequest(core_message);
|
||||
}
|
||||
std::vector<uint8_t> expected_signature;
|
||||
key_deriver_.ClientSignBuffer(data, &expected_signature);
|
||||
ASSERT_EQ(expected_signature, gen_signature);
|
||||
}
|
||||
|
||||
void Session::VerifyCoreLicenseRequest(const std::string& core_message) {
|
||||
oec_util::ODK_LicenseRequest core_license_request;
|
||||
EXPECT_TRUE(
|
||||
oec_util::ParseLicenseRequest(core_message, &core_license_request));
|
||||
EXPECT_EQ(global_features.api_version, core_license_request.api_version);
|
||||
if (nonce_) EXPECT_EQ(nonce_, core_license_request.nonce);
|
||||
EXPECT_EQ(session_id_, core_license_request.session_id);
|
||||
}
|
||||
|
||||
void Session::VerifyCoreRenewalRequest(const std::string& core_message) {
|
||||
oec_util::ODK_RenewalRequest core_renewal_request;
|
||||
EXPECT_TRUE(
|
||||
oec_util::ParseRenewalRequest(core_message, &core_renewal_request));
|
||||
EXPECT_EQ(global_features.api_version, core_renewal_request.api_version);
|
||||
// We do not check the nonce, because we don't know it if it comes from a
|
||||
// previous session.
|
||||
EXPECT_EQ(session_id_, core_renewal_request.session_id);
|
||||
}
|
||||
|
||||
void Session::FillKeyArray(const MessageData& data,
|
||||
OEMCrypto_KeyObject* key_array) {
|
||||
const uint8_t* data_ptr = reinterpret_cast<const uint8_t*>(&data);
|
||||
std::string message = wvcdm::BytesToString(data_ptr, sizeof(MessageData));
|
||||
for (unsigned int i = 0; i < num_keys_; i++) {
|
||||
key_array[i].key_id = GetSubstring(
|
||||
message,
|
||||
wvcdm::BytesToString(data.keys[i].key_id, data.keys[i].key_id_length));
|
||||
key_array[i].key_data_iv = GetSubstring(
|
||||
message,
|
||||
wvcdm::BytesToString(data.keys[i].key_iv, sizeof(data.keys[i].key_iv)));
|
||||
key_array[i].key_data = GetSubstring(
|
||||
message, wvcdm::BytesToString(data.keys[i].key_data,
|
||||
data.keys[i].key_data_length));
|
||||
key_array[i].key_control_iv = GetSubstring(
|
||||
message, wvcdm::BytesToString(data.keys[i].control_iv,
|
||||
sizeof(data.keys[i].control_iv)));
|
||||
const uint8_t* key_control_ptr =
|
||||
reinterpret_cast<const uint8_t*>(&data.keys[i].control);
|
||||
key_array[i].key_control = GetSubstring(
|
||||
message,
|
||||
wvcdm::BytesToString(key_control_ptr, sizeof(data.keys[i].control)));
|
||||
}
|
||||
}
|
||||
|
||||
void Session::FillRefreshArray(OEMCrypto_KeyRefreshObject* key_array,
|
||||
size_t key_count) {
|
||||
std::string message =
|
||||
wvcdm::BytesToString(message_ptr(), sizeof(MessageData));
|
||||
for (size_t i = 0; i < key_count; i++) {
|
||||
key_array[i].key_id = GetSubstring(
|
||||
message,
|
||||
wvcdm::BytesToString(encrypted_license().keys[i].key_id,
|
||||
sizeof(encrypted_license().keys[i].key_id)),
|
||||
key_count <= 1);
|
||||
key_array[i].key_control_iv = GetSubstring();
|
||||
key_array[i].key_control = GetSubstring(
|
||||
message,
|
||||
wvcdm::BytesToString(reinterpret_cast<const uint8_t*>(
|
||||
&encrypted_license().keys[i].control),
|
||||
sizeof(encrypted_license().keys[i].control)));
|
||||
}
|
||||
}
|
||||
|
||||
void Session::EncryptCTR(const vector<uint8_t>& in_buffer, const uint8_t* key,
|
||||
const uint8_t* starting_iv,
|
||||
vector<uint8_t>* out_buffer) {
|
||||
@@ -1516,11 +1000,11 @@ void Session::TestDecryptCTR(bool select_key_first,
|
||||
OEMCrypto_SubSampleDescription subsample_description;
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(GenerateSimpleSampleDescription(
|
||||
encrypted_data.data(), encrypted_data.size(), output_buffer.data(),
|
||||
output_buffer.size(), &sample_description, &subsample_description));
|
||||
encrypted_data, output_buffer, &sample_description,
|
||||
&subsample_description));
|
||||
|
||||
// Generate test data
|
||||
EXPECT_EQ(1, GetRandBytes(unencrypted_data.data(), unencrypted_data.size()));
|
||||
EXPECT_EQ(GetRandBytes(unencrypted_data.data(), unencrypted_data.size()), 1);
|
||||
EncryptCTR(unencrypted_data, license_.keys[key_index].key_data,
|
||||
&sample_description.iv[0], &encrypted_data);
|
||||
|
||||
@@ -1532,11 +1016,11 @@ void Session::TestDecryptCTR(bool select_key_first,
|
||||
|
||||
// We only have a few errors that we test are reported.
|
||||
if (expected_result == OEMCrypto_SUCCESS) { // No error.
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
|
||||
ASSERT_EQ(unencrypted_data, output_buffer);
|
||||
ASSERT_EQ(sts, OEMCrypto_SUCCESS);
|
||||
ASSERT_EQ(output_buffer, unencrypted_data);
|
||||
} else {
|
||||
ASSERT_NO_FATAL_FAILURE(TestDecryptResult(expected_result, sts));
|
||||
ASSERT_NE(unencrypted_data, output_buffer);
|
||||
ASSERT_NE(output_buffer, unencrypted_data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1886,10 +1370,6 @@ void Session::GenerateReport(const std::string& pst,
|
||||
EXPECT_GE(kHardwareSecureClock, pst_report().clock_security_level());
|
||||
EXPECT_EQ(pst.length(), pst_report().pst_length());
|
||||
EXPECT_EQ(0, memcmp(pst.c_str(), pst_report().pst(), pst.length()));
|
||||
// Also, we the session to be able to sign the release message with the
|
||||
// correct mac keys from the usage table entry. Note that we sign both the PST
|
||||
// report above with ClientSignPstReport, and we sign the renewal request.
|
||||
ASSERT_NO_FATAL_FAILURE(VerifyRenewalRequestSignature());
|
||||
}
|
||||
|
||||
void Session::VerifyPST(const Test_PST_Report& expected) {
|
||||
@@ -1939,33 +1419,4 @@ void Session::VerifyReport(Test_PST_Report expected,
|
||||
expected.seconds_since_last_decrypt = MaybeAdjustTime(time_last_decrypt, now);
|
||||
ASSERT_NO_FATAL_FAILURE(VerifyPST(expected));
|
||||
}
|
||||
|
||||
void Session::GenerateVerifyReport(const std::string& pst,
|
||||
OEMCrypto_Usage_Entry_Status status,
|
||||
int64_t time_license_received,
|
||||
int64_t time_first_decrypt,
|
||||
int64_t time_last_decrypt) {
|
||||
ASSERT_NO_FATAL_FAILURE(GenerateReport(pst));
|
||||
Test_PST_Report expected(pst, status);
|
||||
ASSERT_NO_FATAL_FAILURE(VerifyReport(expected, time_license_received,
|
||||
time_first_decrypt, time_last_decrypt));
|
||||
// The PST report was signed above. Below we verify that the entire message
|
||||
// that is sent to the server will be signed by the right mac keys.
|
||||
ASSERT_NO_FATAL_FAILURE(VerifyRenewalRequestSignature());
|
||||
}
|
||||
|
||||
const uint8_t* Session::message_ptr() {
|
||||
return reinterpret_cast<const uint8_t*>(&encrypted_license());
|
||||
}
|
||||
|
||||
void Session::set_message_size(size_t size) {
|
||||
message_size_ = size;
|
||||
ASSERT_GE(message_size_, sizeof(MessageData));
|
||||
ASSERT_LE(message_size_, kMaxMessageSize);
|
||||
}
|
||||
|
||||
const uint8_t* Session::encrypted_entitled_message_ptr() {
|
||||
return reinterpret_cast<const uint8_t*>(encrypted_entitled_message_.data());
|
||||
}
|
||||
|
||||
} // namespace wvoec
|
||||
|
||||
@@ -13,10 +13,11 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "core_message_deserialize.h"
|
||||
#include "core_message_serialize.h"
|
||||
#include "odk.h"
|
||||
#include "oec_device_features.h"
|
||||
#include "oec_key_deriver.h"
|
||||
#include "oec_util.h"
|
||||
#include "oemcrypto_types.h"
|
||||
#include "pst_report.h"
|
||||
|
||||
@@ -63,10 +64,8 @@ constexpr size_t kTestKeyIdMaxLength = 16;
|
||||
|
||||
// Most content will use a key id that is 16 bytes long.
|
||||
constexpr int kDefaultKeyIdLength = 16;
|
||||
|
||||
constexpr size_t kMaxPSTLength = 255; // In specification.
|
||||
constexpr size_t kMaxMessageSize = 8 * 1024; // In specification.
|
||||
constexpr size_t kMaxCoreMessage = 200 * kMaxNumKeys + 200; // Rough estimate.
|
||||
constexpr size_t kMaxPSTLength = 255; // In specification.
|
||||
constexpr size_t kMaxCoreMessage = 200 * kMaxNumKeys + 200; // Rough estimate.
|
||||
|
||||
typedef struct {
|
||||
uint8_t key_id[kTestKeyIdMaxLength];
|
||||
@@ -117,10 +116,8 @@ struct EntitledContentKeyData {
|
||||
// returns 1 on success, -1 if not supported, or 0 if other failure.
|
||||
int GetRandBytes(unsigned char* buf, int num);
|
||||
|
||||
void GenerateSimpleSampleDescription(const uint8_t* input_data,
|
||||
size_t input_data_length,
|
||||
uint8_t* output_buffer,
|
||||
size_t output_buffer_length,
|
||||
void GenerateSimpleSampleDescription(const std::vector<uint8_t>& in,
|
||||
std::vector<uint8_t>& out,
|
||||
OEMCrypto_SampleDescription* sample,
|
||||
OEMCrypto_SubSampleDescription* subsample);
|
||||
|
||||
@@ -183,6 +180,7 @@ class RoundTrip {
|
||||
|
||||
// Accessors are all read/write because tests modify default values.
|
||||
Session* session() { return session_; }
|
||||
void set_session(Session* session) { session_ = session; }
|
||||
CoreRequest& core_request() { return core_request_; }
|
||||
CoreResponse& core_response() { return core_response_; }
|
||||
ResponseData& response_data() { return response_data_; }
|
||||
@@ -190,7 +188,6 @@ class RoundTrip {
|
||||
std::vector<uint8_t>& encrypted_response_buffer() {
|
||||
return encrypted_response_;
|
||||
}
|
||||
void set_session(Session* session) { session_ = session; }
|
||||
|
||||
// Set the size of the buffer used the encrypted license.
|
||||
void set_message_size(size_t size) { message_size_ = size; }
|
||||
@@ -228,10 +225,11 @@ class RoundTrip {
|
||||
};
|
||||
|
||||
class ProvisioningRoundTrip
|
||||
: public RoundTrip</* CoreRequest */ oec_util::ODK_ProvisioningRequest,
|
||||
OEMCrypto_PrepAndSignProvisioningRequest,
|
||||
/* CoreResponse */ ODK_ParsedProvisioning,
|
||||
/* ResponseData */ RSAPrivateKeyMessage> {
|
||||
: public RoundTrip<
|
||||
/* CoreRequest */ oemcrypto_core_message::ODK_ProvisioningRequest,
|
||||
OEMCrypto_PrepAndSignProvisioningRequest,
|
||||
/* CoreResponse */ ODK_ParsedProvisioning,
|
||||
/* ResponseData */ RSAPrivateKeyMessage> {
|
||||
public:
|
||||
ProvisioningRoundTrip(Session* session,
|
||||
const std::vector<uint8_t>& encoded_rsa_key)
|
||||
@@ -269,10 +267,11 @@ class ProvisioningRoundTrip
|
||||
};
|
||||
|
||||
class LicenseRoundTrip
|
||||
: public RoundTrip</* CoreRequest */ oec_util::ODK_LicenseRequest,
|
||||
OEMCrypto_PrepAndSignLicenseRequest,
|
||||
/* CoreResponse */ ODK_ParsedLicense,
|
||||
/* ResponseData */ MessageData> {
|
||||
: public RoundTrip<
|
||||
/* CoreRequest */ oemcrypto_core_message::ODK_LicenseRequest,
|
||||
OEMCrypto_PrepAndSignLicenseRequest,
|
||||
/* CoreResponse */ ODK_ParsedLicense,
|
||||
/* ResponseData */ MessageData> {
|
||||
public:
|
||||
LicenseRoundTrip(Session* session)
|
||||
: RoundTrip(session),
|
||||
@@ -285,8 +284,9 @@ class LicenseRoundTrip
|
||||
expect_request_has_correct_nonce_(true),
|
||||
license_type_(OEMCrypto_ContentLicense) {}
|
||||
void CreateDefaultResponse() override;
|
||||
// Create a license with four keys, one each that is allowed to do generic
|
||||
// encrypt, decrypt, sign and verify.
|
||||
// Create a license with four keys. Each key is responsible for one of generic
|
||||
// encrypt (key 0), decrypt (key 1), sign (key 2) and verify (key 3). Each key
|
||||
// is allowed only one type of operation.
|
||||
void CreateResponseWithGenericCryptoKeys();
|
||||
// Fill the |core_response| substrings.
|
||||
virtual void FillCoreResponseSubstrings();
|
||||
@@ -308,7 +308,8 @@ class LicenseRoundTrip
|
||||
// Set the number of keys to use in the license.
|
||||
void set_num_keys(uint32_t num_keys) { num_keys_ = num_keys; }
|
||||
uint32_t num_keys() const { return num_keys_; }
|
||||
// Set the pst for the license.
|
||||
// Get/Set the pst for the license and usage table entry.
|
||||
const std::string& pst() const { return pst_; }
|
||||
void set_pst(const std::string& pst) { pst_ = pst; }
|
||||
// Set the minimum SRM version for the license.
|
||||
void set_minimum_srm_version(uint32_t minimum_srm_version) {
|
||||
@@ -364,12 +365,13 @@ class LicenseRoundTrip
|
||||
OEMCrypto_LicenseType license_type_;
|
||||
};
|
||||
|
||||
class RenewalRoundTrip : public RoundTrip<
|
||||
/* CoreRequest */ oec_util::ODK_RenewalRequest,
|
||||
OEMCrypto_PrepAndSignRenewalRequest,
|
||||
// Renewal response info is same as request:
|
||||
/* CoreResponse */ oec_util::ODK_RenewalRequest,
|
||||
/* ResponseData */ MessageData> {
|
||||
class RenewalRoundTrip
|
||||
: public RoundTrip<
|
||||
/* CoreRequest */ oemcrypto_core_message::ODK_RenewalRequest,
|
||||
OEMCrypto_PrepAndSignRenewalRequest,
|
||||
// Renewal response info is same as request:
|
||||
/* CoreResponse */ oemcrypto_core_message::ODK_RenewalRequest,
|
||||
/* ResponseData */ MessageData> {
|
||||
public:
|
||||
RenewalRoundTrip(LicenseRoundTrip* license_messages)
|
||||
: RoundTrip(license_messages->session()),
|
||||
@@ -451,79 +453,6 @@ class Session {
|
||||
// Generate known mac and enc keys using OEMCrypto_DeriveKeysFromSessionKey
|
||||
// and also fill out enc_key_, mac_key_server_, and mac_key_client_.
|
||||
void GenerateDerivedKeysFromSessionKey();
|
||||
// Loads and verifies the keys in the message pointed to by message_ptr()
|
||||
// using OEMCrypto_LoadKeys. This message should have already been created
|
||||
// by FillSimpleMessage, modified if needed, and then encrypted and signed by
|
||||
// the server's mac key in EncryptAndSign.
|
||||
void LoadTestKeys(const std::string& pst = "", bool new_mac_keys = true);
|
||||
// Loads the entitlement keys in the message pointed to by message_ptr()
|
||||
// using OEMCrypto_LoadKeys. This message should have already been created
|
||||
// by FillSimpleEntitlementMessage, modified if needed, and then encrypted
|
||||
// and signed by the server's mac key in EncryptAndSign.
|
||||
void LoadEntitlementTestKeys(const std::string& pst = "",
|
||||
bool new_mac_keys = true,
|
||||
OEMCryptoResult expected_sts = OEMCrypto_SUCCESS);
|
||||
// Fills an OEMCrypto_EntitledContentKeyObject using the information from
|
||||
// the license_ and randomly generated content keys. This method should be
|
||||
// called after LoadEntitlementTestKeys.
|
||||
void FillEntitledKeyArray();
|
||||
// Encrypts and loads the entitled content keys via
|
||||
// OEMCrypto_LoadEntitledContentKeys.
|
||||
void LoadEntitledContentKeys(
|
||||
OEMCryptoResult expected_sts = OEMCrypto_SUCCESS);
|
||||
// This uses OEMCrypto_QueryKeyControl to check that the keys in OEMCrypto
|
||||
// have the correct key control data.
|
||||
void VerifyTestKeys();
|
||||
// This uses OEMCrypto_QueryKeyControl to check that the keys in OEMCrypto
|
||||
// have the correct key control data.
|
||||
void VerifyEntitlementTestKeys();
|
||||
// This creates a refresh key or license renewal message, signs it with the
|
||||
// server's mac key, and calls OEMCrypto_RefreshKeys.
|
||||
void RefreshTestKeys(const size_t key_count, uint32_t control_bits,
|
||||
uint32_t nonce, OEMCryptoResult expected_result);
|
||||
// This fills the data structure license_ with key information. This data
|
||||
// can be modified, and then should be encrypted and signed in EncryptAndSign
|
||||
// before being loaded in LoadTestKeys.
|
||||
void FillSimpleMessage(uint32_t duration, uint32_t control, uint32_t nonce,
|
||||
const std::string& pst = "");
|
||||
// This fills the data structure license_ with entitlement key information.
|
||||
// This data can be modified, and then should be encrypted and signed in
|
||||
// EncryptAndSign before being loaded in LoadEntitlementTestKeys.
|
||||
void FillSimpleEntitlementMessage(
|
||||
uint32_t duration, uint32_t control,
|
||||
uint32_t nonce, const std::string& pst = "");
|
||||
// Like FillSimpleMessage, this fills encrypted_license_ with data. The name
|
||||
// is a little misleading: the license renewal message is not encrypted, it
|
||||
// is just signed. The signature is computed in RefreshTestKeys, above.
|
||||
void FillRefreshMessage(size_t key_count, uint32_t control_bits,
|
||||
uint32_t nonce);
|
||||
// Sets the OEMCrypto_Substring parameters of the LoadKeys method.
|
||||
// Specifically, it sets the |enc_mac_keys_iv|, |enc_mac_keys|, |pst|, and
|
||||
// |srm_restriction_data| in that order. For testing purposes,
|
||||
// |srm_restriction_data| will always be nullptr.
|
||||
void SetLoadKeysSubstringParams();
|
||||
// This copies data from license_ to encrypted_license_, and then encrypts
|
||||
// each field in the key array appropriately. It then signes the buffer with
|
||||
// the server mac keys. It then fills out the key_array_ so that pointers in
|
||||
// that array point to the locations in the encrypted message.
|
||||
void EncryptAndSign();
|
||||
// This checks the signature generated by OEMCrypto_PrepAndSignLicenseRequest
|
||||
// against that generaged by ClientSignBuffer.
|
||||
void VerifyLicenseRequestSignature(size_t data_length = 400);
|
||||
// This checks the signature generated by OEMCrypto_PrepAndSignRenewalRequest
|
||||
// against that generaged by ClientSignBuffer.
|
||||
void VerifyRenewalRequestSignature(size_t data_length = 400);
|
||||
// Verify the core message matches data from this session.
|
||||
void VerifyCoreLicenseRequest(const std::string& core_message);
|
||||
// Verify the core message matches data from this session.
|
||||
void VerifyCoreRenewalRequest(const std::string& core_message);
|
||||
// Set the pointers in key_array[*] to point values inside data. This is
|
||||
// needed to satisfy range checks in OEMCrypto_LoadKeys.
|
||||
void FillKeyArray(const MessageData& data, OEMCrypto_KeyObject* key_array);
|
||||
// As in FillKeyArray but for the license renewal message passed to
|
||||
// OEMCrypto_RefreshKeys.
|
||||
void FillRefreshArray(OEMCrypto_KeyRefreshObject* key_array,
|
||||
size_t key_count);
|
||||
// Encrypt a block of data using CTR mode.
|
||||
void EncryptCTR(const vector<uint8_t>& in_buffer, const uint8_t* key,
|
||||
const uint8_t* starting_iv, vector<uint8_t>* out_buffer);
|
||||
@@ -625,12 +554,6 @@ class Session {
|
||||
int64_t time_license_received = 0,
|
||||
int64_t time_first_decrypt = 0,
|
||||
int64_t time_last_decrypt = 0);
|
||||
// Same as above, but generates the report with the given status.
|
||||
void GenerateVerifyReport(const std::string& pst,
|
||||
OEMCrypto_Usage_Entry_Status status,
|
||||
int64_t time_license_received = 0,
|
||||
int64_t time_first_decrypt = 0,
|
||||
int64_t time_last_decrypt = 0);
|
||||
// Create an entry in the old usage table based on the given report.
|
||||
void CreateOldEntry(const Test_PST_Report &report);
|
||||
// Create a new entry and copy the old entry into it. Then very the report
|
||||
@@ -640,50 +563,9 @@ class Session {
|
||||
|
||||
// The unencrypted license response or license renewal response.
|
||||
MessageData& license() { return license_; }
|
||||
// The encrypted license response or license renewal response.
|
||||
MessageData& encrypted_license() { return padded_message_; }
|
||||
|
||||
// A pointer to the buffer holding encrypted_license.
|
||||
const uint8_t* message_ptr();
|
||||
|
||||
// An array of key objects for use in LoadKeys.
|
||||
OEMCrypto_KeyObject* key_array() { return key_array_; }
|
||||
|
||||
// An array of key objects for LoadEntitledContentKeys.
|
||||
OEMCrypto_EntitledContentKeyObject* entitled_key_array() {
|
||||
return entitled_key_array_;
|
||||
}
|
||||
|
||||
// The last signature generated with the server's mac key.
|
||||
std::vector<uint8_t>& signature() { return signature_; }
|
||||
|
||||
// Set the number of keys to use in the license(), encrypted_license()
|
||||
// and key_array().
|
||||
void set_num_keys(int num_keys) { num_keys_ = num_keys; }
|
||||
// The current number of keys to use in the license(), encrypted_license()
|
||||
// and key_array().
|
||||
unsigned int num_keys() const { return num_keys_; }
|
||||
|
||||
// Set the size of the buffer used the encrypted license.
|
||||
// Must be between sizeof(MessageData) and kMaxMessageSize.
|
||||
void set_message_size(size_t size);
|
||||
// The size of the encrypted message.
|
||||
size_t message_size() { return message_size_; }
|
||||
|
||||
void set_license(const MessageData& license) { license_ = license; }
|
||||
|
||||
// The OEMCrypto_Substrings associated with the encrypted license that are
|
||||
// passed to LoadKeys.
|
||||
vector<OEMCrypto_Substring> load_keys_params() { return load_keys_params_; }
|
||||
OEMCrypto_Substring enc_mac_keys_iv_substr() { return load_keys_params_[0]; }
|
||||
OEMCrypto_Substring enc_mac_keys_substr() { return load_keys_params_[1]; }
|
||||
OEMCrypto_Substring pst_substr() { return load_keys_params_[2]; }
|
||||
OEMCrypto_Substring srm_restriction_data_substr() {
|
||||
return load_keys_params_[3];
|
||||
}
|
||||
|
||||
// Pointer to buffer holding |encrypted_entitled_message_|
|
||||
const uint8_t* encrypted_entitled_message_ptr();
|
||||
const KeyDeriver& key_deriver() const { return key_deriver_; }
|
||||
void set_mac_keys(const uint8_t* mac_keys) {
|
||||
key_deriver_.set_mac_keys(mac_keys);
|
||||
@@ -698,26 +580,10 @@ class Session {
|
||||
RSA* public_rsa_;
|
||||
vector<uint8_t> pst_report_buffer_;
|
||||
MessageData license_;
|
||||
struct PaddedMessageData : public MessageData {
|
||||
uint8_t padding[kMaxMessageSize - sizeof(MessageData)];
|
||||
} padded_message_;
|
||||
size_t message_size_; // How much of the padded message to use.
|
||||
OEMCrypto_KeyObject key_array_[kMaxNumKeys];
|
||||
vector<OEMCrypto_Substring> load_keys_params_;
|
||||
std::vector<uint8_t> signature_;
|
||||
unsigned int num_keys_;
|
||||
|
||||
vector<uint8_t> encrypted_usage_entry_;
|
||||
uint32_t usage_entry_number_;
|
||||
string pst_;
|
||||
|
||||
// Clear Entitlement key data. This is the backing data for
|
||||
// |entitled_key_array_|.
|
||||
EntitledContentKeyData entitled_key_data_[kMaxNumKeys];
|
||||
// Message containing data from |key_array| and |entitled_key_data_|.
|
||||
std::string entitled_message_;
|
||||
// Entitled key object. Pointers are backed by |entitled_key_data_|.
|
||||
OEMCrypto_EntitledContentKeyObject entitled_key_array_[kMaxNumKeys];
|
||||
std::string encrypted_entitled_message_;
|
||||
};
|
||||
|
||||
} // namespace wvoec
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -21,7 +21,6 @@
|
||||
'<(oemcrypto_dir)/ref/src',
|
||||
'<(oemcrypto_dir)/test',
|
||||
'<(oemcrypto_dir)/odk/include',
|
||||
'<(oemcrypto_dir)/util/include',
|
||||
],
|
||||
'defines': [
|
||||
'OEMCRYPTO_TESTS',
|
||||
|
||||
Reference in New Issue
Block a user