[RESTRICT AUTOMERGE] Sync OEMCrypto, ODK files and unit tests
run android/copy_files from cdm repo to sync files in Android tm-widevine-release. Changes include: 1. Update ODK to 17.1 2. update in license_protocol.proto 3. updates in oemcrypto unit tests 4. A few cdm and util test updates 5. Prov4 unit test fixes Originating CLs: https://widevine-internal-review.googlesource.com/c/cdm/+/155289/ https://widevine-internal-review.googlesource.com/c/cdm/+/155429/ https://widevine-internal-review.googlesource.com/c/cdm/+/155430/ https://widevine-internal-review.googlesource.com/c/cdm/+/154415/ https://widevine-internal-review.googlesource.com/c/cdm/+/156457/ https://widevine-internal-review.googlesource.com/c/cdm/+/156878/ https://widevine-internal-review.googlesource.com/c/cdm/+/156879/ https://widevine-internal-review.googlesource.com/c/cdm/+/156425/ https://widevine-internal-review.googlesource.com/c/cdm/+/156486/ https://widevine-internal-review.googlesource.com/c/cdm/+/156539/ https://widevine-internal-review.googlesource.com/c/cdm/+/156542/ Test: ran oemcrypto unit tests and ODK tests Test: ran gts media test cases Bug: 239201888 Change-Id: Iad9aff72aec5ba42296582837f34dd704bc11810
This commit is contained in:
@@ -120,6 +120,11 @@ typedef enum OEMCrypto_Usage_Entry_Status {
|
||||
kInactiveUnused = 4,
|
||||
} OEMCrypto_Usage_Entry_Status;
|
||||
|
||||
typedef enum OEMCrypto_ProvisioningRenewalType {
|
||||
OEMCrypto_NoRenewal = 0,
|
||||
OEMCrypto_RenewalACert = 1,
|
||||
} OEMCrypto_ProvisioningRenewalType;
|
||||
|
||||
/**
|
||||
* OEMCrypto_LicenseType is used in the license message to indicate if the key
|
||||
* objects are for content keys, or for entitlement keys.
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
#ifndef WIDEVINE_ODK_INCLUDE_CORE_MESSAGE_DESERIALIZE_H_
|
||||
#define WIDEVINE_ODK_INCLUDE_CORE_MESSAGE_DESERIALIZE_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "core_message_types.h"
|
||||
|
||||
namespace oemcrypto_core_message {
|
||||
@@ -53,6 +55,18 @@ bool CoreProvisioningRequestFromMessage(
|
||||
const std::string& oemcrypto_core_message,
|
||||
ODK_ProvisioningRequest* core_provisioning_request);
|
||||
|
||||
/**
|
||||
* Counterpart (deserializer) of ODK_PrepareCoreRenewedProvisioningRequest
|
||||
* (serializer)
|
||||
*
|
||||
* Parameters:
|
||||
* [in] oemcrypto_core_message
|
||||
* [out] core_provisioning_request
|
||||
*/
|
||||
bool CoreRenewedProvisioningRequestFromMessage(
|
||||
const std::string& oemcrypto_core_message,
|
||||
ODK_ProvisioningRequest* core_provisioning_request);
|
||||
|
||||
/**
|
||||
* Serializer counterpart is not used and is therefore not implemented.
|
||||
*
|
||||
|
||||
@@ -30,13 +30,13 @@ struct CoreMessageFeatures {
|
||||
uint32_t maximum_major_version = 17;
|
||||
uint32_t maximum_minor_version = 0;
|
||||
|
||||
bool operator==(const CoreMessageFeatures& other) const;
|
||||
bool operator!=(const CoreMessageFeatures& other) const {
|
||||
bool operator==(const CoreMessageFeatures &other) const;
|
||||
bool operator!=(const CoreMessageFeatures &other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const CoreMessageFeatures& features);
|
||||
std::ostream &operator<<(std::ostream &os, const CoreMessageFeatures &features);
|
||||
|
||||
} // namespace features
|
||||
} // namespace oemcrypto_core_message
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
#ifndef WIDEVINE_ODK_INCLUDE_CORE_MESSAGE_SERIALIZE_H_
|
||||
#define WIDEVINE_ODK_INCLUDE_CORE_MESSAGE_SERIALIZE_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "core_message_features.h"
|
||||
#include "core_message_types.h"
|
||||
#include "odk_structs.h"
|
||||
|
||||
@@ -96,7 +96,8 @@ struct ODK_RenewalRequest {
|
||||
};
|
||||
|
||||
/**
|
||||
* Output structure for CoreProvisioningRequestFromMessage
|
||||
* Output structure for CoreProvisioningRequestFromMessage and
|
||||
* CoreRenewedProvisioningRequestFromMessage
|
||||
* Input structure for CreateCoreProvisioningResponse
|
||||
*/
|
||||
struct ODK_ProvisioningRequest {
|
||||
@@ -105,6 +106,8 @@ struct ODK_ProvisioningRequest {
|
||||
uint32_t nonce;
|
||||
uint32_t session_id;
|
||||
std::string device_id;
|
||||
uint16_t renewal_type;
|
||||
std::string renewal_data;
|
||||
};
|
||||
|
||||
} // namespace oemcrypto_core_message
|
||||
|
||||
@@ -326,7 +326,7 @@ OEMCryptoResult ODK_PrepareCoreRenewalRequest(uint8_t* message,
|
||||
* OEMCrypto_GetDeviceID. The device ID shall be unique to the device, and
|
||||
* stable across reboots and factory resets for an L1 device.
|
||||
*
|
||||
* NOTE: if the message pointer is null and/or input core_message_size is
|
||||
* NOTE: if the message pointer is null and/or input core_message_length is
|
||||
* zero, this function returns OEMCrypto_ERROR_SHORT_BUFFER and sets output
|
||||
* core_message_size to the size needed.
|
||||
*
|
||||
@@ -351,10 +351,56 @@ OEMCryptoResult ODK_PrepareCoreRenewalRequest(uint8_t* message,
|
||||
* This method is new in version 16 of the API.
|
||||
*/
|
||||
OEMCryptoResult ODK_PrepareCoreProvisioningRequest(
|
||||
uint8_t* message, size_t message_length, size_t* core_message_size,
|
||||
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);
|
||||
|
||||
/**
|
||||
* Modifies the message to include a core renewal provisioning request at the
|
||||
* beginning of the message buffer. The values in nonce_values are used to
|
||||
* populate the message.
|
||||
*
|
||||
* This shall be called by OEMCrypto from
|
||||
* OEMCrypto_PrepAndSignProvisioningRequest.
|
||||
*
|
||||
* The buffer device_id shall be the same string returned by
|
||||
* OEMCrypto_GetDeviceID. The device ID shall be unique to the device, and
|
||||
* stable across reboots and factory resets for an L1 device.
|
||||
*
|
||||
* NOTE: if the message pointer is null and/or input core_message_length is
|
||||
* zero, this function returns OEMCrypto_ERROR_SHORT_BUFFER and sets output
|
||||
* core_message_size to the size needed.
|
||||
*
|
||||
* @param[in,out] message: pointer to memory for the entire message. Modified by
|
||||
* the ODK library.
|
||||
* @param[in] message_length: length of the entire message buffer.
|
||||
* @param[in,out] core_message_size: length of the core message at the beginning
|
||||
* of the message. (in) size of buffer reserved for the core message, in
|
||||
* bytes. (out) actual length of the core message, in bytes.
|
||||
* @param[in] nonce_values: pointer to the session's nonce data.
|
||||
* @param[in] device_id: For devices with a keybox, this is the device ID from
|
||||
* the keybox. For devices with an OEM Certificate, this is a device
|
||||
* unique id string.
|
||||
* @param[in] device_id_length: length of device_id. The device ID can be at
|
||||
* most 64 bytes.
|
||||
* @param[in] renewal_type: type of renewal used
|
||||
* @param[in] renewal_data: renewal data used. For renewal_type = 1,
|
||||
* renewal_data is the Android attestation batch certificate.
|
||||
* @param[in] renewal_data_length: length of renewal_data
|
||||
*
|
||||
* @retval OEMCrypto_SUCCESS
|
||||
* @retval OEMCrypto_ERROR_SHORT_BUFFER: core_message_size is too small
|
||||
* @retval OEMCrypto_ERROR_INVALID_CONTEXT
|
||||
*
|
||||
* @version
|
||||
* This method is new in version 17 of the API.
|
||||
*/
|
||||
OEMCryptoResult ODK_PrepareCoreRenewedProvisioningRequest(
|
||||
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, uint16_t renewal_type, const uint8_t* renewal_data,
|
||||
size_t renewal_data_length);
|
||||
|
||||
/// @}
|
||||
|
||||
/// @addtogroup odk_timer
|
||||
|
||||
@@ -35,10 +35,10 @@ extern "C" {
|
||||
*/
|
||||
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
# define ALIGNED __attribute__((aligned))
|
||||
#define ALIGNED __attribute__((aligned))
|
||||
#else
|
||||
# define ALIGNED
|
||||
# error ODK_Message must be aligned to the maximum useful alignment of the \
|
||||
#define ALIGNED
|
||||
#error ODK_Message must be aligned to the maximum useful alignment of the \
|
||||
machine you are compiling for. Define the ALIGNED macro accordingly.
|
||||
#endif
|
||||
|
||||
@@ -48,19 +48,19 @@ typedef struct {
|
||||
} ALIGNED ODK_Message;
|
||||
|
||||
typedef enum {
|
||||
MESSAGE_STATUS_OK = 0xe937fcf7,
|
||||
MESSAGE_STATUS_UNKNOWN_ERROR = 0xe06c1190,
|
||||
MESSAGE_STATUS_OVERFLOW_ERROR = 0xc43ae4bc,
|
||||
MESSAGE_STATUS_OK = 0x7937fcf7,
|
||||
MESSAGE_STATUS_UNKNOWN_ERROR = 0x706c1190,
|
||||
MESSAGE_STATUS_OVERFLOW_ERROR = 0x543ae4bc,
|
||||
MESSAGE_STATUS_UNDERFLOW_ERROR = 0x7123cd0b,
|
||||
MESSAGE_STATUS_PARSE_ERROR = 0x0b9f6189,
|
||||
MESSAGE_STATUS_NULL_POINTER_ERROR = 0x2d66837a,
|
||||
MESSAGE_STATUS_API_VALUE_ERROR = 0x6ba34f47,
|
||||
MESSAGE_STATUS_END_OF_MESSAGE_ERROR = 0x998db72a,
|
||||
MESSAGE_STATUS_INVALID_ENUM_VALUE = 0xedb88197,
|
||||
MESSAGE_STATUS_END_OF_MESSAGE_ERROR = 0x798db72a,
|
||||
MESSAGE_STATUS_INVALID_ENUM_VALUE = 0x7db88197,
|
||||
MESSAGE_STATUS_INVALID_TAG_ERROR = 0x14dce06a,
|
||||
MESSAGE_STATUS_NOT_INITIALIZED = 0x2990b6c6,
|
||||
MESSAGE_STATUS_OUT_OF_MEMORY = 0xfc5c64cc,
|
||||
MESSAGE_STATUS_MAP_SHARED_MEMORY_FAILED = 0xfafecacf,
|
||||
MESSAGE_STATUS_OUT_OF_MEMORY = 0x7c5c64cc,
|
||||
MESSAGE_STATUS_MAP_SHARED_MEMORY_FAILED = 0x7afecacf,
|
||||
MESSAGE_STATUS_SECURE_BUFFER_ERROR = 0x78f0e873
|
||||
} ODK_MessageStatus;
|
||||
|
||||
|
||||
@@ -16,10 +16,10 @@ extern "C" {
|
||||
|
||||
/* The version of this library. */
|
||||
#define ODK_MAJOR_VERSION 17
|
||||
#define ODK_MINOR_VERSION 0
|
||||
#define ODK_MINOR_VERSION 1
|
||||
|
||||
/* ODK Version string. Date changed automatically on each release. */
|
||||
#define ODK_RELEASE_DATE "ODK v17.0 2022-02-15"
|
||||
#define ODK_RELEASE_DATE "ODK v17.1 2022-06-17"
|
||||
|
||||
/* The lowest version number for an ODK message. */
|
||||
#define ODK_FIRST_VERSION 16
|
||||
@@ -27,6 +27,7 @@ extern "C" {
|
||||
/* Some useful constants. */
|
||||
#define ODK_DEVICE_ID_LEN_MAX 64
|
||||
#define ODK_SHA256_HASH_SIZE 32
|
||||
#define ODK_KEYBOX_RENEWAL_DATA_SIZE 1600
|
||||
|
||||
/// @addtogroup odk_timer
|
||||
/// @{
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
#include "OEMCryptoCENCCommon.h"
|
||||
#include "odk_serialize.h"
|
||||
#include "odk_structs.h"
|
||||
#include "odk_structs_priv.h"
|
||||
@@ -52,6 +53,7 @@ bool ParseRequest(uint32_t message_type,
|
||||
core_request->api_minor_version = core_message.nonce_values.api_minor_version;
|
||||
core_request->nonce = core_message.nonce_values.nonce;
|
||||
core_request->session_id = core_message.nonce_values.session_id;
|
||||
|
||||
// Verify that the minor version matches the released version for the given
|
||||
// major version.
|
||||
if (core_request->api_major_version < ODK_FIRST_VERSION) {
|
||||
@@ -68,10 +70,13 @@ bool ParseRequest(uint32_t message_type,
|
||||
// For v16, a release and a renewal use the same message structure.
|
||||
// However, for future API versions, the release might be a separate
|
||||
// message. Otherwise, we expect an exact match of message types.
|
||||
// A provisioning request may contain a renewed provisioning message.
|
||||
if (message_type != ODK_Common_Request_Type &&
|
||||
core_message.message_type != message_type &&
|
||||
!(message_type == ODK_Renewal_Request_Type &&
|
||||
core_message.message_type == ODK_Release_Request_Type)) {
|
||||
core_message.message_type == ODK_Release_Request_Type) &&
|
||||
!(message_type == ODK_Provisioning_Request_Type &&
|
||||
core_message.message_type == ODK_Renewed_Provisioning_Request_Type)) {
|
||||
return false;
|
||||
}
|
||||
// Verify that the amount of buffer we read, which is GetOffset, is not more
|
||||
@@ -125,6 +130,42 @@ bool CoreProvisioningRequestFromMessage(
|
||||
}
|
||||
core_provisioning_request->device_id.assign(
|
||||
reinterpret_cast<const char*>(device_id), device_id_length);
|
||||
core_provisioning_request->renewal_type = OEMCrypto_NoRenewal;
|
||||
core_provisioning_request->renewal_data.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CoreRenewedProvisioningRequestFromMessage(
|
||||
const std::string& oemcrypto_core_message,
|
||||
ODK_ProvisioningRequest* core_provisioning_request) {
|
||||
const auto unpacker = Unpack_ODK_PreparedRenewedProvisioningRequest;
|
||||
ODK_PreparedRenewedProvisioningRequest prepared_provision = {};
|
||||
if (!ParseRequest(ODK_Renewed_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);
|
||||
|
||||
if (prepared_provision.renewal_data_length >
|
||||
sizeof(prepared_provision.renewal_data)) {
|
||||
return false;
|
||||
}
|
||||
core_provisioning_request->renewal_type = OEMCrypto_RenewalACert;
|
||||
core_provisioning_request->renewal_data.assign(
|
||||
reinterpret_cast<const char*>(prepared_provision.renewal_data),
|
||||
prepared_provision.renewal_data_length);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace oemcrypto_core_message {
|
||||
namespace features {
|
||||
const CoreMessageFeatures CoreMessageFeatures::kDefaultFeatures;
|
||||
|
||||
bool CoreMessageFeatures::operator==(const CoreMessageFeatures& other) const {
|
||||
bool CoreMessageFeatures::operator==(const CoreMessageFeatures &other) const {
|
||||
return maximum_major_version == other.maximum_major_version &&
|
||||
maximum_minor_version == other.maximum_minor_version;
|
||||
}
|
||||
@@ -23,7 +23,7 @@ CoreMessageFeatures CoreMessageFeatures::DefaultFeatures(
|
||||
features.maximum_minor_version = 5; // 16.5
|
||||
break;
|
||||
case 17:
|
||||
features.maximum_minor_version = 0; // 17.0
|
||||
features.maximum_minor_version = 1; // 17.1
|
||||
break;
|
||||
default:
|
||||
features.maximum_minor_version = 0;
|
||||
@@ -31,8 +31,8 @@ CoreMessageFeatures CoreMessageFeatures::DefaultFeatures(
|
||||
return features;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os,
|
||||
const CoreMessageFeatures& features) {
|
||||
std::ostream &operator<<(std::ostream &os,
|
||||
const CoreMessageFeatures &features) {
|
||||
return os << "v" << features.maximum_major_version << "."
|
||||
<< features.maximum_minor_version;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "odk_serialize.h"
|
||||
#include "odk_structs.h"
|
||||
#include "odk_structs_priv.h"
|
||||
#include "odk_target.h"
|
||||
#include "serialization_base.h"
|
||||
|
||||
namespace oemcrypto_core_message {
|
||||
@@ -122,6 +123,9 @@ bool CreateCoreLicenseResponse(const CoreMessageFeatures& features,
|
||||
license_response)) {
|
||||
return false;
|
||||
}
|
||||
if (ODK_MAX_NUM_KEYS < license_response.parsed_license->key_array_length) {
|
||||
return false;
|
||||
}
|
||||
if (license_response.request.core_message.nonce_values.api_major_version ==
|
||||
16) {
|
||||
ODK_LicenseResponseV16 license_response_v16;
|
||||
@@ -143,7 +147,9 @@ bool CreateCoreLicenseResponse(const CoreMessageFeatures& features,
|
||||
license_response_v16.parsed_license.key_array_length =
|
||||
license_response.parsed_license->key_array_length;
|
||||
uint32_t i;
|
||||
for (i = 0; i < license_response_v16.parsed_license.key_array_length; i++) {
|
||||
for (i = 0; i < license_response_v16.parsed_license.key_array_length &&
|
||||
i < license_response.parsed_license->key_array_length;
|
||||
i++) {
|
||||
license_response_v16.parsed_license.key_array[i] =
|
||||
license_response.parsed_license->key_array[i];
|
||||
}
|
||||
|
||||
@@ -101,8 +101,11 @@ bool CreateCoreLicenseResponseFromProto(const CoreMessageFeatures& features,
|
||||
}
|
||||
case video_widevine::License_KeyContainer::CONTENT:
|
||||
case video_widevine::License_KeyContainer::OPERATOR_SESSION:
|
||||
case video_widevine::License_KeyContainer::OEM_CONTENT:
|
||||
case video_widevine::License_KeyContainer::OEM_ENTITLEMENT:
|
||||
case video_widevine::License_KeyContainer::ENTITLEMENT: {
|
||||
if (k.type() == video_widevine::License_KeyContainer::ENTITLEMENT) {
|
||||
if (k.type() == video_widevine::License_KeyContainer::ENTITLEMENT ||
|
||||
k.type() == video_widevine::License_KeyContainer::OEM_ENTITLEMENT) {
|
||||
any_entitlement = true;
|
||||
} else {
|
||||
any_content = true;
|
||||
|
||||
@@ -72,6 +72,17 @@ static OEMCryptoResult ODK_PrepareRequest(
|
||||
&msg, (ODK_PreparedProvisioningRequest*)prepared_request_buffer);
|
||||
break;
|
||||
}
|
||||
case ODK_Renewed_Provisioning_Request_Type: {
|
||||
core_message->message_length = ODK_RENEWED_PROVISIONING_REQUEST_SIZE;
|
||||
if (sizeof(ODK_PreparedRenewedProvisioningRequest) >
|
||||
prepared_request_buffer_length) {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
Pack_ODK_PreparedRenewedProvisioningRequest(
|
||||
&msg,
|
||||
(ODK_PreparedRenewedProvisioningRequest*)prepared_request_buffer);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
@@ -238,6 +249,37 @@ OEMCryptoResult ODK_PrepareCoreProvisioningRequest(
|
||||
sizeof(ODK_PreparedProvisioningRequest));
|
||||
}
|
||||
|
||||
OEMCryptoResult ODK_PrepareCoreRenewedProvisioningRequest(
|
||||
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, uint16_t renewal_type, const uint8_t* renewal_data,
|
||||
size_t renewal_data_length) {
|
||||
if (core_message_length == NULL || nonce_values == NULL) {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
ODK_PreparedRenewedProvisioningRequest provisioning_request = {0};
|
||||
if (device_id_length > sizeof(provisioning_request.device_id)) {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
provisioning_request.device_id_length = (uint32_t)device_id_length;
|
||||
if (device_id) {
|
||||
memcpy(provisioning_request.device_id, device_id, device_id_length);
|
||||
}
|
||||
if (renewal_data_length > sizeof(provisioning_request.renewal_data)) {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
provisioning_request.renewal_type = renewal_type;
|
||||
provisioning_request.renewal_data_length = (uint32_t)renewal_data_length;
|
||||
if (renewal_data) {
|
||||
memcpy(provisioning_request.renewal_data, renewal_data,
|
||||
renewal_data_length);
|
||||
}
|
||||
return ODK_PrepareRequest(message, message_length, core_message_length,
|
||||
ODK_Renewed_Provisioning_Request_Type, nonce_values,
|
||||
&provisioning_request,
|
||||
sizeof(provisioning_request));
|
||||
}
|
||||
|
||||
/* @@ parse response functions */
|
||||
|
||||
OEMCryptoResult ODK_ParseLicense(
|
||||
|
||||
@@ -128,12 +128,22 @@ void Pack_ODK_PreparedRenewalRequest(ODK_Message* msg,
|
||||
}
|
||||
|
||||
void Pack_ODK_PreparedProvisioningRequest(
|
||||
ODK_Message* msg, ODK_PreparedProvisioningRequest const* obj) {
|
||||
ODK_Message* msg, const ODK_PreparedProvisioningRequest* obj) {
|
||||
Pack_ODK_CoreMessage(msg, &obj->core_message);
|
||||
Pack_uint32_t(msg, &obj->device_id_length);
|
||||
PackArray(msg, &obj->device_id[0], sizeof(obj->device_id));
|
||||
}
|
||||
|
||||
void Pack_ODK_PreparedRenewedProvisioningRequest(
|
||||
ODK_Message* msg, const ODK_PreparedRenewedProvisioningRequest* obj) {
|
||||
Pack_ODK_CoreMessage(msg, &obj->core_message);
|
||||
Pack_uint32_t(msg, &obj->device_id_length);
|
||||
PackArray(msg, &obj->device_id[0], sizeof(obj->device_id));
|
||||
Pack_uint16_t(msg, &obj->renewal_type);
|
||||
Pack_uint32_t(msg, &obj->renewal_data_length);
|
||||
PackArray(msg, &obj->renewal_data[0], sizeof(obj->renewal_data));
|
||||
}
|
||||
|
||||
/* @@ kdo serialize */
|
||||
|
||||
void Pack_ODK_LicenseResponse(ODK_Message* msg,
|
||||
@@ -156,7 +166,7 @@ void Pack_ODK_RenewalResponse(ODK_Message* msg,
|
||||
}
|
||||
|
||||
void Pack_ODK_ProvisioningResponse(ODK_Message* msg,
|
||||
ODK_ProvisioningResponse const* obj) {
|
||||
const ODK_ProvisioningResponse* obj) {
|
||||
Pack_ODK_PreparedProvisioningRequest(msg, &obj->request);
|
||||
Pack_ODK_ParsedProvisioning(
|
||||
msg, (const ODK_ParsedProvisioning*)obj->parsed_provisioning);
|
||||
@@ -302,6 +312,16 @@ void Unpack_ODK_PreparedProvisioningRequest(
|
||||
UnpackArray(msg, &obj->device_id[0], sizeof(obj->device_id));
|
||||
}
|
||||
|
||||
void Unpack_ODK_PreparedRenewedProvisioningRequest(
|
||||
ODK_Message* msg, ODK_PreparedRenewedProvisioningRequest* obj) {
|
||||
Unpack_ODK_CoreMessage(msg, &obj->core_message);
|
||||
Unpack_uint32_t(msg, &obj->device_id_length);
|
||||
UnpackArray(msg, &obj->device_id[0], sizeof(obj->device_id));
|
||||
Unpack_uint16_t(msg, &obj->renewal_type);
|
||||
Unpack_uint32_t(msg, &obj->renewal_data_length);
|
||||
UnpackArray(msg, &obj->renewal_data[0], obj->renewal_data_length);
|
||||
}
|
||||
|
||||
void Unpack_ODK_PreparedCommonRequest(ODK_Message* msg,
|
||||
ODK_PreparedCommonRequest* obj) {
|
||||
Unpack_ODK_CoreMessage(msg, &obj->core_message);
|
||||
|
||||
@@ -22,6 +22,8 @@ void Pack_ODK_PreparedRenewalRequest(ODK_Message* msg,
|
||||
const ODK_PreparedRenewalRequest* obj);
|
||||
void Pack_ODK_PreparedProvisioningRequest(
|
||||
ODK_Message* msg, const ODK_PreparedProvisioningRequest* obj);
|
||||
void Pack_ODK_PreparedRenewedProvisioningRequest(
|
||||
ODK_Message* msg, const ODK_PreparedRenewedProvisioningRequest* obj);
|
||||
|
||||
/* odk unpack */
|
||||
void Unpack_ODK_CoreMessage(ODK_Message* msg, ODK_CoreMessage* obj);
|
||||
@@ -47,6 +49,8 @@ void Unpack_ODK_PreparedRenewalRequest(ODK_Message* msg,
|
||||
ODK_PreparedRenewalRequest* obj);
|
||||
void Unpack_ODK_PreparedProvisioningRequest(
|
||||
ODK_Message* msg, ODK_PreparedProvisioningRequest* obj);
|
||||
void Unpack_ODK_PreparedRenewedProvisioningRequest(
|
||||
ODK_Message* msg, ODK_PreparedRenewedProvisioningRequest* obj);
|
||||
|
||||
void Unpack_ODK_PreparedCommonRequest(ODK_Message* msg,
|
||||
ODK_PreparedCommonRequest* obj);
|
||||
|
||||
@@ -24,6 +24,7 @@ typedef uint32_t ODK_MessageType;
|
||||
#define ODK_Renewal_Response_Type ((ODK_MessageType)4u)
|
||||
#define ODK_Provisioning_Request_Type ((ODK_MessageType)5u)
|
||||
#define ODK_Provisioning_Response_Type ((ODK_MessageType)6u)
|
||||
#define ODK_Renewed_Provisioning_Request_Type ((ODK_MessageType)11u)
|
||||
|
||||
// Reserve future message types to support forward compatibility.
|
||||
#define ODK_Release_Request_Type ((ODK_MessageType)7u)
|
||||
@@ -52,6 +53,15 @@ typedef struct {
|
||||
uint8_t device_id[ODK_DEVICE_ID_LEN_MAX];
|
||||
} ODK_PreparedProvisioningRequest;
|
||||
|
||||
typedef struct {
|
||||
ODK_CoreMessage core_message;
|
||||
uint32_t device_id_length;
|
||||
uint8_t device_id[ODK_DEVICE_ID_LEN_MAX];
|
||||
uint16_t renewal_type;
|
||||
uint32_t renewal_data_length;
|
||||
uint8_t renewal_data[ODK_KEYBOX_RENEWAL_DATA_SIZE];
|
||||
} ODK_PreparedRenewedProvisioningRequest;
|
||||
|
||||
typedef struct {
|
||||
ODK_CoreMessage core_message;
|
||||
} ODK_PreparedCommonRequest;
|
||||
@@ -96,6 +106,7 @@ typedef struct {
|
||||
#define ODK_LICENSE_REQUEST_SIZE 20u
|
||||
#define ODK_RENEWAL_REQUEST_SIZE 28u
|
||||
#define ODK_PROVISIONING_REQUEST_SIZE 88u
|
||||
#define ODK_RENEWED_PROVISIONING_REQUEST_SIZE 1694u
|
||||
|
||||
// These are the possible timer status values.
|
||||
#define ODK_CLOCK_TIMER_STATUS_UNDEFINED 0u // Should not happen.
|
||||
|
||||
@@ -38,7 +38,7 @@ static void PackBytes(ODK_Message* message, const uint8_t* ptr, size_t count) {
|
||||
}
|
||||
|
||||
void Pack_enum(ODK_Message* message, int value) {
|
||||
uint32_t v32 = value;
|
||||
uint32_t v32 = (uint32_t)value;
|
||||
Pack_uint32_t(message, &v32);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// source code may only be used and distributed under the Widevine
|
||||
// License Agreement.
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "OEMCryptoCENCCommon.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "odk.h"
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
#include "OEMCryptoCENCCommon.h"
|
||||
#include "core_message_deserialize.h"
|
||||
@@ -27,6 +28,8 @@ using oemcrypto_core_message::ODK_RenewalRequest;
|
||||
using oemcrypto_core_message::deserialize::CoreLicenseRequestFromMessage;
|
||||
using oemcrypto_core_message::deserialize::CoreProvisioningRequestFromMessage;
|
||||
using oemcrypto_core_message::deserialize::CoreRenewalRequestFromMessage;
|
||||
using oemcrypto_core_message::deserialize::
|
||||
CoreRenewedProvisioningRequestFromMessage;
|
||||
|
||||
using oemcrypto_core_message::features::CoreMessageFeatures;
|
||||
|
||||
@@ -270,6 +273,35 @@ TEST(OdkTest, NullRequestTest) {
|
||||
ODK_PrepareCoreProvisioningRequest(
|
||||
message, ODK_PROVISIONING_REQUEST_SIZE, &core_message_length,
|
||||
&nonce_values, nullptr, 0uL));
|
||||
|
||||
EXPECT_EQ(ODK_ERROR_CORE_MESSAGE,
|
||||
ODK_PrepareCoreRenewedProvisioningRequest(
|
||||
nullptr, 0uL, &core_message_length, nullptr, nullptr, 0uL,
|
||||
OEMCrypto_RenewalACert, nullptr, 0uL));
|
||||
EXPECT_EQ(ODK_ERROR_CORE_MESSAGE,
|
||||
ODK_PrepareCoreRenewedProvisioningRequest(
|
||||
nullptr, 0uL, nullptr, &nonce_values, nullptr, 0uL,
|
||||
OEMCrypto_RenewalACert, nullptr, 0uL));
|
||||
|
||||
// Null device id in renewed provisioning request is ok
|
||||
uint8_t renewed_message[ODK_RENEWED_PROVISIONING_REQUEST_SIZE] = {0};
|
||||
uint8_t renewal_data[ODK_KEYBOX_RENEWAL_DATA_SIZE] = {0};
|
||||
uint32_t renewal_data_length = ODK_KEYBOX_RENEWAL_DATA_SIZE;
|
||||
core_message_length = ODK_RENEWED_PROVISIONING_REQUEST_SIZE;
|
||||
EXPECT_EQ(OEMCrypto_SUCCESS,
|
||||
ODK_PrepareCoreRenewedProvisioningRequest(
|
||||
renewed_message, ODK_RENEWED_PROVISIONING_REQUEST_SIZE,
|
||||
&core_message_length, &nonce_values, nullptr, 0uL,
|
||||
OEMCrypto_RenewalACert, renewal_data, renewal_data_length));
|
||||
|
||||
// Null renewal data in renewed provisioning request is ok
|
||||
uint8_t device_id[ODK_DEVICE_ID_LEN_MAX] = {0};
|
||||
uint32_t device_id_length = ODK_DEVICE_ID_LEN_MAX;
|
||||
core_message_length = ODK_RENEWED_PROVISIONING_REQUEST_SIZE;
|
||||
ODK_PrepareCoreRenewedProvisioningRequest(
|
||||
renewed_message, ODK_RENEWED_PROVISIONING_REQUEST_SIZE,
|
||||
&core_message_length, &nonce_values, device_id, device_id_length,
|
||||
OEMCrypto_RenewalACert, nullptr, 0uL);
|
||||
}
|
||||
|
||||
TEST(OdkTest, NullResponseTest) {
|
||||
@@ -422,6 +454,21 @@ TEST(OdkTest, PrepareCoreProvisioningRequest) {
|
||||
&core_message_length, &nonce_values, device_id, sizeof(device_id)));
|
||||
}
|
||||
|
||||
TEST(OdkTest, PrepareCoreRenewedProvisioningRequest) {
|
||||
uint8_t provisioning_message[ODK_RENEWED_PROVISIONING_REQUEST_SIZE] = {0};
|
||||
size_t core_message_length = sizeof(provisioning_message);
|
||||
ODK_NonceValues nonce_values;
|
||||
memset(&nonce_values, 0, sizeof(nonce_values));
|
||||
uint8_t device_id[ODK_DEVICE_ID_LEN_MAX] = {0};
|
||||
uint8_t renewal_data[ODK_KEYBOX_RENEWAL_DATA_SIZE] = {0};
|
||||
EXPECT_EQ(
|
||||
OEMCrypto_SUCCESS,
|
||||
ODK_PrepareCoreRenewedProvisioningRequest(
|
||||
provisioning_message, sizeof(provisioning_message),
|
||||
&core_message_length, &nonce_values, device_id, sizeof(device_id),
|
||||
OEMCrypto_RenewalACert, renewal_data, sizeof(renewal_data)));
|
||||
}
|
||||
|
||||
TEST(OdkTest, PrepareCoreProvisioningRequestDeviceId) {
|
||||
uint8_t provisioning_message[ODK_PROVISIONING_REQUEST_SIZE] = {0};
|
||||
size_t core_message_length = sizeof(provisioning_message);
|
||||
@@ -435,6 +482,36 @@ TEST(OdkTest, PrepareCoreProvisioningRequestDeviceId) {
|
||||
sizeof(device_id_invalid)));
|
||||
}
|
||||
|
||||
TEST(OdkTest, PrepareCoreRenewedProvisioningRequestDeviceId) {
|
||||
uint8_t provisioning_message[ODK_PROVISIONING_REQUEST_SIZE] = {0};
|
||||
size_t core_message_length = sizeof(provisioning_message);
|
||||
ODK_NonceValues nonce_values;
|
||||
memset(&nonce_values, 0, sizeof(nonce_values));
|
||||
uint8_t device_id_invalid[ODK_DEVICE_ID_LEN_MAX + 1] = {0};
|
||||
uint8_t renewal_data[ODK_KEYBOX_RENEWAL_DATA_SIZE] = {0};
|
||||
EXPECT_EQ(ODK_ERROR_CORE_MESSAGE,
|
||||
ODK_PrepareCoreRenewedProvisioningRequest(
|
||||
provisioning_message, sizeof(provisioning_message),
|
||||
&core_message_length, &nonce_values, device_id_invalid,
|
||||
sizeof(device_id_invalid), OEMCrypto_RenewalACert, renewal_data,
|
||||
sizeof(renewal_data)));
|
||||
}
|
||||
|
||||
TEST(OdkTest, PrepareCoreRenewedProvisioningRequestRenewalDataInvalid) {
|
||||
uint8_t provisioning_message[ODK_PROVISIONING_REQUEST_SIZE] = {0};
|
||||
size_t core_message_length = sizeof(provisioning_message);
|
||||
ODK_NonceValues nonce_values;
|
||||
memset(&nonce_values, 0, sizeof(nonce_values));
|
||||
uint8_t device_id[ODK_DEVICE_ID_LEN_MAX] = {0};
|
||||
uint8_t renewal_data_invalid[ODK_KEYBOX_RENEWAL_DATA_SIZE + 1] = {0};
|
||||
EXPECT_EQ(ODK_ERROR_CORE_MESSAGE,
|
||||
ODK_PrepareCoreRenewedProvisioningRequest(
|
||||
provisioning_message, sizeof(provisioning_message),
|
||||
&core_message_length, &nonce_values, device_id,
|
||||
sizeof(device_id), OEMCrypto_RenewalACert, renewal_data_invalid,
|
||||
sizeof(renewal_data_invalid)));
|
||||
}
|
||||
|
||||
// Serialize and de-serialize license request
|
||||
TEST(OdkTest, LicenseRequestRoundtrip) {
|
||||
std::vector<ODK_Field> empty;
|
||||
@@ -497,6 +574,39 @@ TEST(OdkTest, ProvisionRequestRoundtrip) {
|
||||
kdo_parse_func);
|
||||
}
|
||||
|
||||
TEST(OdkTest, RenewedProvisionRequestRoundtrip) {
|
||||
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);
|
||||
uint16_t renewal_type = OEMCrypto_RenewalACert;
|
||||
uint32_t renewal_data_length = ODK_KEYBOX_RENEWAL_DATA_SIZE / 2;
|
||||
uint8_t renewal_data[ODK_KEYBOX_RENEWAL_DATA_SIZE] = {0};
|
||||
memset(renewal_data, 0xff, renewal_data_length);
|
||||
std::vector<ODK_Field> extra_fields = {
|
||||
{ODK_UINT32, &device_id_length, "device_id_length"},
|
||||
{ODK_DEVICEID, device_id, "device_id"},
|
||||
{ODK_UINT16, &renewal_type, "renewal_type"},
|
||||
{ODK_UINT32, &renewal_data_length, "renewal_data_length"},
|
||||
{ODK_RENEWALDATA, renewal_data, "renewal_data"},
|
||||
};
|
||||
auto odk_prepare_func = [&](uint8_t* const buf, size_t* size,
|
||||
const ODK_NonceValues* nonce_values) {
|
||||
return ODK_PrepareCoreRenewedProvisioningRequest(
|
||||
buf, SIZE_MAX, size, nonce_values, device_id, device_id_length,
|
||||
renewal_type, renewal_data, renewal_data_length);
|
||||
};
|
||||
auto kdo_parse_func =
|
||||
[&](const std::string& oemcrypto_core_message,
|
||||
ODK_ProvisioningRequest* core_provisioning_request) {
|
||||
bool ok = CoreRenewedProvisioningRequestFromMessage(
|
||||
oemcrypto_core_message, core_provisioning_request);
|
||||
return ok;
|
||||
};
|
||||
ValidateRequest<ODK_ProvisioningRequest>(
|
||||
ODK_Renewed_Provisioning_Request_Type, extra_fields, odk_prepare_func,
|
||||
kdo_parse_func);
|
||||
}
|
||||
|
||||
TEST(OdkTest, ParseLicenseErrorNonce) {
|
||||
ODK_LicenseResponseParams params;
|
||||
ODK_SetDefaultLicenseResponseParams(¶ms, ODK_MAJOR_VERSION);
|
||||
@@ -761,6 +871,7 @@ std::vector<VersionParameters> TestCases() {
|
||||
{17, 16, 4, 16, 4},
|
||||
{17, 16, 5, 16, 5},
|
||||
{17, 17, 0, 17, 0},
|
||||
{17, 17, 1, 17, 1},
|
||||
};
|
||||
return test_cases;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "OEMCryptoCENCCommon.h"
|
||||
@@ -329,6 +330,8 @@ size_t ODK_FieldLength(ODK_FieldType type) {
|
||||
return sizeof(uint32_t) + sizeof(uint32_t);
|
||||
case ODK_DEVICEID:
|
||||
return ODK_DEVICE_ID_LEN_MAX;
|
||||
case ODK_RENEWALDATA:
|
||||
return ODK_KEYBOX_RENEWAL_DATA_SIZE;
|
||||
case ODK_HASH:
|
||||
return ODK_SHA256_HASH_SIZE;
|
||||
default:
|
||||
@@ -385,6 +388,7 @@ OEMCryptoResult ODK_WriteSingleField(uint8_t* buf, const ODK_Field* field) {
|
||||
break;
|
||||
}
|
||||
case ODK_DEVICEID:
|
||||
case ODK_RENEWALDATA:
|
||||
case ODK_HASH: {
|
||||
const size_t field_len = ODK_FieldLength(field->type);
|
||||
const uint8_t* const id = static_cast<uint8_t*>(field->value);
|
||||
@@ -444,6 +448,7 @@ OEMCryptoResult ODK_ReadSingleField(const uint8_t* buf,
|
||||
break;
|
||||
}
|
||||
case ODK_DEVICEID:
|
||||
case ODK_RENEWALDATA:
|
||||
case ODK_HASH: {
|
||||
const size_t field_len = ODK_FieldLength(field->type);
|
||||
uint8_t* const id = static_cast<uint8_t*>(field->value);
|
||||
@@ -503,6 +508,7 @@ OEMCryptoResult ODK_DumpSingleField(const uint8_t* buf,
|
||||
break;
|
||||
}
|
||||
case ODK_DEVICEID:
|
||||
case ODK_RENEWALDATA:
|
||||
case ODK_HASH: {
|
||||
const size_t field_len = ODK_FieldLength(field->type);
|
||||
std::cerr << field->name << ": ";
|
||||
|
||||
@@ -21,6 +21,7 @@ enum ODK_FieldType {
|
||||
ODK_UINT64,
|
||||
ODK_SUBSTRING,
|
||||
ODK_DEVICEID,
|
||||
ODK_RENEWALDATA,
|
||||
ODK_HASH,
|
||||
// The "stressable" types are the ones we can put in a stress test that packs
|
||||
// and unpacks random data and can expect to get back the same thing.
|
||||
|
||||
@@ -30,6 +30,8 @@ LOCAL_SRC_FILES:= \
|
||||
ota_keybox_test.cpp \
|
||||
wvcrc.cpp \
|
||||
../../cdm/util/test/test_sleep.cpp \
|
||||
../util/src/oemcrypto_ecc_key.cpp \
|
||||
../util/src/oemcrypto_rsa_key.cpp \
|
||||
|
||||
LOCAL_C_INCLUDES += \
|
||||
$(LOCAL_PATH)/fuzz_tests \
|
||||
@@ -37,6 +39,7 @@ LOCAL_C_INCLUDES += \
|
||||
$(LOCAL_PATH)/../odk/include \
|
||||
$(LOCAL_PATH)/../odk/kdo/include \
|
||||
$(LOCAL_PATH)/../ref/src \
|
||||
$(LOCAL_PATH)/../util/include \
|
||||
vendor/widevine/libwvdrmengine/cdm/core/include \
|
||||
vendor/widevine/libwvdrmengine/cdm/util/include \
|
||||
vendor/widevine/libwvdrmengine/cdm/util/test \
|
||||
|
||||
@@ -83,19 +83,23 @@ void Encryptor::PadAndEncryptProvisioningMessage(
|
||||
// This generates the data for deriving one key. If there are failures in
|
||||
// this function, then there is something wrong with the test program and its
|
||||
// dependency on BoringSSL.
|
||||
void KeyDeriver::DeriveKey(const uint8_t* key, const vector<uint8_t>& context,
|
||||
int counter, vector<uint8_t>* out) {
|
||||
void KeyDeriver::DeriveKey(const uint8_t* key, size_t master_key_size,
|
||||
const vector<uint8_t>& context, int counter,
|
||||
vector<uint8_t>* out) {
|
||||
ASSERT_NE(key, nullptr);
|
||||
ASSERT_FALSE(context.empty());
|
||||
ASSERT_GE(4, counter);
|
||||
ASSERT_LE(1, counter);
|
||||
ASSERT_NE(out, nullptr);
|
||||
// For RSA, the master key is expected to be 16 bytes; for EC key, 32 bytes.
|
||||
ASSERT_TRUE(master_key_size == KEY_SIZE || master_key_size == 2 * KEY_SIZE);
|
||||
|
||||
const EVP_CIPHER* cipher = EVP_aes_128_cbc();
|
||||
const EVP_CIPHER* cipher =
|
||||
master_key_size == KEY_SIZE ? EVP_aes_128_cbc() : EVP_aes_256_cbc();
|
||||
CMAC_CTX* cmac_ctx = CMAC_CTX_new();
|
||||
ASSERT_NE(nullptr, cmac_ctx);
|
||||
|
||||
ASSERT_TRUE(CMAC_Init(cmac_ctx, key, KEY_SIZE, cipher, nullptr));
|
||||
ASSERT_TRUE(CMAC_Init(cmac_ctx, key, master_key_size, cipher, nullptr));
|
||||
|
||||
std::vector<uint8_t> message;
|
||||
message.push_back(static_cast<uint8_t>(counter));
|
||||
@@ -114,24 +118,24 @@ void KeyDeriver::DeriveKey(const uint8_t* key, const vector<uint8_t>& context,
|
||||
// This generates the data for deriving a set of keys. If there are failures in
|
||||
// this function, then there is something wrong with the test program and its
|
||||
// dependency on BoringSSL.
|
||||
void KeyDeriver::DeriveKeys(const uint8_t* master_key,
|
||||
void KeyDeriver::DeriveKeys(const uint8_t* master_key, size_t master_key_size,
|
||||
const vector<uint8_t>& mac_key_context,
|
||||
const vector<uint8_t>& enc_key_context) {
|
||||
// Generate derived key for mac key
|
||||
std::vector<uint8_t> mac_key_part2;
|
||||
DeriveKey(master_key, mac_key_context, 1, &mac_key_server_);
|
||||
DeriveKey(master_key, mac_key_context, 2, &mac_key_part2);
|
||||
DeriveKey(master_key, master_key_size, mac_key_context, 1, &mac_key_server_);
|
||||
DeriveKey(master_key, master_key_size, mac_key_context, 2, &mac_key_part2);
|
||||
mac_key_server_.insert(mac_key_server_.end(), mac_key_part2.begin(),
|
||||
mac_key_part2.end());
|
||||
|
||||
DeriveKey(master_key, mac_key_context, 3, &mac_key_client_);
|
||||
DeriveKey(master_key, mac_key_context, 4, &mac_key_part2);
|
||||
DeriveKey(master_key, master_key_size, mac_key_context, 3, &mac_key_client_);
|
||||
DeriveKey(master_key, master_key_size, mac_key_context, 4, &mac_key_part2);
|
||||
mac_key_client_.insert(mac_key_client_.end(), mac_key_part2.begin(),
|
||||
mac_key_part2.end());
|
||||
|
||||
// Generate derived key for encryption key
|
||||
std::vector<uint8_t> enc_key;
|
||||
DeriveKey(master_key, enc_key_context, 1, &enc_key);
|
||||
DeriveKey(master_key, master_key_size, enc_key_context, 1, &enc_key);
|
||||
set_enc_key(enc_key);
|
||||
}
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ class KeyDeriver : public Encryptor {
|
||||
KeyDeriver& operator=(const KeyDeriver&) = default;
|
||||
|
||||
// Generate mac and enc keys give the master key.
|
||||
void DeriveKeys(const uint8_t* master_key,
|
||||
void DeriveKeys(const uint8_t* master_key, size_t master_key_size,
|
||||
const std::vector<uint8_t>& mac_key_context,
|
||||
const std::vector<uint8_t>& enc_key_context);
|
||||
// Sign the buffer with server's mac key.
|
||||
@@ -80,9 +80,11 @@ class KeyDeriver : public Encryptor {
|
||||
void set_mac_keys(const uint8_t* mac_keys);
|
||||
|
||||
private:
|
||||
// Internal utility function to derive key using CMAC-128
|
||||
void DeriveKey(const uint8_t* key, const std::vector<uint8_t>& context,
|
||||
int counter, std::vector<uint8_t>* out);
|
||||
// Internal utility function to derive key using CMAC-128 or CMAC-256 based on
|
||||
// master_key_size.
|
||||
void DeriveKey(const uint8_t* key, size_t master_key_size,
|
||||
const std::vector<uint8_t>& context, int counter,
|
||||
std::vector<uint8_t>* out);
|
||||
|
||||
std::vector<uint8_t> mac_key_server_;
|
||||
std::vector<uint8_t> mac_key_client_;
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -309,12 +310,12 @@ void ProvisioningRoundTrip::PrepareSession(
|
||||
OEMCrypto_BootCertificateChain) {
|
||||
// TODO(chelu): change this to CSR provisioning.
|
||||
session_->LoadOEMCert(true);
|
||||
session_->GenerateRSASessionKey(&message_key_, &encrypted_message_key_);
|
||||
session_->GenerateRsaSessionKey(&message_key_, &encrypted_message_key_);
|
||||
encryptor_.set_enc_key(message_key_);
|
||||
} else {
|
||||
EXPECT_EQ(global_features.provisioning_method, OEMCrypto_OEMCertificate);
|
||||
session_->LoadOEMCert(true);
|
||||
session_->GenerateRSASessionKey(&message_key_, &encrypted_message_key_);
|
||||
session_->GenerateRsaSessionKey(&message_key_, &encrypted_message_key_);
|
||||
encryptor_.set_enc_key(message_key_);
|
||||
}
|
||||
}
|
||||
@@ -323,7 +324,7 @@ void ProvisioningRoundTrip::VerifyRequestSignature(
|
||||
const vector<uint8_t>& data, const vector<uint8_t>& generated_signature,
|
||||
size_t /* core_message_length */) {
|
||||
if (global_features.provisioning_method == OEMCrypto_OEMCertificate) {
|
||||
session()->VerifyRSASignature(data, generated_signature.data(),
|
||||
session()->VerifyRsaSignature(data, generated_signature.data(),
|
||||
generated_signature.size(), kSign_RSASSA_PSS);
|
||||
} else {
|
||||
EXPECT_EQ(global_features.provisioning_method, OEMCrypto_Keybox);
|
||||
@@ -564,12 +565,12 @@ void LicenseRoundTrip::VerifyRequestSignature(
|
||||
if (global_features.api_version < 17) {
|
||||
const std::vector<uint8_t> subdata(data.begin() + core_message_length,
|
||||
data.end());
|
||||
session()->VerifyRSASignature(subdata, generated_signature.data(),
|
||||
session()->VerifyRsaSignature(subdata, generated_signature.data(),
|
||||
generated_signature.size(), kSign_RSASSA_PSS);
|
||||
SHA256(data.data(), core_message_length, request_hash_);
|
||||
} else {
|
||||
session()->VerifyRSASignature(data, generated_signature.data(),
|
||||
generated_signature.size(), kSign_RSASSA_PSS);
|
||||
session()->VerifySignature(data, generated_signature.data(),
|
||||
generated_signature.size(), kSign_RSASSA_PSS);
|
||||
SHA256(data.data(), core_message_length, request_hash_);
|
||||
}
|
||||
}
|
||||
@@ -1422,17 +1423,15 @@ OEMCryptoResult RenewalRoundTrip::LoadResponse(Session* session) {
|
||||
}
|
||||
}
|
||||
|
||||
Session::Session()
|
||||
: open_(false),
|
||||
forced_session_id_(false),
|
||||
session_id_(0),
|
||||
nonce_(0),
|
||||
public_rsa_(nullptr) {}
|
||||
std::unordered_map<util::EccCurve, std::unique_ptr<util::EccPrivateKey>,
|
||||
std::hash<int>>
|
||||
Session::server_ephemeral_keys_;
|
||||
std::mutex Session::ephemeral_key_map_lock_;
|
||||
|
||||
Session::Session() {}
|
||||
|
||||
Session::~Session() {
|
||||
if (!forced_session_id_ && open_) close();
|
||||
if (public_rsa_) RSA_free(public_rsa_);
|
||||
if (public_ec_) EC_KEY_free(public_ec_);
|
||||
}
|
||||
|
||||
void Session::open() {
|
||||
@@ -1505,17 +1504,19 @@ void Session::GenerateDerivedKeysFromKeybox(
|
||||
OEMCrypto_GenerateDerivedKeys(
|
||||
session_id(), mac_context.data(), mac_context.size(),
|
||||
enc_context.data(), enc_context.size()));
|
||||
key_deriver_.DeriveKeys(keybox.device_key_, mac_context, enc_context);
|
||||
key_deriver_.DeriveKeys(keybox.device_key_, sizeof(keybox.device_key_),
|
||||
mac_context, enc_context);
|
||||
}
|
||||
|
||||
void Session::GenerateDerivedKeysFromSessionKey() {
|
||||
// Uses test certificate.
|
||||
vector<uint8_t> session_key;
|
||||
vector<uint8_t> enc_session_key;
|
||||
ASSERT_NE(public_rsa_, nullptr) << "No public RSA key loaded in test code.";
|
||||
ASSERT_TRUE(public_rsa_ || public_ec_)
|
||||
<< "No public RSA/ECC key loaded in test code";
|
||||
// A failure here probably indicates that there is something wrong with the
|
||||
// test program and its dependency on BoringSSL.
|
||||
ASSERT_TRUE(GenerateRSASessionKey(&session_key, &enc_session_key));
|
||||
ASSERT_TRUE(GenerateSessionKey(&session_key, &enc_session_key));
|
||||
vector<uint8_t> mac_context;
|
||||
vector<uint8_t> enc_context;
|
||||
FillDefaultContext(&mac_context, &enc_context);
|
||||
@@ -1526,7 +1527,8 @@ void Session::GenerateDerivedKeysFromSessionKey() {
|
||||
mac_context.data(), mac_context.size(), enc_context.data(),
|
||||
enc_context.size()));
|
||||
|
||||
key_deriver_.DeriveKeys(session_key.data(), mac_context, enc_context);
|
||||
key_deriver_.DeriveKeys(session_key.data(), session_key.size(), mac_context,
|
||||
enc_context);
|
||||
}
|
||||
|
||||
void Session::TestDecryptCTR(bool select_key_first,
|
||||
@@ -1640,12 +1642,11 @@ void Session::LoadOEMCert(bool verify_cert) {
|
||||
boringssl_ptr<EVP_PKEY, EVP_PKEY_free> pubkey(X509_get_pubkey(x509_cert));
|
||||
ASSERT_TRUE(pubkey.NotNull());
|
||||
if (i == 0) {
|
||||
public_rsa_ = EVP_PKEY_get1_RSA(pubkey.get());
|
||||
if (!public_rsa_) {
|
||||
cerr << "d2i_RSAPrivateKey failed.\n";
|
||||
dump_boringssl_error();
|
||||
ASSERT_TRUE(nullptr != public_rsa_);
|
||||
}
|
||||
public_rsa_ =
|
||||
util::RsaPublicKey::FromSslHandle(EVP_PKEY_get0_RSA(pubkey.get()));
|
||||
ASSERT_TRUE(public_rsa_)
|
||||
<< "Failed to extract public RSA key from OEM certificate";
|
||||
return;
|
||||
}
|
||||
if (verify_cert) {
|
||||
vector<char> buffer(80);
|
||||
@@ -1677,199 +1678,181 @@ void Session::LoadOEMCert(bool verify_cert) {
|
||||
}
|
||||
}
|
||||
|
||||
void Session::PreparePublicKey(const uint8_t* rsa_key, size_t rsa_key_length) {
|
||||
if (rsa_key == nullptr) {
|
||||
rsa_key = kTestRSAPKCS8PrivateKeyInfo2_2048;
|
||||
rsa_key_length = sizeof(kTestRSAPKCS8PrivateKeyInfo2_2048);
|
||||
}
|
||||
uint8_t* p = const_cast<uint8_t*>(rsa_key);
|
||||
boringssl_ptr<BIO, BIO_vfree> bio(
|
||||
BIO_new_mem_buf(p, static_cast<int>(rsa_key_length)));
|
||||
ASSERT_TRUE(bio.NotNull());
|
||||
boringssl_ptr<PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO_free> pkcs8_pki(
|
||||
d2i_PKCS8_PRIV_KEY_INFO_bio(bio.get(), nullptr));
|
||||
ASSERT_TRUE(pkcs8_pki.NotNull());
|
||||
boringssl_ptr<EVP_PKEY, EVP_PKEY_free> evp(EVP_PKCS82PKEY(pkcs8_pki.get()));
|
||||
ASSERT_TRUE(evp.NotNull());
|
||||
if (public_rsa_) RSA_free(public_rsa_);
|
||||
public_rsa_ = EVP_PKEY_get1_RSA(evp.get());
|
||||
if (!public_rsa_) {
|
||||
cerr << "d2i_RSAPrivateKey failed. ";
|
||||
dump_boringssl_error();
|
||||
FAIL() << "Could not parse public RSA key.";
|
||||
}
|
||||
switch (RSA_check_key(public_rsa_)) {
|
||||
case 1: // valid.
|
||||
void Session::SetTestRsaPublicKey() {
|
||||
public_ec_.reset();
|
||||
public_rsa_ = util::RsaPublicKey::LoadPrivateKeyInfo(
|
||||
kTestRSAPKCS8PrivateKeyInfo2_2048,
|
||||
sizeof(kTestRSAPKCS8PrivateKeyInfo2_2048));
|
||||
ASSERT_TRUE(public_rsa_) << "Could not parse test RSA public key #2";
|
||||
}
|
||||
|
||||
void Session::SetPublicKeyFromPrivateKeyInfo(OEMCrypto_PrivateKeyType key_type,
|
||||
const uint8_t* buffer,
|
||||
size_t length) {
|
||||
switch (key_type) {
|
||||
case OEMCrypto_RSA_Private_Key:
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
SetRsaPublicKeyFromPrivateKeyInfo(buffer, length));
|
||||
return;
|
||||
case 0: // not valid.
|
||||
dump_boringssl_error();
|
||||
FAIL() << "[rsa key not valid] ";
|
||||
default: // -1 == check failed.
|
||||
dump_boringssl_error();
|
||||
FAIL() << "[error checking rsa key] ";
|
||||
}
|
||||
}
|
||||
|
||||
void Session::SetRsaPublicKey(const uint8_t* buffer, size_t length) {
|
||||
if (public_rsa_) {
|
||||
RSA_free(public_rsa_);
|
||||
public_rsa_ = nullptr;
|
||||
}
|
||||
if (public_ec_) {
|
||||
EC_KEY_free(public_ec_);
|
||||
public_ec_ = nullptr;
|
||||
}
|
||||
public_rsa_ = d2i_RSA_PUBKEY(nullptr, &buffer, length);
|
||||
if (!public_rsa_) {
|
||||
cout << "d2i_RSAPrivateKey failed. ";
|
||||
dump_boringssl_error();
|
||||
FAIL() << "Could not parse public RSA key.";
|
||||
}
|
||||
switch (RSA_check_key(public_rsa_)) {
|
||||
case 1: // valid.
|
||||
case OEMCrypto_ECC_Private_Key:
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
SetEccPublicKeyFromPrivateKeyInfo(buffer, length));
|
||||
return;
|
||||
case 0: // not valid.
|
||||
dump_boringssl_error();
|
||||
FAIL() << "[rsa key not valid] ";
|
||||
default: // -1 == check failed.
|
||||
dump_boringssl_error();
|
||||
FAIL() << "[error checking rsa key] ";
|
||||
}
|
||||
FAIL() << "Unknown key type: " << static_cast<int>(key_type);
|
||||
}
|
||||
|
||||
void Session::SetEcPublicKey(const uint8_t* buffer, size_t length) {
|
||||
if (public_rsa_) {
|
||||
RSA_free(public_rsa_);
|
||||
public_rsa_ = nullptr;
|
||||
}
|
||||
if (public_ec_) {
|
||||
EC_KEY_free(public_ec_);
|
||||
public_ec_ = nullptr;
|
||||
}
|
||||
public_ec_ = d2i_EC_PUBKEY(nullptr, &buffer, length);
|
||||
if (!public_ec_) {
|
||||
cout << "d2i_RSAPrivateKey failed. ";
|
||||
dump_boringssl_error();
|
||||
FAIL() << "Could not parse public RSA key.";
|
||||
}
|
||||
switch (EC_KEY_check_key(public_ec_)) {
|
||||
case 1: // valid.
|
||||
void Session::SetRsaPublicKeyFromPrivateKeyInfo(const uint8_t* buffer,
|
||||
size_t length) {
|
||||
public_ec_.reset();
|
||||
public_rsa_ = util::RsaPublicKey::LoadPrivateKeyInfo(buffer, length);
|
||||
ASSERT_TRUE(public_rsa_) << "Could not parse RSA public key";
|
||||
}
|
||||
|
||||
void Session::SetEccPublicKeyFromPrivateKeyInfo(const uint8_t* buffer,
|
||||
size_t length) {
|
||||
public_rsa_.reset();
|
||||
public_ec_ = util::EccPublicKey::LoadPrivateKeyInfo(buffer, length);
|
||||
ASSERT_TRUE(public_ec_) << "Could not parse ECC public key";
|
||||
}
|
||||
|
||||
void Session::SetPublicKeyFromSubjectPublicKey(
|
||||
OEMCrypto_PrivateKeyType key_type, const uint8_t* buffer, size_t length) {
|
||||
switch (key_type) {
|
||||
case OEMCrypto_RSA_Private_Key:
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
SetRsaPublicKeyFromSubjectPublicKey(buffer, length));
|
||||
return;
|
||||
case OEMCrypto_ECC_Private_Key:
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
SetEccPublicKeyFromSubjectPublicKey(buffer, length));
|
||||
return;
|
||||
case 0: // not valid.
|
||||
default:
|
||||
dump_boringssl_error();
|
||||
FAIL() << "[ec key not valid] ";
|
||||
}
|
||||
FAIL() << "Unknown key type: " << static_cast<int>(key_type);
|
||||
}
|
||||
|
||||
bool Session::VerifyPSSSignature(EVP_PKEY* pkey, const uint8_t* message,
|
||||
size_t message_length,
|
||||
const uint8_t* signature,
|
||||
size_t signature_length) {
|
||||
boringssl_ptr<EVP_MD_CTX, EVP_MD_CTX_free> md_ctx(EVP_MD_CTX_new());
|
||||
EVP_PKEY_CTX* pkey_ctx = nullptr;
|
||||
|
||||
if (EVP_DigestVerifyInit(md_ctx.get(), &pkey_ctx, EVP_sha1(),
|
||||
nullptr /* no ENGINE */, pkey) != 1) {
|
||||
LOGE("EVP_DigestVerifyInit failed in VerifyPSSSignature");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (EVP_PKEY_CTX_set_signature_md(pkey_ctx,
|
||||
const_cast<EVP_MD*>(EVP_sha1())) != 1) {
|
||||
LOGE("EVP_PKEY_CTX_set_signature_md failed in VerifyPSSSignature");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING) != 1) {
|
||||
LOGE("EVP_PKEY_CTX_set_rsa_padding failed in VerifyPSSSignature");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_ctx, SHA_DIGEST_LENGTH) != 1) {
|
||||
LOGE("EVP_PKEY_CTX_set_rsa_pss_saltlen failed in VerifyPSSSignature");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (EVP_DigestVerifyUpdate(md_ctx.get(), message, message_length) != 1) {
|
||||
LOGE("EVP_DigestVerifyUpdate failed in VerifyPSSSignature");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (EVP_DigestVerifyFinal(md_ctx.get(), const_cast<uint8_t*>(signature),
|
||||
signature_length) != 1) {
|
||||
LOGE(
|
||||
"EVP_DigestVerifyFinal failed in VerifyPSSSignature. (Probably a bad "
|
||||
"signature.)");
|
||||
goto err;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
err:
|
||||
dump_boringssl_error();
|
||||
return false;
|
||||
void Session::SetRsaPublicKeyFromSubjectPublicKey(const uint8_t* buffer,
|
||||
size_t length) {
|
||||
public_ec_.reset();
|
||||
public_rsa_ = util::RsaPublicKey::Load(buffer, length);
|
||||
ASSERT_TRUE(public_rsa_) << "Could not parse RSA public key";
|
||||
}
|
||||
|
||||
void Session::VerifyRSASignature(const vector<uint8_t>& message,
|
||||
void Session::SetEccPublicKeyFromSubjectPublicKey(const uint8_t* buffer,
|
||||
size_t length) {
|
||||
public_rsa_.reset();
|
||||
public_ec_ = util::EccPublicKey::Load(buffer, length);
|
||||
ASSERT_TRUE(public_ec_) << "Could not parse ECC public key";
|
||||
}
|
||||
|
||||
void Session::VerifyRsaSignature(const vector<uint8_t>& message,
|
||||
const uint8_t* signature,
|
||||
size_t signature_length,
|
||||
RSA_Padding_Scheme padding_scheme) {
|
||||
ASSERT_NE(public_rsa_, nullptr) << "No public RSA key loaded in test code.";
|
||||
|
||||
ASSERT_EQ(static_cast<size_t>(RSA_size(public_rsa_)), signature_length)
|
||||
<< "Signature size is wrong. " << signature_length << ", should be "
|
||||
<< RSA_size(public_rsa_);
|
||||
|
||||
if (padding_scheme == kSign_RSASSA_PSS) {
|
||||
boringssl_ptr<EVP_PKEY, EVP_PKEY_free> pkey(EVP_PKEY_new());
|
||||
ASSERT_EQ(1, EVP_PKEY_set1_RSA(pkey.get(), public_rsa_));
|
||||
|
||||
const bool ok =
|
||||
VerifyPSSSignature(pkey.get(), message.data(), message.size(),
|
||||
signature, signature_length);
|
||||
EXPECT_TRUE(ok) << "PSS signature check failed.";
|
||||
} else if (padding_scheme == kSign_PKCS1_Block1) {
|
||||
vector<uint8_t> padded_digest(signature_length);
|
||||
int size;
|
||||
// RSA_public_decrypt decrypts the signature, and then verifies that
|
||||
// it was padded with RSA PKCS1 padding.
|
||||
size = RSA_public_decrypt(static_cast<int>(signature_length), signature,
|
||||
padded_digest.data(), public_rsa_,
|
||||
RSA_PKCS1_PADDING);
|
||||
EXPECT_GT(size, 0);
|
||||
padded_digest.resize(size);
|
||||
EXPECT_EQ(message, padded_digest);
|
||||
} else {
|
||||
EXPECT_TRUE(false) << "Padding scheme not supported.";
|
||||
ASSERT_TRUE(public_rsa_) << "No public RSA key loaded in test code";
|
||||
if (padding_scheme != kSign_RSASSA_PSS &&
|
||||
padding_scheme != kSign_PKCS1_Block1) {
|
||||
FAIL() << "Padding scheme not supported: " << padding_scheme;
|
||||
return;
|
||||
}
|
||||
const util::RsaSignatureAlgorithm algorithm =
|
||||
padding_scheme == kSign_RSASSA_PSS ? util::kRsaPssDefault
|
||||
: util::kRsaPkcs1Cast;
|
||||
const OEMCryptoResult result = public_rsa_->VerifySignature(
|
||||
message.data(), message.size(), signature, signature_length, algorithm);
|
||||
ASSERT_EQ(result, OEMCrypto_SUCCESS) << "RSA signature check failed";
|
||||
}
|
||||
|
||||
bool Session::GenerateRSASessionKey(vector<uint8_t>* session_key,
|
||||
void Session::VerifyEccSignature(const vector<uint8_t>& message,
|
||||
const uint8_t* signature,
|
||||
size_t signature_length) {
|
||||
ASSERT_TRUE(public_ec_) << "No public ECC key loaded in test code";
|
||||
const OEMCryptoResult result = public_ec_->VerifySignature(
|
||||
message.data(), message.size(), signature, signature_length);
|
||||
ASSERT_EQ(result, OEMCrypto_SUCCESS) << "ECC signature check failed";
|
||||
}
|
||||
|
||||
void Session::VerifySignature(const vector<uint8_t>& message,
|
||||
const uint8_t* signature, size_t signature_length,
|
||||
RSA_Padding_Scheme padding_scheme) {
|
||||
if (public_rsa_ != nullptr) {
|
||||
return VerifyRsaSignature(message, signature, signature_length,
|
||||
padding_scheme);
|
||||
} else if (public_ec_ != nullptr) {
|
||||
return VerifyEccSignature(message, signature, signature_length);
|
||||
}
|
||||
FAIL() << "No public RSA or ECC key loaded in test code";
|
||||
}
|
||||
|
||||
bool Session::GenerateRsaSessionKey(vector<uint8_t>* session_key,
|
||||
vector<uint8_t>* enc_session_key) {
|
||||
if (!public_rsa_) {
|
||||
cerr << "No public RSA key loaded in test code.\n";
|
||||
cerr << "No public RSA key loaded in test code\n";
|
||||
return false;
|
||||
}
|
||||
*session_key = wvutil::a2b_hex("6fa479c731d2770b6a61a5d1420bb9d1");
|
||||
enc_session_key->assign(RSA_size(public_rsa_), 0);
|
||||
int status = RSA_public_encrypt(
|
||||
static_cast<int>(session_key->size()), &(session_key->front()),
|
||||
&(enc_session_key->front()), public_rsa_, RSA_PKCS1_OAEP_PADDING);
|
||||
int size = static_cast<int>(RSA_size(public_rsa_));
|
||||
if (status != size) {
|
||||
cerr << "GenerateRSASessionKey error encrypting session key.\n";
|
||||
dump_boringssl_error();
|
||||
*enc_session_key = public_rsa_->EncryptSessionKey(*session_key);
|
||||
if (enc_session_key->empty()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Session::InstallRSASessionTestKey(const vector<uint8_t>& wrapped_rsa_key) {
|
||||
bool Session::GenerateEccSessionKey(vector<uint8_t>* session_key,
|
||||
vector<uint8_t>* ecdh_public_key_data) {
|
||||
if (!public_ec_) {
|
||||
cerr << "No public ECC key loaded in test code\n";
|
||||
return false;
|
||||
}
|
||||
std::unique_lock<std::mutex> lock(Session::ephemeral_key_map_lock_);
|
||||
const util::EccCurve curve = public_ec_->curve();
|
||||
if (server_ephemeral_keys_.count(curve) == 0) {
|
||||
server_ephemeral_keys_[curve] = util::EccPrivateKey::New(curve);
|
||||
}
|
||||
if (server_ephemeral_keys_.count(curve) == 0) {
|
||||
cerr << "Failed to find/create server ECC key for curve "
|
||||
<< util::EccCurveToString(curve) << std::endl;
|
||||
return false;
|
||||
}
|
||||
*session_key = server_ephemeral_keys_[curve]->DeriveSessionKey(*public_ec_);
|
||||
if (session_key->empty()) {
|
||||
return false;
|
||||
}
|
||||
*ecdh_public_key_data = server_ephemeral_keys_[curve]->SerializeAsPublicKey();
|
||||
if (ecdh_public_key_data->empty()) {
|
||||
session_key->clear();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Session::GenerateSessionKey(vector<uint8_t>* session_key,
|
||||
vector<uint8_t>* key_material) {
|
||||
if (public_rsa_ != nullptr) {
|
||||
return GenerateRsaSessionKey(session_key, key_material);
|
||||
} else if (public_ec_ != nullptr) {
|
||||
return GenerateEccSessionKey(session_key, key_material);
|
||||
}
|
||||
cerr << "No public RSA or ECC key loaded in test code\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
void Session::LoadWrappedDrmKey(OEMCrypto_PrivateKeyType key_type,
|
||||
const vector<uint8_t>& wrapped_drm_key) {
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS,
|
||||
OEMCrypto_LoadDRMPrivateKey(session_id(), OEMCrypto_RSA_Private_Key,
|
||||
wrapped_rsa_key.data(),
|
||||
wrapped_rsa_key.size()));
|
||||
OEMCrypto_LoadDRMPrivateKey(session_id(), key_type,
|
||||
wrapped_drm_key.data(),
|
||||
wrapped_drm_key.size()));
|
||||
}
|
||||
|
||||
void Session::LoadWrappedRsaDrmKey(const vector<uint8_t>& wrapped_rsa_key) {
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
LoadWrappedDrmKey(OEMCrypto_RSA_Private_Key, wrapped_rsa_key));
|
||||
}
|
||||
|
||||
void Session::LoadWrappedEccDrmKey(const vector<uint8_t>& wrapped_ecc_key) {
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
LoadWrappedDrmKey(OEMCrypto_ECC_Private_Key, wrapped_ecc_key));
|
||||
}
|
||||
|
||||
void Session::CreateNewUsageEntry(OEMCryptoResult* status) {
|
||||
|
||||
@@ -8,9 +8,8 @@
|
||||
// OEMCrypto unit tests
|
||||
//
|
||||
#include <gtest/gtest.h>
|
||||
#include <openssl/ec.h>
|
||||
#include <openssl/rsa.h>
|
||||
#include <time.h>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@@ -21,7 +20,9 @@
|
||||
#include "odk.h"
|
||||
#include "oec_device_features.h"
|
||||
#include "oec_key_deriver.h"
|
||||
#include "oemcrypto_ecc_key.h"
|
||||
#include "oemcrypto_fuzz_structs.h"
|
||||
#include "oemcrypto_rsa_key.h"
|
||||
#include "oemcrypto_types.h"
|
||||
#include "pst_report.h"
|
||||
|
||||
@@ -571,38 +572,75 @@ class Session {
|
||||
void RewrapRSAKey(const struct RSAPrivateKeyMessage& encrypted,
|
||||
size_t message_size, const std::vector<uint8_t>& signature,
|
||||
vector<uint8_t>* wrapped_key, bool force);
|
||||
// Loads the specified RSA public key into public_rsa_. If rsa_key is null,
|
||||
// the default test key is loaded.
|
||||
void PreparePublicKey(const uint8_t* rsa_key = nullptr,
|
||||
size_t rsa_key_length = 0);
|
||||
// Loads the default test RSA public key into public_rsa_.
|
||||
void SetTestRsaPublicKey();
|
||||
// Loads the specified DRM public key into the appropriate key.
|
||||
// The provided key is serialized as an ASN.1 DER encoded PrivateKeyInfo.
|
||||
void SetPublicKeyFromPrivateKeyInfo(OEMCrypto_PrivateKeyType key_type,
|
||||
const uint8_t* buffer, size_t length);
|
||||
// Loads the specified RSA public key into public_rsa_.
|
||||
void SetRsaPublicKey(const uint8_t* buffer, size_t length);
|
||||
// The provided key is serialized as an ASN.1 DER encoded PrivateKeyInfo.
|
||||
void SetRsaPublicKeyFromPrivateKeyInfo(const uint8_t* buffer, size_t length);
|
||||
// Loads the specified EC public key into public_ec_.
|
||||
void SetEcPublicKey(const uint8_t* buffer, size_t length);
|
||||
// The provided key is serialized as an ASN.1 DER encoded PrivateKeyInfo.
|
||||
void SetEccPublicKeyFromPrivateKeyInfo(const uint8_t* buffer, size_t length);
|
||||
|
||||
// Loads the specified DRM public key into the appropriate key.
|
||||
// The provided key is serialized as an ASN.1 DER encoded SubjectPublicKey.
|
||||
void SetPublicKeyFromSubjectPublicKey(OEMCrypto_PrivateKeyType key_type,
|
||||
const uint8_t* buffer, size_t length);
|
||||
// Loads the specified RSA public key into public_rsa_.
|
||||
// The provided key is serialized as an ASN.1 DER encoded SubjectPublicKey.
|
||||
void SetRsaPublicKeyFromSubjectPublicKey(const uint8_t* buffer,
|
||||
size_t length);
|
||||
// Loads the specified EC public key into public_ec_.
|
||||
// The provided key is serialized as an ASN.1 DER encoded SubjectPublicKey.
|
||||
void SetEccPublicKeyFromSubjectPublicKey(const uint8_t* buffer,
|
||||
size_t length);
|
||||
|
||||
// Verifies the given signature is from the given message and RSA key, pkey.
|
||||
static bool VerifyPSSSignature(EVP_PKEY* pkey, const uint8_t* message,
|
||||
size_t message_length,
|
||||
const uint8_t* signature,
|
||||
size_t signature_length);
|
||||
// Verify that the message was signed by the private key associated with
|
||||
// |public_rsa_| using the specified padding scheme.
|
||||
void VerifyRSASignature(const vector<uint8_t>& message,
|
||||
void VerifyRsaSignature(const vector<uint8_t>& message,
|
||||
const uint8_t* signature, size_t signature_length,
|
||||
RSA_Padding_Scheme padding_scheme);
|
||||
// Verify that the message was signed by the private key associated with
|
||||
// |public_ecc_| using Widevine ECDSA.
|
||||
void VerifyEccSignature(const vector<uint8_t>& message,
|
||||
const uint8_t* signature, size_t signature_length);
|
||||
// Verify RSA or ECC signature based on the key type installed. The
|
||||
// padding_scheme will be ignored in case of ECC key.
|
||||
void VerifySignature(const vector<uint8_t>& message, const uint8_t* signature,
|
||||
size_t signature_length,
|
||||
RSA_Padding_Scheme padding_scheme);
|
||||
|
||||
// Encrypts a known session key with public_rsa_ for use in future calls to
|
||||
// OEMCrypto_DeriveKeysFromSessionKey or OEMCrypto_RewrapDeviceRSAKey30.
|
||||
// The unencrypted session key is stored in session_key.
|
||||
bool GenerateRSASessionKey(vector<uint8_t>* session_key,
|
||||
bool GenerateRsaSessionKey(vector<uint8_t>* session_key,
|
||||
vector<uint8_t>* enc_session_key);
|
||||
// Derives a session key with public_ec_ and a ephemeral "server" ECC key
|
||||
// for use in future calls to OEMCrypto_DeriveKeysFromSessionKey.
|
||||
// The unencrypted session key is stored in session_key.
|
||||
bool GenerateEccSessionKey(vector<uint8_t>* session_key,
|
||||
vector<uint8_t>* ecdh_public_key_data);
|
||||
// Based on the key type installed, call GenerateRsaSessionKey or
|
||||
// GenerateEccSessionKey.
|
||||
bool GenerateSessionKey(vector<uint8_t>* session_key,
|
||||
vector<uint8_t>* key_material);
|
||||
|
||||
// Calls OEMCrypto_RewrapDeviceRSAKey30 with the given provisioning response
|
||||
// message. If force is true, we assert that the key loads successfully.
|
||||
void RewrapRSAKey30(const struct RSAPrivateKeyMessage& encrypted,
|
||||
const std::vector<uint8_t>& encrypted_message_key,
|
||||
vector<uint8_t>* wrapped_key, bool force);
|
||||
// Loads the specified wrapped_rsa_key into OEMCrypto, and then runs
|
||||
// GenerateDerivedKeysFromSessionKey to install known encryption and mac keys.
|
||||
void InstallRSASessionTestKey(const vector<uint8_t>& wrapped_rsa_key);
|
||||
|
||||
void LoadWrappedDrmKey(OEMCrypto_PrivateKeyType key_type,
|
||||
const vector<uint8_t>& wrapped_drm_key);
|
||||
// Loads the specified wrapped_rsa_key into OEMCrypto.
|
||||
void LoadWrappedRsaDrmKey(const vector<uint8_t>& wrapped_rsa_key);
|
||||
// Loads the specified wrapped_ecc_key into OEMCrypto.
|
||||
void LoadWrappedEccDrmKey(const vector<uint8_t>& wrapped_ecc_key);
|
||||
|
||||
// Creates a new usage entry, and keeps track of the index.
|
||||
// If status is null, we expect success, otherwise status is set to the
|
||||
// return value.
|
||||
@@ -676,21 +714,34 @@ class Session {
|
||||
OEMCryptoResult actual_select_result,
|
||||
OEMCryptoResult actual_decryt_result);
|
||||
|
||||
bool open_;
|
||||
bool forced_session_id_;
|
||||
OEMCrypto_SESSION session_id_;
|
||||
bool open_ = false;
|
||||
bool forced_session_id_ = false;
|
||||
OEMCrypto_SESSION session_id_ = 0;
|
||||
KeyDeriver key_deriver_;
|
||||
uint32_t nonce_;
|
||||
uint32_t nonce_ = 0;
|
||||
// Only one of RSA or EC should be set.
|
||||
RSA* public_rsa_ = nullptr;
|
||||
EC_KEY* public_ec_ = nullptr;
|
||||
std::unique_ptr<util::RsaPublicKey> public_rsa_;
|
||||
std::unique_ptr<util::EccPublicKey> public_ec_;
|
||||
// In provisioning 4.0, the shared session key is derived from either
|
||||
// 1. (client side) client private key + server ephemeral public key, or
|
||||
// 2. (server side) server ephemeral private key + client public key
|
||||
// Encryption key and mac keys are derived from the shared session key, and
|
||||
// are inserted in to the default license response which simulates the
|
||||
// response from a license server. In order for these keys to be deterministic
|
||||
// across multiple test calls of GenerateDerivedKeysFromSessionKey(), which
|
||||
// simulates how the server derives keys, the ephemeral keys used by the
|
||||
// "server" need to be stored for re-use.
|
||||
static std::unordered_map<
|
||||
util::EccCurve, std::unique_ptr<util::EccPrivateKey>, std::hash<int>>
|
||||
server_ephemeral_keys_;
|
||||
static std::mutex ephemeral_key_map_lock_;
|
||||
vector<uint8_t> pst_report_buffer_;
|
||||
MessageData license_ = {};
|
||||
|
||||
vector<uint8_t> encrypted_usage_entry_;
|
||||
uint32_t usage_entry_number_;
|
||||
uint32_t usage_entry_number_ = 0;
|
||||
string pst_;
|
||||
};
|
||||
}; // class Session
|
||||
|
||||
// Used for OEMCrypto Fuzzing: Convert byte to a valid boolean to avoid errors
|
||||
// generated by msan.
|
||||
|
||||
@@ -75,29 +75,35 @@ void SessionUtil::EnsureTestKeys() {
|
||||
// are installed in OEMCrypto and in the test session.
|
||||
void SessionUtil::InstallTestRSAKey(Session* s) {
|
||||
if (global_features.provisioning_method == OEMCrypto_BootCertificateChain) {
|
||||
const size_t buffer_size = 5000; // Make sure it is large enough.
|
||||
std::vector<uint8_t> public_key(buffer_size);
|
||||
size_t public_key_size = buffer_size;
|
||||
std::vector<uint8_t> public_key_signature(buffer_size);
|
||||
size_t public_key_signature_size = buffer_size;
|
||||
std::vector<uint8_t> wrapped_private_key(buffer_size);
|
||||
size_t wrapped_private_key_size = buffer_size;
|
||||
OEMCrypto_PrivateKeyType key_type;
|
||||
// Assume OEM cert has been loaded.
|
||||
ASSERT_EQ(
|
||||
OEMCrypto_SUCCESS,
|
||||
OEMCrypto_GenerateCertificateKeyPair(
|
||||
s->session_id(), public_key.data(), &public_key_size,
|
||||
public_key_signature.data(), &public_key_signature_size,
|
||||
wrapped_private_key.data(), &wrapped_private_key_size, &key_type));
|
||||
// Assume the public key has been verified by the server and the DRM cert is
|
||||
// returned.
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS,
|
||||
OEMCrypto_LoadDRMPrivateKey(s->session_id(), key_type,
|
||||
wrapped_private_key.data(),
|
||||
wrapped_private_key_size));
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
s->SetRsaPublicKey(public_key.data(), public_key_size));
|
||||
if (wrapped_rsa_key_.size() == 0) {
|
||||
// If we don't have a wrapped key yet, create one.
|
||||
// This wrapped key will be shared by all sessions in the test.
|
||||
const size_t buffer_size = 5000; // Make sure it is large enough.
|
||||
std::vector<uint8_t> public_key(buffer_size);
|
||||
size_t public_key_size = buffer_size;
|
||||
std::vector<uint8_t> public_key_signature(buffer_size);
|
||||
size_t public_key_signature_size = buffer_size;
|
||||
std::vector<uint8_t> wrapped_private_key(buffer_size);
|
||||
size_t wrapped_private_key_size = buffer_size;
|
||||
OEMCrypto_PrivateKeyType key_type;
|
||||
// Assume OEM cert has been loaded.
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS,
|
||||
OEMCrypto_GenerateCertificateKeyPair(
|
||||
s->session_id(), public_key.data(), &public_key_size,
|
||||
public_key_signature.data(), &public_key_signature_size,
|
||||
wrapped_private_key.data(), &wrapped_private_key_size,
|
||||
&key_type));
|
||||
// Assume the public key has been verified by the server and the DRM cert
|
||||
// is returned.
|
||||
wrapped_private_key.resize(wrapped_private_key_size);
|
||||
public_key.resize(public_key_size);
|
||||
wrapped_rsa_key_ = wrapped_private_key;
|
||||
drm_public_key_ = public_key;
|
||||
key_type_ = key_type;
|
||||
}
|
||||
ASSERT_NO_FATAL_FAILURE(s->LoadWrappedDrmKey(key_type_, wrapped_rsa_key_));
|
||||
ASSERT_NO_FATAL_FAILURE(s->SetPublicKeyFromSubjectPublicKey(
|
||||
key_type_, drm_public_key_.data(), drm_public_key_.size()));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -108,10 +114,10 @@ void SessionUtil::InstallTestRSAKey(Session* s) {
|
||||
ASSERT_NO_FATAL_FAILURE(CreateWrappedRSAKey());
|
||||
}
|
||||
// Load the wrapped rsa test key.
|
||||
ASSERT_NO_FATAL_FAILURE(s->InstallRSASessionTestKey(wrapped_rsa_key_));
|
||||
ASSERT_NO_FATAL_FAILURE(s->LoadWrappedRsaDrmKey(wrapped_rsa_key_));
|
||||
}
|
||||
// Test RSA key should be loaded.
|
||||
ASSERT_NO_FATAL_FAILURE(s->PreparePublicKey());
|
||||
ASSERT_NO_FATAL_FAILURE(s->SetTestRsaPublicKey());
|
||||
}
|
||||
|
||||
} // namespace wvoec
|
||||
|
||||
@@ -1,38 +1,40 @@
|
||||
#include <assert.h>
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <openssl/aes.h>
|
||||
#include <openssl/rand.h>
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
|
||||
#include "OEMCryptoCENC.h"
|
||||
#include "oec_session_util.h"
|
||||
#include "oec_test_data.h"
|
||||
#include "OEMCryptoCENC.h"
|
||||
|
||||
namespace wvoec {
|
||||
|
||||
class SessionUtil {
|
||||
public:
|
||||
SessionUtil()
|
||||
: encoded_rsa_key_(kTestRSAPKCS8PrivateKeyInfo2_2048,
|
||||
kTestRSAPKCS8PrivateKeyInfo2_2048 +
|
||||
sizeof(kTestRSAPKCS8PrivateKeyInfo2_2048)) {}
|
||||
public:
|
||||
SessionUtil()
|
||||
: encoded_rsa_key_(kTestRSAPKCS8PrivateKeyInfo2_2048,
|
||||
kTestRSAPKCS8PrivateKeyInfo2_2048 +
|
||||
sizeof(kTestRSAPKCS8PrivateKeyInfo2_2048)) {}
|
||||
|
||||
// Create a new wrapped DRM Certificate.
|
||||
void CreateWrappedRSAKey();
|
||||
// Create a new wrapped DRM Certificate.
|
||||
void CreateWrappedRSAKey();
|
||||
|
||||
// This is used to force installation of a keybox. This overwrites the
|
||||
// production keybox -- it does NOT use OEMCrypto_LoadTestKeybox.
|
||||
void InstallKeybox(const wvoec::WidevineKeybox& keybox, bool good);
|
||||
// This is used to force installation of a keybox. This overwrites the
|
||||
// production keybox -- it does NOT use OEMCrypto_LoadTestKeybox.
|
||||
void InstallKeybox(const wvoec::WidevineKeybox& keybox, bool good);
|
||||
|
||||
// This loads the test keybox or the test RSA key, using LoadTestKeybox or
|
||||
// LoadTestRSAKey as needed.
|
||||
void EnsureTestKeys();
|
||||
// This loads the test keybox or the test RSA key, using LoadTestKeybox or
|
||||
// LoadTestRSAKey as needed.
|
||||
void EnsureTestKeys();
|
||||
|
||||
void InstallTestRSAKey(Session* s);
|
||||
void InstallTestRSAKey(Session* s);
|
||||
|
||||
std::vector<uint8_t> encoded_rsa_key_;
|
||||
std::vector<uint8_t> wrapped_rsa_key_;
|
||||
wvoec::WidevineKeybox keybox_;
|
||||
std::vector<uint8_t> encoded_rsa_key_;
|
||||
std::vector<uint8_t> wrapped_rsa_key_;
|
||||
OEMCrypto_PrivateKeyType key_type_;
|
||||
std::vector<uint8_t> drm_public_key_;
|
||||
wvoec::WidevineKeybox keybox_;
|
||||
};
|
||||
|
||||
} // namespace wvoec
|
||||
|
||||
@@ -275,7 +275,7 @@ TEST_F(OEMCryptoClientTest, VersionNumber) {
|
||||
// If any of the following fail, then it is time to update the log message
|
||||
// above.
|
||||
EXPECT_EQ(ODK_MAJOR_VERSION, 17);
|
||||
EXPECT_EQ(ODK_MINOR_VERSION, 0);
|
||||
EXPECT_EQ(ODK_MINOR_VERSION, 1);
|
||||
EXPECT_EQ(kCurrentAPI, 17u);
|
||||
OEMCrypto_Security_Level level = OEMCrypto_SecurityLevel();
|
||||
EXPECT_GT(level, OEMCrypto_Level_Unknown);
|
||||
@@ -1214,8 +1214,8 @@ TEST_F(OEMCryptoProv30Test, GetCertOnlyAPI16) {
|
||||
Session s;
|
||||
ASSERT_NO_FATAL_FAILURE(s.open());
|
||||
// Install the DRM Cert's RSA key.
|
||||
ASSERT_NO_FATAL_FAILURE(s.InstallRSASessionTestKey(wrapped_rsa_key_));
|
||||
ASSERT_NO_FATAL_FAILURE(s.PreparePublicKey());
|
||||
ASSERT_NO_FATAL_FAILURE(s.LoadWrappedRsaDrmKey(wrapped_rsa_key_));
|
||||
ASSERT_NO_FATAL_FAILURE(s.SetTestRsaPublicKey());
|
||||
// Request the OEM Cert. -- This should NOT load the OEM Private key.
|
||||
vector<uint8_t> public_cert;
|
||||
size_t public_cert_length = 0;
|
||||
@@ -1247,8 +1247,8 @@ TEST_F(OEMCryptoProv30Test, OEMCryptoMemoryGetOEMPublicCertForHugeCertLength) {
|
||||
Session s;
|
||||
ASSERT_NO_FATAL_FAILURE(s.open());
|
||||
// Install the DRM Cert's RSA key.
|
||||
ASSERT_NO_FATAL_FAILURE(s.InstallRSASessionTestKey(wrapped_rsa_key_));
|
||||
ASSERT_NO_FATAL_FAILURE(s.PreparePublicKey());
|
||||
ASSERT_NO_FATAL_FAILURE(s.LoadWrappedRsaDrmKey(wrapped_rsa_key_));
|
||||
ASSERT_NO_FATAL_FAILURE(s.SetTestRsaPublicKey());
|
||||
|
||||
auto oemcrypto_function = [](size_t input_length) {
|
||||
size_t public_cert_length = input_length;
|
||||
@@ -1355,15 +1355,8 @@ TEST_F(OEMCryptoProv40Test, GenerateCertificateKeyPairSuccess) {
|
||||
public_key_signature.resize(public_key_signature_size);
|
||||
wrapped_private_key.resize(wrapped_private_key_size);
|
||||
// Parse the public key generated to make sure it is correctly formatted.
|
||||
if (key_type == OEMCrypto_PrivateKeyType::OEMCrypto_RSA_Private_Key) {
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
s.SetRsaPublicKey(public_key.data(), public_key_size));
|
||||
} else if (key_type == OEMCrypto_PrivateKeyType::OEMCrypto_ECC_Private_Key) {
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
s.SetEcPublicKey(public_key.data(), public_key_size));
|
||||
} else {
|
||||
FAIL() << "Unknown private key type: " << static_cast<int>(key_type);
|
||||
}
|
||||
ASSERT_NO_FATAL_FAILURE(s.SetPublicKeyFromSubjectPublicKey(
|
||||
key_type, public_key.data(), public_key_size));
|
||||
}
|
||||
|
||||
// Verifies the generated key pairs are different on each call.
|
||||
@@ -1522,11 +1515,19 @@ TEST_F(OEMCryptoProv40Test, InstallOemPrivateKeyCanBeUsed) {
|
||||
wrapped_private_key2.resize(wrapped_private_key_size2);
|
||||
|
||||
// Verify public_key_signature2 with public_key1.
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
s.SetRsaPublicKey(public_key1.data(), public_key1.size()));
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
s.VerifyRSASignature(public_key2, public_key_signature2.data(),
|
||||
public_key_signature2.size(), kSign_RSASSA_PSS));
|
||||
if (key_type2 == OEMCrypto_PrivateKeyType::OEMCrypto_RSA_Private_Key) {
|
||||
ASSERT_NO_FATAL_FAILURE(s.SetRsaPublicKeyFromSubjectPublicKey(
|
||||
public_key1.data(), public_key1.size()));
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
s.VerifyRsaSignature(public_key2, public_key_signature2.data(),
|
||||
public_key_signature2.size(), kSign_RSASSA_PSS));
|
||||
} else if (key_type2 == OEMCrypto_PrivateKeyType::OEMCrypto_ECC_Private_Key) {
|
||||
ASSERT_NO_FATAL_FAILURE(s.SetEccPublicKeyFromSubjectPublicKey(
|
||||
public_key1.data(), public_key1.size()));
|
||||
ASSERT_NO_FATAL_FAILURE(s.VerifyEccSignature(public_key2,
|
||||
public_key_signature2.data(),
|
||||
public_key_signature2.size()));
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
@@ -5363,7 +5364,7 @@ TEST_F(OEMCryptoLoadsCertificate, LoadRSASessionKey) {
|
||||
ASSERT_NO_FATAL_FAILURE(CreateWrappedRSAKey());
|
||||
Session s;
|
||||
ASSERT_NO_FATAL_FAILURE(s.open());
|
||||
ASSERT_NO_FATAL_FAILURE(s.InstallRSASessionTestKey(wrapped_rsa_key_));
|
||||
ASSERT_NO_FATAL_FAILURE(s.LoadWrappedRsaDrmKey(wrapped_rsa_key_));
|
||||
}
|
||||
|
||||
TEST_F(OEMCryptoLoadsCertificate, SignProvisioningRequest) {
|
||||
@@ -5797,14 +5798,10 @@ TEST_F(OEMCryptoLoadsCertificate,
|
||||
|
||||
// Test that a wrapped RSA key can be loaded.
|
||||
TEST_F(OEMCryptoLoadsCertificate, LoadWrappedRSAKey) {
|
||||
OEMCryptoResult sts;
|
||||
ASSERT_NO_FATAL_FAILURE(CreateWrappedRSAKey());
|
||||
Session s;
|
||||
ASSERT_NO_FATAL_FAILURE(s.open());
|
||||
sts = OEMCrypto_LoadDRMPrivateKey(s.session_id(), OEMCrypto_RSA_Private_Key,
|
||||
wrapped_rsa_key_.data(),
|
||||
wrapped_rsa_key_.size());
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
|
||||
ASSERT_NO_FATAL_FAILURE(s.LoadWrappedRsaDrmKey(wrapped_rsa_key_));
|
||||
}
|
||||
|
||||
TEST_F(OEMCryptoLoadsCertificate,
|
||||
@@ -5813,9 +5810,8 @@ TEST_F(OEMCryptoLoadsCertificate,
|
||||
auto oemcrypto_function = [&](size_t wrapped_rsa_key_length) {
|
||||
Session s;
|
||||
s.open();
|
||||
vector<uint8_t> wrapped_rsa_key_buffer(wrapped_rsa_key_length);
|
||||
memcpy(wrapped_rsa_key_buffer.data(), wrapped_rsa_key_.data(),
|
||||
wrapped_rsa_key_.size());
|
||||
vector<uint8_t> wrapped_rsa_key_buffer = wrapped_rsa_key_;
|
||||
wrapped_rsa_key_buffer.resize(wrapped_rsa_key_length);
|
||||
OEMCryptoResult result = OEMCrypto_LoadDRMPrivateKey(
|
||||
s.session_id(), OEMCrypto_RSA_Private_Key,
|
||||
wrapped_rsa_key_buffer.data(), wrapped_rsa_key_buffer.size());
|
||||
@@ -5856,9 +5852,9 @@ class OEMCryptoLoadsCertVariousKeys : public OEMCryptoLoadsCertificate {
|
||||
ASSERT_NO_FATAL_FAILURE(CreateWrappedRSAKey());
|
||||
Session s;
|
||||
ASSERT_NO_FATAL_FAILURE(s.open());
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
s.PreparePublicKey(encoded_rsa_key_.data(), encoded_rsa_key_.size()));
|
||||
ASSERT_NO_FATAL_FAILURE(s.InstallRSASessionTestKey(wrapped_rsa_key_));
|
||||
ASSERT_NO_FATAL_FAILURE(s.SetRsaPublicKeyFromPrivateKeyInfo(
|
||||
encoded_rsa_key_.data(), encoded_rsa_key_.size()));
|
||||
ASSERT_NO_FATAL_FAILURE(s.LoadWrappedRsaDrmKey(wrapped_rsa_key_));
|
||||
|
||||
LicenseRoundTrip license_messages(&s);
|
||||
ASSERT_NO_FATAL_FAILURE(license_messages.SignAndVerifyRequest());
|
||||
@@ -5927,12 +5923,9 @@ TEST_F(OEMCryptoLoadsCertificate, TestMultipleRSAKeys) {
|
||||
Session s1; // Session s1 loads the default rsa key, but doesn't use it
|
||||
// until after s2 uses its key.
|
||||
ASSERT_NO_FATAL_FAILURE(s1.open());
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
s1.PreparePublicKey(encoded_rsa_key_.data(), encoded_rsa_key_.size()));
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS,
|
||||
OEMCrypto_LoadDRMPrivateKey(
|
||||
s1.session_id(), OEMCrypto_RSA_Private_Key,
|
||||
wrapped_rsa_key_.data(), wrapped_rsa_key_.size()));
|
||||
ASSERT_NO_FATAL_FAILURE(s1.SetRsaPublicKeyFromPrivateKeyInfo(
|
||||
encoded_rsa_key_.data(), encoded_rsa_key_.size()));
|
||||
ASSERT_NO_FATAL_FAILURE(s1.LoadWrappedRsaDrmKey(wrapped_rsa_key_));
|
||||
|
||||
Session s2; // Session s2 uses a different rsa key.
|
||||
encoded_rsa_key_.assign(kTestRSAPKCS8PrivateKeyInfo4_2048,
|
||||
@@ -5940,9 +5933,9 @@ TEST_F(OEMCryptoLoadsCertificate, TestMultipleRSAKeys) {
|
||||
sizeof(kTestRSAPKCS8PrivateKeyInfo4_2048));
|
||||
ASSERT_NO_FATAL_FAILURE(CreateWrappedRSAKey());
|
||||
ASSERT_NO_FATAL_FAILURE(s2.open());
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
s2.PreparePublicKey(encoded_rsa_key_.data(), encoded_rsa_key_.size()));
|
||||
ASSERT_NO_FATAL_FAILURE(s2.InstallRSASessionTestKey(wrapped_rsa_key_));
|
||||
ASSERT_NO_FATAL_FAILURE(s2.SetRsaPublicKeyFromPrivateKeyInfo(
|
||||
encoded_rsa_key_.data(), encoded_rsa_key_.size()));
|
||||
ASSERT_NO_FATAL_FAILURE(s2.LoadWrappedRsaDrmKey(wrapped_rsa_key_));
|
||||
LicenseRoundTrip license_messages2(&s2);
|
||||
ASSERT_NO_FATAL_FAILURE(license_messages2.SignAndVerifyRequest());
|
||||
ASSERT_NO_FATAL_FAILURE(license_messages2.CreateDefaultResponse());
|
||||
@@ -5976,10 +5969,10 @@ TEST_F(OEMCryptoLoadsCertificate, TestMaxDRMKeys) {
|
||||
kTestRSAPKCS8PrivateKeys_2048[key_index].end());
|
||||
ASSERT_NO_FATAL_FAILURE(CreateWrappedRSAKey());
|
||||
ASSERT_NO_FATAL_FAILURE(sessions[i]->open());
|
||||
ASSERT_NO_FATAL_FAILURE(sessions[i]->PreparePublicKey(
|
||||
ASSERT_NO_FATAL_FAILURE(sessions[i]->SetRsaPublicKeyFromPrivateKeyInfo(
|
||||
encoded_rsa_key_.data(), encoded_rsa_key_.size()));
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
sessions[i]->InstallRSASessionTestKey(wrapped_rsa_key_));
|
||||
sessions[i]->LoadWrappedRsaDrmKey(wrapped_rsa_key_));
|
||||
}
|
||||
|
||||
// Attempts to load one more key than the kMaxTotalDRMPrivateKeys
|
||||
@@ -6053,7 +6046,7 @@ class OEMCryptoUsesCertificate : public OEMCryptoLoadsCertificate {
|
||||
ASSERT_NO_FATAL_FAILURE(session_.open());
|
||||
if (global_features.derive_key_method ==
|
||||
DeviceFeatures::LOAD_TEST_RSA_KEY) {
|
||||
ASSERT_NO_FATAL_FAILURE(session_.PreparePublicKey(
|
||||
ASSERT_NO_FATAL_FAILURE(session_.SetRsaPublicKeyFromPrivateKeyInfo(
|
||||
encoded_rsa_key_.data(), encoded_rsa_key_.size()));
|
||||
} else {
|
||||
InstallTestRSAKey(&session_);
|
||||
@@ -6092,10 +6085,7 @@ TEST_F(OEMCryptoLoadsCertificate, RSAPerformance) {
|
||||
while (clock.now() - start_time < kTestDuration) {
|
||||
Session s;
|
||||
ASSERT_NO_FATAL_FAILURE(s.open());
|
||||
sts = OEMCrypto_LoadDRMPrivateKey(s.session_id(), OEMCrypto_RSA_Private_Key,
|
||||
wrapped_rsa_key_.data(),
|
||||
wrapped_rsa_key_.size());
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
|
||||
ASSERT_NO_FATAL_FAILURE(s.LoadWrappedRsaDrmKey(wrapped_rsa_key_));
|
||||
const size_t size = 50;
|
||||
vector<uint8_t> licenseRequest(size);
|
||||
GetRandBytes(licenseRequest.data(), licenseRequest.size());
|
||||
@@ -6133,15 +6123,12 @@ TEST_F(OEMCryptoLoadsCertificate, RSAPerformance) {
|
||||
|
||||
Session s;
|
||||
ASSERT_NO_FATAL_FAILURE(s.open());
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS,
|
||||
OEMCrypto_LoadDRMPrivateKey(
|
||||
s.session_id(), OEMCrypto_RSA_Private_Key,
|
||||
wrapped_rsa_key_.data(), wrapped_rsa_key_.size()));
|
||||
ASSERT_NO_FATAL_FAILURE(s.LoadWrappedRsaDrmKey(wrapped_rsa_key_));
|
||||
vector<uint8_t> session_key;
|
||||
vector<uint8_t> enc_session_key;
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
s.PreparePublicKey(encoded_rsa_key_.data(), encoded_rsa_key_.size()));
|
||||
ASSERT_TRUE(s.GenerateRSASessionKey(&session_key, &enc_session_key));
|
||||
ASSERT_NO_FATAL_FAILURE(s.SetRsaPublicKeyFromPrivateKeyInfo(
|
||||
encoded_rsa_key_.data(), encoded_rsa_key_.size()));
|
||||
ASSERT_TRUE(s.GenerateRsaSessionKey(&session_key, &enc_session_key));
|
||||
vector<uint8_t> mac_context;
|
||||
vector<uint8_t> enc_context;
|
||||
s.FillDefaultContext(&mac_context, &enc_context);
|
||||
@@ -6188,7 +6175,7 @@ TEST_F(OEMCryptoLoadsCertificate, RSAPerformance) {
|
||||
TEST_F(OEMCryptoUsesCertificate, GenerateDerivedKeysLargeBuffer) {
|
||||
vector<uint8_t> session_key;
|
||||
vector<uint8_t> enc_session_key;
|
||||
ASSERT_TRUE(session_.GenerateRSASessionKey(&session_key, &enc_session_key));
|
||||
ASSERT_TRUE(session_.GenerateRsaSessionKey(&session_key, &enc_session_key));
|
||||
const size_t max_size = GetResourceValue(kLargeMessageSize);
|
||||
vector<uint8_t> mac_context(max_size);
|
||||
vector<uint8_t> enc_context(max_size);
|
||||
@@ -6208,7 +6195,7 @@ TEST_F(OEMCryptoUsesCertificate,
|
||||
OEMCryptoMemoryDeriveKeysFromSessionKeyForHugeMacContext) {
|
||||
vector<uint8_t> session_key;
|
||||
vector<uint8_t> enc_session_key;
|
||||
ASSERT_TRUE(session_.GenerateRSASessionKey(&session_key, &enc_session_key));
|
||||
ASSERT_TRUE(session_.GenerateRsaSessionKey(&session_key, &enc_session_key));
|
||||
vector<uint8_t> mac_context;
|
||||
vector<uint8_t> enc_context;
|
||||
session_.FillDefaultContext(&mac_context, &enc_context);
|
||||
@@ -6228,7 +6215,7 @@ TEST_F(OEMCryptoUsesCertificate,
|
||||
OEMCryptoMemoryDeriveKeysFromSessionKeyForHugeEncContext) {
|
||||
vector<uint8_t> session_key;
|
||||
vector<uint8_t> enc_session_key;
|
||||
ASSERT_TRUE(session_.GenerateRSASessionKey(&session_key, &enc_session_key));
|
||||
ASSERT_TRUE(session_.GenerateRsaSessionKey(&session_key, &enc_session_key));
|
||||
vector<uint8_t> mac_context;
|
||||
vector<uint8_t> enc_context;
|
||||
session_.FillDefaultContext(&mac_context, &enc_context);
|
||||
@@ -6248,7 +6235,7 @@ TEST_F(OEMCryptoUsesCertificate,
|
||||
OEMCryptoMemoryDeriveKeysFromSessionKeyForHugeEncSessionKey) {
|
||||
vector<uint8_t> session_key;
|
||||
vector<uint8_t> enc_session_key;
|
||||
ASSERT_TRUE(session_.GenerateRSASessionKey(&session_key, &enc_session_key));
|
||||
ASSERT_TRUE(session_.GenerateRsaSessionKey(&session_key, &enc_session_key));
|
||||
vector<uint8_t> mac_context;
|
||||
vector<uint8_t> enc_context;
|
||||
session_.FillDefaultContext(&mac_context, &enc_context);
|
||||
@@ -6272,10 +6259,7 @@ class OEMCryptoLoadsCertificateAlternates : public OEMCryptoLoadsCertificate {
|
||||
OEMCryptoResult sts;
|
||||
Session s;
|
||||
ASSERT_NO_FATAL_FAILURE(s.open());
|
||||
sts = OEMCrypto_LoadDRMPrivateKey(s.session_id(), OEMCrypto_RSA_Private_Key,
|
||||
wrapped_rsa_key_.data(),
|
||||
wrapped_rsa_key_.size());
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
|
||||
ASSERT_NO_FATAL_FAILURE(s.LoadWrappedRsaDrmKey(wrapped_rsa_key_));
|
||||
|
||||
// Sign a Message
|
||||
vector<uint8_t> licenseRequest(size);
|
||||
@@ -6302,20 +6286,16 @@ class OEMCryptoLoadsCertificateAlternates : public OEMCryptoLoadsCertificate {
|
||||
}
|
||||
|
||||
void TestSignature(RSA_Padding_Scheme scheme, size_t size) {
|
||||
OEMCryptoResult sts;
|
||||
Session s;
|
||||
ASSERT_NO_FATAL_FAILURE(s.open());
|
||||
sts = OEMCrypto_LoadDRMPrivateKey(s.session_id(), OEMCrypto_RSA_Private_Key,
|
||||
wrapped_rsa_key_.data(),
|
||||
wrapped_rsa_key_.size());
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
|
||||
ASSERT_NO_FATAL_FAILURE(s.LoadWrappedRsaDrmKey(wrapped_rsa_key_));
|
||||
|
||||
vector<uint8_t> licenseRequest(size);
|
||||
GetRandBytes(licenseRequest.data(), licenseRequest.size());
|
||||
size_t signature_length = 0;
|
||||
sts = OEMCrypto_GenerateRSASignature(s.session_id(), licenseRequest.data(),
|
||||
licenseRequest.size(), nullptr,
|
||||
&signature_length, scheme);
|
||||
OEMCryptoResult sts = OEMCrypto_GenerateRSASignature(
|
||||
s.session_id(), licenseRequest.data(), licenseRequest.size(), nullptr,
|
||||
&signature_length, scheme);
|
||||
ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts);
|
||||
ASSERT_NE(static_cast<size_t>(0), signature_length);
|
||||
|
||||
@@ -6328,26 +6308,22 @@ class OEMCryptoLoadsCertificateAlternates : public OEMCryptoLoadsCertificate {
|
||||
<< "Failed to sign with padding scheme=" << (int)scheme
|
||||
<< ", size=" << size;
|
||||
signature.resize(signature_length);
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
s.PreparePublicKey(encoded_rsa_key_.data(), encoded_rsa_key_.size()));
|
||||
ASSERT_NO_FATAL_FAILURE(s.VerifyRSASignature(
|
||||
ASSERT_NO_FATAL_FAILURE(s.SetRsaPublicKeyFromPrivateKeyInfo(
|
||||
encoded_rsa_key_.data(), encoded_rsa_key_.size()));
|
||||
ASSERT_NO_FATAL_FAILURE(s.VerifyRsaSignature(
|
||||
licenseRequest, signature.data(), signature_length, scheme));
|
||||
}
|
||||
|
||||
void DisallowDeriveKeys() {
|
||||
OEMCryptoResult sts;
|
||||
Session s;
|
||||
ASSERT_NO_FATAL_FAILURE(s.open());
|
||||
sts = OEMCrypto_LoadDRMPrivateKey(s.session_id(), OEMCrypto_RSA_Private_Key,
|
||||
wrapped_rsa_key_.data(),
|
||||
wrapped_rsa_key_.size());
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
|
||||
ASSERT_NO_FATAL_FAILURE(s.LoadWrappedRsaDrmKey(wrapped_rsa_key_));
|
||||
s.GenerateNonce();
|
||||
vector<uint8_t> session_key;
|
||||
vector<uint8_t> enc_session_key;
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
s.PreparePublicKey(encoded_rsa_key_.data(), encoded_rsa_key_.size()));
|
||||
ASSERT_TRUE(s.GenerateRSASessionKey(&session_key, &enc_session_key));
|
||||
ASSERT_NO_FATAL_FAILURE(s.SetRsaPublicKeyFromPrivateKeyInfo(
|
||||
encoded_rsa_key_.data(), encoded_rsa_key_.size()));
|
||||
ASSERT_TRUE(s.GenerateRsaSessionKey(&session_key, &enc_session_key));
|
||||
vector<uint8_t> mac_context;
|
||||
vector<uint8_t> enc_context;
|
||||
s.FillDefaultContext(&mac_context, &enc_context);
|
||||
@@ -6394,10 +6370,7 @@ TEST_F(OEMCryptoLoadsCertificateAlternates,
|
||||
if (key_loaded_) {
|
||||
Session s;
|
||||
ASSERT_NO_FATAL_FAILURE(s.open());
|
||||
sts = OEMCrypto_LoadDRMPrivateKey(s.session_id(), OEMCrypto_RSA_Private_Key,
|
||||
wrapped_rsa_key_.data(),
|
||||
wrapped_rsa_key_.size());
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
|
||||
ASSERT_NO_FATAL_FAILURE(s.LoadWrappedRsaDrmKey(wrapped_rsa_key_));
|
||||
|
||||
vector<uint8_t> message_buffer(10);
|
||||
size_t signature_length = 0;
|
||||
@@ -6421,7 +6394,6 @@ TEST_F(OEMCryptoLoadsCertificateAlternates,
|
||||
|
||||
TEST_F(OEMCryptoLoadsCertificateAlternates,
|
||||
OEMCryptoMemoryGenerateRSASignatureForHugeSignatureLength) {
|
||||
OEMCryptoResult sts;
|
||||
LoadWithAllowedSchemes(kSign_PKCS1_Block1, false);
|
||||
// If the device is a cast receiver, then this scheme is required.
|
||||
if (global_features.cast_receiver) {
|
||||
@@ -6430,10 +6402,7 @@ TEST_F(OEMCryptoLoadsCertificateAlternates,
|
||||
if (key_loaded_) {
|
||||
Session s;
|
||||
ASSERT_NO_FATAL_FAILURE(s.open());
|
||||
sts = OEMCrypto_LoadDRMPrivateKey(s.session_id(), OEMCrypto_RSA_Private_Key,
|
||||
wrapped_rsa_key_.data(),
|
||||
wrapped_rsa_key_.size());
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
|
||||
ASSERT_NO_FATAL_FAILURE(s.LoadWrappedRsaDrmKey(wrapped_rsa_key_));
|
||||
|
||||
vector<uint8_t> message_buffer(50);
|
||||
vector<uint8_t> signature;
|
||||
@@ -6643,10 +6612,7 @@ class OEMCryptoCastReceiverTest : public OEMCryptoLoadsCertificateAlternates {
|
||||
OEMCryptoResult sts;
|
||||
Session s;
|
||||
ASSERT_NO_FATAL_FAILURE(s.open());
|
||||
sts = OEMCrypto_LoadDRMPrivateKey(s.session_id(), OEMCrypto_RSA_Private_Key,
|
||||
wrapped_rsa_key_.data(),
|
||||
wrapped_rsa_key_.size());
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
|
||||
ASSERT_NO_FATAL_FAILURE(s.LoadWrappedRsaDrmKey(wrapped_rsa_key_));
|
||||
|
||||
// The application will compute the SHA-1 Hash of the message, so this
|
||||
// test must do that also.
|
||||
@@ -6678,8 +6644,8 @@ class OEMCryptoCastReceiverTest : public OEMCryptoLoadsCertificateAlternates {
|
||||
<< "Failed to sign with padding scheme=" << (int)scheme
|
||||
<< ", size=" << message.size();
|
||||
signature.resize(signature_length);
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
s.PreparePublicKey(encoded_rsa_key_.data(), encoded_rsa_key_.size()));
|
||||
ASSERT_NO_FATAL_FAILURE(s.SetRsaPublicKeyFromPrivateKeyInfo(
|
||||
encoded_rsa_key_.data(), encoded_rsa_key_.size()));
|
||||
|
||||
// Verify that the signature matches the official test vector.
|
||||
ASSERT_EQ(correct_signature.size(), signature_length);
|
||||
@@ -6688,9 +6654,9 @@ class OEMCryptoCastReceiverTest : public OEMCryptoLoadsCertificateAlternates {
|
||||
|
||||
// Also verify that our verification algorithm agrees. This is not needed
|
||||
// to test OEMCrypto, but it does verify that this test is valid.
|
||||
ASSERT_NO_FATAL_FAILURE(s.VerifyRSASignature(digest, signature.data(),
|
||||
ASSERT_NO_FATAL_FAILURE(s.VerifyRsaSignature(digest, signature.data(),
|
||||
signature_length, scheme));
|
||||
ASSERT_NO_FATAL_FAILURE(s.VerifyRSASignature(
|
||||
ASSERT_NO_FATAL_FAILURE(s.VerifyRsaSignature(
|
||||
digest, correct_signature.data(), correct_signature.size(), scheme));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -268,7 +268,7 @@ TEST_F(OTAKeyboxProvisioningTest, BasicTest) {
|
||||
std::copy(bit_size_string.begin(), bit_size_string.end(),
|
||||
std::back_inserter(enc_context));
|
||||
KeyDeriver keys;
|
||||
keys.DeriveKeys(model_key.data(), mac_context, enc_context);
|
||||
keys.DeriveKeys(model_key.data(), model_key.size(), mac_context, enc_context);
|
||||
const std::vector<uint8_t> message(
|
||||
request.data(),
|
||||
request.data() + request.size() - HMAC_SHA256_SIGNATURE_SIZE);
|
||||
|
||||
Reference in New Issue
Block a user