Initial Code Drop
This is the initial code drop of the reference implementation and test cases for the Widevine Whitebox API. In this drop, the full reference implementation for the AEAD white-box is provided and all test cases verifying the top-level behave have are enabled. Since the implementations can vary so much the testing is mostly left to verifying the return codes for specific parameter conditions. A full reference implementation for the license white-box is provided, however not all tests are implemented or enabled. A number of tests have been disabled as they required a loaded license and test licenses are still being worked on. The two license white-box API functions that are the further from competition are ProcessLicenseResponse() and MaskedDecryt(). ProcessLicenseResponse() is still being worked on and MaskedDecrypt() is waiting on Decrypt() to be fully functional. Most tests focus on verifying return code for specific parameter conditions, but as test licenses are created, tests looking to test the internal behaviour of license management will be added to ProcessLicenseResponse(), Decrypt(), and MaskedDecrypt().
This commit is contained in:
89
api/BUILD
Normal file
89
api/BUILD
Normal file
@@ -0,0 +1,89 @@
|
||||
# Copyright 2020 Google LLC. All Rights Reserved.
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
cc_library(
|
||||
name = "result",
|
||||
hdrs = [
|
||||
"result.h",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "aead_whitebox",
|
||||
hdrs = [
|
||||
"aead_whitebox.h",
|
||||
],
|
||||
deps = [
|
||||
":result",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "license_whitebox",
|
||||
hdrs = [
|
||||
"license_whitebox.h",
|
||||
],
|
||||
deps = [
|
||||
":result",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "test_data",
|
||||
testonly = True,
|
||||
hdrs = [
|
||||
"test_data.h",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "license_test_helper",
|
||||
testonly = True,
|
||||
srcs = ["license_test_helper.cc"],
|
||||
hdrs = ["license_test_helper.h"],
|
||||
deps = [
|
||||
"//chromium_deps/base",
|
||||
"//chromium_deps/cdm/keys:dev_certs",
|
||||
"//chromium_deps/cdm/protos:license_protocol_proto",
|
||||
"//crypto_utils:aes_cbc_encryptor",
|
||||
"//crypto_utils:crypto_util",
|
||||
"//crypto_utils:rsa_key",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "aead_whitebox_test",
|
||||
testonly = True,
|
||||
srcs = [
|
||||
"aead_whitebox_create_test.cc",
|
||||
"aead_whitebox_decrypt_test.cc",
|
||||
"aead_whitebox_encrypt_test.cc",
|
||||
],
|
||||
deps = [
|
||||
":aead_whitebox",
|
||||
":test_data",
|
||||
"//chromium_deps/testing:gtest",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "license_whitebox_test",
|
||||
testonly = True,
|
||||
srcs = [
|
||||
"license_whitebox_create_test.cc",
|
||||
"license_whitebox_decrypt_test.cc",
|
||||
"license_whitebox_get_secret_string_test.cc",
|
||||
"license_whitebox_sign_renewal_request_test.cc",
|
||||
"license_whitebox_verify_renewal_response_test.cc",
|
||||
],
|
||||
deps = [
|
||||
":license_test_helper",
|
||||
":license_whitebox",
|
||||
":test_data",
|
||||
"//chromium_deps/cdm/keys:api",
|
||||
"//chromium_deps/cdm/protos:license_protocol_proto",
|
||||
"//chromium_deps/testing:gtest",
|
||||
"//crypto_utils:rsa_key",
|
||||
],
|
||||
)
|
||||
132
api/aead_whitebox.h
Normal file
132
api/aead_whitebox.h
Normal file
@@ -0,0 +1,132 @@
|
||||
// Copyright 2020 Google LLC. All Rights Reserved.
|
||||
|
||||
#ifndef WHITEBOX_API_AEAD_WHITEBOX_H_
|
||||
#define WHITEBOX_API_AEAD_WHITEBOX_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "api/result.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// The opaque type representing a single AEAD white-box instance.
|
||||
typedef struct WB_Aead_Whitebox WB_Aead_Whitebox;
|
||||
|
||||
// Creates a new white-box instance using |whitebox_init_data|. A pointer to the
|
||||
// white-box instance will be returned via |whitebox|.
|
||||
//
|
||||
// Args:
|
||||
// whitebox_init_data (in): The implementation-specific initialization data
|
||||
// needed to initialize a new white-box instance. It contains the white-box
|
||||
// representation of an AES key.
|
||||
//
|
||||
// whitebox_init_data_size (in) : The number of bytes in |whitebox_init_data|.
|
||||
//
|
||||
// context (in) : The device-specific data to bind the ciphertexts to the
|
||||
// device. It is used to derive a context specific key from the initial master
|
||||
// key.
|
||||
//
|
||||
// context_size (in) : The number of bytes in |context|.
|
||||
//
|
||||
// whitebox (out) : The output parameter used to return the new white-box
|
||||
// instance.
|
||||
//
|
||||
// Returns:
|
||||
// WB_RESULT_OK if the white-box instance was successfully created.
|
||||
//
|
||||
// WB_RESULT_INVALID_PARAMETER if |whitebox_init_data| was null, if
|
||||
// |whitebox_init_data| was invalid, if |whitebox_init_data_size| was zero, if
|
||||
// |context| was null, if |context_size| was zero, or if |whitebox| was null.
|
||||
//
|
||||
// WB_RESULT_OUT_OF_MEMORY if the necessary memory could not be allocated.
|
||||
WB_Result WB_Aead_Create(const uint8_t* whitebox_init_data,
|
||||
size_t whitebox_init_data_size,
|
||||
const uint8_t* context,
|
||||
size_t context_size,
|
||||
WB_Aead_Whitebox** whitebox);
|
||||
|
||||
// Releases all resources used by the white-box instance pointed to by
|
||||
// |whitebox|.
|
||||
//
|
||||
// Args:
|
||||
// whitebox (in) : A pointer to a white-box instance. Passing in null will
|
||||
// result in a no-op.
|
||||
void WB_Aead_Delete(WB_Aead_Whitebox* whitebox);
|
||||
|
||||
// Encrypts |input_data| and writes the cipher data, nonce and the data
|
||||
// verification tag to |output_data|. The implementation should generate and use
|
||||
// a random nonce internally to randomize the operation. |output_data_size|
|
||||
// should be at least |input_data_size| + 22 to allow room for the nonce and the
|
||||
// verification tag.
|
||||
//
|
||||
// Args:
|
||||
// whitebox (in) : The white-box containing the keys for encryption.
|
||||
//
|
||||
// input_data (in) : The plaintext.
|
||||
//
|
||||
// input_data_size (in) : The number of bytes in |input_data|.
|
||||
//
|
||||
// output_data (out) : The output parameter for the ciphertext, nonce, and
|
||||
// verification tag.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// Returns:
|
||||
// WB_RESULT_OK if |input_data| was successfully encrypted.
|
||||
//
|
||||
// WB_RESULT_INVALID_PARAMETER if |whitebox| was null, if |input_data| was
|
||||
// null, if |input_data_size| was zero, 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 WB_Aead_Encrypt(const WB_Aead_Whitebox* whitebox,
|
||||
const uint8_t* input_data,
|
||||
size_t input_data_size,
|
||||
uint8_t* output_data,
|
||||
size_t* output_data_size);
|
||||
|
||||
// Decrypts |input_data| and writes the plaintext to |output_data|. |input_data|
|
||||
// must have been encrypted using WB_Aead_Encrypt() with the same |whitebox|.
|
||||
//
|
||||
// Args:
|
||||
// whitebox (in) : The white-box containing the keys for decryption.
|
||||
//
|
||||
// input_data (in) : The ciphertext, nonce and verification tag.
|
||||
//
|
||||
// input_data_size (in) : The number of bytes in |input_data|.
|
||||
//
|
||||
// output_data (out) : The output parameter for the plaintext.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// Returns:
|
||||
// WB_RESULT_OK if |input_data| was successfully decrypted.
|
||||
//
|
||||
// WB_RESULT_INVALID_PARAMETER if |whitebox| was null, if |input_data| was
|
||||
// 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,
|
||||
const uint8_t* input_data,
|
||||
size_t input_data_size,
|
||||
uint8_t* output_data,
|
||||
size_t* output_data_size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // WHITEBOX_API_AEAD_WHITEBOX_H_
|
||||
75
api/aead_whitebox_create_test.cc
Normal file
75
api/aead_whitebox_create_test.cc
Normal file
@@ -0,0 +1,75 @@
|
||||
// Copyright 2020 Google LLC. All Rights Reserved.
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "api/aead_whitebox.h"
|
||||
#include "api/test_data.h"
|
||||
#include "testing/include/gtest/gtest.h"
|
||||
|
||||
namespace {
|
||||
|
||||
class AeadWhiteboxCreateTest : public ::testing::Test {
|
||||
protected:
|
||||
void TearDown() override { WB_Aead_Delete(whitebox_); }
|
||||
|
||||
WB_Aead_Whitebox* whitebox_ = nullptr;
|
||||
|
||||
const std::vector<uint8_t> init_data_ = GetValidAeadInitData();
|
||||
|
||||
const std::vector<uint8_t> context_ = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
};
|
||||
|
||||
TEST_F(AeadWhiteboxCreateTest, Success) {
|
||||
ASSERT_EQ(WB_Aead_Create(init_data_.data(), init_data_.size(),
|
||||
context_.data(), context_.size(), &whitebox_),
|
||||
WB_RESULT_OK);
|
||||
ASSERT_TRUE(whitebox_);
|
||||
}
|
||||
|
||||
TEST_F(AeadWhiteboxCreateTest, InvalidParameterForNullInitData) {
|
||||
ASSERT_EQ(WB_Aead_Create(nullptr, init_data_.size(), context_.data(),
|
||||
context_.size(), &whitebox_),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
ASSERT_FALSE(whitebox_);
|
||||
}
|
||||
|
||||
TEST_F(AeadWhiteboxCreateTest, InvalidParameterForInvalidInitData) {
|
||||
const std::vector<uint8_t> invalid_init_data = GetInvalidAeadInitData();
|
||||
|
||||
ASSERT_EQ(WB_Aead_Create(invalid_init_data.data(), invalid_init_data.size(),
|
||||
context_.data(), context_.size(), &whitebox_),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
ASSERT_FALSE(whitebox_);
|
||||
}
|
||||
|
||||
TEST_F(AeadWhiteboxCreateTest, InvalidParameterForZeroInitDataSize) {
|
||||
ASSERT_EQ(WB_Aead_Create(init_data_.data(), 0, context_.data(),
|
||||
context_.size(), &whitebox_),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
ASSERT_FALSE(whitebox_);
|
||||
}
|
||||
|
||||
TEST_F(AeadWhiteboxCreateTest, InvalidParameterForNullContext) {
|
||||
ASSERT_EQ(WB_Aead_Create(init_data_.data(), init_data_.size(), nullptr,
|
||||
context_.size(), &whitebox_),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
ASSERT_FALSE(whitebox_);
|
||||
}
|
||||
|
||||
TEST_F(AeadWhiteboxCreateTest, InvalidParameterForZeroContextSize) {
|
||||
ASSERT_EQ(WB_Aead_Create(init_data_.data(), init_data_.size(),
|
||||
context_.data(), 0, &whitebox_),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
ASSERT_FALSE(whitebox_);
|
||||
}
|
||||
|
||||
TEST_F(AeadWhiteboxCreateTest, InvalidParameterForNullWhitebox) {
|
||||
ASSERT_EQ(WB_Aead_Create(init_data_.data(), init_data_.size(),
|
||||
context_.data(), context_.size(), nullptr),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
ASSERT_FALSE(whitebox_);
|
||||
}
|
||||
} // namespace
|
||||
156
api/aead_whitebox_decrypt_test.cc
Normal file
156
api/aead_whitebox_decrypt_test.cc
Normal file
@@ -0,0 +1,156 @@
|
||||
// Copyright 2020 Google LLC. All Rights Reserved.
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "api/aead_whitebox.h"
|
||||
#include "api/test_data.h"
|
||||
#include "testing/include/gtest/gtest.h"
|
||||
|
||||
namespace {
|
||||
// For our decrypt tests, we assume that WB_Aead_Create() and WB_Aead_Encrypt()
|
||||
// work and each test will have valid encrypted data.
|
||||
class AeadWhiteboxDecryptTest : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
const std::vector<uint8_t> init_data = GetValidAeadInitData();
|
||||
|
||||
const std::vector<uint8_t> context = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
ASSERT_EQ(WB_Aead_Create(init_data.data(), init_data.size(), context.data(),
|
||||
context.size(), &whitebox_),
|
||||
WB_RESULT_OK);
|
||||
|
||||
// Regardless of implementation, we need to have enough room in our cipher
|
||||
// text buffer to hold the additional information added by
|
||||
// WB_Aead_Encrypt(). So this number just needs to be reasonably big
|
||||
// compared to the input.
|
||||
size_t ciphertext_size = 256;
|
||||
ciphertext_.resize(ciphertext_size);
|
||||
ASSERT_EQ(WB_Aead_Encrypt(whitebox_, plaintext_.data(), plaintext_.size(),
|
||||
ciphertext_.data(), &ciphertext_size),
|
||||
WB_RESULT_OK);
|
||||
ciphertext_.resize(ciphertext_size);
|
||||
}
|
||||
|
||||
void TearDown() override { WB_Aead_Delete(whitebox_); }
|
||||
|
||||
WB_Aead_Whitebox* whitebox_ = nullptr;
|
||||
|
||||
// Since we are going to verify decryption, we need the plaintext to be
|
||||
// recognizable after it is encrypted and then decrypted.
|
||||
const std::vector<uint8_t> plaintext_ = {
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
|
||||
};
|
||||
|
||||
// Because the ciphertext will be implementation specific, we can't use
|
||||
// golden data for it. |ciphertext_| will be initialized in SetUp().
|
||||
std::vector<uint8_t> ciphertext_;
|
||||
};
|
||||
|
||||
TEST_F(AeadWhiteboxDecryptTest, Success) {
|
||||
// The plaintext should be smaller than the input, but we use the input so
|
||||
// that we will always have enough.
|
||||
size_t plaintext_size = ciphertext_.size();
|
||||
std::vector<uint8_t> plaintext(plaintext_size);
|
||||
|
||||
ASSERT_EQ(WB_Aead_Decrypt(whitebox_, ciphertext_.data(), ciphertext_.size(),
|
||||
plaintext.data(), &plaintext_size),
|
||||
WB_RESULT_OK);
|
||||
plaintext.resize(plaintext_size);
|
||||
|
||||
ASSERT_EQ(plaintext, plaintext_);
|
||||
}
|
||||
|
||||
TEST_F(AeadWhiteboxDecryptTest, InvalidParameterForNullWhitebox) {
|
||||
// The plaintext should be smaller than the input, but we use the input so
|
||||
// that we will always have enough.
|
||||
size_t plaintext_size = ciphertext_.size();
|
||||
std::vector<uint8_t> plaintext(plaintext_size);
|
||||
|
||||
ASSERT_EQ(WB_Aead_Decrypt(nullptr, ciphertext_.data(), ciphertext_.size(),
|
||||
plaintext.data(), &plaintext_size),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(AeadWhiteboxDecryptTest, InvalidParameterForNullInputData) {
|
||||
// The plaintext should be smaller than the input, but we use the input so
|
||||
// that we will always have enough.
|
||||
size_t plaintext_size = ciphertext_.size();
|
||||
std::vector<uint8_t> plaintext(plaintext_size);
|
||||
|
||||
ASSERT_EQ(WB_Aead_Decrypt(whitebox_, nullptr, ciphertext_.size(),
|
||||
plaintext.data(), &plaintext_size),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(AeadWhiteboxDecryptTest, InvalidParameterForInvalidInputDataSize) {
|
||||
// The plaintext should be smaller than the input, but we use the input so
|
||||
// that we will always have enough.
|
||||
size_t plaintext_size = ciphertext_.size();
|
||||
std::vector<uint8_t> plaintext(plaintext_size);
|
||||
|
||||
// Because we don't know how big the authentication tag will be, we need to
|
||||
// keep the number small. However, we don't use zero as "no data" is different
|
||||
// than "invalid data".
|
||||
ASSERT_EQ(WB_Aead_Decrypt(whitebox_, ciphertext_.data(), 4, plaintext.data(),
|
||||
&plaintext_size),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(AeadWhiteboxDecryptTest, InvalidParameterForNullOutputData) {
|
||||
// The plaintext should be smaller than the input, but we use the input so
|
||||
// that we will always have enough.
|
||||
size_t plaintext_size = ciphertext_.size();
|
||||
std::vector<uint8_t> plaintext(plaintext_size);
|
||||
|
||||
ASSERT_EQ(WB_Aead_Decrypt(whitebox_, ciphertext_.data(), ciphertext_.size(),
|
||||
nullptr, &plaintext_size),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(AeadWhiteboxDecryptTest, InvalidParameterForNullOutputDataSize) {
|
||||
// The plaintext should be smaller than the input, but we use the input so
|
||||
// that we will always have enough.
|
||||
size_t plaintext_size = ciphertext_.size();
|
||||
std::vector<uint8_t> plaintext(plaintext_size);
|
||||
|
||||
ASSERT_EQ(WB_Aead_Decrypt(whitebox_, ciphertext_.data(), ciphertext_.size(),
|
||||
plaintext.data(), nullptr),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(AeadWhiteboxDecryptTest, BufferTooSmall) {
|
||||
// We know what the plaintext should be, so if we make the plaintext buffer
|
||||
// just a little too small, it should fail without the test needing to know
|
||||
// anything out the implementation.
|
||||
size_t plaintext_size = plaintext_.size() - 1;
|
||||
std::vector<uint8_t> plaintext(plaintext_size);
|
||||
|
||||
ASSERT_EQ(WB_Aead_Decrypt(whitebox_, ciphertext_.data(), ciphertext_.size(),
|
||||
plaintext.data(), &plaintext_size),
|
||||
WB_RESULT_BUFFER_TOO_SMALL);
|
||||
ASSERT_EQ(plaintext_size, plaintext_.size());
|
||||
}
|
||||
|
||||
TEST_F(AeadWhiteboxDecryptTest, DataVerificationError) {
|
||||
// The plaintext should be smaller than the input, but we use the input so
|
||||
// that we will always have enough.
|
||||
size_t plaintext_size = ciphertext_.size();
|
||||
std::vector<uint8_t> plaintext(plaintext_size);
|
||||
|
||||
// Create invalid input by flipping one byte. Use bitwise-not so that, no
|
||||
// matter the original value, it will be different (we don't know what the
|
||||
// cipher text will actually be).
|
||||
std::vector<uint8_t> invalid_input = ciphertext_;
|
||||
invalid_input[0] = ~invalid_input[0];
|
||||
|
||||
ASSERT_EQ(
|
||||
WB_Aead_Decrypt(whitebox_, invalid_input.data(), invalid_input.size(),
|
||||
plaintext.data(), &plaintext_size),
|
||||
WB_RESULT_DATA_VERIFICATION_ERROR);
|
||||
}
|
||||
} // namespace
|
||||
119
api/aead_whitebox_encrypt_test.cc
Normal file
119
api/aead_whitebox_encrypt_test.cc
Normal file
@@ -0,0 +1,119 @@
|
||||
// Copyright 2020 Google LLC. All Rights Reserved.
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "api/aead_whitebox.h"
|
||||
#include "api/test_data.h"
|
||||
#include "testing/include/gtest/gtest.h"
|
||||
|
||||
namespace {
|
||||
// Regardless of implementation, we need to have enough room in our output
|
||||
// buffer to hold the additional information added by WB_Aead_Encrypt(). So
|
||||
// this number just needs to be reasonably big compared to the input.
|
||||
constexpr size_t kDefaultOutputSize = 256;
|
||||
|
||||
// For our encrypt tests, we assume that WB_Aead_Create() works and each test
|
||||
// will have a functional instance. In these tests, we will not be checking the
|
||||
// encrypt and decrypt flow, just the return codes of encrypt. For checking the
|
||||
// encrypt-decrypt flow, see the decrypt tests as they will assume encryption
|
||||
// works.
|
||||
class AeadWhiteboxEncryptTest : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
const std::vector<uint8_t> init_data = GetValidAeadInitData();
|
||||
|
||||
const std::vector<uint8_t> context = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
ASSERT_EQ(WB_Aead_Create(init_data.data(), init_data.size(), context.data(),
|
||||
context.size(), &whitebox_),
|
||||
WB_RESULT_OK);
|
||||
}
|
||||
|
||||
void TearDown() override { WB_Aead_Delete(whitebox_); }
|
||||
|
||||
WB_Aead_Whitebox* whitebox_ = nullptr;
|
||||
|
||||
// Because we are not going to verify the encryption in these tests, we don't
|
||||
// need the input to be recognizable.
|
||||
const std::vector<uint8_t> input_ = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
};
|
||||
|
||||
TEST_F(AeadWhiteboxEncryptTest, Success) {
|
||||
size_t output_size = kDefaultOutputSize;
|
||||
std::vector<uint8_t> output(output_size);
|
||||
|
||||
ASSERT_EQ(WB_Aead_Encrypt(whitebox_, input_.data(), input_.size(),
|
||||
output.data(), &output_size),
|
||||
WB_RESULT_OK);
|
||||
|
||||
// Additional information should have been added to the output (compared to
|
||||
// the input).
|
||||
ASSERT_GT(output_size, input_.size());
|
||||
}
|
||||
|
||||
TEST_F(AeadWhiteboxEncryptTest, InvalidParameterForNullWhitebox) {
|
||||
size_t output_size = kDefaultOutputSize;
|
||||
std::vector<uint8_t> output(output_size);
|
||||
|
||||
ASSERT_EQ(WB_Aead_Encrypt(nullptr, input_.data(), input_.size(),
|
||||
output.data(), &output_size),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(AeadWhiteboxEncryptTest, InvalidParameterForNullInputData) {
|
||||
size_t output_size = kDefaultOutputSize;
|
||||
std::vector<uint8_t> output(output_size);
|
||||
|
||||
ASSERT_EQ(WB_Aead_Encrypt(whitebox_, nullptr, input_.size(), output.data(),
|
||||
&output_size),
|
||||
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);
|
||||
|
||||
ASSERT_EQ(WB_Aead_Encrypt(whitebox_, input_.data(), input_.size(), nullptr,
|
||||
&output_size),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(AeadWhiteboxEncryptTest, InvalidParameterForNullOutputDataSize) {
|
||||
size_t output_size = kDefaultOutputSize;
|
||||
std::vector<uint8_t> output(output_size);
|
||||
|
||||
ASSERT_EQ(WB_Aead_Encrypt(whitebox_, input_.data(), input_.size(),
|
||||
output.data(), nullptr),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(AeadWhiteboxEncryptTest, BufferTooSmallForSmallOutputBuffer) {
|
||||
// Because WB_Aead_Encrypt() will add more data, having the output be the
|
||||
// same size as the input will make it by definition too small.
|
||||
size_t output_size = input_.size();
|
||||
std::vector<uint8_t> output(output_size);
|
||||
|
||||
ASSERT_EQ(WB_Aead_Encrypt(whitebox_, input_.data(), input_.size(),
|
||||
output.data(), &output_size),
|
||||
WB_RESULT_BUFFER_TOO_SMALL);
|
||||
|
||||
// Additional room should be needed. We don't know how much more, so we can
|
||||
// only check that |output_size| is larger than |input_.size()|.
|
||||
ASSERT_GT(output_size, input_.size());
|
||||
}
|
||||
} // namespace
|
||||
345
api/license_test_helper.cc
Normal file
345
api/license_test_helper.cc
Normal file
@@ -0,0 +1,345 @@
|
||||
// Copyright 2020 Google LLC. All Rights Reserved.
|
||||
|
||||
#include "api/license_test_helper.h"
|
||||
|
||||
#include <ctime>
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "cdm/keys/certs.h"
|
||||
#include "cdm/protos/license_protocol.pb.h"
|
||||
#include "crypto_utils/aes_cbc_encryptor.h"
|
||||
#include "crypto_utils/crypto_util.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
namespace {
|
||||
|
||||
struct KeyData {
|
||||
video_widevine::License_KeyContainer_SecurityLevel level;
|
||||
std::vector<uint8_t> key_id;
|
||||
std::vector<uint8_t> iv;
|
||||
std::vector<uint8_t> content_key;
|
||||
};
|
||||
|
||||
// clang-format off
|
||||
KeyData SoftwareCryptoKey() {
|
||||
return {
|
||||
video_widevine::License_KeyContainer_SecurityLevel_SW_SECURE_CRYPTO,
|
||||
{ // key_id:
|
||||
'0', '1', '2', '3', '4', '5', '6', '7',
|
||||
'8', '9', '0', '1', '2', '3', '4', '5',
|
||||
},
|
||||
{ // iv:
|
||||
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
|
||||
0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
|
||||
},
|
||||
{ // content_key:
|
||||
0xeb, 0xdd, 0x62, 0xf1, 0x68, 0x14, 0xd2, 0x7b,
|
||||
0x68, 0xef, 0x12, 0x2a, 0xfc, 0xe4, 0xae, 0x3c,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
KeyData SoftwareDecodeKey() {
|
||||
return {
|
||||
video_widevine::License_KeyContainer_SecurityLevel_SW_SECURE_DECODE,
|
||||
{ // key_id:
|
||||
'9', '8', '7', '6', '5', '4', '3', '2',
|
||||
'1', '0', '9', '8', '7', '6', '5', '4',
|
||||
},
|
||||
{ // iv:
|
||||
0x17, 0x18, 0x19, 0x20, 0x21, 0x22, 0x23, 0x24,
|
||||
0x25, 0x26, 0x27, 0x28, 0x29, 0x30, 0x31, 0x32,
|
||||
},
|
||||
{ // content_key:
|
||||
0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b,
|
||||
0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
KeyData HardwareCryptoKey() {
|
||||
return {
|
||||
video_widevine::License_KeyContainer_SecurityLevel_HW_SECURE_CRYPTO,
|
||||
{ // key_id:
|
||||
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
|
||||
'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
|
||||
},
|
||||
{ // iv:
|
||||
0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x40,
|
||||
0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
|
||||
},
|
||||
{ // content_key:
|
||||
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
|
||||
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
|
||||
}
|
||||
};
|
||||
}
|
||||
// clang-format on
|
||||
|
||||
// Returns |buffer| converted into a string.
|
||||
std::string AsString(const uint8_t* buffer, size_t buffer_size) {
|
||||
return std::string(reinterpret_cast<const char*>(buffer), buffer_size);
|
||||
}
|
||||
|
||||
// Returns the encryption key based on |license_request| and |session_key|.
|
||||
std::string GetEncryptionKey(const std::string& license_request,
|
||||
const std::string& session_key) {
|
||||
return widevine::crypto_util::DeriveKey(
|
||||
session_key, widevine::crypto_util::kWrappingKeyLabel, license_request,
|
||||
widevine::crypto_util::kWrappingKeySizeBits);
|
||||
}
|
||||
|
||||
std::string AesEncrypt(const std::string& key,
|
||||
const uint8_t* iv,
|
||||
size_t iv_size,
|
||||
const uint8_t* plain_text,
|
||||
size_t plain_text_size) {
|
||||
widevine::AesCbcEncryptor encryptor;
|
||||
encryptor.SetKey(reinterpret_cast<const uint8_t*>(key.data()), key.size());
|
||||
std::vector<uint8_t> cipher_text(plain_text_size);
|
||||
CHECK(encryptor.Encrypt(iv, iv_size, plain_text, plain_text_size,
|
||||
cipher_text.data()));
|
||||
return AsString(cipher_text.data(), cipher_text.size());
|
||||
}
|
||||
|
||||
video_widevine::License::KeyContainer CreateContentKeyContainer(
|
||||
const KeyData& key_data,
|
||||
const std::string& key_container_encryption_key) {
|
||||
video_widevine::License::KeyContainer key;
|
||||
key.set_id(key_data.key_id.data(), key_data.key_id.size());
|
||||
key.set_iv(key_data.iv.data(), key_data.iv.size());
|
||||
key.set_type(video_widevine::License_KeyContainer_KeyType_CONTENT);
|
||||
key.set_level(key_data.level);
|
||||
key.set_key(AesEncrypt(key_container_encryption_key, key_data.iv.data(),
|
||||
key_data.iv.size(), key_data.content_key.data(),
|
||||
key_data.content_key.size()));
|
||||
return key;
|
||||
}
|
||||
|
||||
video_widevine::License::KeyContainer CreateSigningKeyContainer(
|
||||
const std::string& key_container_encryption_key) {
|
||||
const std::vector<uint8_t> signing_iv = {
|
||||
'0', '1', '2', '3', '4', '5', '6', '7',
|
||||
'8', '9', '0', '1', '2', '3', '4', '5',
|
||||
};
|
||||
// 512 bits of key material for dual server/client signing key.
|
||||
const std::vector<uint8_t> signing_key = {
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
||||
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
|
||||
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
|
||||
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
|
||||
};
|
||||
|
||||
video_widevine::License::KeyContainer key;
|
||||
key.set_iv(signing_iv.data(), signing_iv.size());
|
||||
key.set_type(video_widevine::License_KeyContainer_KeyType_SIGNING);
|
||||
key.set_key(AesEncrypt(key_container_encryption_key, signing_iv.data(),
|
||||
signing_iv.size(), signing_key.data(),
|
||||
signing_key.size()));
|
||||
return key;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::vector<uint8_t> GetSoftwareCryptoKeyId() {
|
||||
const auto key = SoftwareCryptoKey();
|
||||
return key.key_id;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> GetSoftwareDecodeKeyId() {
|
||||
const auto key = SoftwareDecodeKey();
|
||||
return key.key_id;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> GetHardwareCryptoKeyId() {
|
||||
const auto key = HardwareCryptoKey();
|
||||
return key.key_id;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> GetUnusedKeyId() {
|
||||
return {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
|
||||
}
|
||||
|
||||
// Returns a serialized license request with the following data:
|
||||
// {
|
||||
// "requestTime": <current time>,
|
||||
// "clientId": {
|
||||
// "clientCapabilities": {
|
||||
// "videoResolutionConstraints": true,
|
||||
// "clientToken": true,
|
||||
// "sessionToken": false,
|
||||
// "maxHdcpVersion": "HDCP_V1"
|
||||
// },
|
||||
// "clientInfo": [{
|
||||
// "name": "architecture_name",
|
||||
// "value": "x86-64"
|
||||
// },{
|
||||
// "name": "company_name",
|
||||
// "value": "Google"
|
||||
// },{
|
||||
// "name": "model_name",
|
||||
// "value": "ChromeCDM"
|
||||
// },{
|
||||
// "name": "platform_name",
|
||||
// "value": "Windows"
|
||||
// },{
|
||||
// "name": "widevine_cdm_version",
|
||||
// "value": "4.10.1686.29"
|
||||
// }],
|
||||
// "type": "DRM_DEVICE_CERTIFICATE",
|
||||
// "token": {
|
||||
// <test device certificate>
|
||||
// }"
|
||||
// }",
|
||||
// "contentId": {
|
||||
// "webmKeyId": {
|
||||
// "licenseType": "STREAMING",
|
||||
// "requestId": "REQUEST_ID",
|
||||
// "header": "01234567890123456"
|
||||
// }
|
||||
// },
|
||||
// "protocolVersion": "VERSION_2_1",
|
||||
// "type": "NEW"
|
||||
// }
|
||||
std::string CreateLicenseRequest() {
|
||||
video_widevine::LicenseRequest license_request;
|
||||
|
||||
// Request time is now.
|
||||
license_request.set_request_time(std::time(nullptr));
|
||||
|
||||
auto* client_id = license_request.mutable_client_id();
|
||||
auto* capabilities = client_id->mutable_client_capabilities();
|
||||
capabilities->set_video_resolution_constraints(true);
|
||||
capabilities->set_client_token(true);
|
||||
capabilities->set_session_token(false);
|
||||
capabilities->set_max_hdcp_version(
|
||||
video_widevine::ClientIdentification::ClientCapabilities::HDCP_V1);
|
||||
|
||||
auto* client_info = client_id->add_client_info();
|
||||
client_info->set_name("architecture_name");
|
||||
client_info->set_value("x86-64");
|
||||
client_info = client_id->add_client_info();
|
||||
client_info->set_name("company_name");
|
||||
client_info->set_value("Google");
|
||||
client_info = client_id->add_client_info();
|
||||
client_info->set_name("model_name");
|
||||
client_info->set_value("ChromeCDM");
|
||||
client_info = client_id->add_client_info();
|
||||
client_info->set_name("platform_name");
|
||||
client_info->set_value("Windows");
|
||||
client_info = client_id->add_client_info();
|
||||
client_info->set_name("widevine_cdm_version");
|
||||
client_info->set_value("4.10.1686.29");
|
||||
|
||||
client_id->set_type(
|
||||
video_widevine::ClientIdentification_TokenType_DRM_DEVICE_CERTIFICATE);
|
||||
client_id->set_token(wvcdm::kRsaDrmCertificate,
|
||||
wvcdm::kRsaDrmCertificateSize);
|
||||
|
||||
auto* content_id = license_request.mutable_content_id();
|
||||
auto* webm_key_id = content_id->mutable_webm_key_id();
|
||||
webm_key_id->set_license_type(video_widevine::STREAMING);
|
||||
webm_key_id->set_request_id("REQUEST_ID");
|
||||
webm_key_id->set_header("01234567890123456");
|
||||
|
||||
license_request.set_protocol_version(video_widevine::VERSION_2_1);
|
||||
license_request.set_type(video_widevine::LicenseRequest::NEW);
|
||||
return license_request.SerializeAsString();
|
||||
}
|
||||
|
||||
// Returns a serialized license using values from |serialized_license_request|
|
||||
// as well as |session_key|. Some of the values match what is in the license
|
||||
// request, but as they are not checked it really doesn't matter. The license
|
||||
// will contain all 3 keys |kKeyId1|, |kKeyId2|, and |kKeyId3| with different
|
||||
// security levels. The license is of the form:
|
||||
// {
|
||||
// "id": {
|
||||
// "requestId": "REQUEST_ID",
|
||||
// "sessionId": "SESSION_ID",
|
||||
// "type": "STREAMING",
|
||||
// "version": 0
|
||||
// },
|
||||
// "policy": {
|
||||
// "canPlay": true,
|
||||
// "canPersist": false,
|
||||
// "canRenew": true,
|
||||
// "licenseDurationSeconds": "600",
|
||||
// "renewalDelaySeconds": "30",
|
||||
// "renewalRetryIntervalSeconds": "10",
|
||||
// "renewWithUsage": false
|
||||
// },
|
||||
// "key": [{
|
||||
// "iv": <|kInitializationVector|>,
|
||||
// "type": "SIGNING",
|
||||
// "key": <computed>
|
||||
// },{
|
||||
// "id": <|kKeyId1|>,
|
||||
// "iv": <|kInitializationVector|>,
|
||||
// "type": "CONTENT",
|
||||
// "level": "SW_SECURE_CRYPTO",
|
||||
// "key": <|kContentKey1|>
|
||||
// },{
|
||||
// "id": <|kKeyId2|>,
|
||||
// "iv": <|kInitializationVector|>,
|
||||
// "type": "CONTENT",
|
||||
// "level": "SW_SECURE_DECODE",
|
||||
// "key": <|kContentKey2|>
|
||||
// },{
|
||||
// "id": <|kKeyId3|>,
|
||||
// "iv": <|kInitializationVector|>,
|
||||
// "type": "CONTENT",
|
||||
// "level": "HW_SECURE_CRYPTO",
|
||||
// "key": <|kContentKey3|>
|
||||
// }],
|
||||
// "licenseStartTime": <from license_request.requestTime>,
|
||||
// "remoteAttestationVerified": false,
|
||||
// "platformVerificationStatus": "PLATFORM_UNVERIFIED"
|
||||
// }
|
||||
std::string CreateLicense(const std::string& serialized_license_request,
|
||||
const std::string& session_key) {
|
||||
DCHECK_EQ(session_key.size(), 16u);
|
||||
|
||||
// Extract values needed for the license.
|
||||
video_widevine::LicenseRequest license_request;
|
||||
CHECK(license_request.ParseFromString(serialized_license_request));
|
||||
const auto request_time = license_request.request_time();
|
||||
|
||||
const std::string key_container_encryption_key =
|
||||
GetEncryptionKey(serialized_license_request, session_key);
|
||||
|
||||
// Now create the license.
|
||||
video_widevine::License license;
|
||||
auto* license_id = license.mutable_id();
|
||||
license_id->set_request_id("REQUEST_ID");
|
||||
license_id->set_session_id("SESSION_ID");
|
||||
license_id->set_type(video_widevine::STREAMING);
|
||||
license_id->set_version(0);
|
||||
|
||||
auto* policy = license.mutable_policy();
|
||||
policy->set_can_play(true);
|
||||
policy->set_can_persist(false);
|
||||
policy->set_can_renew(true);
|
||||
policy->set_license_duration_seconds(600);
|
||||
policy->set_renewal_delay_seconds(30);
|
||||
policy->set_renewal_retry_interval_seconds(10);
|
||||
policy->set_renew_with_usage(false);
|
||||
|
||||
license.add_key()->CopyFrom(
|
||||
CreateSigningKeyContainer(key_container_encryption_key));
|
||||
license.add_key()->CopyFrom(CreateContentKeyContainer(
|
||||
SoftwareCryptoKey(), key_container_encryption_key));
|
||||
license.add_key()->CopyFrom(CreateContentKeyContainer(
|
||||
SoftwareDecodeKey(), key_container_encryption_key));
|
||||
license.add_key()->CopyFrom(CreateContentKeyContainer(
|
||||
HardwareCryptoKey(), key_container_encryption_key));
|
||||
|
||||
license.set_license_start_time(request_time);
|
||||
license.set_remote_attestation_verified(false);
|
||||
license.set_platform_verification_status(
|
||||
video_widevine::PlatformVerificationStatus::PLATFORM_UNVERIFIED);
|
||||
|
||||
return license.SerializeAsString();
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
33
api/license_test_helper.h
Normal file
33
api/license_test_helper.h
Normal file
@@ -0,0 +1,33 @@
|
||||
// Copyright 2020 Google LLC. All Rights Reserved.
|
||||
|
||||
#ifndef WHITEBOX_API_LICENSE_TEST_HELPER_H_
|
||||
#define WHITEBOX_API_LICENSE_TEST_HELPER_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace widevine {
|
||||
|
||||
// Returns a serialized LicenseRequest.
|
||||
std::string CreateLicenseRequest();
|
||||
|
||||
// Returns a serialized License based on |serialized_license_request| and
|
||||
// |session_key|. The license contains 3 content keys (key1, key2, key3)
|
||||
// with levels SW_SECURE_CRYPTO, SW_SECURE_DECODE, and HW_SECURE_CRYPTO,
|
||||
// respectively.
|
||||
std::string CreateLicense(const std::string& serialized_license_request,
|
||||
const std::string& session_key);
|
||||
|
||||
// Returns the key ID for each key.
|
||||
std::vector<uint8_t> GetSoftwareCryptoKeyId();
|
||||
std::vector<uint8_t> GetSoftwareDecodeKeyId();
|
||||
std::vector<uint8_t> GetHardwareCryptoKeyId();
|
||||
|
||||
// Returns a key ID which is not included in the license.
|
||||
std::vector<uint8_t> GetUnusedKeyId();
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
#endif // WHITEBOX_API_LICENSE_TEST_HELPER_H_
|
||||
397
api/license_whitebox.h
Normal file
397
api/license_whitebox.h
Normal file
@@ -0,0 +1,397 @@
|
||||
// Copyright 2020 Google LLC. All Rights Reserved.
|
||||
|
||||
#ifndef WHITEBOX_API_LICENSE_WHITEBOX_H_
|
||||
#define WHITEBOX_API_LICENSE_WHITEBOX_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "api/result.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// The opaque type representing a single white-box instance.
|
||||
typedef struct WB_License_Whitebox WB_License_Whitebox;
|
||||
|
||||
typedef enum {
|
||||
WB_CIPHER_MODE_CTR,
|
||||
WB_CIPHER_MODE_CBC,
|
||||
} WB_CipherMode;
|
||||
|
||||
// Creates a new white-box instance using |whitebox_init_data|. A pointer to the
|
||||
// white-box instance will be returned via |whitebox|.
|
||||
//
|
||||
// Args:
|
||||
// whitebox_init_data (in) : The implementation-specific initialization data
|
||||
// needed to initialize a new white-box instance.
|
||||
//
|
||||
// whitebox_init_data_size (in) : The number of bytes in |whitebox_init_data|.
|
||||
//
|
||||
// whitebox (out) : The output parameter used to return the new white-box
|
||||
// instance.
|
||||
//
|
||||
// Returns:
|
||||
// WB_RESULT_OK if the white-box instance was successfully created.
|
||||
//
|
||||
// WB_RESULT_INVALID_PARAMETER if |whitebox_init_data| was null or invalid.
|
||||
//
|
||||
// WB_RESULT_OUT_OF_MEMORY if the necessary memory could not be allocated.
|
||||
WB_Result WB_License_Create(const uint8_t* whitebox_init_data,
|
||||
size_t whitebox_init_data_size,
|
||||
WB_License_Whitebox** whitebox);
|
||||
|
||||
// Releases all resources used by the white-box instance pointed to by
|
||||
// |whitebox|.
|
||||
//
|
||||
// Args:
|
||||
// whitebox (in) : A pointer to a white-box instance. Passing in null will
|
||||
// result in a no-op.
|
||||
void WB_License_Delete(WB_License_Whitebox* whitebox);
|
||||
|
||||
// Signs a license request using the CDM's private signing key.
|
||||
//
|
||||
// Args:
|
||||
// whitebox (in) : A pointer to the current white-box instance.
|
||||
//
|
||||
// license_request (in) : The license request in serialized form.
|
||||
//
|
||||
// license_request_size (in) : The number of bytes in the license_request.
|
||||
//
|
||||
// signature (out) : The generated signature for |license_request|.
|
||||
//
|
||||
// signature_size (in/out) : As input, this contains the max number of bytes
|
||||
// that can be written to |signature|. As output, |signature_size| is set to
|
||||
// the required size on WB_RESULT_OK and WB_RESULT_BUFFER_TOO_SMALL.
|
||||
//
|
||||
// Returns:
|
||||
// WB_RESULT_OK if the signature was successfully generated.
|
||||
//
|
||||
// WB_RESULT_INVALID_PARAMETER if |whitebox| was null, if |license_request| was
|
||||
// null, if |license_request_size| was zero, if |signature| was null, or if
|
||||
// |signature_size| was null.
|
||||
//
|
||||
// WB_RESULT_BUFFER_TOO_SMALL if |signature_size| (as input) was less than the
|
||||
// required size.
|
||||
WB_Result WB_License_SignLicenseRequest(const WB_License_Whitebox* whitebox,
|
||||
const uint8_t* license_request,
|
||||
size_t license_request_size,
|
||||
uint8_t* signature,
|
||||
size_t* signature_size);
|
||||
|
||||
// Verifies a license response using HMAC and the server signing key, extract
|
||||
// the content keys and signing keys, and load them into the white-box.
|
||||
//
|
||||
// This function can only be called once per white-box instance. To parse a new
|
||||
// license response, a new white-box instance must be used.
|
||||
//
|
||||
// Args:
|
||||
// whitebox (in/out) : The white-box instance that will load the keys.
|
||||
//
|
||||
// message (in) : The message field of the license response.
|
||||
//
|
||||
// message_size (in) : The number of bytes in |message|.
|
||||
//
|
||||
// signature (in) : The signature field of the license response.
|
||||
//
|
||||
// signature_size (in) : The number of bytes in |signature|.
|
||||
//
|
||||
// session_key (in) : The session key field of the license response.
|
||||
//
|
||||
// session_key_size (in) : The number of bytes in |session_key|.
|
||||
//
|
||||
// license_request (in) : The license request that was sent in order to get the
|
||||
// license response.
|
||||
//
|
||||
// license_request_size (in) : The number of bytes in |license_request|.
|
||||
//
|
||||
// Returns:
|
||||
// WB_RESULT_OK if the response was verified and the keys were loaded into
|
||||
// |whitebox|.
|
||||
//
|
||||
// WB_RESULT_INVALID_SIGNATURE if |message|'s signature does not match
|
||||
// |signature|.
|
||||
//
|
||||
// WB_RESULT_INVALID_PARAMETER if |whitebox| was null, if |message| was null,
|
||||
// if |message_size| was zero, if |message| did not conform to the expected
|
||||
// format, if |signature| was null, if |signature_size| was incorrect, if
|
||||
// |session_key| was null, if |session_key_size| was incorrect, if
|
||||
// |session_key| could not be unwrapped correctly, if |license_request| was
|
||||
// null, if |license_request_size| was zero, or if |license_request| was
|
||||
// malformed.
|
||||
WB_Result WB_License_ProcessLicenseResponse(WB_License_Whitebox* whitebox,
|
||||
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,
|
||||
const uint8_t* license_request,
|
||||
size_t license_request_size);
|
||||
|
||||
// Signs |message| and return the signature via |signature| using HMAC and the
|
||||
// client renewal signing key
|
||||
//
|
||||
// Args:
|
||||
// whitebox (in) : The white-box instance containing the signing key.
|
||||
//
|
||||
// message (in) : The message that should be signed.
|
||||
//
|
||||
// message_size (in) : The number of bytes in |message|.
|
||||
//
|
||||
// signature (out) : The output parameter used to return the signature.
|
||||
//
|
||||
// signature_size (in/out) : As input, this contains the max number of bytes
|
||||
// that can be written to |signature|. As output, |signature_size| is set to
|
||||
// the required size on WB_RESULT_OK and WB_RESULT_BUFFER_TOO_SMALL.
|
||||
//
|
||||
// Returns:
|
||||
// WB_RESULT_OK if |message| was successfully signed.
|
||||
//
|
||||
// WB_RESULT_INVALID_PARAMETER if |whitebox| was null, if |message| was null,
|
||||
// if |message_size| was zero, if |signature| was null, or if |signature_size|
|
||||
// was null.
|
||||
//
|
||||
// WB_RESULT_BUFFER_TOO_SMALL if |signature_size| (as input) was less than the
|
||||
// required size.
|
||||
//
|
||||
// WB_RESULT_INVALID_STATE if |whitebox| had no signing keys.
|
||||
WB_Result WB_License_SignRenewalRequest(const WB_License_Whitebox* whitebox,
|
||||
const uint8_t* message,
|
||||
size_t message_size,
|
||||
uint8_t* signature,
|
||||
size_t* signature_size);
|
||||
|
||||
// Verifies the renewal response using HMAC and the server signing key.
|
||||
//
|
||||
// Args:
|
||||
// whitebox (in) : The white-box containing the server signing key.
|
||||
//
|
||||
// message (in) : The message that needs to be verified.
|
||||
//
|
||||
// message_size (in) : The number of bytes in |message|.
|
||||
//
|
||||
// signature (in) : The expected signature for |message|.
|
||||
//
|
||||
// signature_size (in) : The number of bytes in |signature|.
|
||||
//
|
||||
// Returns:
|
||||
// WB_RESULT_OK if |message|'s signature matches |signature|.
|
||||
//
|
||||
// WB_RESULT_INVALID_PARAMETER if |whitebox| was null, if |message| was null,
|
||||
// if |message_size| was zero, if |signature| was null, of if |signature_size|
|
||||
// was incorrect.
|
||||
//
|
||||
// WB_RESULT_INVALID_SIGNATURE if |message|'s signature did not match
|
||||
// |signature|.
|
||||
//
|
||||
// WB_RESULT_INVALID_STATE if |whitebox| had no keys.
|
||||
WB_Result WB_License_VerifyRenewalResponse(const WB_License_Whitebox* whitebox,
|
||||
const uint8_t* message,
|
||||
size_t message_size,
|
||||
const uint8_t* signature,
|
||||
size_t signature_size);
|
||||
|
||||
// Gets the secret string needed by WB_License_Unmask() in order to unmask the
|
||||
// masked decrypted content returned by WB_License_MaskedDecrypt().
|
||||
//
|
||||
// Args:
|
||||
// whitebox (in) : The white-box instance that will be used for the calls to
|
||||
// WB_License_MaskedDecrypt().
|
||||
//
|
||||
// mode (in) : The AES decryption mode that will be used for the calls to
|
||||
// WB_License_MaskedDecrypt().
|
||||
//
|
||||
// key_id (in) : The key id that will be used for the call to
|
||||
// WB_License_MaskedDecrypt(). The key id must match a key loaded in
|
||||
// |whitebox|.
|
||||
//
|
||||
// key_id_size (in) : The number of bytes in |key_id|.
|
||||
//
|
||||
// secret_string (out) : The output parameter used to return the secret string
|
||||
// that can be passed to WB_License_Unmask().
|
||||
//
|
||||
// secret_string_size (in/out) : As input, this contains the max number of
|
||||
// bytes that can be written to |secret_string|. As output,
|
||||
// |secret_string_size| is set to the required size on WB_RESULT_OK and
|
||||
// WB_RESULT_BUFFER_TOO_SMALL.
|
||||
//
|
||||
// Returns:
|
||||
// WB_RESULT_OK if |key_id| matches a key from the license response and the
|
||||
// secret string was written to |secret_string|.
|
||||
//
|
||||
// WB_RESULT_INVALID_PARAMETER if |whitebox| was null, if |mode| was invalid,
|
||||
// if |key_id| was null, if |key_id_size| was zero, if |secret_string| was
|
||||
// null, or if |secret_string_size| was null.
|
||||
//
|
||||
// WB_RESULT_NO_SUCH_KEY if |key_id| did not match any keys in |whitebox|.
|
||||
//
|
||||
// WB_RESULT_WRONG_KEY_TYPE if |key_id| referred to a key in |whitebox| but the
|
||||
// key was not a content key.
|
||||
//
|
||||
// WB_RESULT_INSUFFICIENT_SECURITY_LEVEL if |key_id| refers to a key in
|
||||
// |whitebox| but the key's security level was neither SW_SECURE_CRYPTO nor
|
||||
// SW_SECURE_DECODE.
|
||||
//
|
||||
// WB_RESULT_BUFFER_TOO_SMALL if |secret_string_size| (as input) was less than
|
||||
// the required size.
|
||||
//
|
||||
// WB_RESULT_INVALID_STATE if |whitebox| had no keys.
|
||||
WB_Result WB_License_GetSecretString(const WB_License_Whitebox* whitebox,
|
||||
WB_CipherMode mode,
|
||||
const uint8_t* key_id,
|
||||
size_t key_id_size,
|
||||
uint8_t* secret_string,
|
||||
size_t* secret_string_size);
|
||||
|
||||
// Decrypts |input_data| and writes the plaintext to |output_data|.
|
||||
//
|
||||
// Args:
|
||||
// whitebox (in) : The white-box containing the keys needed to decrypt
|
||||
// |input_data|.
|
||||
//
|
||||
// mode (in) : The decryption algorithm that should be used to decrypt
|
||||
// |input_data|.
|
||||
//
|
||||
// key_id (in) : The identifier for which key in |whitebox| to use to decrypt
|
||||
// |input_data|.
|
||||
//
|
||||
// key_id_size (in) : The number of bytes in |key_id|.
|
||||
//
|
||||
// input_data (in) : The ciphertext.
|
||||
//
|
||||
// input_data_size (in) : The number of bytes in |input_data|. If |mode| is set
|
||||
// to CBC, then |input_data_size| must be a multiple of 16. Regardless of mode,
|
||||
// this |input_data_size| must be greater than zero.
|
||||
//
|
||||
// iv (in) : The iv.
|
||||
//
|
||||
// iv_size (in) : The number of bytes in |iv|. This must be 16.
|
||||
//
|
||||
// output_data (out) : The output parameter for the plaintext.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// Returns:
|
||||
// WB_RESULT_OK if |input_data| was successfully decrypted.
|
||||
//
|
||||
// WB_RESULT_INVALID_PARAMETER if |whitebox| was null, if |mode| was invalid,
|
||||
// if |key_id| was null, if |key_id_size| was zero, if |input_data| was null,
|
||||
// if |input_data_size| was invalid, if |iv| was null, if |iv_size| was
|
||||
// invalid, if |output_data| was null, or if |output_data_size| was null.
|
||||
//
|
||||
// WB_RESULT_NO_SUCH_KEY if |key_id| matches no key in |whitebox|.
|
||||
//
|
||||
// WB_RESULT_WRONG_KEY_TYPE if |key_id| referred to a key in |whitebox| but
|
||||
// the key was not a content key.
|
||||
//
|
||||
// WB_RESULT_INSUFFICIENT_SECURITY_LEVEL if |key_id| referred to a key in
|
||||
// |whitebox| but the key's security level was not SW_SECURE_CRYPTO.
|
||||
//
|
||||
// WB_RESULT_INVALID_STATE if |whitebox| had no keys.
|
||||
//
|
||||
// WB_RESULT_BUFFER_TOO_SMALL if |output_data_size| (as input) was less than
|
||||
// the required size.
|
||||
WB_Result WB_License_Decrypt(const WB_License_Whitebox* whitebox,
|
||||
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);
|
||||
|
||||
// Decrypts |input_data| and write the obfuscated plaintext to
|
||||
// |masked_output_data|. The obfuscated plaintext can be deobfuscated using
|
||||
// WB_License_GetSecretString() and WB_License_Unmask().
|
||||
//
|
||||
// Args:
|
||||
// whitebox (in) : The white-box containing the keys needed to decrypt
|
||||
// |input_data|.
|
||||
//
|
||||
// mode (in) : The decryption algorithm that should be used to decrypt
|
||||
// |input_data|.
|
||||
//
|
||||
// key_id (in) : The identifier for which key in |whitebox| to use to decrypt
|
||||
// |input_data|.
|
||||
//
|
||||
// key_id_size (in) : The number of bytes in |key_id|.
|
||||
//
|
||||
// input_data (in) : The ciphertext.
|
||||
//
|
||||
// input_data_size (in) : The number of bytes in |input_data|. If |mode| is set
|
||||
// to CBC, then |input_data_size| must be a multiple of 16. Regardless of mode,
|
||||
// this |input_data_size| must be greater than zero.
|
||||
//
|
||||
// iv (in) : The iv.
|
||||
//
|
||||
// iv_size (in) : The number of bytes in |iv|. This must be 16.
|
||||
//
|
||||
// masked_output_data (out) : The output parameter for the obfuscated
|
||||
// plaintext.
|
||||
//
|
||||
// masked_output_data_size (in/out) : As input, this contains the max number of
|
||||
// bytes that can be written to |masked_output_data|. As output,
|
||||
// |masked_output_data_size| is set to the required size on WB_RESULT_OK and
|
||||
// WB_RESULT_BUFFER_TOO_SMALL.
|
||||
//
|
||||
// Returns:
|
||||
// WB_RESULT_OK if |input_data| was successfully decrypted.
|
||||
//
|
||||
// WB_RESULT_INVALID_PARAMETER if |whitebox| was null, if |mode| was invalid,
|
||||
// if |key_id| was null, if |key_id_size| was zero, if |input_data| was null,
|
||||
// if |input_data_size| was invalid, if |iv| was null, if |iv_size| was
|
||||
// invalid, if |output_data| was null, or if |output_data_size| was null.
|
||||
//
|
||||
// WB_RESULT_NO_SUCH_KEY if |key_id| matches no key in |whitebox|.
|
||||
//
|
||||
// WB_RESULT_WRONG_KEY_TYPE if |key_id| referred to a key in |whitebox| but the
|
||||
// key was not a content key.
|
||||
//
|
||||
// WB_RESULT_INSUFFICIENT_SECURITY_LEVEL if |key_id| referred to a key in
|
||||
// |whitebox| but the key's security level was neither SW_SECURE_CRYPTO nor
|
||||
// SW_SECURE_DECODE.
|
||||
//
|
||||
// WB_RESULT_INVALID_STATE if |whitebox| had no keys.
|
||||
//
|
||||
// WB_RESULT_BUFFER_TOO_SMALL if |masked_output_data_size| (as input) was less
|
||||
// than the required size.
|
||||
WB_Result WB_License_MaskedDecrypt(const WB_License_Whitebox* whitebox,
|
||||
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);
|
||||
|
||||
// Unmasks the data in |buffer| using |secret_string|. |buffer| is operated on
|
||||
// in-place.
|
||||
//
|
||||
// Args:
|
||||
// secret_string (in) : The "key" used to unmask the data in |buffer|.
|
||||
//
|
||||
// secret_string_size (in) : The number of bytes in |secret_string|.
|
||||
//
|
||||
// buffer (in/out) : As input, this is the masked data. As output, this is the
|
||||
// unmasked data. The number of bytes in the masked data will be equal to the
|
||||
// number of bytes in the unmasked data.
|
||||
//
|
||||
// buffer_size (in) : The number of bytes in |buffer|.
|
||||
void WB_License_Unmask(const uint8_t* secret_string,
|
||||
size_t secret_string_size,
|
||||
uint8_t* buffer,
|
||||
size_t buffer_size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // WHITEBOX_API_LICENSE_WHITEBOX_H_
|
||||
57
api/license_whitebox_create_test.cc
Normal file
57
api/license_whitebox_create_test.cc
Normal file
@@ -0,0 +1,57 @@
|
||||
// Copyright 2020 Google LLC. All Rights Reserved.
|
||||
|
||||
#include "api/license_whitebox.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "api/test_data.h"
|
||||
#include "testing/include/gtest/gtest.h"
|
||||
|
||||
namespace {
|
||||
|
||||
class LicenseWhiteboxCreateTest : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
init_data_ = GetLicenseInitData();
|
||||
ASSERT_GT(init_data_.size(), 0u);
|
||||
}
|
||||
|
||||
void TearDown() override { WB_License_Delete(whitebox_); }
|
||||
|
||||
WB_License_Whitebox* whitebox_ = nullptr;
|
||||
std::vector<uint8_t> init_data_;
|
||||
};
|
||||
|
||||
TEST_F(LicenseWhiteboxCreateTest, Succeeds) {
|
||||
ASSERT_EQ(WB_License_Create(init_data_.data(), init_data_.size(), &whitebox_),
|
||||
WB_RESULT_OK);
|
||||
ASSERT_TRUE(whitebox_);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxCreateTest, InvalidParameterForNullInitData) {
|
||||
ASSERT_EQ(WB_License_Create(nullptr, init_data_.size(), &whitebox_),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
ASSERT_FALSE(whitebox_);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxCreateTest, InvalidParameterForZeroInitDataSize) {
|
||||
ASSERT_EQ(WB_License_Create(init_data_.data(), 0, &whitebox_),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
ASSERT_FALSE(whitebox_);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxCreateTest, InvalidParameterForInvalidInitData) {
|
||||
const std::vector<uint8_t> invalid_init_data = GetInvalidLicenseInitData();
|
||||
|
||||
ASSERT_EQ(WB_License_Create(invalid_init_data.data(),
|
||||
invalid_init_data.size(), &whitebox_),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
ASSERT_FALSE(whitebox_);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxCreateTest, InvalidParameterForNullWhitebox) {
|
||||
ASSERT_EQ(WB_License_Create(init_data_.data(), init_data_.size(), nullptr),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
355
api/license_whitebox_decrypt_test.cc
Normal file
355
api/license_whitebox_decrypt_test.cc
Normal file
@@ -0,0 +1,355 @@
|
||||
// Copyright 2020 Google LLC. All Rights Reserved.
|
||||
|
||||
#include "api/license_whitebox.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "api/test_data.h"
|
||||
#include "testing/include/gtest/gtest.h"
|
||||
|
||||
namespace {
|
||||
|
||||
// All decrypt tests require a valid (initialized) white-box. However, not all
|
||||
// tests need to load a license, so that is left out of SetUp().
|
||||
class LicenseWhiteboxDecryptTest : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
// All of our tests here require a valid whitebox.
|
||||
std::vector<uint8_t> init_data = GetLicenseInitData();
|
||||
ASSERT_GT(init_data.size(), 0u);
|
||||
ASSERT_EQ(WB_License_Create(init_data.data(), init_data.size(), &whitebox_),
|
||||
WB_RESULT_OK);
|
||||
ASSERT_TRUE(whitebox_);
|
||||
}
|
||||
|
||||
void TearDown() override { WB_License_Delete(whitebox_); }
|
||||
|
||||
void LoadLicense() {
|
||||
// TODO: Load the license here. It would be nice if we could do it in
|
||||
// SetUp(), but since we need to support the WB_RESULT_INVALID_STATE test
|
||||
// case, we need a way to not load a license.
|
||||
}
|
||||
|
||||
WB_License_Whitebox* whitebox_ = nullptr;
|
||||
|
||||
// TODO(vaage): Replace key ids with the key ids in the license. Since we
|
||||
// don't have a license right now, any id will do.
|
||||
const std::vector<uint8_t> crypto_key_id_ = {0xAA, 0xBB, 0xAA, 0xBB};
|
||||
const std::vector<uint8_t> decode_key_id_ = {0xAA, 0xBB, 0xAA, 0xBB};
|
||||
const std::vector<uint8_t> renewal_key_id_ = {0xAA, 0xBB, 0xAA, 0xBB};
|
||||
|
||||
// TODO(vaage): Replace with golden ciphertext that will match the golden
|
||||
// plaintext.
|
||||
const std::vector<uint8_t> ciphertext_ = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
const std::vector<uint8_t> iv_ = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
// TODO(vaage): Replace with golden plaintext that will match the golden
|
||||
// ciphertext.
|
||||
const std::vector<uint8_t> plaintext_ = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
};
|
||||
|
||||
TEST_F(LicenseWhiteboxDecryptTest, Success) {
|
||||
// TODO(vaage): This test will fail for now because it will attempt to look-up
|
||||
// the key before attempting to decrypt anything. Since |plaintext_| and
|
||||
// |ciphertext_| are not even set correctly, this wouldn't pass even if it had
|
||||
// a license.
|
||||
GTEST_SKIP();
|
||||
|
||||
LoadLicense();
|
||||
|
||||
size_t plaintext_size = ciphertext_.size();
|
||||
std::vector<uint8_t> plaintext(plaintext_size);
|
||||
|
||||
ASSERT_EQ(WB_License_Decrypt(
|
||||
whitebox_, WB_CIPHER_MODE_CBC, crypto_key_id_.data(),
|
||||
crypto_key_id_.size(), ciphertext_.data(), ciphertext_.size(),
|
||||
iv_.data(), iv_.size(), plaintext.data(), &plaintext_size),
|
||||
WB_RESULT_OK);
|
||||
|
||||
// Since we are not using any padding, the ciphertext and plaintext should be
|
||||
// the same size. However, in the case that something went wrong, resize the
|
||||
// plaintext so that ASSERT_EQ will operate on the correct size.
|
||||
plaintext.resize(plaintext_size);
|
||||
|
||||
ASSERT_EQ(plaintext, plaintext_);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForNullWhitebox) {
|
||||
LoadLicense();
|
||||
|
||||
size_t plaintext_size = ciphertext_.size();
|
||||
std::vector<uint8_t> plaintext(plaintext_size);
|
||||
|
||||
ASSERT_EQ(WB_License_Decrypt(
|
||||
nullptr, WB_CIPHER_MODE_CBC, crypto_key_id_.data(),
|
||||
crypto_key_id_.size(), ciphertext_.data(), ciphertext_.size(),
|
||||
iv_.data(), iv_.size(), plaintext.data(), &plaintext_size),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForInvalidCipherMode) {
|
||||
// TODO(vaage): This test will fail for now because it will attempt to
|
||||
// look-up the key before attempting to decrypt anything.
|
||||
GTEST_SKIP();
|
||||
|
||||
LoadLicense();
|
||||
|
||||
// In order to trick the compiler into letting us pass an invalid enum value
|
||||
// to WB__License_Decrypt(), we need to cast it. If we don't do this, the
|
||||
// compiler tries to save us.
|
||||
const WB_CipherMode invalid_mode = static_cast<WB_CipherMode>(0xFF);
|
||||
|
||||
size_t plaintext_size = ciphertext_.size();
|
||||
std::vector<uint8_t> plaintext(plaintext_size);
|
||||
|
||||
ASSERT_EQ(WB_License_Decrypt(whitebox_, invalid_mode, crypto_key_id_.data(),
|
||||
crypto_key_id_.size(), ciphertext_.data(),
|
||||
ciphertext_.size(), iv_.data(), iv_.size(),
|
||||
plaintext.data(), &plaintext_size),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForNullKeyId) {
|
||||
LoadLicense();
|
||||
|
||||
size_t plaintext_size = ciphertext_.size();
|
||||
std::vector<uint8_t> plaintext(plaintext_size);
|
||||
|
||||
ASSERT_EQ(WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC, nullptr,
|
||||
crypto_key_id_.size(), ciphertext_.data(),
|
||||
ciphertext_.size(), iv_.data(), iv_.size(),
|
||||
plaintext.data(), &plaintext_size),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForNullZeroKeyIdSize) {
|
||||
LoadLicense();
|
||||
|
||||
size_t plaintext_size = ciphertext_.size();
|
||||
std::vector<uint8_t> plaintext(plaintext_size);
|
||||
|
||||
ASSERT_EQ(
|
||||
WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC, crypto_key_id_.data(),
|
||||
0, ciphertext_.data(), ciphertext_.size(), iv_.data(),
|
||||
iv_.size(), plaintext.data(), &plaintext_size),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForNullInputData) {
|
||||
// TODO(vaage): This test will fail for now because it will attempt to
|
||||
// look-up the key before attempting to decrypt anything.
|
||||
GTEST_SKIP();
|
||||
|
||||
LoadLicense();
|
||||
|
||||
size_t plaintext_size = ciphertext_.size();
|
||||
std::vector<uint8_t> plaintext(plaintext_size);
|
||||
|
||||
ASSERT_EQ(WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC,
|
||||
crypto_key_id_.data(), crypto_key_id_.size(),
|
||||
nullptr, ciphertext_.size(), iv_.data(),
|
||||
iv_.size(), plaintext.data(), &plaintext_size),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
// AES CBC requires that the input be block aligned (multiple of 16). CTR does
|
||||
// not care. Size zero input is not considered invalid input.
|
||||
TEST_F(LicenseWhiteboxDecryptTest,
|
||||
InvalidParameterForCBCAndInvalidInputDataSize) {
|
||||
// TODO(vaage): This test will fail for now because it will attempt to
|
||||
// look-up the key before attempting to decrypt anything.
|
||||
GTEST_SKIP();
|
||||
|
||||
LoadLicense();
|
||||
|
||||
size_t plaintext_size = ciphertext_.size();
|
||||
std::vector<uint8_t> plaintext(plaintext_size);
|
||||
|
||||
ASSERT_EQ(WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC,
|
||||
crypto_key_id_.data(), crypto_key_id_.size(),
|
||||
ciphertext_.data(), 14, iv_.data(), iv_.size(),
|
||||
plaintext.data(), &plaintext_size),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForNullIV) {
|
||||
// TODO(vaage): This test will fail for now because it will attempt to
|
||||
// look-up the key before attempting to decrypt anything.
|
||||
GTEST_SKIP();
|
||||
|
||||
LoadLicense();
|
||||
|
||||
size_t plaintext_size = ciphertext_.size();
|
||||
std::vector<uint8_t> plaintext(plaintext_size);
|
||||
|
||||
ASSERT_EQ(WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC,
|
||||
crypto_key_id_.data(), crypto_key_id_.size(),
|
||||
ciphertext_.data(), ciphertext_.size(), nullptr,
|
||||
iv_.size(), plaintext.data(), &plaintext_size),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
// IV size should be 16. Any number other than 16 should fail.
|
||||
TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForInvalidIVSize) {
|
||||
// TODO(vaage): This test will fail for now because it will attempt to
|
||||
// look-up the key before attempting to decrypt anything.
|
||||
GTEST_SKIP();
|
||||
|
||||
LoadLicense();
|
||||
|
||||
size_t plaintext_size = ciphertext_.size();
|
||||
std::vector<uint8_t> plaintext(plaintext_size);
|
||||
|
||||
ASSERT_EQ(WB_License_Decrypt(
|
||||
whitebox_, WB_CIPHER_MODE_CBC, crypto_key_id_.data(),
|
||||
crypto_key_id_.size(), ciphertext_.data(), ciphertext_.size(),
|
||||
iv_.data(), 9, plaintext.data(), &plaintext_size),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForNullOutput) {
|
||||
// TODO(vaage): This test will fail for now because it will attempt to
|
||||
// look-up the key before attempting to decrypt anything.
|
||||
GTEST_SKIP();
|
||||
|
||||
LoadLicense();
|
||||
|
||||
size_t plaintext_size = ciphertext_.size();
|
||||
|
||||
ASSERT_EQ(WB_License_Decrypt(
|
||||
whitebox_, WB_CIPHER_MODE_CBC, crypto_key_id_.data(),
|
||||
crypto_key_id_.size(), ciphertext_.data(), ciphertext_.size(),
|
||||
iv_.data(), iv_.size(), nullptr, &plaintext_size),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForNullOutputSize) {
|
||||
// TODO(vaage): This test will fail for now because it will attempt to
|
||||
// look-up the key before attempting to decrypt anything.
|
||||
GTEST_SKIP();
|
||||
|
||||
LoadLicense();
|
||||
|
||||
size_t plaintext_size = ciphertext_.size();
|
||||
std::vector<uint8_t> plaintext(plaintext_size);
|
||||
|
||||
ASSERT_EQ(WB_License_Decrypt(
|
||||
whitebox_, WB_CIPHER_MODE_CBC, crypto_key_id_.data(),
|
||||
crypto_key_id_.size(), ciphertext_.data(), ciphertext_.size(),
|
||||
iv_.data(), iv_.size(), plaintext.data(), nullptr),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxDecryptTest, NoSuchKey) {
|
||||
// TODO(vaage): This test will fail for now because it will attempt to
|
||||
// look-up the key and see that there are no keys, which will result in
|
||||
// WB_RESULT_INVALID_STATE.
|
||||
GTEST_SKIP();
|
||||
|
||||
LoadLicense();
|
||||
|
||||
// For our fake key id, it is uncommon for key ids to be short. So using a one
|
||||
// byte key id should safe as they are allowed, just not likely to appear in
|
||||
// any test license.
|
||||
std::vector<uint8_t> not_a_key_id = {0xFF};
|
||||
|
||||
size_t plaintext_size = ciphertext_.size();
|
||||
std::vector<uint8_t> plaintext(plaintext_size);
|
||||
|
||||
ASSERT_EQ(WB_License_Decrypt(
|
||||
whitebox_, WB_CIPHER_MODE_CBC, not_a_key_id.data(),
|
||||
not_a_key_id.size(), ciphertext_.data(), ciphertext_.size(),
|
||||
iv_.data(), iv_.size(), plaintext.data(), &plaintext_size),
|
||||
WB_RESULT_NO_SUCH_KEY);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxDecryptTest, WrongKeyType) {
|
||||
// TODO(vaage): This test will fail for now because it will attempt to
|
||||
// look-up the key and see that there are no keys, which will result in
|
||||
// WB_RESULT_INVALID_STATE.
|
||||
GTEST_SKIP();
|
||||
|
||||
LoadLicense();
|
||||
|
||||
size_t plaintext_size = ciphertext_.size();
|
||||
std::vector<uint8_t> plaintext(plaintext_size);
|
||||
|
||||
ASSERT_EQ(WB_License_Decrypt(
|
||||
whitebox_, WB_CIPHER_MODE_CBC, renewal_key_id_.data(),
|
||||
renewal_key_id_.size(), ciphertext_.data(), ciphertext_.size(),
|
||||
iv_.data(), iv_.size(), plaintext.data(), &plaintext_size),
|
||||
WB_RESULT_WRONG_KEY_TYPE);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxDecryptTest, InsufficientSecurityLevel) {
|
||||
// TODO(vaage): This test will fail for now because it will attempt to
|
||||
// look-up the key and see that there are no keys, which will result in
|
||||
// WB_RESULT_INVALID_STATE.
|
||||
GTEST_SKIP();
|
||||
|
||||
LoadLicense();
|
||||
|
||||
size_t plaintext_size = ciphertext_.size();
|
||||
std::vector<uint8_t> plaintext(plaintext_size);
|
||||
|
||||
// Use the software decode key as they are limited to
|
||||
// WB_License_MaskedDecrypt().
|
||||
ASSERT_EQ(WB_License_Decrypt(
|
||||
whitebox_, WB_CIPHER_MODE_CBC, decode_key_id_.data(),
|
||||
decode_key_id_.size(), ciphertext_.data(), ciphertext_.size(),
|
||||
iv_.data(), iv_.size(), plaintext.data(), &plaintext_size),
|
||||
WB_RESULT_INSUFFICIENT_SECURITY_LEVEL);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxDecryptTest, InvalidState) {
|
||||
// Unlike the other tests, we do not call LoadLicense() as the criteria for
|
||||
// WB_RESULT_INVALID_STATE is that no key can be found and keys are provided
|
||||
// via a license.
|
||||
|
||||
size_t plaintext_size = ciphertext_.size();
|
||||
std::vector<uint8_t> plaintext(plaintext_size);
|
||||
|
||||
ASSERT_EQ(WB_License_Decrypt(
|
||||
whitebox_, WB_CIPHER_MODE_CBC, crypto_key_id_.data(),
|
||||
crypto_key_id_.size(), ciphertext_.data(), ciphertext_.size(),
|
||||
iv_.data(), iv_.size(), plaintext.data(), &plaintext_size),
|
||||
WB_RESULT_INVALID_STATE);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxDecryptTest, BufferTooSmall) {
|
||||
// TODO(vaage): This test will fail for now because it will attempt to
|
||||
// look-up the key before attempting to decrypt anything.
|
||||
GTEST_SKIP();
|
||||
|
||||
// Our ciphertext will be large enough that we should not need to worry about
|
||||
// using a constant here.
|
||||
size_t plaintext_size = 8;
|
||||
std::vector<uint8_t> plaintext(plaintext_size);
|
||||
|
||||
ASSERT_EQ(WB_License_Decrypt(
|
||||
whitebox_, WB_CIPHER_MODE_CBC, crypto_key_id_.data(),
|
||||
crypto_key_id_.size(), ciphertext_.data(), ciphertext_.size(),
|
||||
iv_.data(), iv_.size(), plaintext.data(), &plaintext_size),
|
||||
WB_RESULT_BUFFER_TOO_SMALL);
|
||||
|
||||
// We don't use padding so the reported plaintext size should be the same as
|
||||
// the cipher text size.
|
||||
ASSERT_EQ(plaintext_size, ciphertext_.size());
|
||||
}
|
||||
} // namespace
|
||||
180
api/license_whitebox_get_secret_string_test.cc
Normal file
180
api/license_whitebox_get_secret_string_test.cc
Normal file
@@ -0,0 +1,180 @@
|
||||
// Copyright 2020 Google LLC. All Rights Reserved.
|
||||
|
||||
#include "api/license_whitebox.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "api/test_data.h"
|
||||
#include "testing/include/gtest/gtest.h"
|
||||
|
||||
namespace {
|
||||
|
||||
class LicenseWhiteboxGetSecretStringTest : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
// The secret string size is implementation specific, so this number just
|
||||
// needs to be reasonably large to accommodate different implementations.
|
||||
secret_string_size_ = 256;
|
||||
secret_string_.resize(secret_string_size_);
|
||||
|
||||
const std::vector<uint8_t> init_data = GetLicenseInitData();
|
||||
|
||||
ASSERT_EQ(WB_License_Create(init_data.data(), init_data.size(), &whitebox_),
|
||||
WB_RESULT_OK);
|
||||
ASSERT_TRUE(whitebox_);
|
||||
}
|
||||
|
||||
void TearDown() override { WB_License_Delete(whitebox_); }
|
||||
|
||||
void LoadLicense() {
|
||||
// TODO: We will need to load a license for most tests as they will require
|
||||
// a key id look-up and we get keys from licenses.
|
||||
}
|
||||
|
||||
// TODO: These needs to be replaced with the actual keys in the loaded
|
||||
// license.
|
||||
const std::vector<uint8_t> crypto_key_id_ = {1, 2, 3};
|
||||
const std::vector<uint8_t> hardware_key_id_ = {2, 3, 4};
|
||||
const std::vector<uint8_t> bad_key_id_ = {3, 4, 5};
|
||||
const std::vector<uint8_t> non_content_key_id_ = {4, 5, 6};
|
||||
|
||||
size_t secret_string_size_;
|
||||
std::vector<uint8_t> secret_string_;
|
||||
|
||||
WB_License_Whitebox* whitebox_;
|
||||
};
|
||||
|
||||
TEST_F(LicenseWhiteboxGetSecretStringTest, Success) {
|
||||
// TODO: This test needs to be skipped right now because we can't load a
|
||||
// license and we need a license to have key ids.
|
||||
GTEST_SKIP();
|
||||
|
||||
LoadLicense();
|
||||
ASSERT_EQ(
|
||||
WB_License_GetSecretString(whitebox_, WB_CIPHER_MODE_CBC,
|
||||
crypto_key_id_.data(), crypto_key_id_.size(),
|
||||
secret_string_.data(), &secret_string_size_),
|
||||
WB_RESULT_OK);
|
||||
secret_string_.resize(secret_string_size_);
|
||||
ASSERT_GT(secret_string_.size(), 0);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxGetSecretStringTest, InvalidParameterForNullWhitebox) {
|
||||
LoadLicense();
|
||||
ASSERT_EQ(
|
||||
WB_License_GetSecretString(nullptr, WB_CIPHER_MODE_CBC,
|
||||
crypto_key_id_.data(), crypto_key_id_.size(),
|
||||
secret_string_.data(), &secret_string_size_),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxGetSecretStringTest, InvalidParameterForNullKeyId) {
|
||||
LoadLicense();
|
||||
ASSERT_EQ(WB_License_GetSecretString(
|
||||
whitebox_, WB_CIPHER_MODE_CBC, nullptr, crypto_key_id_.size(),
|
||||
secret_string_.data(), &secret_string_size_),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxGetSecretStringTest, InvalidParameterForZeroKeyIdSize) {
|
||||
LoadLicense();
|
||||
ASSERT_EQ(WB_License_GetSecretString(
|
||||
whitebox_, WB_CIPHER_MODE_CBC, crypto_key_id_.data(), 0,
|
||||
secret_string_.data(), &secret_string_size_),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxGetSecretStringTest,
|
||||
InvalidParameterForNullSecretString) {
|
||||
LoadLicense();
|
||||
ASSERT_EQ(WB_License_GetSecretString(
|
||||
whitebox_, WB_CIPHER_MODE_CBC, crypto_key_id_.data(),
|
||||
crypto_key_id_.size(), nullptr, &secret_string_size_),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxGetSecretStringTest,
|
||||
InvalidParameterForNullSecretStringSize) {
|
||||
LoadLicense();
|
||||
ASSERT_EQ(WB_License_GetSecretString(
|
||||
whitebox_, WB_CIPHER_MODE_CBC, crypto_key_id_.data(),
|
||||
crypto_key_id_.size(), secret_string_.data(), nullptr),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxGetSecretStringTest, NoSuchKey) {
|
||||
// TODO: This test needs to be skipped right now because we can't load a
|
||||
// license and we need a license to have key ids.
|
||||
GTEST_SKIP();
|
||||
|
||||
LoadLicense();
|
||||
ASSERT_EQ(
|
||||
WB_License_GetSecretString(whitebox_, WB_CIPHER_MODE_CBC,
|
||||
bad_key_id_.data(), bad_key_id_.size(),
|
||||
secret_string_.data(), &secret_string_size_),
|
||||
WB_RESULT_NO_SUCH_KEY);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxGetSecretStringTest, WrongKeyType) {
|
||||
// TODO: This test needs to be skipped right now because we can't load a
|
||||
// license and we need a license to have key ids.
|
||||
GTEST_SKIP();
|
||||
|
||||
LoadLicense();
|
||||
ASSERT_EQ(WB_License_GetSecretString(
|
||||
whitebox_, WB_CIPHER_MODE_CBC, non_content_key_id_.data(),
|
||||
non_content_key_id_.size(), secret_string_.data(),
|
||||
&secret_string_size_),
|
||||
WB_RESULT_NO_SUCH_KEY);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxGetSecretStringTest, InsufficientSecurityLevel) {
|
||||
// TODO: This test needs to be skipped right now because we can't load a
|
||||
// license and we need a license to have key ids.
|
||||
GTEST_SKIP();
|
||||
|
||||
LoadLicense();
|
||||
ASSERT_EQ(
|
||||
WB_License_GetSecretString(
|
||||
whitebox_, WB_CIPHER_MODE_CBC, hardware_key_id_.data(),
|
||||
hardware_key_id_.size(), secret_string_.data(), &secret_string_size_),
|
||||
WB_RESULT_NO_SUCH_KEY);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxGetSecretStringTest, BufferTooSmall) {
|
||||
// TODO: This test needs to be skipped right now because we can't load a
|
||||
// license and we need a license to have key ids.
|
||||
GTEST_SKIP();
|
||||
|
||||
LoadLicense();
|
||||
|
||||
// Since the secret string is implementation specific, we don't want to make
|
||||
// any big assumptions about what would be too small. Using 1 is fairly safe
|
||||
// as it would not introduce enough variation to be effective. We avoid using
|
||||
// zero here so that we can verify that we are not just checking for zero.
|
||||
secret_string_size_ = 1;
|
||||
ASSERT_EQ(
|
||||
WB_License_GetSecretString(whitebox_, WB_CIPHER_MODE_CBC,
|
||||
crypto_key_id_.data(), crypto_key_id_.size(),
|
||||
secret_string_.data(), &secret_string_size_),
|
||||
WB_RESULT_BUFFER_TOO_SMALL);
|
||||
|
||||
// Make sure that the output included the required size. We don't know what
|
||||
// it is, so we rely on checking that it is just bigger than the "too small"
|
||||
// size.
|
||||
ASSERT_GT(secret_string_size_, 1);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxGetSecretStringTest, InvalidState) {
|
||||
// Purposely do not load a license so that we won't have any keys, causing
|
||||
// use to be in an invalid state.
|
||||
|
||||
ASSERT_EQ(
|
||||
WB_License_GetSecretString(whitebox_, WB_CIPHER_MODE_CBC,
|
||||
crypto_key_id_.data(), crypto_key_id_.size(),
|
||||
secret_string_.data(), &secret_string_size_),
|
||||
WB_RESULT_INVALID_STATE);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
149
api/license_whitebox_sign_renewal_request_test.cc
Normal file
149
api/license_whitebox_sign_renewal_request_test.cc
Normal file
@@ -0,0 +1,149 @@
|
||||
// Copyright 2020 Google LLC. All Rights Reserved.
|
||||
|
||||
#include "api/license_whitebox.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "api/test_data.h"
|
||||
#include "testing/include/gtest/gtest.h"
|
||||
|
||||
namespace {
|
||||
|
||||
const uint8_t kDummyMessage[] = {
|
||||
0x1e, 0x70, 0xbd, 0xeb, 0x24, 0xf2, 0x9d, 0x05, 0xc5, 0xb5,
|
||||
0xf4, 0xca, 0xe6, 0x1d, 0x01, 0x97, 0x29, 0xf4, 0xe0, 0x7c,
|
||||
0xfd, 0xcc, 0x97, 0x8d, 0xc2, 0xbb, 0x2d, 0x9b, 0x6b, 0x45,
|
||||
0x06, 0xbd, 0x2c, 0x66, 0x10, 0x42, 0x73, 0x8d, 0x88, 0x9b,
|
||||
0x18, 0xcc, 0xcb, 0x7e, 0x43, 0x23, 0x06, 0xe9, 0x8f, 0x8f,
|
||||
};
|
||||
const size_t kDummyMessageSize = sizeof(kDummyMessage);
|
||||
|
||||
// Size of a license signature. This must be big enough to hold the signature
|
||||
// returned by WB_License_SignRenewalRequest().
|
||||
constexpr size_t kSignatureSize = 256;
|
||||
|
||||
// These tests assume that WB_License_Create() and
|
||||
// WB_License_ProcessLicenseResponse() work as all tests will require a valid
|
||||
// white-box instance with a valid license already loaded.
|
||||
class LicenseWhiteboxSignRenewalRequestTest : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
const std::vector<uint8_t> init_data_ = GetLicenseInitData();
|
||||
ASSERT_EQ(
|
||||
WB_License_Create(init_data_.data(), init_data_.size(), &whitebox_),
|
||||
WB_RESULT_OK);
|
||||
}
|
||||
|
||||
void TearDown() override { WB_License_Delete(whitebox_); }
|
||||
|
||||
void LoadLicense() {
|
||||
// TODO: Load the license here. It would be nice if we could do it in
|
||||
// SetUp(), but since we need to support the WB_RESULT_INVALID_STATE test
|
||||
// case, we need a way to not load a license.
|
||||
}
|
||||
|
||||
WB_License_Whitebox* whitebox_ = nullptr;
|
||||
};
|
||||
|
||||
// TODO: Implement the success test case (ideally with a real renewal request).
|
||||
|
||||
TEST_F(LicenseWhiteboxSignRenewalRequestTest, InvalidParameterForNullWhitebox) {
|
||||
LoadLicense();
|
||||
|
||||
size_t signature_size = kSignatureSize;
|
||||
std::vector<uint8_t> signature(signature_size);
|
||||
|
||||
ASSERT_EQ(
|
||||
WB_License_SignRenewalRequest(nullptr, kDummyMessage, kDummyMessageSize,
|
||||
signature.data(), &signature_size),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxSignRenewalRequestTest, InvalidParameterForNullMessage) {
|
||||
LoadLicense();
|
||||
|
||||
size_t signature_size = kSignatureSize;
|
||||
std::vector<uint8_t> signature(signature_size);
|
||||
|
||||
ASSERT_EQ(WB_License_SignRenewalRequest(whitebox_, nullptr, kDummyMessageSize,
|
||||
signature.data(), &signature_size),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxSignRenewalRequestTest,
|
||||
InvalidParameterForZeroMessageSize) {
|
||||
LoadLicense();
|
||||
|
||||
size_t signature_size = kSignatureSize;
|
||||
std::vector<uint8_t> signature(signature_size);
|
||||
|
||||
ASSERT_EQ(WB_License_SignRenewalRequest(whitebox_, kDummyMessage, 0,
|
||||
signature.data(), &signature_size),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxSignRenewalRequestTest,
|
||||
InvalidParameterForNullSignature) {
|
||||
LoadLicense();
|
||||
|
||||
size_t signature_size = kSignatureSize;
|
||||
|
||||
ASSERT_EQ(
|
||||
WB_License_SignRenewalRequest(whitebox_, kDummyMessage, kDummyMessageSize,
|
||||
nullptr, &signature_size),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxSignRenewalRequestTest,
|
||||
InvalidParameterForNullSignatureSize) {
|
||||
LoadLicense();
|
||||
|
||||
size_t signature_size = kSignatureSize;
|
||||
std::vector<uint8_t> signature(signature_size);
|
||||
|
||||
ASSERT_EQ(
|
||||
WB_License_SignRenewalRequest(whitebox_, kDummyMessage, kDummyMessageSize,
|
||||
signature.data(), nullptr),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxSignRenewalRequestTest, BufferTooSmall) {
|
||||
// TODO: This test must be skipped as the "too small" check takes place after
|
||||
// we check for a key.
|
||||
GTEST_SKIP();
|
||||
|
||||
LoadLicense();
|
||||
|
||||
// We need the signature to be too small. While it would be possible to use
|
||||
// zero, using a non-zero value ensures that we are not combining "empty" and
|
||||
// "too small".
|
||||
size_t signature_size = 1;
|
||||
std::vector<uint8_t> signature(signature_size);
|
||||
|
||||
ASSERT_EQ(
|
||||
WB_License_SignRenewalRequest(whitebox_, kDummyMessage, kDummyMessageSize,
|
||||
signature.data(), &signature_size),
|
||||
WB_RESULT_BUFFER_TOO_SMALL);
|
||||
|
||||
// Since the API does not limit the signature size, we can't specify the
|
||||
// actual expected size, however, it should at least be greater than our "too
|
||||
// small" size.
|
||||
ASSERT_GT(signature_size, 1);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxSignRenewalRequestTest, InvalidState) {
|
||||
// Unlike the other tests, we do not call LoadLicense() because we need to
|
||||
// have no license loaded in order to have no renewal key, which is the
|
||||
// criteria WB_RESULT_INVALID_STATE.
|
||||
|
||||
size_t signature_size = kSignatureSize;
|
||||
std::vector<uint8_t> signature(signature_size);
|
||||
|
||||
ASSERT_EQ(
|
||||
WB_License_SignRenewalRequest(whitebox_, kDummyMessage, kDummyMessageSize,
|
||||
signature.data(), &signature_size),
|
||||
WB_RESULT_INVALID_STATE);
|
||||
}
|
||||
} // namespace
|
||||
257
api/license_whitebox_test.cc
Normal file
257
api/license_whitebox_test.cc
Normal file
@@ -0,0 +1,257 @@
|
||||
// Copyright 2020 Google LLC. All Rights Reserved.
|
||||
|
||||
#include "api/license_whitebox.h"
|
||||
|
||||
#include <ctime>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "api/license_test_helper.h"
|
||||
#include "api/test_data.h"
|
||||
#include "crypto_utils/rsa_key.h"
|
||||
#include "testing/include/gtest/gtest.h"
|
||||
|
||||
using RsaPublicKey = widevine::RsaPublicKey;
|
||||
|
||||
namespace {
|
||||
|
||||
const uint8_t kDummyMessage[] = {
|
||||
0x1e, 0x70, 0xbd, 0xeb, 0x24, 0xf2, 0x9d, 0x05, 0xc5, 0xb5,
|
||||
0xf4, 0xca, 0xe6, 0x1d, 0x01, 0x97, 0x29, 0xf4, 0xe0, 0x7c,
|
||||
0xfd, 0xcc, 0x97, 0x8d, 0xc2, 0xbb, 0x2d, 0x9b, 0x6b, 0x45,
|
||||
0x06, 0xbd, 0x2c, 0x66, 0x10, 0x42, 0x73, 0x8d, 0x88, 0x9b,
|
||||
0x18, 0xcc, 0xcb, 0x7e, 0x43, 0x23, 0x06, 0xe9, 0x8f, 0x8f,
|
||||
};
|
||||
const size_t kDummyMessageSize = sizeof(kDummyMessage);
|
||||
|
||||
// Size of a license signature. This must be big enough to hold the signature
|
||||
// returned by WB_License_SignLicenseRequest().
|
||||
constexpr size_t kSignatureSize = 256;
|
||||
|
||||
std::string AsString(const uint8_t* buffer, size_t buffer_size) {
|
||||
return std::string(reinterpret_cast<const char*>(buffer), buffer_size);
|
||||
}
|
||||
|
||||
// Used to test the rest of the whitebox API as this class creates |whitebox_|
|
||||
// during setup, so it is available for all tests. It also creates |public_key_|
|
||||
// which is the matching RSA public key that can be used to verify signing
|
||||
// operations inside the whitebox.
|
||||
class LicenseWhiteboxTest : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
const std::vector<uint8_t> init_data = GetLicenseInitData();
|
||||
ASSERT_EQ(WB_License_Create(init_data.data(), init_data.size(), &whitebox_),
|
||||
WB_RESULT_OK);
|
||||
|
||||
std::vector<uint8_t> public_key = GetMatchingLicensePublicKey();
|
||||
public_key_.reset(
|
||||
RsaPublicKey::Create(AsString(public_key.data(), public_key.size())));
|
||||
ASSERT_TRUE(public_key_);
|
||||
}
|
||||
|
||||
void TearDown() override { WB_License_Delete(whitebox_); }
|
||||
|
||||
// Public key as an usable object.
|
||||
std::unique_ptr<RsaPublicKey> public_key_;
|
||||
|
||||
WB_License_Whitebox* whitebox_;
|
||||
};
|
||||
|
||||
TEST_F(LicenseWhiteboxTest, SignAndVerifySucceedsWithRandomData) {
|
||||
uint8_t signature[kSignatureSize];
|
||||
size_t signature_size = kSignatureSize;
|
||||
ASSERT_EQ(
|
||||
WB_License_SignLicenseRequest(whitebox_, kDummyMessage, kDummyMessageSize,
|
||||
signature, &signature_size),
|
||||
WB_RESULT_OK);
|
||||
|
||||
// Verify the signature.
|
||||
ASSERT_TRUE(
|
||||
public_key_->VerifySignature(AsString(kDummyMessage, kDummyMessageSize),
|
||||
AsString(signature, signature_size)));
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxTest, SignAndVerifySucceedsWithLicenseRequest) {
|
||||
const std::string license_request = widevine::CreateLicenseRequest();
|
||||
|
||||
uint8_t signature[kSignatureSize];
|
||||
size_t signature_size = kSignatureSize;
|
||||
ASSERT_EQ(
|
||||
WB_License_SignLicenseRequest(
|
||||
whitebox_, reinterpret_cast<const uint8_t*>(license_request.data()),
|
||||
license_request.size(), signature, &signature_size),
|
||||
WB_RESULT_OK);
|
||||
|
||||
ASSERT_TRUE(public_key_->VerifySignature(
|
||||
license_request, AsString(signature, signature_size)));
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxTest, SignLicenseRequestFailsWithWhiteboxNull) {
|
||||
uint8_t signature[kSignatureSize];
|
||||
size_t signature_size = kSignatureSize;
|
||||
ASSERT_EQ(
|
||||
WB_License_SignLicenseRequest(nullptr, kDummyMessage, kDummyMessageSize,
|
||||
signature, &signature_size),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxTest, SignLicenseRequestFailsWithMessageNull) {
|
||||
uint8_t signature[kSignatureSize];
|
||||
size_t signature_size = kSignatureSize;
|
||||
ASSERT_EQ(WB_License_SignLicenseRequest(whitebox_, nullptr, kDummyMessageSize,
|
||||
signature, &signature_size),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxTest, SignLicenseRequestFailsWithMessageSizeZero) {
|
||||
uint8_t signature[kSignatureSize];
|
||||
size_t signature_size = kSignatureSize;
|
||||
ASSERT_EQ(WB_License_SignLicenseRequest(whitebox_, kDummyMessage, 0,
|
||||
signature, &signature_size),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxTest, SignLicenseRequestFailsWithSignatureNull) {
|
||||
size_t signature_size = kSignatureSize;
|
||||
ASSERT_EQ(
|
||||
WB_License_SignLicenseRequest(whitebox_, kDummyMessage, kDummyMessageSize,
|
||||
nullptr, &signature_size),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxTest, SignLicenseRequestFailsWithSignatureSizeNull) {
|
||||
uint8_t signature[kSignatureSize];
|
||||
ASSERT_EQ(
|
||||
WB_License_SignLicenseRequest(whitebox_, kDummyMessage, kDummyMessageSize,
|
||||
signature, nullptr),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxTest, SignLicenseRequestFailsWithSignatureSizeZero) {
|
||||
uint8_t signature[kSignatureSize];
|
||||
size_t signature_size = 0;
|
||||
ASSERT_EQ(
|
||||
WB_License_SignLicenseRequest(whitebox_, kDummyMessage, kDummyMessageSize,
|
||||
signature, &signature_size),
|
||||
WB_RESULT_BUFFER_TOO_SMALL);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxTest, SignLicenseRequestFailsWithSignatureTooSmall) {
|
||||
uint8_t signature[10];
|
||||
size_t signature_size = sizeof(signature);
|
||||
ASSERT_EQ(
|
||||
WB_License_SignLicenseRequest(whitebox_, kDummyMessage, kDummyMessageSize,
|
||||
signature, &signature_size),
|
||||
WB_RESULT_BUFFER_TOO_SMALL);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxTest, ProcessLicenseResponseFailsWithWhiteboxNull) {
|
||||
std::vector<uint8_t> message{1, 2, 3};
|
||||
std::vector<uint8_t> signature{4, 5, 6, 7};
|
||||
std::vector<uint8_t> session_key{1, 2, 3, 4, 5, 6, 7, 8};
|
||||
std::vector<uint8_t> license_request{10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
|
||||
ASSERT_EQ(WB_License_ProcessLicenseResponse(
|
||||
nullptr, message.data(), message.size(), signature.data(),
|
||||
signature.size(), session_key.data(), session_key.size(),
|
||||
license_request.data(), license_request.size()),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxTest, ProcessLicenseResponseFailsWithMessageNull) {
|
||||
std::vector<uint8_t> message{1, 2, 3};
|
||||
std::vector<uint8_t> signature{4, 5, 6, 7};
|
||||
std::vector<uint8_t> session_key{1, 2, 3, 4, 5, 6, 7, 8};
|
||||
std::vector<uint8_t> license_request{10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
|
||||
ASSERT_EQ(WB_License_ProcessLicenseResponse(
|
||||
whitebox_, nullptr, message.size(), signature.data(),
|
||||
signature.size(), session_key.data(), session_key.size(),
|
||||
license_request.data(), license_request.size()),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxTest, ProcessLicenseResponseFailsWithMessageSizeZero) {
|
||||
std::vector<uint8_t> message{/* empty */};
|
||||
std::vector<uint8_t> signature{4, 5, 6, 7};
|
||||
std::vector<uint8_t> session_key{1, 2, 3, 4, 5, 6, 7, 8};
|
||||
std::vector<uint8_t> license_request{10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
|
||||
ASSERT_EQ(WB_License_ProcessLicenseResponse(
|
||||
whitebox_, message.data(), message.size(), signature.data(),
|
||||
signature.size(), session_key.data(), session_key.size(),
|
||||
license_request.data(), license_request.size()),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxTest, ProcessLicenseResponseFailsWithSignatureNull) {
|
||||
std::vector<uint8_t> message{1, 2, 3};
|
||||
std::vector<uint8_t> signature{4, 5, 6, 7};
|
||||
std::vector<uint8_t> session_key{1, 2, 3, 4, 5, 6, 7, 8};
|
||||
std::vector<uint8_t> license_request{10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
|
||||
ASSERT_EQ(WB_License_ProcessLicenseResponse(
|
||||
whitebox_, nullptr, message.size(), nullptr, signature.size(),
|
||||
session_key.data(), session_key.size(), license_request.data(),
|
||||
license_request.size()),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxTest, ProcessLicenseResponseFailsWithSignatureSizeZero) {
|
||||
std::vector<uint8_t> message{1, 2, 3};
|
||||
std::vector<uint8_t> signature{/* empty */};
|
||||
std::vector<uint8_t> session_key{1, 2, 3, 4, 5, 6, 7, 8};
|
||||
std::vector<uint8_t> license_request{10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
|
||||
ASSERT_EQ(WB_License_ProcessLicenseResponse(
|
||||
whitebox_, message.data(), message.size(), signature.data(),
|
||||
signature.size(), session_key.data(), session_key.size(),
|
||||
license_request.data(), license_request.size()),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxTest, ProcessLicenseResponseFailsWithSessionKeyNull) {
|
||||
std::vector<uint8_t> message{1, 2, 3};
|
||||
std::vector<uint8_t> signature{4, 5, 6, 7};
|
||||
std::vector<uint8_t> session_key{1, 2, 3, 4, 5, 6, 7, 8};
|
||||
std::vector<uint8_t> license_request{10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
|
||||
ASSERT_EQ(WB_License_ProcessLicenseResponse(
|
||||
whitebox_, message.data(), message.size(), signature.data(),
|
||||
signature.size(), nullptr, session_key.size(),
|
||||
license_request.data(), license_request.size()),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxTest, ProcessLicenseResponseFailsWithSessionKeySizeZero) {
|
||||
std::vector<uint8_t> message{1, 2, 3};
|
||||
std::vector<uint8_t> signature{4, 5, 6, 7};
|
||||
std::vector<uint8_t> session_key{/* empty */};
|
||||
std::vector<uint8_t> license_request{10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
|
||||
ASSERT_EQ(WB_License_ProcessLicenseResponse(
|
||||
whitebox_, message.data(), message.size(), signature.data(),
|
||||
signature.size(), session_key.data(), session_key.size(),
|
||||
license_request.data(), license_request.size()),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxTest, ProcessLicenseResponseFailsWithRequestNull) {
|
||||
std::vector<uint8_t> message{1, 2, 3};
|
||||
std::vector<uint8_t> signature{4, 5, 6, 7};
|
||||
std::vector<uint8_t> session_key{1, 2, 3, 4, 5, 6, 7, 8};
|
||||
std::vector<uint8_t> license_request{10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
|
||||
ASSERT_EQ(WB_License_ProcessLicenseResponse(
|
||||
whitebox_, message.data(), message.size(), signature.data(),
|
||||
signature.size(), session_key.data(), session_key.size(),
|
||||
nullptr, license_request.size()),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxTest, ProcessLicenseResponseFailsWithRequestSizeZero) {
|
||||
std::vector<uint8_t> message{1, 2, 3};
|
||||
std::vector<uint8_t> signature{4, 5, 6, 7};
|
||||
std::vector<uint8_t> session_key{1, 2, 3, 4, 5, 6, 7, 8};
|
||||
std::vector<uint8_t> license_request{/* empty */};
|
||||
ASSERT_EQ(WB_License_ProcessLicenseResponse(
|
||||
whitebox_, message.data(), message.size(), signature.data(),
|
||||
signature.size(), session_key.data(), session_key.size(),
|
||||
license_request.data(), license_request.size()),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
142
api/license_whitebox_verify_renewal_response_test.cc
Normal file
142
api/license_whitebox_verify_renewal_response_test.cc
Normal file
@@ -0,0 +1,142 @@
|
||||
// Copyright 2020 Google LLC. All Rights Reserved.
|
||||
|
||||
#include "api/license_whitebox.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "api/test_data.h"
|
||||
#include "testing/include/gtest/gtest.h"
|
||||
|
||||
namespace {
|
||||
|
||||
// TODO: This needs to be the real response.
|
||||
const uint8_t kResponse[] = {
|
||||
0x1e, 0x70, 0xbd, 0xeb, 0x24, 0xf2, 0x9d, 0x05, 0xc5, 0xb5,
|
||||
0xf4, 0xca, 0xe6, 0x1d, 0x01, 0x97, 0x29, 0xf4, 0xe0, 0x7c,
|
||||
0xfd, 0xcc, 0x97, 0x8d, 0xc2, 0xbb, 0x2d, 0x9b, 0x6b, 0x45,
|
||||
0x06, 0xbd, 0x2c, 0x66, 0x10, 0x42, 0x73, 0x8d, 0x88, 0x9b,
|
||||
0x18, 0xcc, 0xcb, 0x7e, 0x43, 0x23, 0x06, 0xe9, 0x8f, 0x8f,
|
||||
};
|
||||
const size_t kResponseSize = sizeof(kResponse);
|
||||
|
||||
// TODO: This needs to be the actual signature.
|
||||
const uint8_t kSignature[] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
const uint8_t kSignatureSize = sizeof(kSignature);
|
||||
|
||||
// These tests assume that WB_License_Create() and
|
||||
// WB_License_ProcessLicenseResponse() work as all tests will require a valid
|
||||
// white-box instance with a valid license already loaded.
|
||||
class LicenseWhiteboxVerifyRenewalResponseTest : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
const std::vector<uint8_t> init_data_ = GetLicenseInitData();
|
||||
ASSERT_EQ(
|
||||
WB_License_Create(init_data_.data(), init_data_.size(), &whitebox_),
|
||||
WB_RESULT_OK);
|
||||
}
|
||||
|
||||
void TearDown() override { WB_License_Delete(whitebox_); }
|
||||
|
||||
void LoadLicense() {
|
||||
// TODO: Load the license here. It would be nice if we could do it in
|
||||
// SetUp(), but since we need to support the WB_RESULT_INVALID_STATE test
|
||||
// case, we need a way to not load a license.
|
||||
}
|
||||
|
||||
WB_License_Whitebox* whitebox_ = nullptr;
|
||||
};
|
||||
|
||||
// TODO: Implement the success test case.
|
||||
|
||||
TEST_F(LicenseWhiteboxVerifyRenewalResponseTest,
|
||||
InvalidParameterForNullWhitebox) {
|
||||
LoadLicense();
|
||||
|
||||
ASSERT_EQ(WB_License_VerifyRenewalResponse(nullptr, kResponse, kResponseSize,
|
||||
kSignature, kSignatureSize),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxVerifyRenewalResponseTest,
|
||||
InvalidParameterForNullMessage) {
|
||||
LoadLicense();
|
||||
|
||||
ASSERT_EQ(WB_License_VerifyRenewalResponse(whitebox_, nullptr, kResponseSize,
|
||||
kSignature, kSignatureSize),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxVerifyRenewalResponseTest,
|
||||
InvalidParameterForZeroMessageSize) {
|
||||
LoadLicense();
|
||||
|
||||
ASSERT_EQ(WB_License_VerifyRenewalResponse(whitebox_, kResponse, 0,
|
||||
kSignature, kSignatureSize),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxVerifyRenewalResponseTest,
|
||||
InvalidParameterForNullSignature) {
|
||||
LoadLicense();
|
||||
|
||||
ASSERT_EQ(WB_License_VerifyRenewalResponse(
|
||||
whitebox_, kResponse, kResponseSize, nullptr, kSignatureSize),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxVerifyRenewalResponseTest,
|
||||
InvalidParameterForInvalidSignatureSize) {
|
||||
// TODO: This test needs to be skipped for now as the check for signature size
|
||||
// is done after we compute the signature. This requires a valid license to
|
||||
// have been loaded.
|
||||
GTEST_SKIP();
|
||||
|
||||
// We need an invalid signature size, the easiest way to do this is to take
|
||||
// the expected signature size and trim a bit off the end.
|
||||
const size_t invalid_signature_size = kSignatureSize - 1;
|
||||
|
||||
LoadLicense();
|
||||
|
||||
ASSERT_EQ(
|
||||
WB_License_VerifyRenewalResponse(whitebox_, kResponse, kResponseSize,
|
||||
kSignature, invalid_signature_size),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxVerifyRenewalResponseTest,
|
||||
InvalidSignatureForInvalidSignature) {
|
||||
// TODO: This test needs to be skipped for now as comparing signatures
|
||||
// requires a key provided by a valid license.
|
||||
GTEST_SKIP();
|
||||
|
||||
// In order to create an invalid signature, copy the valid signature and flip
|
||||
// one byte. This will ensure that our invalid signature is always different
|
||||
// from the valid signature.
|
||||
std::vector<uint8_t> invalid_signature(kSignature,
|
||||
kSignature + kSignatureSize);
|
||||
invalid_signature[0] = ~invalid_signature[0];
|
||||
|
||||
LoadLicense();
|
||||
|
||||
ASSERT_EQ(WB_License_VerifyRenewalResponse(
|
||||
whitebox_, kResponse, kResponseSize, invalid_signature.data(),
|
||||
invalid_signature.size()),
|
||||
WB_RESULT_INVALID_SIGNATURE);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxVerifyRenewalResponseTest, InvalidStateForNoLicense) {
|
||||
// Unlike the other tests, we do not call LoadLicense() as the criteria for
|
||||
// WB_RESULT_INVALID_STATE is that no key can be found and keys are provided
|
||||
// via a license.
|
||||
ASSERT_EQ(
|
||||
WB_License_VerifyRenewalResponse(whitebox_, kResponse, kResponseSize,
|
||||
kSignature, kSignatureSize),
|
||||
WB_RESULT_INVALID_STATE);
|
||||
}
|
||||
} // namespace
|
||||
55
api/result.h
Normal file
55
api/result.h
Normal file
@@ -0,0 +1,55 @@
|
||||
// Copyright 2020 Google LLC. All Rights Reserved.
|
||||
|
||||
#ifndef WHITEBOX_API_RESULT_H_
|
||||
#define WHITEBOX_API_RESULT_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Error codes returned by all the white-box API functions. See the function
|
||||
// returning the result for additional details.
|
||||
typedef enum {
|
||||
// The operation completed successfully.
|
||||
WB_RESULT_OK = 0,
|
||||
|
||||
// The operation failed because memory could not be allocated.
|
||||
WB_RESULT_OUT_OF_MEMORY = 1,
|
||||
|
||||
// The state of white-box does not allow execution of this operation. For
|
||||
// example, another API function may need to be called first.
|
||||
WB_RESULT_INVALID_STATE = 2,
|
||||
|
||||
// The output buffer length is too small to contain the operation's output.
|
||||
WB_RESULT_BUFFER_TOO_SMALL = 3,
|
||||
|
||||
// There is an issue with one or more parameters.
|
||||
WB_RESULT_INVALID_PARAMETER = 4,
|
||||
|
||||
// The message and signature did not pass signature verification.
|
||||
WB_RESULT_INVALID_SIGNATURE = 5,
|
||||
|
||||
// A key was requested that had not been loaded into the white-box.
|
||||
WB_RESULT_NO_SUCH_KEY = 6,
|
||||
|
||||
// A key was requested that was found in the white-box, but is being used
|
||||
// improperly.
|
||||
WB_RESULT_WRONG_KEY_TYPE = 7,
|
||||
|
||||
// The requested key's security level is not sufficient for the operation.
|
||||
WB_RESULT_INSUFFICIENT_SECURITY_LEVEL = 8,
|
||||
|
||||
// The input data failed to be verified. This may happen if the data was
|
||||
// corrupted or tampered.
|
||||
WB_RESULT_DATA_VERIFICATION_ERROR = 9,
|
||||
|
||||
// The padding at the end of the decrypted data does not match the
|
||||
// specification.
|
||||
WB_RESULT_INVALID_PADDING = 10,
|
||||
} WB_Result;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // WHITEBOX_API_WB_RESULT_H_
|
||||
27
api/test_data.h
Normal file
27
api/test_data.h
Normal file
@@ -0,0 +1,27 @@
|
||||
// Copyright 2020 Google LLC. All Rights Reserved.
|
||||
|
||||
#ifndef WHITEBOX_API_TEST_DATA_H_
|
||||
#define WHITEBOX_API_TEST_DATA_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <vector>
|
||||
|
||||
// Returns init data that the aead white-box will accept.
|
||||
std::vector<uint8_t> GetValidAeadInitData();
|
||||
|
||||
// Returns init data that the aead white-box will reject.
|
||||
std::vector<uint8_t> GetInvalidAeadInitData();
|
||||
|
||||
// Returns valid init_data needed by the license whitebox tests when calling
|
||||
// WB_License_Create().
|
||||
std::vector<uint8_t> GetLicenseInitData();
|
||||
|
||||
// Returns the matching public key for the data returned by
|
||||
// GetLicenseInitData(). The format is a DER encoded PKCS#1 RSAPublicKey.
|
||||
std::vector<uint8_t> GetMatchingLicensePublicKey();
|
||||
|
||||
// Returns invalid non-empty init_data that should be rejected by
|
||||
// WB_License_Create().
|
||||
std::vector<uint8_t> GetInvalidLicenseInitData();
|
||||
|
||||
#endif // WHITEBOX_API_TEST_DATA_H_
|
||||
Reference in New Issue
Block a user