Add ODK query func and short-buffer for AEAD
This commit is contained in:
@@ -2,12 +2,13 @@
|
||||
|
||||
module(name = "whitebox")
|
||||
|
||||
bazel_dep(name = "abseil-cpp", version = "20240722.0.bcr.2")
|
||||
bazel_dep(name = "boringssl", version = "0.20241209.0")
|
||||
# https://registry.bazel.build/
|
||||
bazel_dep(name = "abseil-cpp", version = "20250127.0")
|
||||
bazel_dep(name = "boringssl", version = "0.20250212.0")
|
||||
bazel_dep(name = "gflags", version = "2.2.2")
|
||||
bazel_dep(name = "glog", version = "0.7.1")
|
||||
bazel_dep(name = "googletest", version = "1.15.2")
|
||||
bazel_dep(name = "protobuf", version = "29.2")
|
||||
bazel_dep(name = "googletest", version = "1.16.0")
|
||||
bazel_dep(name = "protobuf", version = "29.3")
|
||||
|
||||
# The ODK library depends on proto files in the "chromium_deps" folder. Since
|
||||
# this would create a circular dependency, this creates a new module for it.
|
||||
|
||||
@@ -283,6 +283,7 @@ cc_library(
|
||||
"license_whitebox_entitlement_content_key_test.cc",
|
||||
"license_whitebox_generic_crypto_test.cc",
|
||||
"license_whitebox_get_secret_string_test.cc",
|
||||
"license_whitebox_get_max_odk_version_test.cc",
|
||||
"license_whitebox_key_control_block_test.cc",
|
||||
"license_whitebox_license_key_mode.cc",
|
||||
"license_whitebox_masked_decrypt_test.cc",
|
||||
|
||||
@@ -96,6 +96,8 @@ WB_Result WB_Aead_Encrypt(const WB_Aead_Whitebox* whitebox,
|
||||
|
||||
// Decrypts |input_data| and writes the plaintext to |output_data|. |input_data|
|
||||
// must have been encrypted using WB_Aead_Encrypt() with the same |whitebox|.
|
||||
// |output_data| must be at least as big as |input_data|, this doesn't support
|
||||
// WB_RESULT_BUFFER_TOO_SMALL.
|
||||
//
|
||||
// This must support |input_data| and |output_data| pointing to the same buffer
|
||||
// (in-place decrypt).
|
||||
@@ -111,7 +113,7 @@ WB_Result WB_Aead_Encrypt(const WB_Aead_Whitebox* whitebox,
|
||||
//
|
||||
// output_data_size (in/out) : As input, this contains the max number of bytes
|
||||
// that can be written to |output_data|. As output, |output_data_size| is set
|
||||
// to the required size on WB_RESULT_OK and WB_RESULT_BUFFER_TOO_SMALL.
|
||||
// to the required size on WB_RESULT_OK.
|
||||
//
|
||||
// Returns:
|
||||
// WB_RESULT_OK if |input_data| was successfully decrypted.
|
||||
@@ -120,9 +122,6 @@ WB_Result WB_Aead_Encrypt(const WB_Aead_Whitebox* whitebox,
|
||||
// null, if |input_data_size| was invalid, if |output_data| was null, or if
|
||||
// |output_data_size| was null.
|
||||
//
|
||||
// WB_RESULT_BUFFER_TOO_SMALL if |output_data_size| (as input) was less than
|
||||
// the required size.
|
||||
//
|
||||
// WB_RESULT_DATA_VERIFICATION_ERROR if |input_data| failed data verification.
|
||||
// The state of |output_data| is undefined.
|
||||
WB_Result WB_Aead_Decrypt(const WB_Aead_Whitebox* whitebox,
|
||||
|
||||
@@ -214,10 +214,9 @@ TEST_F(AeadWhiteboxDecryptTest, BufferTooSmall) {
|
||||
size_t plaintext_size = plaintext_.size() - 1;
|
||||
std::vector<uint8_t> plaintext(plaintext_size);
|
||||
|
||||
ASSERT_EQ(WB_Aead_Decrypt(whitebox_, ciphertext_.data(), ciphertext_.size(),
|
||||
ASSERT_NE(WB_Aead_Decrypt(whitebox_, ciphertext_.data(), ciphertext_.size(),
|
||||
plaintext.data(), &plaintext_size),
|
||||
WB_RESULT_BUFFER_TOO_SMALL);
|
||||
ASSERT_EQ(plaintext_size, plaintext_.size());
|
||||
WB_RESULT_OK);
|
||||
}
|
||||
|
||||
TEST_F(AeadWhiteboxDecryptTest, DataVerificationError) {
|
||||
|
||||
@@ -100,15 +100,6 @@ TEST_F(AeadWhiteboxEncryptTest, InvalidParameterForNullInputData) {
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(AeadWhiteboxEncryptTest, InvalidParameterForZeroInputSize) {
|
||||
size_t output_size = kDefaultOutputSize;
|
||||
std::vector<uint8_t> output(output_size);
|
||||
|
||||
ASSERT_EQ(
|
||||
WB_Aead_Encrypt(whitebox_, input_.data(), 0, output.data(), &output_size),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(AeadWhiteboxEncryptTest, InvalidParameterForNullOutputData) {
|
||||
size_t output_size = kDefaultOutputSize;
|
||||
std::vector<uint8_t> output(output_size);
|
||||
@@ -141,4 +132,15 @@ TEST_F(AeadWhiteboxEncryptTest, BufferTooSmallForSmallOutputBuffer) {
|
||||
// only check that |output_size| is larger than |input_.size()|.
|
||||
ASSERT_GT(output_size, input_.size());
|
||||
}
|
||||
|
||||
TEST_F(AeadWhiteboxEncryptTest, BufferTooSmallWithNullArgs) {
|
||||
// Should calculate the required size even if 0 or null buffer arguments.
|
||||
size_t output_size = 0;
|
||||
|
||||
ASSERT_EQ(
|
||||
WB_Aead_Encrypt(whitebox_, nullptr, input_.size(), nullptr, &output_size),
|
||||
WB_RESULT_BUFFER_TOO_SMALL);
|
||||
ASSERT_GT(output_size, input_.size());
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
@@ -23,14 +23,12 @@
|
||||
// defined, we will define non-versioned function names.
|
||||
// #define WB_VERSION 28
|
||||
|
||||
#ifdef WB_VERSION
|
||||
// We do this triple-indirection here since the ## operator handles arguments
|
||||
// a bit weird and we want to expand the WB_RESULT as a value, not an identifier
|
||||
//
|
||||
// a bit weird and we want to expand the |version| as a value, not an identifier
|
||||
#define WB_CONCAT_VERSION__(a, b) a ## b
|
||||
#define WB_CONCAT_VERSION_(name, version) WB_CONCAT_VERSION__(name##_v, version)
|
||||
#ifdef WB_VERSION
|
||||
// WB_CONCAT_VERSION(Foo) -> Foo_v28
|
||||
# define WB_CONCAT_VERSION__(a, b) a ## b
|
||||
# define WB_CONCAT_VERSION_(name, version) \
|
||||
WB_CONCAT_VERSION__(name ## _v, version)
|
||||
# define WB_CONCAT_VERSION(name) WB_CONCAT_VERSION_(name, WB_VERSION)
|
||||
#else
|
||||
# define WB_CONCAT_VERSION(name) name
|
||||
@@ -44,6 +42,21 @@ extern "C" {
|
||||
typedef struct WB_CONCAT_VERSION(WB_License_Whitebox)
|
||||
WB_CONCAT_VERSION(WB_License_Whitebox);
|
||||
|
||||
// Returns the maximum supported version of ODK messages.
|
||||
//
|
||||
// Args:
|
||||
// odk_major_version(out): Where to put the major ODK version that is supported
|
||||
//
|
||||
// odk_minor_version(out): Where to put the minor ODK version that is supported
|
||||
//
|
||||
// Returns:
|
||||
// WB_RESULT_OK on success.
|
||||
//
|
||||
// WB_RESULT_INVALID_PARAMETER if either argument was null.
|
||||
WB_Result WB_CONCAT_VERSION(WB_License_GetMaxOdkVersion)(
|
||||
uint16_t* odk_major_version,
|
||||
uint16_t* odk_minor_version);
|
||||
|
||||
// Creates a new white-box instance using the implementation's internal private
|
||||
// key|. A pointer to the white-box instance will be returned via |whitebox|.
|
||||
//
|
||||
|
||||
29
whitebox/api/license_whitebox_get_max_odk_version_test.cc
Normal file
29
whitebox/api/license_whitebox_get_max_odk_version_test.cc
Normal file
@@ -0,0 +1,29 @@
|
||||
// Copyright 2025 Google LLC. All Rights Reserved.
|
||||
|
||||
#include "api/license_whitebox_latest.h"
|
||||
#include "odk.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
TEST(LicenseWhiteboxGetMaxOdkVersionTest, Success) {
|
||||
uint16_t major, minor;
|
||||
ASSERT_EQ(WB_License_GetMaxOdkVersion(&major, &minor), WB_RESULT_OK);
|
||||
EXPECT_GE(major, ODK_FIRST_VERSION);
|
||||
EXPECT_LE(major, ODK_MAJOR_VERSION);
|
||||
if (major == ODK_MAJOR_VERSION) {
|
||||
EXPECT_LE(minor, ODK_MINOR_VERSION);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(LicenseWhiteboxGetMaxOdkVersionTest, NullArguments) {
|
||||
uint16_t major, minor;
|
||||
EXPECT_EQ(WB_License_GetMaxOdkVersion(nullptr, nullptr),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
EXPECT_EQ(WB_License_GetMaxOdkVersion(nullptr, &minor),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
EXPECT_EQ(WB_License_GetMaxOdkVersion(&major, nullptr),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
@@ -13,6 +13,13 @@ extern "C" {
|
||||
|
||||
typedef WB_CONCAT_VERSION(WB_License_Whitebox) WB_License_Whitebox;
|
||||
|
||||
inline WB_Result WB_License_GetMaxOdkVersion(
|
||||
uint16_t* odk_major_version,
|
||||
uint16_t* odk_minor_version) {
|
||||
return WB_CONCAT_VERSION(WB_License_GetMaxOdkVersion)(odk_major_version,
|
||||
odk_minor_version);
|
||||
}
|
||||
|
||||
inline WB_Result WB_License_Create(
|
||||
const uint8_t* whitebox_init_data,
|
||||
size_t whitebox_init_data_size,
|
||||
|
||||
360
whitebox/api/license_whitebox_wrapper.h
Normal file
360
whitebox/api/license_whitebox_wrapper.h
Normal file
@@ -0,0 +1,360 @@
|
||||
// Copyright 2025 Google LLC. All Rights Reserved.
|
||||
|
||||
#ifndef WHITEBOX_API_LICENSE_WHITEBOX_WRAPPER_H_
|
||||
#define WHITEBOX_API_LICENSE_WHITEBOX_WRAPPER_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "api/license_whitebox.h"
|
||||
#include "api/result.h"
|
||||
|
||||
/// Defines an interface for a wrapper type for interacting with different
|
||||
/// versions of the whitebox. These wrappers should handle changes between
|
||||
/// older API versions and will call into the specific versioned-APIs.
|
||||
///
|
||||
/// Creating an instance of a wrapper is done with the T::Create() method, a
|
||||
/// static method that returns a WB_Result and gives the wrapper instance as
|
||||
/// a smart pointer.
|
||||
///
|
||||
/// For example:
|
||||
/// std::unique_ptr<LicenseWhiteboxWrapper> whitebox;
|
||||
/// auto res = LicenseWhiteboxWrapper_v30::Create(nullptr, 0, &whitebox);
|
||||
/// assert(res == WB_RESULT_OK);
|
||||
/// whitebox->SignLicenseRequest(...);
|
||||
class LicenseWhiteboxWrapper {
|
||||
public:
|
||||
LicenseWhiteboxWrapper() {}
|
||||
LicenseWhiteboxWrapper(const LicenseWhiteboxWrapper&) = delete;
|
||||
LicenseWhiteboxWrapper(LicenseWhiteboxWrapper&&) = delete;
|
||||
virtual ~LicenseWhiteboxWrapper() {}
|
||||
|
||||
LicenseWhiteboxWrapper& operator=(const LicenseWhiteboxWrapper&) = delete;
|
||||
LicenseWhiteboxWrapper& operator=(LicenseWhiteboxWrapper&&) = delete;
|
||||
|
||||
//// Implementations should also define the following static functions.
|
||||
//static void Unmask(const uint8_t* masked_data,
|
||||
// size_t offset,
|
||||
// size_t size,
|
||||
// const uint8_t* secret_string,
|
||||
// size_t secret_string_size,
|
||||
// uint8_t* unmasked_data);
|
||||
//static WB_Result Create(const uint8_t* whitebox_init_data,
|
||||
// size_t whitebox_init_data_size,
|
||||
// std::unique_ptr<LicenseWhiteboxWrapper>* whitebox);
|
||||
//static WB_Result Import(const uint8_t* buffer,
|
||||
// size_t buffer_size,
|
||||
// std::unique_ptr<LicenseWhiteboxWrapper>* whitebox);
|
||||
//static WB_Result SignLicenseRequest_Init();
|
||||
//static WB_Result ProcessLicenseResponse_Init();
|
||||
|
||||
virtual WB_Result ExportKeys(uint8_t* buffer, size_t* buffer_size) = 0;
|
||||
virtual WB_Result SignLicenseRequest(const uint8_t* license_request,
|
||||
size_t license_request_size,
|
||||
uint8_t* signature,
|
||||
size_t* signature_size) = 0;
|
||||
virtual WB_Result ProcessLicenseResponse(WB_LicenseKeyMode license_key_mode,
|
||||
const uint8_t* core_message,
|
||||
size_t core_message_size,
|
||||
const uint8_t* message,
|
||||
size_t message_size,
|
||||
const uint8_t* signature,
|
||||
size_t signature_size,
|
||||
const uint8_t* session_key,
|
||||
size_t session_key_size,
|
||||
size_t provider_key_id,
|
||||
const uint8_t* license_request,
|
||||
size_t license_request_size) = 0;
|
||||
virtual WB_Result LoadEntitledContentKey(const uint8_t* entitlement_key_id,
|
||||
size_t entitlement_key_id_size,
|
||||
const uint8_t* content_key_id,
|
||||
size_t content_key_id_size,
|
||||
const uint8_t* iv,
|
||||
size_t iv_size,
|
||||
const uint8_t* key_data,
|
||||
size_t key_data_size) = 0;
|
||||
virtual WB_Result RemoveEntitledContentKey(const uint8_t* content_key_id,
|
||||
size_t content_key_id_size) = 0;
|
||||
virtual WB_Result QueryKeyStatus(WB_KeyQueryType type,
|
||||
const uint8_t* key_id,
|
||||
size_t key_id_size,
|
||||
WB_KeyStatus* key_status) = 0;
|
||||
virtual WB_Result SignRenewalRequest(const uint8_t* message,
|
||||
size_t message_size,
|
||||
uint8_t* signature,
|
||||
size_t* signature_size) = 0;
|
||||
virtual WB_Result SignPstReport(const uint8_t* message,
|
||||
size_t message_size,
|
||||
uint8_t* signature,
|
||||
size_t* signature_size) = 0;
|
||||
virtual WB_Result VerifyRenewalResponse(const uint8_t* message,
|
||||
size_t message_size,
|
||||
const uint8_t* signature,
|
||||
size_t signature_size) = 0;
|
||||
virtual WB_Result GetSecretString(WB_CipherMode mode,
|
||||
const uint8_t* key_id,
|
||||
size_t key_id_size,
|
||||
uint8_t* secret_string,
|
||||
size_t* secret_string_size) = 0;
|
||||
virtual WB_Result GenericEncrypt(const uint8_t* key_id,
|
||||
size_t key_id_size,
|
||||
const uint8_t* input_data,
|
||||
size_t input_data_size,
|
||||
const uint8_t* iv,
|
||||
size_t iv_size,
|
||||
uint8_t* output_data,
|
||||
size_t* output_data_size) = 0;
|
||||
virtual WB_Result GenericDecrypt(const uint8_t* key_id,
|
||||
size_t key_id_size,
|
||||
const uint8_t* input_data,
|
||||
size_t input_data_size,
|
||||
const uint8_t* iv,
|
||||
size_t iv_size,
|
||||
uint8_t* output_data,
|
||||
size_t* output_data_size) = 0;
|
||||
virtual WB_Result GenericSign(const uint8_t* key_id,
|
||||
size_t key_id_size,
|
||||
const uint8_t* message,
|
||||
size_t message_size,
|
||||
uint8_t* output_data,
|
||||
size_t* output_data_size) = 0;
|
||||
virtual WB_Result GenericVerify(const uint8_t* key_id,
|
||||
size_t key_id_size,
|
||||
const uint8_t* message,
|
||||
size_t message_size,
|
||||
const uint8_t* signature,
|
||||
size_t signature_size) = 0;
|
||||
virtual WB_Result Decrypt(WB_CipherMode mode,
|
||||
const uint8_t* key_id,
|
||||
size_t key_id_size,
|
||||
const uint8_t* input_data,
|
||||
size_t input_data_size,
|
||||
const uint8_t* iv,
|
||||
size_t iv_size,
|
||||
uint8_t* output_data,
|
||||
size_t* output_data_size) = 0;
|
||||
virtual WB_Result MaskedDecrypt(WB_CipherMode mode,
|
||||
const uint8_t* key_id,
|
||||
size_t key_id_size,
|
||||
const uint8_t* input_data,
|
||||
size_t input_data_size,
|
||||
const uint8_t* iv,
|
||||
size_t iv_size,
|
||||
uint8_t* masked_output_data,
|
||||
size_t* masked_output_data_size) = 0;
|
||||
};
|
||||
|
||||
#define WB_DEFINE_DEFAULT_LICENSE_WRAPPER(version) \
|
||||
class WB_CONCAT_VERSION_(LicenseWhiteboxWrapper, version) \
|
||||
: public LicenseWhiteboxWrapper { \
|
||||
public: \
|
||||
using this_type = WB_CONCAT_VERSION_(LicenseWhiteboxWrapper, version); \
|
||||
~WB_CONCAT_VERSION_(LicenseWhiteboxWrapper, version)() override { \
|
||||
WB_CONCAT_VERSION_(WB_License_Delete, version)(whitebox_); \
|
||||
} \
|
||||
\
|
||||
static void Unmask(const uint8_t* masked_data, \
|
||||
size_t offset, \
|
||||
size_t size, \
|
||||
const uint8_t* secret_string, \
|
||||
size_t secret_string_size, \
|
||||
uint8_t* unmasked_data) { \
|
||||
WB_CONCAT_VERSION_(WB_License_Unmask, version)( \
|
||||
masked_data, offset, size, secret_string, secret_string_size, \
|
||||
unmasked_data); \
|
||||
} \
|
||||
static WB_Result Create( \
|
||||
const uint8_t* whitebox_init_data, \
|
||||
size_t whitebox_init_data_size, \
|
||||
std::unique_ptr<LicenseWhiteboxWrapper>* whitebox) { \
|
||||
std::unique_ptr<this_type> wb(new this_type); \
|
||||
const auto result = WB_CONCAT_VERSION_(WB_License_Create, version)( \
|
||||
whitebox_init_data, whitebox_init_data_size, &wb->whitebox_); \
|
||||
*whitebox = std::move(wb); \
|
||||
return result; \
|
||||
} \
|
||||
static WB_Result Import( \
|
||||
const uint8_t* buffer, \
|
||||
size_t buffer_size, \
|
||||
std::unique_ptr<LicenseWhiteboxWrapper>* whitebox) { \
|
||||
std::unique_ptr<this_type> wb(new this_type); \
|
||||
const auto result = WB_CONCAT_VERSION_(WB_License_Import, version)( \
|
||||
buffer, buffer_size, &wb->whitebox_); \
|
||||
*whitebox = std::move(wb); \
|
||||
return result; \
|
||||
} \
|
||||
static WB_Result SignLicenseRequest_Init() { \
|
||||
return WB_CONCAT_VERSION_(WB_License_SignLicenseRequest_Init, \
|
||||
version)(); \
|
||||
} \
|
||||
static WB_Result ProcessLicenseResponse_Init() { \
|
||||
return WB_CONCAT_VERSION_(WB_License_ProcessLicenseResponse_Init, \
|
||||
version)(); \
|
||||
} \
|
||||
\
|
||||
WB_Result ExportKeys(uint8_t* buffer, size_t* buffer_size) override { \
|
||||
return WB_CONCAT_VERSION_(WB_License_ExportKeys, version)( \
|
||||
whitebox_, buffer, buffer_size); \
|
||||
} \
|
||||
WB_Result SignLicenseRequest(const uint8_t* license_request, \
|
||||
size_t license_request_size, \
|
||||
uint8_t* signature, \
|
||||
size_t* signature_size) override { \
|
||||
return WB_CONCAT_VERSION_(WB_License_SignLicenseRequest, version)( \
|
||||
whitebox_, license_request, license_request_size, signature, \
|
||||
signature_size); \
|
||||
} \
|
||||
WB_Result ProcessLicenseResponse(WB_LicenseKeyMode license_key_mode, \
|
||||
const uint8_t* core_message, \
|
||||
size_t core_message_size, \
|
||||
const uint8_t* message, \
|
||||
size_t message_size, \
|
||||
const uint8_t* signature, \
|
||||
size_t signature_size, \
|
||||
const uint8_t* session_key, \
|
||||
size_t session_key_size, \
|
||||
size_t provider_key_id, \
|
||||
const uint8_t* license_request, \
|
||||
size_t license_request_size) override { \
|
||||
return WB_CONCAT_VERSION_(WB_License_ProcessLicenseResponse, version)( \
|
||||
whitebox_, license_key_mode, core_message, core_message_size, \
|
||||
message, message_size, signature, signature_size, session_key, \
|
||||
session_key_size, provider_key_id, license_request, \
|
||||
license_request_size); \
|
||||
} \
|
||||
WB_Result LoadEntitledContentKey(const uint8_t* entitlement_key_id, \
|
||||
size_t entitlement_key_id_size, \
|
||||
const uint8_t* content_key_id, \
|
||||
size_t content_key_id_size, \
|
||||
const uint8_t* iv, \
|
||||
size_t iv_size, \
|
||||
const uint8_t* key_data, \
|
||||
size_t key_data_size) override { \
|
||||
return WB_CONCAT_VERSION_(WB_License_LoadEntitledContentKey, version)( \
|
||||
whitebox_, entitlement_key_id, entitlement_key_id_size, \
|
||||
content_key_id, content_key_id_size, iv, iv_size, key_data, \
|
||||
key_data_size); \
|
||||
} \
|
||||
WB_Result RemoveEntitledContentKey(const uint8_t* content_key_id, \
|
||||
size_t content_key_id_size) override { \
|
||||
return WB_CONCAT_VERSION_(WB_License_RemoveEntitledContentKey, version)( \
|
||||
whitebox_, content_key_id, content_key_id_size); \
|
||||
} \
|
||||
WB_Result QueryKeyStatus(WB_KeyQueryType type, \
|
||||
const uint8_t* key_id, \
|
||||
size_t key_id_size, \
|
||||
WB_KeyStatus* key_status) override { \
|
||||
return WB_CONCAT_VERSION_(WB_License_QueryKeyStatus, version)( \
|
||||
whitebox_, type, key_id, key_id_size, key_status); \
|
||||
} \
|
||||
WB_Result SignRenewalRequest(const uint8_t* message, \
|
||||
size_t message_size, \
|
||||
uint8_t* signature, \
|
||||
size_t* signature_size) override { \
|
||||
return WB_CONCAT_VERSION_(WB_License_SignRenewalRequest, version)( \
|
||||
whitebox_, message, message_size, signature, signature_size); \
|
||||
} \
|
||||
WB_Result SignPstReport(const uint8_t* message, \
|
||||
size_t message_size, \
|
||||
uint8_t* signature, \
|
||||
size_t* signature_size) override { \
|
||||
return WB_CONCAT_VERSION_(WB_License_SignPstReport, version)( \
|
||||
whitebox_, message, message_size, signature, signature_size); \
|
||||
} \
|
||||
WB_Result VerifyRenewalResponse(const uint8_t* message, \
|
||||
size_t message_size, \
|
||||
const uint8_t* signature, \
|
||||
size_t signature_size) override { \
|
||||
return WB_CONCAT_VERSION_(WB_License_VerifyRenewalResponse, version)( \
|
||||
whitebox_, message, message_size, signature, signature_size); \
|
||||
} \
|
||||
WB_Result GetSecretString(WB_CipherMode mode, \
|
||||
const uint8_t* key_id, \
|
||||
size_t key_id_size, \
|
||||
uint8_t* secret_string, \
|
||||
size_t* secret_string_size) override { \
|
||||
return WB_CONCAT_VERSION_(WB_License_GetSecretString, version)( \
|
||||
whitebox_, mode, key_id, key_id_size, secret_string, \
|
||||
secret_string_size); \
|
||||
} \
|
||||
WB_Result GenericEncrypt(const uint8_t* key_id, \
|
||||
size_t key_id_size, \
|
||||
const uint8_t* input_data, \
|
||||
size_t input_data_size, \
|
||||
const uint8_t* iv, \
|
||||
size_t iv_size, \
|
||||
uint8_t* output_data, \
|
||||
size_t* output_data_size) override { \
|
||||
return WB_CONCAT_VERSION_(WB_License_GenericEncrypt, version)( \
|
||||
whitebox_, key_id, key_id_size, input_data, input_data_size, iv, \
|
||||
iv_size, output_data, output_data_size); \
|
||||
} \
|
||||
WB_Result GenericDecrypt(const uint8_t* key_id, \
|
||||
size_t key_id_size, \
|
||||
const uint8_t* input_data, \
|
||||
size_t input_data_size, \
|
||||
const uint8_t* iv, \
|
||||
size_t iv_size, \
|
||||
uint8_t* output_data, \
|
||||
size_t* output_data_size) override { \
|
||||
return WB_CONCAT_VERSION_(WB_License_GenericDecrypt, version)( \
|
||||
whitebox_, key_id, key_id_size, input_data, input_data_size, iv, \
|
||||
iv_size, output_data, output_data_size); \
|
||||
} \
|
||||
WB_Result GenericSign(const uint8_t* key_id, \
|
||||
size_t key_id_size, \
|
||||
const uint8_t* message, \
|
||||
size_t message_size, \
|
||||
uint8_t* output_data, \
|
||||
size_t* output_data_size) override { \
|
||||
return WB_CONCAT_VERSION_(WB_License_GenericSign, version)( \
|
||||
whitebox_, key_id, key_id_size, message, message_size, output_data, \
|
||||
output_data_size); \
|
||||
} \
|
||||
WB_Result GenericVerify(const uint8_t* key_id, \
|
||||
size_t key_id_size, \
|
||||
const uint8_t* message, \
|
||||
size_t message_size, \
|
||||
const uint8_t* signature, \
|
||||
size_t signature_size) override { \
|
||||
return WB_CONCAT_VERSION_(WB_License_GenericVerify, version)( \
|
||||
whitebox_, key_id, key_id_size, message, message_size, signature, \
|
||||
signature_size); \
|
||||
} \
|
||||
WB_Result Decrypt(WB_CipherMode mode, \
|
||||
const uint8_t* key_id, \
|
||||
size_t key_id_size, \
|
||||
const uint8_t* input_data, \
|
||||
size_t input_data_size, \
|
||||
const uint8_t* iv, \
|
||||
size_t iv_size, \
|
||||
uint8_t* output_data, \
|
||||
size_t* output_data_size) override { \
|
||||
return WB_CONCAT_VERSION_(WB_License_Decrypt, version)( \
|
||||
whitebox_, mode, key_id, key_id_size, input_data, input_data_size, \
|
||||
iv, iv_size, output_data, output_data_size); \
|
||||
} \
|
||||
WB_Result MaskedDecrypt(WB_CipherMode mode, \
|
||||
const uint8_t* key_id, \
|
||||
size_t key_id_size, \
|
||||
const uint8_t* input_data, \
|
||||
size_t input_data_size, \
|
||||
const uint8_t* iv, \
|
||||
size_t iv_size, \
|
||||
uint8_t* masked_output_data, \
|
||||
size_t* masked_output_data_size) override { \
|
||||
return WB_CONCAT_VERSION_(WB_License_Decrypt, version)( \
|
||||
whitebox_, mode, key_id, key_id_size, input_data, input_data_size, \
|
||||
iv, iv_size, masked_output_data, masked_output_data_size); \
|
||||
} \
|
||||
\
|
||||
private: \
|
||||
WB_CONCAT_VERSION_(LicenseWhiteboxWrapper, version)() {} \
|
||||
WB_CONCAT_VERSION_(WB_License_Whitebox, version) * whitebox_ = nullptr; \
|
||||
}
|
||||
|
||||
#ifdef WB_VERSION
|
||||
WB_DEFINE_DEFAULT_LICENSE_WRAPPER(WB_VERSION);
|
||||
using LicenseWhiteboxWrapperCurrent = WB_CONCAT_VERSION(LicenseWhiteboxWrapper);
|
||||
#endif
|
||||
|
||||
#endif // WHITEBOX_API_LICENSE_WHITEBOX_WRAPPER_H_
|
||||
@@ -1,3 +1,3 @@
|
||||
# Copyright 2025 Google LLC. All Rights Reserved.
|
||||
module(name = "chromium_deps")
|
||||
bazel_dep(name = "protobuf", version = "29.2")
|
||||
bazel_dep(name = "protobuf", version = "29.3")
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#ifndef WHITEBOX_INIT_HELPER_H_
|
||||
#define WHITEBOX_INIT_HELPER_H_
|
||||
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
@@ -13,18 +14,36 @@
|
||||
#include <type_traits>
|
||||
|
||||
#include <api/license_whitebox.h>
|
||||
#include <api/license_whitebox_wrapper.h>
|
||||
|
||||
class WhiteboxInitHelperBase {
|
||||
public:
|
||||
WhiteboxInitHelperBase () {}
|
||||
virtual ~WhiteboxInitHelperBase () {}
|
||||
|
||||
virtual void StartThread() = 0;
|
||||
|
||||
/** Blocks the current thread until the sign initialization has finished. */
|
||||
virtual WB_Result EnsureSignReady() = 0;
|
||||
|
||||
/**
|
||||
* Blocks the current thread until the process initialization has finished.
|
||||
*/
|
||||
virtual WB_Result EnsureProcessReady() = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Defines a helper class to initialize the Zimperium whitebox. This spawns
|
||||
* a background thread to initialize in the background.
|
||||
*/
|
||||
template <typename Thread = std::thread,
|
||||
template <typename Wrapper = LicenseWhiteboxWrapperCurrent,
|
||||
typename Thread = std::thread,
|
||||
typename Mutex = std::mutex,
|
||||
typename ConditionVariable =
|
||||
typename std::conditional<std::is_same<Mutex, std::mutex>::value,
|
||||
std::condition_variable,
|
||||
std::condition_variable_any>::type>
|
||||
class WhiteboxInitHelper final {
|
||||
class WhiteboxInitHelper final : public WhiteboxInitHelperBase {
|
||||
public:
|
||||
enum class State {
|
||||
None,
|
||||
@@ -34,7 +53,7 @@ class WhiteboxInitHelper final {
|
||||
};
|
||||
|
||||
WhiteboxInitHelper() {}
|
||||
~WhiteboxInitHelper() {
|
||||
~WhiteboxInitHelper() override {
|
||||
if (thread_)
|
||||
thread_->join();
|
||||
}
|
||||
@@ -50,7 +69,7 @@ class WhiteboxInitHelper final {
|
||||
}
|
||||
|
||||
/** Spawns the background thread and starts initializing. */
|
||||
void StartThread() {
|
||||
void StartThread() override {
|
||||
if (!thread_) {
|
||||
thread_.reset(
|
||||
new Thread(std::bind(&WhiteboxInitHelper::ThreadMain, this)));
|
||||
@@ -58,10 +77,11 @@ class WhiteboxInitHelper final {
|
||||
}
|
||||
|
||||
/** Blocks the current thread until the sign initialization has finished. */
|
||||
WB_Result EnsureSignReady() {
|
||||
WB_Result EnsureSignReady() override {
|
||||
StartThread();
|
||||
std::unique_lock<Mutex> lock(mutex_);
|
||||
while (state_ < State::SignReady) {
|
||||
cond_.wait(lock);
|
||||
cond_.wait_for(lock, std::chrono::seconds(1));
|
||||
}
|
||||
return result_;
|
||||
}
|
||||
@@ -69,17 +89,18 @@ class WhiteboxInitHelper final {
|
||||
/**
|
||||
* Blocks the current thread until the process initialization has finished.
|
||||
*/
|
||||
WB_Result EnsureProcessReady() {
|
||||
WB_Result EnsureProcessReady() override {
|
||||
StartThread();
|
||||
std::unique_lock<Mutex> lock(mutex_);
|
||||
while (state_ < State::ProcessReady) {
|
||||
cond_.wait(lock);
|
||||
cond_.wait_for(lock, std::chrono::seconds(1));
|
||||
}
|
||||
return result_;
|
||||
}
|
||||
|
||||
private:
|
||||
void ThreadMain() {
|
||||
auto result = WB_License_SignLicenseRequest_Init();
|
||||
auto result = Wrapper::SignLicenseRequest_Init();
|
||||
{
|
||||
std::unique_lock<Mutex> lock(mutex_);
|
||||
state_ = result == WB_RESULT_OK ? State::SignReady : State::Error;
|
||||
@@ -89,7 +110,7 @@ class WhiteboxInitHelper final {
|
||||
return;
|
||||
}
|
||||
|
||||
result = WB_License_ProcessLicenseResponse_Init();
|
||||
result = Wrapper::ProcessLicenseResponse_Init();
|
||||
|
||||
std::unique_lock<Mutex> lock(mutex_);
|
||||
state_ = result == WB_RESULT_OK ? State::ProcessReady : State::Error;
|
||||
|
||||
@@ -110,41 +110,44 @@ WB_Result WB_Aead_Encrypt(const WB_Aead_Whitebox* whitebox,
|
||||
size_t input_data_size,
|
||||
uint8_t* output_data,
|
||||
size_t* output_data_size) {
|
||||
if (!whitebox || !input_data || !output_data || !output_data_size) {
|
||||
if (!whitebox || !output_data_size) {
|
||||
DVLOG(1) << "Invalid parameter: null pointer.";
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (input_data_size == 0) {
|
||||
DVLOG(1) << "Invalid parameter: array size 0.";
|
||||
const size_t extra_size = EVP_AEAD_max_overhead(whitebox->algorithm);
|
||||
const size_t required_size =
|
||||
input_data_size + whitebox->nonce_size + extra_size;
|
||||
if (*output_data_size < required_size) {
|
||||
DVLOG(1) << "Buffer too small: got=" << *output_data_size
|
||||
<< ", needs=" << required_size << ".";
|
||||
*output_data_size = required_size;
|
||||
return WB_RESULT_BUFFER_TOO_SMALL;
|
||||
}
|
||||
if (!input_data || !output_data) {
|
||||
DVLOG(1) << "Invalid parameter: null pointer.";
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> nonce(whitebox->nonce_size);
|
||||
RAND_bytes(nonce.data(), nonce.size());
|
||||
|
||||
std::vector<uint8_t> output(input_data_size +
|
||||
EVP_AEAD_max_overhead(whitebox->algorithm));
|
||||
// This should support in-place seal:
|
||||
// https://boringssl.googlesource.com/boringssl/+/refs/heads/master/include/openssl/aead.h#84
|
||||
size_t sealed_size;
|
||||
CHECK_EQ(EVP_AEAD_CTX_seal(&whitebox->context, output.data(), &sealed_size,
|
||||
output.size(), nonce.data(), nonce.size(),
|
||||
CHECK_EQ(EVP_AEAD_CTX_seal(&whitebox->context, output_data, &sealed_size,
|
||||
*output_data_size, nonce.data(), nonce.size(),
|
||||
input_data, input_data_size, nullptr, 0),
|
||||
kAeadSuccess);
|
||||
output.resize(sealed_size);
|
||||
|
||||
// At this point, |output| will have the encrypted data and authentication
|
||||
// tag, but in order to decrypt later, we will need the nonce, so append the
|
||||
// nonce to the output.
|
||||
output.insert(output.end(), nonce.begin(), nonce.end());
|
||||
// At this point, |output_data| will have the encrypted data and
|
||||
// authentication tag, but in order to decrypt later, we will need the nonce,
|
||||
// so append the nonce to the output.
|
||||
CHECK_LT(sealed_size, *output_data_size);
|
||||
CHECK(widevine::MemCopy(nonce.data(), nonce.size(), output_data + sealed_size,
|
||||
*output_data_size - sealed_size));
|
||||
|
||||
if (!widevine::MemCopy(output.data(), output.size(), output_data,
|
||||
*output_data_size)) {
|
||||
DVLOG(1) << "Buffer too small: output needs " << output.size() << ".";
|
||||
*output_data_size = output.size();
|
||||
return WB_RESULT_BUFFER_TOO_SMALL;
|
||||
}
|
||||
|
||||
*output_data_size = output.size();
|
||||
*output_data_size = sealed_size + nonce.size();
|
||||
return WB_RESULT_OK;
|
||||
}
|
||||
|
||||
@@ -184,32 +187,16 @@ WB_Result WB_Aead_Decrypt(const WB_Aead_Whitebox* whitebox,
|
||||
const uint8_t* nonce_start = payload_start + payload_size;
|
||||
const size_t nonce_size = whitebox->nonce_size;
|
||||
|
||||
// Remember, the plaintext will be smaller than the payload, but we are going
|
||||
// to start with the payload size as it is our best estimate and will scale it
|
||||
// down when we have the final size.
|
||||
std::vector<uint8_t> plaintext(payload_size);
|
||||
size_t plaintext_size;
|
||||
|
||||
// Since |*output_data_size| is passed by-value, it is a copy and will not
|
||||
// cause a problem when it modifies |*output_data_size|.
|
||||
// This should support in-place open:
|
||||
// https://boringssl.googlesource.com/boringssl/+/refs/heads/master/include/openssl/aead.h#84
|
||||
const int result = EVP_AEAD_CTX_open(
|
||||
&whitebox->context, plaintext.data(), &plaintext_size, plaintext.size(),
|
||||
&whitebox->context, output_data, output_data_size, *output_data_size,
|
||||
nonce_start, nonce_size, payload_start, payload_size, nullptr, 0);
|
||||
|
||||
if (result != kAeadSuccess) {
|
||||
DVLOG(1) << "Data verification error: failed to verify input data.";
|
||||
DVLOG(1) << "Data verification error: failed to verify input data";
|
||||
return WB_RESULT_DATA_VERIFICATION_ERROR;
|
||||
}
|
||||
|
||||
// Know that EVP_AEAD_CTX_open() has opened the payload, we know the actual
|
||||
// plaintext size and can now shrink the vector to the correct size.
|
||||
plaintext.resize(plaintext_size);
|
||||
|
||||
if (!widevine::MemCopy(plaintext.data(), plaintext.size(), output_data,
|
||||
*output_data_size)) {
|
||||
DVLOG(1) << "Buffer too small: output needs " << plaintext.size() << ".";
|
||||
*output_data_size = plaintext.size();
|
||||
return WB_RESULT_BUFFER_TOO_SMALL;
|
||||
}
|
||||
|
||||
*output_data_size = plaintext.size();
|
||||
return WB_RESULT_OK;
|
||||
}
|
||||
|
||||
@@ -110,6 +110,11 @@ std::string MakeString(const uint8_t* data, size_t size) {
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAS_IMPORT_EXPORT
|
||||
# ifndef WB_VERSION
|
||||
# error Must set WB_VERSION with export
|
||||
# endif
|
||||
|
||||
struct WB_License_ExportedData_Key {
|
||||
std::array<uint8_t, 16> key_id;
|
||||
uint8_t key_id_size;
|
||||
@@ -124,8 +129,7 @@ struct WB_License_ExportedData {
|
||||
size_t num_generic_keys;
|
||||
WB_License_ExportedData_Key keys[0];
|
||||
};
|
||||
|
||||
constexpr const uint8_t kExportedVersion = 1;
|
||||
#endif
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -348,6 +352,18 @@ std::vector<widevine::LicenseParser::ProviderKey> CreateProviderKeys(
|
||||
|
||||
} // namespace
|
||||
|
||||
WB_Result WB_CONCAT_VERSION(WB_License_GetMaxOdkVersion)(
|
||||
uint16_t* odk_major_version,
|
||||
uint16_t* odk_minor_version) {
|
||||
if (!odk_major_version || !odk_minor_version) {
|
||||
DVLOG(1) << "Invalid parameter: null pointer.";
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
*odk_major_version = ODK_MAJOR_VERSION;
|
||||
*odk_minor_version = ODK_MINOR_VERSION;
|
||||
return WB_RESULT_OK;
|
||||
}
|
||||
|
||||
WB_Result WB_CONCAT_VERSION(WB_License_Create)(
|
||||
const uint8_t* whitebox_init_data,
|
||||
size_t whitebox_init_data_size,
|
||||
@@ -406,7 +422,7 @@ WB_Result WB_CONCAT_VERSION(WB_License_Import)(
|
||||
}
|
||||
|
||||
auto* info = reinterpret_cast<const WB_License_ExportedData*>(buffer);
|
||||
if (info->version != kExportedVersion) {
|
||||
if (info->version != WB_VERSION) {
|
||||
DVLOG(1) << "Unsupported version of exported data.";
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
@@ -483,7 +499,7 @@ WB_Result WB_CONCAT_VERSION(WB_License_ExportKeys)(
|
||||
if (info->has_renewal) {
|
||||
info->renewal_client = whitebox->renewal_key->client;
|
||||
}
|
||||
info->version = kExportedVersion;
|
||||
info->version = WB_VERSION;
|
||||
info->num_content_keys = whitebox->content_keys.size();
|
||||
info->num_entitlement_keys = whitebox->entitlement_keys.size();
|
||||
info->num_generic_keys = whitebox->generic_keys.size();
|
||||
|
||||
@@ -71,7 +71,11 @@ bool HasEncryptedKeyControlBlock(const ODKContext& context) {
|
||||
size_t encrypted_count = 0;
|
||||
|
||||
for (uint32_t i = 0; i < context.license.key_array_length; i++) {
|
||||
#if ODK_MAJOR_VERSION >= 20
|
||||
const OEMCrypto_KeyObjectV2& key = context.license.key_array[i];
|
||||
#else
|
||||
const OEMCrypto_KeyObject& key = context.license.key_array[i];
|
||||
#endif
|
||||
|
||||
if (key.key_control_iv.length > 0) {
|
||||
encrypted_count++;
|
||||
|
||||
@@ -86,7 +86,11 @@ WB_Result OdkLicenseParser::Parse(const std::string& decryption_key,
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < odk_context.license.key_array_length; ++i) {
|
||||
#if ODK_MAJOR_VERSION >= 20
|
||||
const OEMCrypto_KeyObjectV2& key = odk_context.license.key_array[i];
|
||||
#else
|
||||
const OEMCrypto_KeyObject& key = odk_context.license.key_array[i];
|
||||
#endif
|
||||
const std::string key_id = ExtractItem(key.key_id, message);
|
||||
|
||||
// If there is no key id, we can't add an invalid entry since there would
|
||||
@@ -198,7 +202,11 @@ InternalKey OdkLicenseParser::ParseInternalKey(
|
||||
KeyType key_type,
|
||||
const std::string& decryption_key,
|
||||
const std::string& message,
|
||||
#if ODK_MAJOR_VERSION >= 20
|
||||
const OEMCrypto_KeyObjectV2& key,
|
||||
#else
|
||||
const OEMCrypto_KeyObject& key,
|
||||
#endif
|
||||
const std::vector<ProviderKey>& provider_keys,
|
||||
size_t provider_key_id) const {
|
||||
// This should have been verified before calling the parser.
|
||||
|
||||
@@ -32,7 +32,11 @@ class OdkLicenseParser : public LicenseParser {
|
||||
InternalKey ParseInternalKey(KeyType key_type,
|
||||
const std::string& decryption_key,
|
||||
const std::string& message,
|
||||
#if ODK_MAJOR_VERSION >= 20
|
||||
const OEMCrypto_KeyObjectV2& key,
|
||||
#else
|
||||
const OEMCrypto_KeyObject& key,
|
||||
#endif
|
||||
const std::vector<ProviderKey>& provider_keys,
|
||||
size_t provider_key_id) const;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user