Add ODK query func and short-buffer for AEAD

This commit is contained in:
Jacob Trimble
2025-04-29 21:09:55 +00:00
parent 5da305d9de
commit bea17d8b3b
16 changed files with 534 additions and 83 deletions

View File

@@ -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.

View File

@@ -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",

View File

@@ -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,

View File

@@ -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) {

View File

@@ -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

View File

@@ -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|.
//

View 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

View File

@@ -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,

View 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_

View File

@@ -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")

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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();

View File

@@ -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++;

View File

@@ -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.

View File

@@ -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;