Update partner repo
This adds: - Requires WB_RESULT_NOT_IMPLEMENTED for masked in CE - WB_License_RemoveEntitledContentKey - WB_License_Generic* methods
This commit is contained in:
@@ -56,9 +56,9 @@ git_repository(
|
||||
http_archive(
|
||||
name = "zlib",
|
||||
build_file = "@com_google_protobuf//:third_party/zlib.BUILD",
|
||||
sha256 = "c3e5e9fdd5004dcb542feda5ee4f0ff0744628baf8ed2dd5d66f8ca1197cb1a1",
|
||||
strip_prefix = "zlib-1.2.11",
|
||||
urls = ["https://zlib.net/zlib-1.2.11.tar.gz"],
|
||||
sha256 = "b3a24de97a8fdbc835b9833169501030b8977031bcb54b3b3ac13740f846ab30",
|
||||
strip_prefix = "zlib-1.2.13",
|
||||
urls = ["https://zlib.net/zlib-1.2.13.tar.gz"],
|
||||
)
|
||||
|
||||
# ODK
|
||||
|
||||
@@ -11,11 +11,12 @@ cc_library(
|
||||
}) + select({
|
||||
"//:is_ce": [
|
||||
"ALWAYS_DECRYPT_TO_CLEAR",
|
||||
"HAS_GENERIC_CRYPTO",
|
||||
"HAS_SIGN_PST_REPORT",
|
||||
],
|
||||
"//:is_old_api": [],
|
||||
"//:is_old_vmpra": [],
|
||||
"//conditions:default": [
|
||||
"//conditions:default": [ # Chrome
|
||||
# Comment out HAS_PROVIDER_KEYS temporarily
|
||||
# "HAS_PROVIDER_KEYS",
|
||||
],
|
||||
@@ -160,6 +161,7 @@ cc_library(
|
||||
"golden_data.h",
|
||||
],
|
||||
deps = [
|
||||
":license_whitebox",
|
||||
":shared_settings",
|
||||
":test_key_types",
|
||||
"//chromium_deps/cdm/protos:license_protocol_proto",
|
||||
@@ -258,6 +260,7 @@ cc_library(
|
||||
"license_whitebox_create_test.cc",
|
||||
"license_whitebox_decrypt_test.cc",
|
||||
"license_whitebox_entitlement_content_key_test.cc",
|
||||
"license_whitebox_generic_crypto_test.cc",
|
||||
"license_whitebox_get_secret_string_test.cc",
|
||||
"license_whitebox_key_control_block_test.cc",
|
||||
"license_whitebox_license_key_mode.cc",
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
#include "api/golden_data.h"
|
||||
|
||||
#include "api/license_whitebox.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
namespace {
|
||||
@@ -134,6 +136,70 @@ GoldenData::GoldenData() {
|
||||
0xc9, 0xfc, 0x7b, 0x7d, 0x00, 0xf8, 0x75, 0xef,
|
||||
0xde, 0xd2, 0xd9, 0xdd, 0x8c, 0xcd, 0x20, 0x49
|
||||
};
|
||||
|
||||
generic_.plaintext = {
|
||||
'T', 'h', 'i', 'r', 't', 'y', '-', 't',
|
||||
'w', 'o', ' ', 'b', 'y', 't', 'e', 's',
|
||||
' ', 'o', 'f', ' ', 'r', 'a', 'n', 'd',
|
||||
'o', 'm', ' ', 'd', 'a', 't', 'a', '.',
|
||||
};
|
||||
generic_.iv = {
|
||||
0xad, 0x40, 0xb9, 0xeb, 0x61, 0x2e, 0x55, 0xee,
|
||||
0x12, 0x88, 0x62, 0xfe, 0x13, 0xc8, 0xec, 0x56,
|
||||
};
|
||||
// Generated with:
|
||||
// openssl aes-128-cbc -e -in data.text
|
||||
// -K 69b6db52866148a169a261908397bbb6
|
||||
// -iv ad40b9eb612e55ee128862fe13c8ec56 | xxd -i
|
||||
generic_.encrypted = {
|
||||
0x42, 0xcf, 0x7f, 0xf0, 0xae, 0x8c, 0x44, 0xa5,
|
||||
0xda, 0x95, 0xa0, 0x05, 0x8c, 0xbd, 0xb9, 0x15,
|
||||
0x2f, 0xbd, 0x09, 0xe0, 0x5e, 0x7e, 0x91, 0x19,
|
||||
0x36, 0x11, 0x1c, 0xe3, 0xb5, 0xc6, 0xfd, 0xa0,
|
||||
};
|
||||
// Generated with:
|
||||
// cat data.text |
|
||||
// openssl dgst -sha256
|
||||
// -hmac 569c4ba12b3e7834d2a6573cbc8a8d73ce8d240d1ae25c2cdb1b6cd5da16cf01
|
||||
// | xxd -i
|
||||
generic_.signature = {
|
||||
0x20, 0x63, 0xf9, 0x6b, 0xbb, 0x6a, 0xe6, 0x75,
|
||||
0x5e, 0x24, 0x5e, 0x45, 0xab, 0xda, 0x4a, 0xf1,
|
||||
0xbd, 0x99, 0x28, 0x27, 0x90, 0x83, 0xd6, 0x1b,
|
||||
0xef, 0x40, 0xad, 0x05, 0x2c, 0x1c, 0x05, 0x36,
|
||||
};
|
||||
|
||||
generic_.encrypt_decrypt_key.id = GetFreeId();
|
||||
generic_.encrypt_decrypt_key.kcb_flags =
|
||||
WB_KCB_FLAGS_ALLOW_DECRYPT | WB_KCB_FLAGS_ALLOW_ENCRYPT;
|
||||
generic_.encrypt_decrypt_key.key_size = 16u;
|
||||
generic_.encrypt_decrypt_key.key = {
|
||||
0x69, 0xb6, 0xdb, 0x52, 0x86, 0x61, 0x48, 0xa1,
|
||||
0x69, 0xa2, 0x61, 0x90, 0x83, 0x97, 0xbb, 0xb6,
|
||||
};
|
||||
generic_.encrypt_key = generic_.encrypt_decrypt_key;
|
||||
generic_.encrypt_key.id = GetFreeId();
|
||||
generic_.encrypt_key.kcb_flags = WB_KCB_FLAGS_ALLOW_ENCRYPT;
|
||||
generic_.decrypt_key = generic_.encrypt_decrypt_key;
|
||||
generic_.decrypt_key.id = GetFreeId();
|
||||
generic_.decrypt_key.kcb_flags = WB_KCB_FLAGS_ALLOW_DECRYPT;
|
||||
|
||||
generic_.sign_verify_key.id = GetFreeId();
|
||||
generic_.sign_verify_key.kcb_flags =
|
||||
WB_KCB_FLAGS_ALLOW_SIGN | WB_KCB_FLAGS_ALLOW_VERIFY;
|
||||
generic_.sign_verify_key.key_size = 32u;
|
||||
generic_.sign_verify_key.key = {
|
||||
0x56, 0x9c, 0x4b, 0xa1, 0x2b, 0x3e, 0x78, 0x34,
|
||||
0xd2, 0xa6, 0x57, 0x3c, 0xbc, 0x8a, 0x8d, 0x73,
|
||||
0xce, 0x8d, 0x24, 0x0d, 0x1a, 0xe2, 0x5c, 0x2c,
|
||||
0xdb, 0x1b, 0x6c, 0xd5, 0xda, 0x16, 0xcf, 0x01,
|
||||
};
|
||||
generic_.sign_key = generic_.sign_verify_key;
|
||||
generic_.sign_key.id = GetFreeId();
|
||||
generic_.sign_key.kcb_flags = WB_KCB_FLAGS_ALLOW_SIGN;
|
||||
generic_.verify_key = generic_.sign_verify_key;
|
||||
generic_.verify_key.id = GetFreeId();
|
||||
generic_.verify_key.kcb_flags = WB_KCB_FLAGS_ALLOW_VERIFY;
|
||||
}
|
||||
|
||||
KeyId GoldenData::GetFreeId() {
|
||||
|
||||
@@ -43,6 +43,25 @@ class GoldenData {
|
||||
|
||||
EntitlementKeyData entitlement_key;
|
||||
};
|
||||
struct GenericContent {
|
||||
std::vector<uint8_t> plaintext;
|
||||
// |plaintext| encrypted with |encryption_key|
|
||||
std::vector<uint8_t> encrypted;
|
||||
// |plaintext| signed with |signing_key|
|
||||
std::vector<uint8_t> signature;
|
||||
|
||||
// IV used to encrypt the plaintext.
|
||||
AesIv iv;
|
||||
|
||||
// All these keys have the same key value but different key usage and key ID
|
||||
GenericKeyData encrypt_decrypt_key;
|
||||
GenericKeyData decrypt_key;
|
||||
GenericKeyData encrypt_key;
|
||||
|
||||
GenericKeyData sign_verify_key;
|
||||
GenericKeyData sign_key;
|
||||
GenericKeyData verify_key;
|
||||
};
|
||||
|
||||
GoldenData();
|
||||
|
||||
@@ -52,12 +71,15 @@ class GoldenData {
|
||||
|
||||
const EntitlementContent& EntitlementContent() const { return entitlement_; }
|
||||
|
||||
const GenericContent& GenericContent() const { return generic_; }
|
||||
|
||||
KeyId GetFreeId();
|
||||
|
||||
private:
|
||||
Content cbc_content_;
|
||||
Content ctr_content_;
|
||||
struct EntitlementContent entitlement_;
|
||||
struct GenericContent generic_;
|
||||
|
||||
uint8_t next_id_ = 0;
|
||||
};
|
||||
|
||||
@@ -23,8 +23,20 @@ typedef enum {
|
||||
typedef enum {
|
||||
WB_KEY_QUERY_TYPE_SIGNING_KEY,
|
||||
WB_KEY_QUERY_TYPE_CONTENT_KEY,
|
||||
WB_KEY_QUERY_TYPE_GENERIC_KEY,
|
||||
} WB_KeyQueryType;
|
||||
|
||||
typedef enum {
|
||||
WB_KCB_FLAGS_ALLOW_ENCRYPT = (1u << 8),
|
||||
WB_KCB_FLAGS_ALLOW_DECRYPT = (1u << 7),
|
||||
WB_KCB_FLAGS_ALLOW_SIGN = (1u << 6),
|
||||
WB_KCB_FLAGS_ALLOW_VERIFY = (1u << 5),
|
||||
|
||||
WB_KCB_FLAGS_GENERIC_MASK = 0x1e0,
|
||||
WB_KCB_FLAGS_SECURITY_LEVEL_SHIFT = 26,
|
||||
WB_KCB_FLAGS_SECURITY_LEVEL_MASK = 0x3 << WB_KCB_FLAGS_SECURITY_LEVEL_SHIFT,
|
||||
} WB_KcbFlags;
|
||||
|
||||
typedef enum {
|
||||
// The key was found in the license but there was something wrong it and could
|
||||
// not be loaded.
|
||||
@@ -124,7 +136,7 @@ WB_Result WB_License_SignLicenseRequest(const WB_License_Whitebox* whitebox,
|
||||
//
|
||||
// Extracts and loads content and signing keys for use. Any content keys that
|
||||
// exceed the security levels permitted by the instance, will be thrown away,
|
||||
// but the key ids are retained (see WB_RESULT_INSUFFICIENT_SECURITY_LEVEL). All
|
||||
// but the key ids are retained (see WB_RESULT_INSUFFICIENT_PERMISSIONS ). All
|
||||
// non-content keys and non-signing keys will be thrown away.
|
||||
//
|
||||
// This function can only be called once per white-box instance. To parse a new
|
||||
@@ -252,6 +264,27 @@ WB_Result WB_License_LoadEntitledContentKey(WB_License_Whitebox* whitebox,
|
||||
const uint8_t* key_data,
|
||||
size_t key_data_size);
|
||||
|
||||
// Removes a content key that was previously loaded from an entitlement key.
|
||||
//
|
||||
// Args:
|
||||
// whitebox (in) : An initialized white-box instance.
|
||||
//
|
||||
// content_key_id (in) : The ID of the content key.
|
||||
//
|
||||
// content_key_id_size (in) : The number of bytes in |content_key_id|.
|
||||
//
|
||||
// Returns:
|
||||
// WB_RESULT_OK if the key was loaded successfully.
|
||||
//
|
||||
// WB_INVALID_PARAMETER if any of the pointers are null.
|
||||
//
|
||||
// WB_RESULT_KEY_UNAVAILABLE if the requested key was not added.
|
||||
//
|
||||
// WB_RESULT_INVALID_STATE if |whitebox| had not loaded a license.
|
||||
WB_Result WB_License_RemoveEntitledContentKey(WB_License_Whitebox* whitebox,
|
||||
const uint8_t* content_key_id,
|
||||
size_t content_key_id_size);
|
||||
|
||||
// Queries the white-box to know whether or not the white-box loaded a specific
|
||||
// key and to know what operations can be performed with that key.
|
||||
//
|
||||
@@ -427,7 +460,7 @@ WB_Result WB_License_VerifyRenewalResponse(const WB_License_Whitebox* whitebox,
|
||||
// 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_INSUFFICIENT_SECURITY_LEVEL if |key_id| referred to a key from
|
||||
// WB_RESULT_INSUFFICIENT_PERMISSIONS if |key_id| referred to a key from
|
||||
// the license, but the |whitebox| was not allowed to use it.
|
||||
//
|
||||
// WB_RESULT_KEY_UNAVAILABLE if |key_id| did not match a content key from the
|
||||
@@ -444,6 +477,222 @@ WB_Result WB_License_GetSecretString(const WB_License_Whitebox* whitebox,
|
||||
uint8_t* secret_string,
|
||||
size_t* secret_string_size);
|
||||
|
||||
// Performs a generic crypto "encrypt" operation to encrypt |input_data| and
|
||||
// places the result in |output_data|.
|
||||
//
|
||||
// This must support |input_data| and |output_data| pointing to the same buffer
|
||||
// (in-place operation).
|
||||
//
|
||||
// Args:
|
||||
// whitebox (in) : The white-box containing the keys needed 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|.
|
||||
//
|
||||
// iv(in) This is the IV.
|
||||
//
|
||||
// iv_size (in) : The number of bytes in |iv|.
|
||||
//
|
||||
// output_data (out) : The output parameter for the output.
|
||||
//
|
||||
// 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_KEY_UNAVAILABLE if |key_id| did not match a generic key from the
|
||||
// loaded license.
|
||||
//
|
||||
// WB_RESULT_BUFFER_TOO_SMALL if |output_data_size| (as input) was less than
|
||||
// the required size.
|
||||
//
|
||||
// WB_RESULT_INVALID_STATE if |whitebox| had not loaded a license.
|
||||
//
|
||||
// WB_RESULT_INVALID_SIGNATURE if the |mode| is Verify and the calculated
|
||||
// signature doesn't match the value given in |output_data|.
|
||||
//
|
||||
// WB_RESULT_INSUFFICIENT_PERMISSIONS if the key doesn't have the correct
|
||||
// permission bit sets in the KCB to allow encrypt.
|
||||
WB_Result WB_License_GenericEncrypt(const WB_License_Whitebox* whitebox,
|
||||
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);
|
||||
|
||||
// Performs a generic crypto "decrypt" operation to decrypt |input_data| and
|
||||
// places the result in |output_data|.
|
||||
//
|
||||
// This must support |input_data| and |output_data| pointing to the same buffer
|
||||
// (in-place operation).
|
||||
//
|
||||
// Args:
|
||||
// whitebox (in) : The white-box containing the keys needed 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|.
|
||||
//
|
||||
// iv(in) This is the IV.
|
||||
//
|
||||
// iv_size (in) : The number of bytes in |iv|.
|
||||
//
|
||||
// output_data (out) : The output parameter for the output.
|
||||
//
|
||||
// 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_KEY_UNAVAILABLE if |key_id| did not match a generic key from the
|
||||
// loaded license.
|
||||
//
|
||||
// WB_RESULT_BUFFER_TOO_SMALL if |output_data_size| (as input) was less than
|
||||
// the required size.
|
||||
//
|
||||
// WB_RESULT_INVALID_STATE if |whitebox| had not loaded a license.
|
||||
//
|
||||
// WB_RESULT_INVALID_SIGNATURE if the |mode| is Verify and the calculated
|
||||
// signature doesn't match the value given in |output_data|.
|
||||
//
|
||||
// WB_RESULT_INSUFFICIENT_PERMISSIONS if the key doesn't have the correct
|
||||
// permission bit sets in the KCB to allow decrypt.
|
||||
WB_Result WB_License_GenericDecrypt(const WB_License_Whitebox* whitebox,
|
||||
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);
|
||||
|
||||
// Performs a generic crypto "sign" operation that takes the given message in
|
||||
// |input_data| and places the signature in |output_data|.
|
||||
//
|
||||
// Args:
|
||||
// whitebox (in) : The white-box containing the keys needed to decrypt
|
||||
// |input_data|.
|
||||
//
|
||||
// mode (in) : The type of generic crypto operation to perform.
|
||||
//
|
||||
// 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|.
|
||||
//
|
||||
// message (in) : The input message.
|
||||
//
|
||||
// message_data_size (in) : The number of bytes in |message|.
|
||||
//
|
||||
// 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_KEY_UNAVAILABLE if |key_id| did not match a generic key from the
|
||||
// loaded license.
|
||||
//
|
||||
// WB_RESULT_BUFFER_TOO_SMALL if |message_size| (as input) was less than
|
||||
// the required size.
|
||||
//
|
||||
// WB_RESULT_INVALID_STATE if |whitebox| had not loaded a license.
|
||||
//
|
||||
// WB_RESULT_INSUFFICIENT_PERMISSIONS if the key doesn't have the correct
|
||||
// permission bit sets in the KCB to allow sign.
|
||||
WB_Result WB_License_GenericSign(const WB_License_Whitebox* whitebox,
|
||||
const uint8_t* key_id,
|
||||
size_t key_id_size,
|
||||
const uint8_t* message,
|
||||
size_t message_size,
|
||||
uint8_t* output_data,
|
||||
size_t* output_data_size);
|
||||
|
||||
// Performs a generic crypto "verify" operation that takes |message| and
|
||||
// verifies the signature matches |signature|.
|
||||
//
|
||||
// Args:
|
||||
// whitebox (in) : The white-box containing the keys needed 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|.
|
||||
//
|
||||
// message (in) : The input message.
|
||||
//
|
||||
// message_size (in) : The number of bytes in |message|.
|
||||
//
|
||||
// signature (in) : The input signature to verify.
|
||||
//
|
||||
// signature_size (in) : The number of bytes in |signature|.
|
||||
//
|
||||
// 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_KEY_UNAVAILABLE if |key_id| did not match a generic key from the
|
||||
// loaded license.
|
||||
//
|
||||
// WB_RESULT_INVALID_STATE if |whitebox| had not loaded a license.
|
||||
//
|
||||
// WB_RESULT_INVALID_SIGNATURE if the |mode| is Verify and the calculated
|
||||
// signature doesn't match the value given in |output_data|.
|
||||
//
|
||||
// WB_RESULT_INSUFFICIENT_PERMISSIONS if the key doesn't have the correct
|
||||
// permission bit sets in the KCB to allow verify.
|
||||
WB_Result WB_License_GenericVerify(const WB_License_Whitebox* whitebox,
|
||||
const uint8_t* key_id,
|
||||
size_t key_id_size,
|
||||
const uint8_t* message,
|
||||
size_t message_size,
|
||||
const uint8_t* signature,
|
||||
size_t signature_size);
|
||||
|
||||
// Decrypts |input_data| and writes the plaintext to |output_data|.
|
||||
//
|
||||
// This must support |input_data| and |output_data| pointing to the same buffer
|
||||
@@ -485,7 +734,7 @@ WB_Result WB_License_GetSecretString(const WB_License_Whitebox* whitebox,
|
||||
// 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_INSUFFICIENT_SECURITY_LEVEL if |key_id| referred to a key from
|
||||
// WB_RESULT_INSUFFICIENT_PERMISSIONS if |key_id| referred to a key from
|
||||
// the license, but the |whitebox| was not allowed to use it.
|
||||
//
|
||||
// WB_RESULT_KEY_UNAVAILABLE if |key_id| did not match a content key from the
|
||||
@@ -552,7 +801,7 @@ WB_Result WB_License_Decrypt(const WB_License_Whitebox* whitebox,
|
||||
// invalid, if |masked_output_data| was null, or if |masked_output_data_size|
|
||||
// was null.
|
||||
//
|
||||
// WB_RESULT_INSUFFICIENT_SECURITY_LEVEL if |key_id| referred to a key from
|
||||
// WB_RESULT_INSUFFICIENT_PERMISSIONS if |key_id| referred to a key from
|
||||
// the license, but the |whitebox| was not allowed to use it.
|
||||
//
|
||||
// WB_RESULT_KEY_UNAVAILABLE if |key_id| did not match a content key from the
|
||||
|
||||
@@ -109,6 +109,7 @@ TEST_P(LicenseWhiteboxDecryptBenchmark, DecryptCTRThroughput) {
|
||||
PrettyPrint("License Decrypt CTR Throughput", throughput, ciphertext_.size());
|
||||
}
|
||||
|
||||
#ifndef ALWAYS_DECRYPT_TO_CLEAR
|
||||
TEST_P(LicenseWhiteboxDecryptBenchmark, MaskedDecryptCBCThroughput) {
|
||||
std::vector<uint8_t> mask(kMaskSize);
|
||||
size_t mask_size = mask.size();
|
||||
@@ -172,6 +173,7 @@ TEST_P(LicenseWhiteboxDecryptBenchmark, MaskedDecryptCTRThroughput) {
|
||||
PrettyPrint("License Masked Decrypt CTR Throughput", throughput,
|
||||
ciphertext_.size());
|
||||
}
|
||||
#endif
|
||||
|
||||
// The first value is the number of blocks. Each block is 16 bytes.
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
|
||||
@@ -497,7 +497,7 @@ TEST_P(LicenseWhiteboxDecryptTest,
|
||||
golden_data_.CBCContent().iv.data(),
|
||||
golden_data_.CBCContent().iv.size(),
|
||||
plaintext_.data(), &plaintext_size_),
|
||||
WB_RESULT_INSUFFICIENT_SECURITY_LEVEL);
|
||||
WB_RESULT_INSUFFICIENT_PERMISSIONS);
|
||||
}
|
||||
|
||||
TEST_P(LicenseWhiteboxDecryptTest, InsufficientSecurityLevelForDecodeKey) {
|
||||
@@ -510,7 +510,7 @@ TEST_P(LicenseWhiteboxDecryptTest, InsufficientSecurityLevelForDecodeKey) {
|
||||
#ifdef ALWAYS_DECRYPT_TO_CLEAR
|
||||
const auto expected = WB_RESULT_OK;
|
||||
#else
|
||||
const auto expected = WB_RESULT_INSUFFICIENT_SECURITY_LEVEL;
|
||||
const auto expected = WB_RESULT_INSUFFICIENT_PERMISSIONS;
|
||||
#endif
|
||||
ASSERT_EQ(WB_License_Decrypt(
|
||||
whitebox_, WB_CIPHER_MODE_CBC,
|
||||
|
||||
@@ -77,4 +77,51 @@ TEST_F(LicenseWhiteboxEntitlementContentKeyTest, Decrypt) {
|
||||
EXPECT_EQ(decrypted, content.plaintext);
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxEntitlementContentKeyTest, Remove) {
|
||||
auto result = WB_License_ProcessLicenseResponse(
|
||||
whitebox_, WB_LICENSE_KEY_MODE_DUAL_KEY, license_.core_message.data(),
|
||||
license_.core_message.size(), license_.message.data(),
|
||||
license_.message.size(), license_.signature.data(),
|
||||
license_.signature.size(), license_.session_key.data(),
|
||||
license_.session_key.size(), kNoProviderKeyId, license_.request.data(),
|
||||
license_.request.size());
|
||||
#ifndef HAS_ENTITLEMENT
|
||||
if (result == WB_RESULT_NOT_IMPLEMENTED)
|
||||
GTEST_SKIP();
|
||||
#endif
|
||||
ASSERT_EQ(result, WB_RESULT_OK);
|
||||
|
||||
const KeyId key_id = golden_data_.GetFreeId();
|
||||
auto& content = golden_data_.EntitlementContent();
|
||||
result = WB_License_LoadEntitledContentKey(
|
||||
whitebox_, content.entitlement_key.id.data(),
|
||||
content.entitlement_key.id.size(), key_id.data(), key_id.size(),
|
||||
content.key_data_iv.data(), content.key_data_iv.size(),
|
||||
content.key_data.data(), content.key_data.size());
|
||||
#ifndef HAS_ENTITLEMENT
|
||||
if (result == WB_RESULT_NOT_IMPLEMENTED)
|
||||
GTEST_SKIP();
|
||||
#endif
|
||||
ASSERT_EQ(result, WB_RESULT_OK);
|
||||
|
||||
ASSERT_EQ(WB_License_RemoveEntitledContentKey(whitebox_,key_id.data(),
|
||||
key_id.size()),
|
||||
WB_RESULT_OK);
|
||||
|
||||
std::vector<uint8_t> decrypted(content.plaintext.size());
|
||||
size_t decrypted_size = decrypted.size();
|
||||
ASSERT_EQ(WB_License_Decrypt(
|
||||
whitebox_,
|
||||
WB_CIPHER_MODE_CTR,
|
||||
key_id.data(),
|
||||
key_id.size(),
|
||||
content.ciphertext.data(),
|
||||
content.ciphertext.size(),
|
||||
content.iv.data(),
|
||||
content.iv.size(),
|
||||
&decrypted[0],
|
||||
&decrypted_size),
|
||||
WB_RESULT_KEY_UNAVAILABLE);
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
699
whitebox/api/license_whitebox_generic_crypto_test.cc
Normal file
699
whitebox/api/license_whitebox_generic_crypto_test.cc
Normal file
@@ -0,0 +1,699 @@
|
||||
// Copyright 2021 Google LLC. All Rights Reserved.
|
||||
|
||||
#include "api/license_whitebox.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "api/golden_data.h"
|
||||
#include "api/license_whitebox_test_base.h"
|
||||
#include "api/test_license_builder.h"
|
||||
#include "testing/gmock/include/gmock/gmock-matchers.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
class LicenseWhiteboxGenericCryptoInvalidTest
|
||||
: public LicenseWhiteboxTestBase, public testing::WithParamInterface<int> {
|
||||
protected:
|
||||
void SetUp() {
|
||||
LicenseWhiteboxTestBase::SetUp();
|
||||
server_ = TestServer::CreateDualKey();
|
||||
}
|
||||
|
||||
std::unique_ptr<TestServer> server_;
|
||||
License license_;
|
||||
};
|
||||
|
||||
TEST_P(LicenseWhiteboxGenericCryptoInvalidTest, Test) {
|
||||
auto& content = golden_data_.GenericContent();
|
||||
GenericKeyData key_data;
|
||||
switch (GetParam()) {
|
||||
case 0:
|
||||
key_data = content.sign_verify_key;
|
||||
key_data.kcb_flags = WB_KCB_FLAGS_ALLOW_ENCRYPT | WB_KCB_FLAGS_ALLOW_SIGN;
|
||||
break;
|
||||
case 1:
|
||||
key_data = content.encrypt_key;
|
||||
key_data.key_size = 32u;
|
||||
break;
|
||||
case 2:
|
||||
key_data = content.encrypt_key;
|
||||
key_data.key_size = 12u;
|
||||
break;
|
||||
case 3:
|
||||
key_data = content.sign_key;
|
||||
key_data.key_size = 12u;
|
||||
break;
|
||||
case 4:
|
||||
key_data = content.sign_key;
|
||||
key_data.key_size = 16u;
|
||||
break;
|
||||
case 5:
|
||||
key_data = content.sign_key;
|
||||
key_data.key_size = 24u;
|
||||
break;
|
||||
default:
|
||||
FAIL();
|
||||
}
|
||||
|
||||
TestLicenseBuilder builder;
|
||||
builder.GetSettings().odk_version = TestLicenseBuilder::OdkVersion::k16_5;
|
||||
builder.AddSigningKey(TestLicenseBuilder::DefaultSigningKey());
|
||||
builder.AddGenericKey(key_data);
|
||||
builder.Build(*server_, &license_);
|
||||
|
||||
auto result = WB_License_ProcessLicenseResponse(
|
||||
whitebox_, WB_LICENSE_KEY_MODE_DUAL_KEY, license_.core_message.data(),
|
||||
license_.core_message.size(), license_.message.data(),
|
||||
license_.message.size(), license_.signature.data(),
|
||||
license_.signature.size(), license_.session_key.data(),
|
||||
license_.session_key.size(), kNoProviderKeyId, license_.request.data(),
|
||||
license_.request.size());
|
||||
// Can return an error while loading, or return success and skip the key
|
||||
if (result != WB_RESULT_OK)
|
||||
return;
|
||||
|
||||
WB_KeyStatus status;
|
||||
EXPECT_EQ(WB_License_QueryKeyStatus(whitebox_, WB_KEY_QUERY_TYPE_GENERIC_KEY,
|
||||
key_data.id.data(), key_data.id.size(),
|
||||
&status),
|
||||
WB_RESULT_KEY_UNAVAILABLE);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(All, LicenseWhiteboxGenericCryptoInvalidTest,
|
||||
testing::Range(0, 6));
|
||||
|
||||
class LicenseWhiteboxGenericCryptoTest
|
||||
: public LicenseWhiteboxTestBase, public testing::WithParamInterface<bool> {
|
||||
protected:
|
||||
void SetUp() {
|
||||
LicenseWhiteboxTestBase::SetUp();
|
||||
server_ = TestServer::CreateDualKey();
|
||||
|
||||
TestLicenseBuilder builder;
|
||||
builder.GetSettings().odk_version = TestLicenseBuilder::OdkVersion::k16_5;
|
||||
builder.AddSigningKey(TestLicenseBuilder::DefaultSigningKey());
|
||||
if (GetParam()) {
|
||||
builder.AddGenericKey(golden_data_.GenericContent().encrypt_decrypt_key);
|
||||
builder.AddGenericKey(golden_data_.GenericContent().sign_verify_key);
|
||||
} else {
|
||||
builder.AddGenericKey(golden_data_.GenericContent().encrypt_key);
|
||||
builder.AddGenericKey(golden_data_.GenericContent().decrypt_key);
|
||||
builder.AddGenericKey(golden_data_.GenericContent().sign_key);
|
||||
builder.AddGenericKey(golden_data_.GenericContent().verify_key);
|
||||
}
|
||||
builder.Build(*server_, &license_);
|
||||
}
|
||||
|
||||
std::unique_ptr<TestServer> server_;
|
||||
License license_;
|
||||
};
|
||||
|
||||
TEST_P(LicenseWhiteboxGenericCryptoTest, NoContentDecrypt) {
|
||||
auto result = WB_License_ProcessLicenseResponse(
|
||||
whitebox_, WB_LICENSE_KEY_MODE_DUAL_KEY, license_.core_message.data(),
|
||||
license_.core_message.size(), license_.message.data(),
|
||||
license_.message.size(), license_.signature.data(),
|
||||
license_.signature.size(), license_.session_key.data(),
|
||||
license_.session_key.size(), kNoProviderKeyId, license_.request.data(),
|
||||
license_.request.size());
|
||||
#ifndef HAS_GENERIC_CRYPTO
|
||||
if (result == WB_RESULT_NOT_IMPLEMENTED)
|
||||
GTEST_SKIP();
|
||||
#endif
|
||||
ASSERT_EQ(result, WB_RESULT_OK);
|
||||
|
||||
auto& content = golden_data_.GenericContent();
|
||||
auto& key_id =
|
||||
GetParam() ? content.encrypt_decrypt_key.id : content.decrypt_key.id;
|
||||
std::vector<uint8_t> actual(content.plaintext.size());
|
||||
size_t actual_size = actual.size();
|
||||
result = WB_License_Decrypt(
|
||||
whitebox_, WB_CIPHER_MODE_CTR, key_id.data(), key_id.size(),
|
||||
content.encrypted.data(), content.encrypted.size(), content.iv.data(),
|
||||
content.iv.size(), actual.data(), &actual_size);
|
||||
#ifndef HAS_GENERIC_CRYPTO
|
||||
if (result == WB_RESULT_NOT_IMPLEMENTED)
|
||||
GTEST_SKIP();
|
||||
#endif
|
||||
ASSERT_EQ(result, WB_RESULT_KEY_UNAVAILABLE);
|
||||
}
|
||||
|
||||
TEST_P(LicenseWhiteboxGenericCryptoTest, NoGenericWithContent) {
|
||||
auto& content = golden_data_.CTRContent();
|
||||
auto& key = content.software_crypto_key;
|
||||
TestLicenseBuilder builder;
|
||||
builder.GetSettings().odk_version = TestLicenseBuilder::OdkVersion::k16_5;
|
||||
builder.AddSigningKey(TestLicenseBuilder::DefaultSigningKey());
|
||||
builder.AddContentKey(key);
|
||||
builder.Build(*server_, &license_);
|
||||
|
||||
auto result = WB_License_ProcessLicenseResponse(
|
||||
whitebox_, WB_LICENSE_KEY_MODE_DUAL_KEY, license_.core_message.data(),
|
||||
license_.core_message.size(), license_.message.data(),
|
||||
license_.message.size(), license_.signature.data(),
|
||||
license_.signature.size(), license_.session_key.data(),
|
||||
license_.session_key.size(), kNoProviderKeyId, license_.request.data(),
|
||||
license_.request.size());
|
||||
#ifndef HAS_GENERIC_CRYPTO
|
||||
if (result == WB_RESULT_NOT_IMPLEMENTED)
|
||||
GTEST_SKIP();
|
||||
#endif
|
||||
ASSERT_EQ(result, WB_RESULT_OK);
|
||||
|
||||
std::vector<uint8_t> actual(content.plaintext.size());
|
||||
size_t actual_size = actual.size();
|
||||
result = WB_License_GenericDecrypt(
|
||||
whitebox_, key.id.data(), key.id.size(), content.ciphertext.data(),
|
||||
content.ciphertext.size(), content.iv.data(), content.iv.size(),
|
||||
actual.data(), &actual_size);
|
||||
#ifndef HAS_GENERIC_CRYPTO
|
||||
if (result == WB_RESULT_NOT_IMPLEMENTED)
|
||||
GTEST_SKIP();
|
||||
#endif
|
||||
ASSERT_EQ(result, WB_RESULT_KEY_UNAVAILABLE);
|
||||
}
|
||||
|
||||
TEST_P(LicenseWhiteboxGenericCryptoTest, Decrypt) {
|
||||
auto result = WB_License_ProcessLicenseResponse(
|
||||
whitebox_, WB_LICENSE_KEY_MODE_DUAL_KEY, license_.core_message.data(),
|
||||
license_.core_message.size(), license_.message.data(),
|
||||
license_.message.size(), license_.signature.data(),
|
||||
license_.signature.size(), license_.session_key.data(),
|
||||
license_.session_key.size(), kNoProviderKeyId, license_.request.data(),
|
||||
license_.request.size());
|
||||
#ifndef HAS_GENERIC_CRYPTO
|
||||
if (result == WB_RESULT_NOT_IMPLEMENTED)
|
||||
GTEST_SKIP();
|
||||
#endif
|
||||
ASSERT_EQ(result, WB_RESULT_OK);
|
||||
|
||||
auto& content = golden_data_.GenericContent();
|
||||
auto& key_id =
|
||||
GetParam() ? content.encrypt_decrypt_key.id : content.decrypt_key.id;
|
||||
std::vector<uint8_t> actual(content.plaintext.size());
|
||||
size_t actual_size = actual.size();
|
||||
result = WB_License_GenericDecrypt(
|
||||
whitebox_, key_id.data(), key_id.size(), content.encrypted.data(),
|
||||
content.encrypted.size(), content.iv.data(), content.iv.size(),
|
||||
actual.data(), &actual_size);
|
||||
#ifndef HAS_GENERIC_CRYPTO
|
||||
if (result == WB_RESULT_NOT_IMPLEMENTED)
|
||||
GTEST_SKIP();
|
||||
#endif
|
||||
ASSERT_EQ(result, WB_RESULT_OK);
|
||||
ASSERT_LE(actual_size, actual.size());
|
||||
actual.resize(actual_size);
|
||||
|
||||
EXPECT_EQ(actual, content.plaintext);
|
||||
}
|
||||
|
||||
TEST_P(LicenseWhiteboxGenericCryptoTest, DecryptShortBuffer) {
|
||||
auto result = WB_License_ProcessLicenseResponse(
|
||||
whitebox_, WB_LICENSE_KEY_MODE_DUAL_KEY, license_.core_message.data(),
|
||||
license_.core_message.size(), license_.message.data(),
|
||||
license_.message.size(), license_.signature.data(),
|
||||
license_.signature.size(), license_.session_key.data(),
|
||||
license_.session_key.size(), kNoProviderKeyId, license_.request.data(),
|
||||
license_.request.size());
|
||||
#ifndef HAS_GENERIC_CRYPTO
|
||||
if (result == WB_RESULT_NOT_IMPLEMENTED)
|
||||
GTEST_SKIP();
|
||||
#endif
|
||||
ASSERT_EQ(result, WB_RESULT_OK);
|
||||
|
||||
auto& content = golden_data_.GenericContent();
|
||||
auto& key_id =
|
||||
GetParam() ? content.encrypt_decrypt_key.id : content.decrypt_key.id;
|
||||
size_t actual_size = 0;
|
||||
result = WB_License_GenericDecrypt(
|
||||
whitebox_, key_id.data(), key_id.size(), content.encrypted.data(),
|
||||
content.encrypted.size(), content.iv.data(), content.iv.size(),
|
||||
nullptr, &actual_size);
|
||||
#ifndef HAS_GENERIC_CRYPTO
|
||||
if (result == WB_RESULT_NOT_IMPLEMENTED)
|
||||
GTEST_SKIP();
|
||||
#endif
|
||||
ASSERT_EQ(result, WB_RESULT_BUFFER_TOO_SMALL);
|
||||
ASSERT_GT(actual_size, 0u);
|
||||
}
|
||||
|
||||
TEST_P(LicenseWhiteboxGenericCryptoTest, DecryptMissingKey) {
|
||||
auto result = WB_License_ProcessLicenseResponse(
|
||||
whitebox_, WB_LICENSE_KEY_MODE_DUAL_KEY, license_.core_message.data(),
|
||||
license_.core_message.size(), license_.message.data(),
|
||||
license_.message.size(), license_.signature.data(),
|
||||
license_.signature.size(), license_.session_key.data(),
|
||||
license_.session_key.size(), kNoProviderKeyId, license_.request.data(),
|
||||
license_.request.size());
|
||||
#ifndef HAS_GENERIC_CRYPTO
|
||||
if (result == WB_RESULT_NOT_IMPLEMENTED)
|
||||
GTEST_SKIP();
|
||||
#endif
|
||||
ASSERT_EQ(result, WB_RESULT_OK);
|
||||
|
||||
auto& content = golden_data_.GenericContent();
|
||||
auto& key_id =
|
||||
!GetParam() ? content.encrypt_decrypt_key.id : content.decrypt_key.id;
|
||||
std::vector<uint8_t> actual(content.plaintext.size());
|
||||
size_t actual_size = actual.size();
|
||||
result = WB_License_GenericDecrypt(
|
||||
whitebox_, key_id.data(), key_id.size(), content.encrypted.data(),
|
||||
content.encrypted.size(), content.iv.data(), content.iv.size(),
|
||||
actual.data(), &actual_size);
|
||||
#ifndef HAS_GENERIC_CRYPTO
|
||||
if (result == WB_RESULT_NOT_IMPLEMENTED)
|
||||
GTEST_SKIP();
|
||||
#endif
|
||||
ASSERT_EQ(result, WB_RESULT_KEY_UNAVAILABLE);
|
||||
}
|
||||
|
||||
TEST_P(LicenseWhiteboxGenericCryptoTest, DecryptKeyUsage) {
|
||||
auto result = WB_License_ProcessLicenseResponse(
|
||||
whitebox_, WB_LICENSE_KEY_MODE_DUAL_KEY, license_.core_message.data(),
|
||||
license_.core_message.size(), license_.message.data(),
|
||||
license_.message.size(), license_.signature.data(),
|
||||
license_.signature.size(), license_.session_key.data(),
|
||||
license_.session_key.size(), kNoProviderKeyId, license_.request.data(),
|
||||
license_.request.size());
|
||||
#ifndef HAS_GENERIC_CRYPTO
|
||||
if (result == WB_RESULT_NOT_IMPLEMENTED)
|
||||
GTEST_SKIP();
|
||||
#endif
|
||||
ASSERT_EQ(result, WB_RESULT_OK);
|
||||
|
||||
auto& content = golden_data_.GenericContent();
|
||||
auto& key_id =
|
||||
GetParam() ? content.sign_verify_key.id : content.sign_key.id;
|
||||
std::vector<uint8_t> actual(content.plaintext.size());
|
||||
size_t actual_size = actual.size();
|
||||
result = WB_License_GenericDecrypt(
|
||||
whitebox_, key_id.data(), key_id.size(), content.encrypted.data(),
|
||||
content.encrypted.size(), content.iv.data(), content.iv.size(),
|
||||
actual.data(), &actual_size);
|
||||
#ifndef HAS_GENERIC_CRYPTO
|
||||
if (result == WB_RESULT_NOT_IMPLEMENTED)
|
||||
GTEST_SKIP();
|
||||
#endif
|
||||
ASSERT_EQ(result, WB_RESULT_INSUFFICIENT_PERMISSIONS);
|
||||
}
|
||||
|
||||
TEST_P(LicenseWhiteboxGenericCryptoTest, DecryptDataSize) {
|
||||
auto result = WB_License_ProcessLicenseResponse(
|
||||
whitebox_, WB_LICENSE_KEY_MODE_DUAL_KEY, license_.core_message.data(),
|
||||
license_.core_message.size(), license_.message.data(),
|
||||
license_.message.size(), license_.signature.data(),
|
||||
license_.signature.size(), license_.session_key.data(),
|
||||
license_.session_key.size(), kNoProviderKeyId, license_.request.data(),
|
||||
license_.request.size());
|
||||
#ifndef HAS_GENERIC_CRYPTO
|
||||
if (result == WB_RESULT_NOT_IMPLEMENTED)
|
||||
GTEST_SKIP();
|
||||
#endif
|
||||
ASSERT_EQ(result, WB_RESULT_OK);
|
||||
|
||||
auto& content = golden_data_.GenericContent();
|
||||
auto& key_id =
|
||||
GetParam() ? content.encrypt_decrypt_key.id : content.decrypt_key.id;
|
||||
std::vector<uint8_t> actual(content.plaintext.size());
|
||||
size_t actual_size = actual.size();
|
||||
// Input size should be a multiple of 16
|
||||
result = WB_License_GenericDecrypt(
|
||||
whitebox_, key_id.data(), key_id.size(), content.encrypted.data(),
|
||||
content.encrypted.size() - 5, content.iv.data(), content.iv.size(),
|
||||
actual.data(), &actual_size);
|
||||
#ifndef HAS_GENERIC_CRYPTO
|
||||
if (result == WB_RESULT_NOT_IMPLEMENTED)
|
||||
GTEST_SKIP();
|
||||
#endif
|
||||
ASSERT_EQ(result, WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_P(LicenseWhiteboxGenericCryptoTest, Encrypt) {
|
||||
auto result = WB_License_ProcessLicenseResponse(
|
||||
whitebox_, WB_LICENSE_KEY_MODE_DUAL_KEY, license_.core_message.data(),
|
||||
license_.core_message.size(), license_.message.data(),
|
||||
license_.message.size(), license_.signature.data(),
|
||||
license_.signature.size(), license_.session_key.data(),
|
||||
license_.session_key.size(), kNoProviderKeyId, license_.request.data(),
|
||||
license_.request.size());
|
||||
#ifndef HAS_GENERIC_CRYPTO
|
||||
if (result == WB_RESULT_NOT_IMPLEMENTED)
|
||||
GTEST_SKIP();
|
||||
#endif
|
||||
ASSERT_EQ(result, WB_RESULT_OK);
|
||||
|
||||
auto& content = golden_data_.GenericContent();
|
||||
auto& key_id =
|
||||
GetParam() ? content.encrypt_decrypt_key.id : content.encrypt_key.id;
|
||||
std::vector<uint8_t> actual(content.encrypted.size());
|
||||
size_t actual_size = actual.size();
|
||||
result = WB_License_GenericEncrypt(
|
||||
whitebox_, key_id.data(), key_id.size(), content.plaintext.data(),
|
||||
content.plaintext.size(), content.iv.data(), content.iv.size(),
|
||||
actual.data(), &actual_size);
|
||||
#ifndef HAS_GENERIC_CRYPTO
|
||||
if (result == WB_RESULT_NOT_IMPLEMENTED)
|
||||
GTEST_SKIP();
|
||||
#endif
|
||||
ASSERT_EQ(result, WB_RESULT_OK);
|
||||
ASSERT_LE(actual_size, actual.size());
|
||||
actual.resize(actual_size);
|
||||
|
||||
EXPECT_EQ(actual, content.encrypted);
|
||||
}
|
||||
|
||||
TEST_P(LicenseWhiteboxGenericCryptoTest, EncryptShortBuffer) {
|
||||
auto result = WB_License_ProcessLicenseResponse(
|
||||
whitebox_, WB_LICENSE_KEY_MODE_DUAL_KEY, license_.core_message.data(),
|
||||
license_.core_message.size(), license_.message.data(),
|
||||
license_.message.size(), license_.signature.data(),
|
||||
license_.signature.size(), license_.session_key.data(),
|
||||
license_.session_key.size(), kNoProviderKeyId, license_.request.data(),
|
||||
license_.request.size());
|
||||
#ifndef HAS_GENERIC_CRYPTO
|
||||
if (result == WB_RESULT_NOT_IMPLEMENTED)
|
||||
GTEST_SKIP();
|
||||
#endif
|
||||
ASSERT_EQ(result, WB_RESULT_OK);
|
||||
|
||||
auto& content = golden_data_.GenericContent();
|
||||
auto& key_id =
|
||||
GetParam() ? content.encrypt_decrypt_key.id : content.encrypt_key.id;
|
||||
size_t actual_size = 0;
|
||||
result = WB_License_GenericEncrypt(
|
||||
whitebox_, key_id.data(), key_id.size(), content.plaintext.data(),
|
||||
content.plaintext.size(), content.iv.data(), content.iv.size(),
|
||||
nullptr, &actual_size);
|
||||
#ifndef HAS_GENERIC_CRYPTO
|
||||
if (result == WB_RESULT_NOT_IMPLEMENTED)
|
||||
GTEST_SKIP();
|
||||
#endif
|
||||
ASSERT_EQ(result, WB_RESULT_BUFFER_TOO_SMALL);
|
||||
ASSERT_GT(actual_size, 0u);
|
||||
}
|
||||
|
||||
TEST_P(LicenseWhiteboxGenericCryptoTest, EncryptMissingKey) {
|
||||
auto result = WB_License_ProcessLicenseResponse(
|
||||
whitebox_, WB_LICENSE_KEY_MODE_DUAL_KEY, license_.core_message.data(),
|
||||
license_.core_message.size(), license_.message.data(),
|
||||
license_.message.size(), license_.signature.data(),
|
||||
license_.signature.size(), license_.session_key.data(),
|
||||
license_.session_key.size(), kNoProviderKeyId, license_.request.data(),
|
||||
license_.request.size());
|
||||
#ifndef HAS_GENERIC_CRYPTO
|
||||
if (result == WB_RESULT_NOT_IMPLEMENTED)
|
||||
GTEST_SKIP();
|
||||
#endif
|
||||
ASSERT_EQ(result, WB_RESULT_OK);
|
||||
|
||||
auto& content = golden_data_.GenericContent();
|
||||
auto& key_id =
|
||||
!GetParam() ? content.encrypt_decrypt_key.id : content.encrypt_key.id;
|
||||
std::vector<uint8_t> actual(content.encrypted.size());
|
||||
size_t actual_size = actual.size();
|
||||
result = WB_License_GenericEncrypt(
|
||||
whitebox_, key_id.data(), key_id.size(), content.plaintext.data(),
|
||||
content.plaintext.size(), content.iv.data(), content.iv.size(),
|
||||
actual.data(), &actual_size);
|
||||
#ifndef HAS_GENERIC_CRYPTO
|
||||
if (result == WB_RESULT_NOT_IMPLEMENTED)
|
||||
GTEST_SKIP();
|
||||
#endif
|
||||
ASSERT_EQ(result, WB_RESULT_KEY_UNAVAILABLE);
|
||||
}
|
||||
|
||||
TEST_P(LicenseWhiteboxGenericCryptoTest, EncryptKeyUsage) {
|
||||
auto result = WB_License_ProcessLicenseResponse(
|
||||
whitebox_, WB_LICENSE_KEY_MODE_DUAL_KEY, license_.core_message.data(),
|
||||
license_.core_message.size(), license_.message.data(),
|
||||
license_.message.size(), license_.signature.data(),
|
||||
license_.signature.size(), license_.session_key.data(),
|
||||
license_.session_key.size(), kNoProviderKeyId, license_.request.data(),
|
||||
license_.request.size());
|
||||
#ifndef HAS_GENERIC_CRYPTO
|
||||
if (result == WB_RESULT_NOT_IMPLEMENTED)
|
||||
GTEST_SKIP();
|
||||
#endif
|
||||
ASSERT_EQ(result, WB_RESULT_OK);
|
||||
|
||||
auto& content = golden_data_.GenericContent();
|
||||
auto& key_id =
|
||||
GetParam() ? content.sign_verify_key.id : content.sign_key.id;
|
||||
std::vector<uint8_t> actual(content.encrypted.size());
|
||||
size_t actual_size = actual.size();
|
||||
result = WB_License_GenericEncrypt(
|
||||
whitebox_, key_id.data(), key_id.size(), content.plaintext.data(),
|
||||
content.plaintext.size(), content.iv.data(), content.iv.size(),
|
||||
actual.data(), &actual_size);
|
||||
#ifndef HAS_GENERIC_CRYPTO
|
||||
if (result == WB_RESULT_NOT_IMPLEMENTED)
|
||||
GTEST_SKIP();
|
||||
#endif
|
||||
ASSERT_EQ(result, WB_RESULT_INSUFFICIENT_PERMISSIONS);
|
||||
}
|
||||
|
||||
TEST_P(LicenseWhiteboxGenericCryptoTest, EncryptDataSize) {
|
||||
auto result = WB_License_ProcessLicenseResponse(
|
||||
whitebox_, WB_LICENSE_KEY_MODE_DUAL_KEY, license_.core_message.data(),
|
||||
license_.core_message.size(), license_.message.data(),
|
||||
license_.message.size(), license_.signature.data(),
|
||||
license_.signature.size(), license_.session_key.data(),
|
||||
license_.session_key.size(), kNoProviderKeyId, license_.request.data(),
|
||||
license_.request.size());
|
||||
#ifndef HAS_GENERIC_CRYPTO
|
||||
if (result == WB_RESULT_NOT_IMPLEMENTED)
|
||||
GTEST_SKIP();
|
||||
#endif
|
||||
ASSERT_EQ(result, WB_RESULT_OK);
|
||||
|
||||
auto& content = golden_data_.GenericContent();
|
||||
auto& key_id =
|
||||
GetParam() ? content.encrypt_decrypt_key.id : content.encrypt_key.id;
|
||||
std::vector<uint8_t> actual(content.encrypted.size());
|
||||
size_t actual_size = actual.size();
|
||||
// Input size should be a multiple of 16
|
||||
result = WB_License_GenericEncrypt(
|
||||
whitebox_, key_id.data(), key_id.size(), content.plaintext.data(),
|
||||
content.plaintext.size() - 5, content.iv.data(), content.iv.size(),
|
||||
actual.data(), &actual_size);
|
||||
#ifndef HAS_GENERIC_CRYPTO
|
||||
if (result == WB_RESULT_NOT_IMPLEMENTED)
|
||||
GTEST_SKIP();
|
||||
#endif
|
||||
ASSERT_EQ(result, WB_RESULT_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
TEST_P(LicenseWhiteboxGenericCryptoTest, Sign) {
|
||||
auto result = WB_License_ProcessLicenseResponse(
|
||||
whitebox_, WB_LICENSE_KEY_MODE_DUAL_KEY, license_.core_message.data(),
|
||||
license_.core_message.size(), license_.message.data(),
|
||||
license_.message.size(), license_.signature.data(),
|
||||
license_.signature.size(), license_.session_key.data(),
|
||||
license_.session_key.size(), kNoProviderKeyId, license_.request.data(),
|
||||
license_.request.size());
|
||||
#ifndef HAS_GENERIC_CRYPTO
|
||||
if (result == WB_RESULT_NOT_IMPLEMENTED)
|
||||
GTEST_SKIP();
|
||||
#endif
|
||||
ASSERT_EQ(result, WB_RESULT_OK);
|
||||
|
||||
auto& content = golden_data_.GenericContent();
|
||||
auto& key_id =
|
||||
GetParam() ? content.sign_verify_key.id : content.sign_key.id;
|
||||
std::vector<uint8_t> actual(content.signature.size());
|
||||
size_t actual_size = actual.size();
|
||||
result = WB_License_GenericSign(
|
||||
whitebox_, key_id.data(), key_id.size(), content.plaintext.data(),
|
||||
content.plaintext.size(), actual.data(), &actual_size);
|
||||
#ifndef HAS_GENERIC_CRYPTO
|
||||
if (result == WB_RESULT_NOT_IMPLEMENTED)
|
||||
GTEST_SKIP();
|
||||
#endif
|
||||
ASSERT_EQ(result, WB_RESULT_OK);
|
||||
ASSERT_LE(actual_size, actual.size());
|
||||
actual.resize(actual_size);
|
||||
|
||||
EXPECT_EQ(actual, content.signature);
|
||||
}
|
||||
|
||||
TEST_P(LicenseWhiteboxGenericCryptoTest, SignShortBuffer) {
|
||||
auto result = WB_License_ProcessLicenseResponse(
|
||||
whitebox_, WB_LICENSE_KEY_MODE_DUAL_KEY, license_.core_message.data(),
|
||||
license_.core_message.size(), license_.message.data(),
|
||||
license_.message.size(), license_.signature.data(),
|
||||
license_.signature.size(), license_.session_key.data(),
|
||||
license_.session_key.size(), kNoProviderKeyId, license_.request.data(),
|
||||
license_.request.size());
|
||||
#ifndef HAS_GENERIC_CRYPTO
|
||||
if (result == WB_RESULT_NOT_IMPLEMENTED)
|
||||
GTEST_SKIP();
|
||||
#endif
|
||||
ASSERT_EQ(result, WB_RESULT_OK);
|
||||
|
||||
auto& content = golden_data_.GenericContent();
|
||||
auto& key_id =
|
||||
GetParam() ? content.sign_verify_key.id : content.sign_key.id;
|
||||
size_t actual_size = 0;
|
||||
result = WB_License_GenericSign(
|
||||
whitebox_, key_id.data(), key_id.size(), content.plaintext.data(),
|
||||
content.plaintext.size(), nullptr, &actual_size);
|
||||
#ifndef HAS_GENERIC_CRYPTO
|
||||
if (result == WB_RESULT_NOT_IMPLEMENTED)
|
||||
GTEST_SKIP();
|
||||
#endif
|
||||
ASSERT_EQ(result, WB_RESULT_BUFFER_TOO_SMALL);
|
||||
ASSERT_GT(actual_size, 0u);
|
||||
}
|
||||
|
||||
TEST_P(LicenseWhiteboxGenericCryptoTest, SignMissingKey) {
|
||||
auto result = WB_License_ProcessLicenseResponse(
|
||||
whitebox_, WB_LICENSE_KEY_MODE_DUAL_KEY, license_.core_message.data(),
|
||||
license_.core_message.size(), license_.message.data(),
|
||||
license_.message.size(), license_.signature.data(),
|
||||
license_.signature.size(), license_.session_key.data(),
|
||||
license_.session_key.size(), kNoProviderKeyId, license_.request.data(),
|
||||
license_.request.size());
|
||||
#ifndef HAS_GENERIC_CRYPTO
|
||||
if (result == WB_RESULT_NOT_IMPLEMENTED)
|
||||
GTEST_SKIP();
|
||||
#endif
|
||||
ASSERT_EQ(result, WB_RESULT_OK);
|
||||
|
||||
auto& content = golden_data_.GenericContent();
|
||||
auto& key_id =
|
||||
!GetParam() ? content.sign_verify_key.id : content.sign_key.id;
|
||||
std::vector<uint8_t> actual(content.signature.size());
|
||||
size_t actual_size = actual.size();
|
||||
result = WB_License_GenericSign(
|
||||
whitebox_, key_id.data(), key_id.size(), content.plaintext.data(),
|
||||
content.plaintext.size(), actual.data(), &actual_size);
|
||||
#ifndef HAS_GENERIC_CRYPTO
|
||||
if (result == WB_RESULT_NOT_IMPLEMENTED)
|
||||
GTEST_SKIP();
|
||||
#endif
|
||||
ASSERT_EQ(result, WB_RESULT_KEY_UNAVAILABLE);
|
||||
}
|
||||
|
||||
TEST_P(LicenseWhiteboxGenericCryptoTest, SignKeyUsage) {
|
||||
auto result = WB_License_ProcessLicenseResponse(
|
||||
whitebox_, WB_LICENSE_KEY_MODE_DUAL_KEY, license_.core_message.data(),
|
||||
license_.core_message.size(), license_.message.data(),
|
||||
license_.message.size(), license_.signature.data(),
|
||||
license_.signature.size(), license_.session_key.data(),
|
||||
license_.session_key.size(), kNoProviderKeyId, license_.request.data(),
|
||||
license_.request.size());
|
||||
#ifndef HAS_GENERIC_CRYPTO
|
||||
if (result == WB_RESULT_NOT_IMPLEMENTED)
|
||||
GTEST_SKIP();
|
||||
#endif
|
||||
ASSERT_EQ(result, WB_RESULT_OK);
|
||||
|
||||
auto& content = golden_data_.GenericContent();
|
||||
auto& key_id =
|
||||
GetParam() ? content.encrypt_decrypt_key.id : content.decrypt_key.id;
|
||||
std::vector<uint8_t> actual(content.signature.size());
|
||||
size_t actual_size = actual.size();
|
||||
result = WB_License_GenericSign(
|
||||
whitebox_, key_id.data(), key_id.size(), content.plaintext.data(),
|
||||
content.plaintext.size(), actual.data(), &actual_size);
|
||||
#ifndef HAS_GENERIC_CRYPTO
|
||||
if (result == WB_RESULT_NOT_IMPLEMENTED)
|
||||
GTEST_SKIP();
|
||||
#endif
|
||||
ASSERT_EQ(result, WB_RESULT_INSUFFICIENT_PERMISSIONS);
|
||||
}
|
||||
|
||||
TEST_P(LicenseWhiteboxGenericCryptoTest, Verify) {
|
||||
auto result = WB_License_ProcessLicenseResponse(
|
||||
whitebox_, WB_LICENSE_KEY_MODE_DUAL_KEY, license_.core_message.data(),
|
||||
license_.core_message.size(), license_.message.data(),
|
||||
license_.message.size(), license_.signature.data(),
|
||||
license_.signature.size(), license_.session_key.data(),
|
||||
license_.session_key.size(), kNoProviderKeyId, license_.request.data(),
|
||||
license_.request.size());
|
||||
#ifndef HAS_GENERIC_CRYPTO
|
||||
if (result == WB_RESULT_NOT_IMPLEMENTED)
|
||||
GTEST_SKIP();
|
||||
#endif
|
||||
ASSERT_EQ(result, WB_RESULT_OK);
|
||||
|
||||
auto& content = golden_data_.GenericContent();
|
||||
auto& key_id =
|
||||
GetParam() ? content.sign_verify_key.id : content.verify_key.id;
|
||||
auto temp_signature = content.signature;
|
||||
// Test once successfully, then make the signature invalid and test again.
|
||||
for (int i = 0; i < 2; i++) {
|
||||
result = WB_License_GenericVerify(
|
||||
whitebox_, key_id.data(), key_id.size(), content.plaintext.data(),
|
||||
content.plaintext.size(), temp_signature.data(), temp_signature.size());
|
||||
#ifndef HAS_GENERIC_CRYPTO
|
||||
if (result == WB_RESULT_NOT_IMPLEMENTED)
|
||||
GTEST_SKIP();
|
||||
#endif
|
||||
ASSERT_EQ(result, i == 0 ? WB_RESULT_OK : WB_RESULT_INVALID_SIGNATURE);
|
||||
temp_signature[2] ^= 0xaa;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(LicenseWhiteboxGenericCryptoTest, VerifyMissingKey) {
|
||||
auto result = WB_License_ProcessLicenseResponse(
|
||||
whitebox_, WB_LICENSE_KEY_MODE_DUAL_KEY, license_.core_message.data(),
|
||||
license_.core_message.size(), license_.message.data(),
|
||||
license_.message.size(), license_.signature.data(),
|
||||
license_.signature.size(), license_.session_key.data(),
|
||||
license_.session_key.size(), kNoProviderKeyId, license_.request.data(),
|
||||
license_.request.size());
|
||||
#ifndef HAS_GENERIC_CRYPTO
|
||||
if (result == WB_RESULT_NOT_IMPLEMENTED)
|
||||
GTEST_SKIP();
|
||||
#endif
|
||||
ASSERT_EQ(result, WB_RESULT_OK);
|
||||
|
||||
auto& content = golden_data_.GenericContent();
|
||||
auto& key_id =
|
||||
!GetParam() ? content.sign_verify_key.id : content.verify_key.id;
|
||||
auto temp_signature = content.signature;
|
||||
result = WB_License_GenericVerify(
|
||||
whitebox_, key_id.data(), key_id.size(), content.plaintext.data(),
|
||||
content.plaintext.size(), temp_signature.data(), temp_signature.size());
|
||||
#ifndef HAS_GENERIC_CRYPTO
|
||||
if (result == WB_RESULT_NOT_IMPLEMENTED)
|
||||
GTEST_SKIP();
|
||||
#endif
|
||||
ASSERT_EQ(result, WB_RESULT_KEY_UNAVAILABLE);
|
||||
}
|
||||
|
||||
TEST_P(LicenseWhiteboxGenericCryptoTest, VerifyKeyUsage) {
|
||||
auto result = WB_License_ProcessLicenseResponse(
|
||||
whitebox_, WB_LICENSE_KEY_MODE_DUAL_KEY, license_.core_message.data(),
|
||||
license_.core_message.size(), license_.message.data(),
|
||||
license_.message.size(), license_.signature.data(),
|
||||
license_.signature.size(), license_.session_key.data(),
|
||||
license_.session_key.size(), kNoProviderKeyId, license_.request.data(),
|
||||
license_.request.size());
|
||||
#ifndef HAS_GENERIC_CRYPTO
|
||||
if (result == WB_RESULT_NOT_IMPLEMENTED)
|
||||
GTEST_SKIP();
|
||||
#endif
|
||||
ASSERT_EQ(result, WB_RESULT_OK);
|
||||
|
||||
auto& content = golden_data_.GenericContent();
|
||||
auto& key_id =
|
||||
GetParam() ? content.encrypt_decrypt_key.id : content.decrypt_key.id;
|
||||
auto temp_signature = content.signature;
|
||||
result = WB_License_GenericVerify(
|
||||
whitebox_, key_id.data(), key_id.size(), content.plaintext.data(),
|
||||
content.plaintext.size(), temp_signature.data(), temp_signature.size());
|
||||
#ifndef HAS_GENERIC_CRYPTO
|
||||
if (result == WB_RESULT_NOT_IMPLEMENTED)
|
||||
GTEST_SKIP();
|
||||
#endif
|
||||
ASSERT_EQ(result, WB_RESULT_INSUFFICIENT_PERMISSIONS);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(All, LicenseWhiteboxGenericCryptoTest,
|
||||
testing::Bool());
|
||||
|
||||
} // namespace widevine
|
||||
@@ -69,64 +69,84 @@ TEST_F(LicenseWhiteboxGetSecretStringTest, SuccessForCBCWithCryptoKey) {
|
||||
TestLicenseBuilder::Settings settings;
|
||||
LoadLicense(settings);
|
||||
|
||||
ASSERT_EQ(WB_License_GetSecretString(
|
||||
whitebox_, WB_CIPHER_MODE_CBC,
|
||||
golden_data_.CBCContent().software_crypto_key.id.data(),
|
||||
golden_data_.CBCContent().software_crypto_key.id.size(),
|
||||
secret_string_.data(), &secret_string_size_),
|
||||
WB_RESULT_OK);
|
||||
auto result = WB_License_GetSecretString(
|
||||
whitebox_, WB_CIPHER_MODE_CBC,
|
||||
golden_data_.CBCContent().software_crypto_key.id.data(),
|
||||
golden_data_.CBCContent().software_crypto_key.id.size(),
|
||||
secret_string_.data(), &secret_string_size_);
|
||||
#ifdef ALWAYS_DECRYPT_TO_CLEAR
|
||||
ASSERT_EQ(result, WB_RESULT_NOT_IMPLEMENTED);
|
||||
#else
|
||||
ASSERT_EQ(result, WB_RESULT_OK);
|
||||
ASSERT_GT(secret_string_size_, 0u);
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxGetSecretStringTest, SuccessForCTRWithCryptoKey) {
|
||||
TestLicenseBuilder::Settings settings;
|
||||
LoadLicense(settings);
|
||||
|
||||
ASSERT_EQ(WB_License_GetSecretString(
|
||||
whitebox_, WB_CIPHER_MODE_CTR,
|
||||
golden_data_.CTRContent().software_crypto_key.id.data(),
|
||||
golden_data_.CTRContent().software_crypto_key.id.size(),
|
||||
secret_string_.data(), &secret_string_size_),
|
||||
WB_RESULT_OK);
|
||||
auto result = WB_License_GetSecretString(
|
||||
whitebox_, WB_CIPHER_MODE_CTR,
|
||||
golden_data_.CTRContent().software_crypto_key.id.data(),
|
||||
golden_data_.CTRContent().software_crypto_key.id.size(),
|
||||
secret_string_.data(), &secret_string_size_);
|
||||
#ifdef ALWAYS_DECRYPT_TO_CLEAR
|
||||
ASSERT_EQ(result, WB_RESULT_NOT_IMPLEMENTED);
|
||||
#else
|
||||
ASSERT_EQ(result, WB_RESULT_OK);
|
||||
ASSERT_GT(secret_string_size_, 0u);
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxGetSecretStringTest, SuccessForCBCWithDecodeKey) {
|
||||
TestLicenseBuilder::Settings settings;
|
||||
LoadLicense(settings);
|
||||
|
||||
ASSERT_EQ(WB_License_GetSecretString(
|
||||
whitebox_, WB_CIPHER_MODE_CBC,
|
||||
golden_data_.CBCContent().software_decode_key.id.data(),
|
||||
golden_data_.CBCContent().software_decode_key.id.size(),
|
||||
secret_string_.data(), &secret_string_size_),
|
||||
WB_RESULT_OK);
|
||||
auto result = WB_License_GetSecretString(
|
||||
whitebox_, WB_CIPHER_MODE_CBC,
|
||||
golden_data_.CBCContent().software_decode_key.id.data(),
|
||||
golden_data_.CBCContent().software_decode_key.id.size(),
|
||||
secret_string_.data(), &secret_string_size_);
|
||||
#ifdef ALWAYS_DECRYPT_TO_CLEAR
|
||||
ASSERT_EQ(result, WB_RESULT_NOT_IMPLEMENTED);
|
||||
#else
|
||||
ASSERT_EQ(result, WB_RESULT_OK);
|
||||
ASSERT_GT(secret_string_size_, 0u);
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxGetSecretStringTest, SuccessForCTRWithDecodeKey) {
|
||||
TestLicenseBuilder::Settings settings;
|
||||
LoadLicense(settings);
|
||||
|
||||
ASSERT_EQ(WB_License_GetSecretString(
|
||||
whitebox_, WB_CIPHER_MODE_CTR,
|
||||
golden_data_.CTRContent().software_decode_key.id.data(),
|
||||
golden_data_.CTRContent().software_decode_key.id.size(),
|
||||
secret_string_.data(), &secret_string_size_),
|
||||
WB_RESULT_OK);
|
||||
auto result = WB_License_GetSecretString(
|
||||
whitebox_, WB_CIPHER_MODE_CTR,
|
||||
golden_data_.CTRContent().software_decode_key.id.data(),
|
||||
golden_data_.CTRContent().software_decode_key.id.size(),
|
||||
secret_string_.data(), &secret_string_size_);
|
||||
#ifdef ALWAYS_DECRYPT_TO_CLEAR
|
||||
ASSERT_EQ(result, WB_RESULT_NOT_IMPLEMENTED);
|
||||
#else
|
||||
ASSERT_EQ(result, WB_RESULT_OK);
|
||||
ASSERT_GT(secret_string_size_, 0u);
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxGetSecretStringTest, InvalidParameterForNullWhitebox) {
|
||||
TestLicenseBuilder::Settings settings;
|
||||
LoadLicense(settings);
|
||||
|
||||
ASSERT_EQ(WB_License_GetSecretString(
|
||||
nullptr, WB_CIPHER_MODE_CBC,
|
||||
golden_data_.CBCContent().software_crypto_key.id.data(),
|
||||
golden_data_.CBCContent().software_crypto_key.id.size(),
|
||||
secret_string_.data(), &secret_string_size_),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
auto result = WB_License_GetSecretString(
|
||||
nullptr, WB_CIPHER_MODE_CBC,
|
||||
golden_data_.CBCContent().software_crypto_key.id.data(),
|
||||
golden_data_.CBCContent().software_crypto_key.id.size(),
|
||||
secret_string_.data(), &secret_string_size_);
|
||||
#ifdef ALWAYS_DECRYPT_TO_CLEAR
|
||||
ASSERT_EQ(result, WB_RESULT_NOT_IMPLEMENTED);
|
||||
#else
|
||||
ASSERT_EQ(result, WB_RESULT_INVALID_PARAMETER);
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxGetSecretStringTest,
|
||||
@@ -139,34 +159,46 @@ TEST_F(LicenseWhiteboxGetSecretStringTest,
|
||||
// compiler tries to save us.
|
||||
const WB_CipherMode invalid_mode = static_cast<WB_CipherMode>(0xFF);
|
||||
|
||||
ASSERT_EQ(WB_License_GetSecretString(
|
||||
whitebox_, invalid_mode,
|
||||
golden_data_.CBCContent().software_crypto_key.id.data(),
|
||||
golden_data_.CBCContent().software_crypto_key.id.size(),
|
||||
secret_string_.data(), &secret_string_size_),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
auto result = WB_License_GetSecretString(
|
||||
whitebox_, invalid_mode,
|
||||
golden_data_.CBCContent().software_crypto_key.id.data(),
|
||||
golden_data_.CBCContent().software_crypto_key.id.size(),
|
||||
secret_string_.data(), &secret_string_size_);
|
||||
#ifdef ALWAYS_DECRYPT_TO_CLEAR
|
||||
ASSERT_EQ(result, WB_RESULT_NOT_IMPLEMENTED);
|
||||
#else
|
||||
ASSERT_EQ(result, WB_RESULT_INVALID_PARAMETER);
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxGetSecretStringTest, InvalidParameterForNullKeyId) {
|
||||
TestLicenseBuilder::Settings settings;
|
||||
LoadLicense(settings);
|
||||
|
||||
ASSERT_EQ(WB_License_GetSecretString(
|
||||
whitebox_, WB_CIPHER_MODE_CBC, nullptr,
|
||||
golden_data_.CBCContent().software_crypto_key.id.size(),
|
||||
secret_string_.data(), &secret_string_size_),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
auto result = WB_License_GetSecretString(
|
||||
whitebox_, WB_CIPHER_MODE_CBC, nullptr,
|
||||
golden_data_.CBCContent().software_crypto_key.id.size(),
|
||||
secret_string_.data(), &secret_string_size_);
|
||||
#ifdef ALWAYS_DECRYPT_TO_CLEAR
|
||||
ASSERT_EQ(result, WB_RESULT_NOT_IMPLEMENTED);
|
||||
#else
|
||||
ASSERT_EQ(result, WB_RESULT_INVALID_PARAMETER);
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxGetSecretStringTest, InvalidParameterForZeroKeyIdSize) {
|
||||
TestLicenseBuilder::Settings settings;
|
||||
LoadLicense(settings);
|
||||
|
||||
ASSERT_EQ(WB_License_GetSecretString(
|
||||
whitebox_, WB_CIPHER_MODE_CBC,
|
||||
golden_data_.CBCContent().software_crypto_key.id.data(), 0,
|
||||
secret_string_.data(), &secret_string_size_),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
auto result = WB_License_GetSecretString(
|
||||
whitebox_, WB_CIPHER_MODE_CBC,
|
||||
golden_data_.CBCContent().software_crypto_key.id.data(), 0,
|
||||
secret_string_.data(), &secret_string_size_);
|
||||
#ifdef ALWAYS_DECRYPT_TO_CLEAR
|
||||
ASSERT_EQ(result, WB_RESULT_NOT_IMPLEMENTED);
|
||||
#else
|
||||
ASSERT_EQ(result, WB_RESULT_INVALID_PARAMETER);
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxGetSecretStringTest, CanProbeSizeWithNullString) {
|
||||
@@ -174,13 +206,17 @@ TEST_F(LicenseWhiteboxGetSecretStringTest, CanProbeSizeWithNullString) {
|
||||
LoadLicense(settings);
|
||||
secret_string_size_ = 0;
|
||||
|
||||
ASSERT_EQ(WB_License_GetSecretString(
|
||||
whitebox_, WB_CIPHER_MODE_CBC,
|
||||
golden_data_.CBCContent().software_crypto_key.id.data(),
|
||||
golden_data_.CBCContent().software_crypto_key.id.size(),
|
||||
nullptr, &secret_string_size_),
|
||||
WB_RESULT_BUFFER_TOO_SMALL);
|
||||
auto result = WB_License_GetSecretString(
|
||||
whitebox_, WB_CIPHER_MODE_CBC,
|
||||
golden_data_.CBCContent().software_crypto_key.id.data(),
|
||||
golden_data_.CBCContent().software_crypto_key.id.size(), nullptr,
|
||||
&secret_string_size_);
|
||||
#ifdef ALWAYS_DECRYPT_TO_CLEAR
|
||||
ASSERT_EQ(result, WB_RESULT_NOT_IMPLEMENTED);
|
||||
#else
|
||||
ASSERT_EQ(result, WB_RESULT_BUFFER_TOO_SMALL);
|
||||
ASSERT_GT(secret_string_size_, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxGetSecretStringTest,
|
||||
@@ -188,12 +224,16 @@ TEST_F(LicenseWhiteboxGetSecretStringTest,
|
||||
TestLicenseBuilder::Settings settings;
|
||||
LoadLicense(settings);
|
||||
|
||||
ASSERT_EQ(WB_License_GetSecretString(
|
||||
whitebox_, WB_CIPHER_MODE_CBC,
|
||||
golden_data_.CBCContent().software_crypto_key.id.data(),
|
||||
golden_data_.CBCContent().software_crypto_key.id.size(),
|
||||
nullptr, &secret_string_size_),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
auto result = WB_License_GetSecretString(
|
||||
whitebox_, WB_CIPHER_MODE_CBC,
|
||||
golden_data_.CBCContent().software_crypto_key.id.data(),
|
||||
golden_data_.CBCContent().software_crypto_key.id.size(), nullptr,
|
||||
&secret_string_size_);
|
||||
#ifdef ALWAYS_DECRYPT_TO_CLEAR
|
||||
ASSERT_EQ(result, WB_RESULT_NOT_IMPLEMENTED);
|
||||
#else
|
||||
ASSERT_EQ(result, WB_RESULT_INVALID_PARAMETER);
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxGetSecretStringTest,
|
||||
@@ -201,12 +241,16 @@ TEST_F(LicenseWhiteboxGetSecretStringTest,
|
||||
TestLicenseBuilder::Settings settings;
|
||||
LoadLicense(settings);
|
||||
|
||||
ASSERT_EQ(WB_License_GetSecretString(
|
||||
whitebox_, WB_CIPHER_MODE_CBC,
|
||||
golden_data_.CBCContent().software_crypto_key.id.data(),
|
||||
golden_data_.CBCContent().software_crypto_key.id.size(),
|
||||
secret_string_.data(), nullptr),
|
||||
WB_RESULT_INVALID_PARAMETER);
|
||||
auto result = WB_License_GetSecretString(
|
||||
whitebox_, WB_CIPHER_MODE_CBC,
|
||||
golden_data_.CBCContent().software_crypto_key.id.data(),
|
||||
golden_data_.CBCContent().software_crypto_key.id.size(),
|
||||
secret_string_.data(), nullptr);
|
||||
#ifdef ALWAYS_DECRYPT_TO_CLEAR
|
||||
ASSERT_EQ(result, WB_RESULT_NOT_IMPLEMENTED);
|
||||
#else
|
||||
ASSERT_EQ(result, WB_RESULT_INVALID_PARAMETER);
|
||||
#endif
|
||||
}
|
||||
|
||||
// For this test, "missing key id" specifically means a key id that was never
|
||||
@@ -216,22 +260,28 @@ TEST_F(LicenseWhiteboxGetSecretStringTest, KeyUnavailableForMissingKeyId) {
|
||||
TestLicenseBuilder::Settings settings;
|
||||
LoadLicense(settings);
|
||||
|
||||
ASSERT_EQ(
|
||||
WB_License_GetSecretString(whitebox_, WB_CIPHER_MODE_CBC,
|
||||
missing_key_id_.data(), missing_key_id_.size(),
|
||||
secret_string_.data(), &secret_string_size_),
|
||||
WB_RESULT_KEY_UNAVAILABLE);
|
||||
auto result = WB_License_GetSecretString(
|
||||
whitebox_, WB_CIPHER_MODE_CBC, missing_key_id_.data(),
|
||||
missing_key_id_.size(), secret_string_.data(), &secret_string_size_);
|
||||
#ifdef ALWAYS_DECRYPT_TO_CLEAR
|
||||
ASSERT_EQ(result, WB_RESULT_NOT_IMPLEMENTED);
|
||||
#else
|
||||
ASSERT_EQ(result, WB_RESULT_KEY_UNAVAILABLE);
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxGetSecretStringTest, KeyUnavailableForNonContentKey) {
|
||||
TestLicenseBuilder::Settings settings;
|
||||
LoadLicense(settings);
|
||||
|
||||
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_KEY_UNAVAILABLE);
|
||||
auto result = WB_License_GetSecretString(
|
||||
whitebox_, WB_CIPHER_MODE_CBC, non_content_key_id_.data(),
|
||||
non_content_key_id_.size(), secret_string_.data(), &secret_string_size_);
|
||||
#ifdef ALWAYS_DECRYPT_TO_CLEAR
|
||||
ASSERT_EQ(result, WB_RESULT_NOT_IMPLEMENTED);
|
||||
#else
|
||||
ASSERT_EQ(result, WB_RESULT_KEY_UNAVAILABLE);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Under normal circumstances, a hardware key should be dropped. The exception
|
||||
@@ -241,12 +291,16 @@ TEST_F(LicenseWhiteboxGetSecretStringTest,
|
||||
TestLicenseBuilder::Settings settings;
|
||||
LoadLicense(settings);
|
||||
|
||||
ASSERT_EQ(WB_License_GetSecretString(
|
||||
whitebox_, WB_CIPHER_MODE_CBC,
|
||||
golden_data_.CBCContent().hardware_key.id.data(),
|
||||
golden_data_.CBCContent().hardware_key.id.size(),
|
||||
secret_string_.data(), &secret_string_size_),
|
||||
WB_RESULT_INSUFFICIENT_SECURITY_LEVEL);
|
||||
auto result = WB_License_GetSecretString(
|
||||
whitebox_, WB_CIPHER_MODE_CBC,
|
||||
golden_data_.CBCContent().hardware_key.id.data(),
|
||||
golden_data_.CBCContent().hardware_key.id.size(), secret_string_.data(),
|
||||
&secret_string_size_);
|
||||
#ifdef ALWAYS_DECRYPT_TO_CLEAR
|
||||
ASSERT_EQ(result, WB_RESULT_NOT_IMPLEMENTED);
|
||||
#else
|
||||
ASSERT_EQ(result, WB_RESULT_INSUFFICIENT_PERMISSIONS);
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxGetSecretStringTest, BufferTooSmall) {
|
||||
@@ -258,29 +312,37 @@ TEST_F(LicenseWhiteboxGetSecretStringTest, BufferTooSmall) {
|
||||
// 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,
|
||||
golden_data_.CBCContent().software_crypto_key.id.data(),
|
||||
golden_data_.CBCContent().software_crypto_key.id.size(),
|
||||
secret_string_.data(), &secret_string_size_),
|
||||
WB_RESULT_BUFFER_TOO_SMALL);
|
||||
auto result = WB_License_GetSecretString(
|
||||
whitebox_, WB_CIPHER_MODE_CBC,
|
||||
golden_data_.CBCContent().software_crypto_key.id.data(),
|
||||
golden_data_.CBCContent().software_crypto_key.id.size(),
|
||||
secret_string_.data(), &secret_string_size_);
|
||||
#ifdef ALWAYS_DECRYPT_TO_CLEAR
|
||||
ASSERT_EQ(result, WB_RESULT_NOT_IMPLEMENTED);
|
||||
#else
|
||||
ASSERT_EQ(result, 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_, 1u);
|
||||
#endif
|
||||
}
|
||||
|
||||
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,
|
||||
golden_data_.CBCContent().software_crypto_key.id.data(),
|
||||
golden_data_.CBCContent().software_crypto_key.id.size(),
|
||||
secret_string_.data(), &secret_string_size_),
|
||||
WB_RESULT_INVALID_STATE);
|
||||
auto result = WB_License_GetSecretString(
|
||||
whitebox_, WB_CIPHER_MODE_CBC,
|
||||
golden_data_.CBCContent().software_crypto_key.id.data(),
|
||||
golden_data_.CBCContent().software_crypto_key.id.size(),
|
||||
secret_string_.data(), &secret_string_size_);
|
||||
#ifdef ALWAYS_DECRYPT_TO_CLEAR
|
||||
ASSERT_EQ(result, WB_RESULT_NOT_IMPLEMENTED);
|
||||
#else
|
||||
ASSERT_EQ(result, WB_RESULT_INVALID_STATE);
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_F(LicenseWhiteboxGetSecretStringTest, KeyUnavailableForInvalidKey) {
|
||||
@@ -291,12 +353,16 @@ TEST_F(LicenseWhiteboxGetSecretStringTest, KeyUnavailableForInvalidKey) {
|
||||
settings.include_content_key_iv = false;
|
||||
LoadLicense(settings);
|
||||
|
||||
ASSERT_EQ(WB_License_GetSecretString(
|
||||
whitebox_, WB_CIPHER_MODE_CBC,
|
||||
golden_data_.CBCContent().software_crypto_key.id.data(),
|
||||
golden_data_.CBCContent().software_crypto_key.id.size(),
|
||||
secret_string_.data(), &secret_string_size_),
|
||||
WB_RESULT_KEY_UNAVAILABLE);
|
||||
auto result = WB_License_GetSecretString(
|
||||
whitebox_, WB_CIPHER_MODE_CBC,
|
||||
golden_data_.CBCContent().software_crypto_key.id.data(),
|
||||
golden_data_.CBCContent().software_crypto_key.id.size(),
|
||||
secret_string_.data(), &secret_string_size_);
|
||||
#ifdef ALWAYS_DECRYPT_TO_CLEAR
|
||||
ASSERT_EQ(result, WB_RESULT_NOT_IMPLEMENTED);
|
||||
#else
|
||||
ASSERT_EQ(result, WB_RESULT_KEY_UNAVAILABLE);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
@@ -66,6 +66,7 @@ TEST_P(LicenseWhiteboxKeyControlBlockTest, Decrypt) {
|
||||
WB_RESULT_OK);
|
||||
}
|
||||
|
||||
#ifndef ALWAYS_DECRYPT_TO_CLEAR
|
||||
TEST_P(LicenseWhiteboxKeyControlBlockTest, MaskedDecrypt) {
|
||||
TestLicenseBuilder builder;
|
||||
builder.GetSettings().key_control_block = kcb_;
|
||||
@@ -100,6 +101,7 @@ TEST_P(LicenseWhiteboxKeyControlBlockTest, MaskedDecrypt) {
|
||||
&plaintext_size),
|
||||
WB_RESULT_OK);
|
||||
}
|
||||
#endif
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
AllCombination,
|
||||
@@ -110,4 +112,49 @@ INSTANTIATE_TEST_SUITE_P(
|
||||
::testing::Values(TestLicenseBuilder::OdkVersion::kNone,
|
||||
TestLicenseBuilder::OdkVersion::k16_3,
|
||||
TestLicenseBuilder::OdkVersion::k16_5)));
|
||||
|
||||
class LicenseWhiteboxKeyControlBlockSingleTest
|
||||
: public LicenseWhiteboxTestBase {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
LicenseWhiteboxTestBase::SetUp();
|
||||
server_ = TestServer::CreateDualKey();
|
||||
}
|
||||
|
||||
std::unique_ptr<TestServer> server_;
|
||||
};
|
||||
|
||||
TEST_F(LicenseWhiteboxKeyControlBlockSingleTest, KcbHeaderError) {
|
||||
TestLicenseBuilder builder;
|
||||
builder.GetSettings().odk_version = TestLicenseBuilder::OdkVersion::k16_5;
|
||||
builder.GetSettings().kcb_header_error = true;
|
||||
|
||||
const auto& content = golden_data_.CBCContent();
|
||||
const auto& key = content.software_crypto_key;
|
||||
|
||||
builder.AddContentKey(key);
|
||||
|
||||
License license;
|
||||
builder.Build(*server_, &license);
|
||||
|
||||
ASSERT_EQ(
|
||||
WB_License_ProcessLicenseResponse(
|
||||
whitebox_, WB_LICENSE_KEY_MODE_DUAL_KEY, license.core_message.data(),
|
||||
license.core_message.size(), license.message.data(),
|
||||
license.message.size(), license.signature.data(),
|
||||
license.signature.size(), license.session_key.data(),
|
||||
license.session_key.size(), kNoProviderKeyId, license.request.data(),
|
||||
license.request.size()),
|
||||
WB_RESULT_OK);
|
||||
|
||||
std::vector<uint8_t> plaintext(content.ciphertext.size());
|
||||
size_t plaintext_size = plaintext.size();
|
||||
auto result = WB_License_Decrypt(
|
||||
whitebox_, WB_CIPHER_MODE_CBC, key.id.data(), key.id.size(),
|
||||
content.ciphertext.data(), content.ciphertext.size(), content.iv.data(),
|
||||
content.iv.size(), plaintext.data(), &plaintext_size);
|
||||
// Either it works or the key should be skipped.
|
||||
if (result != WB_RESULT_OK)
|
||||
EXPECT_EQ(result, WB_RESULT_KEY_UNAVAILABLE);
|
||||
}
|
||||
} // namespace widevine
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -80,6 +80,50 @@ WEAK WB_Result WB_License_SignPstReport(const WB_License_Whitebox* whitebox,
|
||||
return WB_RESULT_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
WEAK WB_Result WB_License_GenericEncrypt(const WB_License_Whitebox* whitebox,
|
||||
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) {
|
||||
return WB_RESULT_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
WEAK WB_Result WB_License_GenericDecrypt(const WB_License_Whitebox* whitebox,
|
||||
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) {
|
||||
return WB_RESULT_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
WEAK WB_Result WB_License_GenericSign(const WB_License_Whitebox* whitebox,
|
||||
const uint8_t* key_id,
|
||||
size_t key_id_size,
|
||||
const uint8_t* message,
|
||||
size_t message_size,
|
||||
uint8_t* output_data,
|
||||
size_t* output_data_size) {
|
||||
return WB_RESULT_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
WEAK WB_Result WB_License_GenericVerify(const WB_License_Whitebox* whitebox,
|
||||
const uint8_t* key_id,
|
||||
size_t key_id_size,
|
||||
const uint8_t* message,
|
||||
size_t message_size,
|
||||
const uint8_t* signature,
|
||||
size_t signature_size) {
|
||||
return WB_RESULT_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
namespace widevine {
|
||||
|
||||
WEAK std::vector<uint8_t> GetLicenseWhiteboxProviderKeysInitData() {
|
||||
|
||||
@@ -91,12 +91,15 @@ TEST_P(LicenseWhiteboxSecurityLevelTest, CanLoadAndUseKey) {
|
||||
plaintext_size_ = content.ciphertext.size();
|
||||
plaintext_.resize(plaintext_size_);
|
||||
|
||||
EXPECT_EQ(WB_License_MaskedDecrypt(
|
||||
whitebox_, WB_CIPHER_MODE_CBC, key->id.data(), key->id.size(),
|
||||
content.ciphertext.data(), content.ciphertext.size(),
|
||||
content.iv.data(), content.iv.size(), plaintext_.data(),
|
||||
&plaintext_size_),
|
||||
WB_RESULT_OK);
|
||||
auto result = WB_License_MaskedDecrypt(
|
||||
whitebox_, WB_CIPHER_MODE_CBC, key->id.data(), key->id.size(),
|
||||
content.ciphertext.data(), content.ciphertext.size(), content.iv.data(),
|
||||
content.iv.size(), plaintext_.data(), &plaintext_size_);
|
||||
#ifdef ALWAYS_DECRYPT_TO_CLEAR
|
||||
ASSERT_EQ(result, WB_RESULT_NOT_IMPLEMENTED);
|
||||
#else
|
||||
ASSERT_EQ(result, WB_RESULT_OK);
|
||||
#endif
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
|
||||
@@ -316,23 +316,6 @@ INSTANTIATE_TEST_SUITE_P(
|
||||
RemoteAttestation::kUnavailable,
|
||||
VerificationStatus::kUnavailable,
|
||||
Mode::kDecryptFail))));
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
NoRemoteAttestationNoVerificationMaskedDecrypt,
|
||||
RemoteAttestationAndVerificationTest,
|
||||
::testing::Combine(
|
||||
::testing::Values(Padding::kNone, Padding::kPKSC8),
|
||||
::testing::Values(std::make_tuple(Key::kCrypto,
|
||||
RemoteAttestation::kUnavailable,
|
||||
VerificationStatus::kUnavailable,
|
||||
Mode::kMaskedDecryptPass),
|
||||
std::make_tuple(Key::kDecode,
|
||||
RemoteAttestation::kUnavailable,
|
||||
VerificationStatus::kUnavailable,
|
||||
Mode::kMaskedDecryptPass),
|
||||
std::make_tuple(Key::kHardware,
|
||||
RemoteAttestation::kUnavailable,
|
||||
VerificationStatus::kUnavailable,
|
||||
Mode::kMaskedDecryptFail))));
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
UnverifiedRemoteAttestationNoVerificationDecrypt,
|
||||
@@ -352,24 +335,6 @@ INSTANTIATE_TEST_SUITE_P(
|
||||
VerificationStatus::kUnavailable,
|
||||
Mode::kDecryptFail))));
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
UnverifiedRemoteAttestationNoVerificationMaskedDecrypt,
|
||||
RemoteAttestationAndVerificationTest,
|
||||
::testing::Combine(
|
||||
::testing::Values(Padding::kNone, Padding::kPKSC8),
|
||||
::testing::Values(std::make_tuple(Key::kCrypto,
|
||||
RemoteAttestation::kUnverified,
|
||||
VerificationStatus::kUnavailable,
|
||||
Mode::kMaskedDecryptPass),
|
||||
std::make_tuple(Key::kDecode,
|
||||
RemoteAttestation::kUnverified,
|
||||
VerificationStatus::kUnavailable,
|
||||
Mode::kMaskedDecryptPass),
|
||||
std::make_tuple(Key::kHardware,
|
||||
RemoteAttestation::kUnverified,
|
||||
VerificationStatus::kUnavailable,
|
||||
Mode::kMaskedDecryptFail))));
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
VerifiedRemoteAttestationNoVerificationDecrypt,
|
||||
RemoteAttestationAndVerificationTest,
|
||||
@@ -388,24 +353,6 @@ INSTANTIATE_TEST_SUITE_P(
|
||||
VerificationStatus::kUnavailable,
|
||||
Mode::kDecryptPass))));
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
VerifiedRemoteAttestationNoVerificationMaskedDecrypt,
|
||||
RemoteAttestationAndVerificationTest,
|
||||
::testing::Combine(
|
||||
::testing::Values(Padding::kNone, Padding::kPKSC8),
|
||||
::testing::Values(std::make_tuple(Key::kCrypto,
|
||||
RemoteAttestation::kVerified,
|
||||
VerificationStatus::kUnavailable,
|
||||
Mode::kMaskedDecryptPass),
|
||||
std::make_tuple(Key::kDecode,
|
||||
RemoteAttestation::kVerified,
|
||||
VerificationStatus::kUnavailable,
|
||||
Mode::kMaskedDecryptPass),
|
||||
std::make_tuple(Key::kHardware,
|
||||
RemoteAttestation::kVerified,
|
||||
VerificationStatus::kUnavailable,
|
||||
Mode::kMaskedDecryptPass))));
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
NoRemoteAttestationHardwareVerificationDecrypt,
|
||||
RemoteAttestationAndVerificationTest,
|
||||
@@ -424,24 +371,6 @@ INSTANTIATE_TEST_SUITE_P(
|
||||
VerificationStatus::kHardwareVerified,
|
||||
Mode::kDecryptPass))));
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
NoRemoteAttestationHardwareVerificationMaskedDecrypt,
|
||||
RemoteAttestationAndVerificationTest,
|
||||
::testing::Combine(
|
||||
::testing::Values(Padding::kNone, Padding::kPKSC8),
|
||||
::testing::Values(std::make_tuple(Key::kCrypto,
|
||||
RemoteAttestation::kUnavailable,
|
||||
VerificationStatus::kHardwareVerified,
|
||||
Mode::kMaskedDecryptPass),
|
||||
std::make_tuple(Key::kDecode,
|
||||
RemoteAttestation::kUnavailable,
|
||||
VerificationStatus::kHardwareVerified,
|
||||
Mode::kMaskedDecryptPass),
|
||||
std::make_tuple(Key::kHardware,
|
||||
RemoteAttestation::kUnavailable,
|
||||
VerificationStatus::kHardwareVerified,
|
||||
Mode::kMaskedDecryptPass))));
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
UnverifiedRemoteAttestationHardwareVerificationDecrypt,
|
||||
RemoteAttestationAndVerificationTest,
|
||||
@@ -460,24 +389,6 @@ INSTANTIATE_TEST_SUITE_P(
|
||||
VerificationStatus::kHardwareVerified,
|
||||
Mode::kDecryptFail))));
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
UnverifiedRemoteAttestationHardwareVerificationMaskedDecrypt,
|
||||
RemoteAttestationAndVerificationTest,
|
||||
::testing::Combine(
|
||||
::testing::Values(Padding::kNone, Padding::kPKSC8),
|
||||
::testing::Values(std::make_tuple(Key::kCrypto,
|
||||
RemoteAttestation::kUnverified,
|
||||
VerificationStatus::kHardwareVerified,
|
||||
Mode::kMaskedDecryptPass),
|
||||
std::make_tuple(Key::kDecode,
|
||||
RemoteAttestation::kUnverified,
|
||||
VerificationStatus::kHardwareVerified,
|
||||
Mode::kMaskedDecryptPass),
|
||||
std::make_tuple(Key::kHardware,
|
||||
RemoteAttestation::kUnverified,
|
||||
VerificationStatus::kHardwareVerified,
|
||||
Mode::kMaskedDecryptFail))));
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
VerifiedRemoteAttestationHardwareVerificationDecrypt,
|
||||
RemoteAttestationAndVerificationTest,
|
||||
@@ -496,24 +407,6 @@ INSTANTIATE_TEST_SUITE_P(
|
||||
VerificationStatus::kHardwareVerified,
|
||||
Mode::kDecryptPass))));
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
VerifiedRemoteAttestationHardwareVerificationMaskedDecrypt,
|
||||
RemoteAttestationAndVerificationTest,
|
||||
::testing::Combine(
|
||||
::testing::Values(Padding::kNone, Padding::kPKSC8),
|
||||
::testing::Values(std::make_tuple(Key::kCrypto,
|
||||
RemoteAttestation::kVerified,
|
||||
VerificationStatus::kHardwareVerified,
|
||||
Mode::kMaskedDecryptPass),
|
||||
std::make_tuple(Key::kDecode,
|
||||
RemoteAttestation::kVerified,
|
||||
VerificationStatus::kHardwareVerified,
|
||||
Mode::kMaskedDecryptPass),
|
||||
std::make_tuple(Key::kHardware,
|
||||
RemoteAttestation::kVerified,
|
||||
VerificationStatus::kHardwareVerified,
|
||||
Mode::kMaskedDecryptPass))));
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
NoRemoteAttestationOtherVerificationDecrypt,
|
||||
RemoteAttestationAndVerificationTest,
|
||||
@@ -532,24 +425,6 @@ INSTANTIATE_TEST_SUITE_P(
|
||||
VerificationStatus::kOther,
|
||||
Mode::kDecryptFail))));
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
NoRemoteAttestationOtherVerificationMaskedDecrypt,
|
||||
RemoteAttestationAndVerificationTest,
|
||||
::testing::Combine(
|
||||
::testing::Values(Padding::kNone, Padding::kPKSC8),
|
||||
::testing::Values(std::make_tuple(Key::kCrypto,
|
||||
RemoteAttestation::kUnavailable,
|
||||
VerificationStatus::kOther,
|
||||
Mode::kMaskedDecryptPass),
|
||||
std::make_tuple(Key::kDecode,
|
||||
RemoteAttestation::kUnavailable,
|
||||
VerificationStatus::kOther,
|
||||
Mode::kMaskedDecryptPass),
|
||||
std::make_tuple(Key::kHardware,
|
||||
RemoteAttestation::kUnavailable,
|
||||
VerificationStatus::kOther,
|
||||
Mode::kMaskedDecryptFail))));
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
UnverifiedRemoteAttestationOtherVerificationDecrypt,
|
||||
RemoteAttestationAndVerificationTest,
|
||||
@@ -568,6 +443,151 @@ INSTANTIATE_TEST_SUITE_P(
|
||||
VerificationStatus::kOther,
|
||||
Mode::kDecryptFail))));
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
VerifiedRemoteAttestationOtherVerificationDecrypt,
|
||||
RemoteAttestationAndVerificationTest,
|
||||
::testing::Combine(
|
||||
::testing::Values(Padding::kNone, Padding::kPKSC8),
|
||||
::testing::Values(std::make_tuple(Key::kCrypto,
|
||||
RemoteAttestation::kVerified,
|
||||
VerificationStatus::kOther,
|
||||
Mode::kDecryptPass),
|
||||
std::make_tuple(Key::kDecode,
|
||||
RemoteAttestation::kVerified,
|
||||
VerificationStatus::kOther,
|
||||
Mode::kDecryptFail),
|
||||
std::make_tuple(Key::kHardware,
|
||||
RemoteAttestation::kVerified,
|
||||
VerificationStatus::kOther,
|
||||
Mode::kDecryptFail))));
|
||||
|
||||
#ifndef ALWAYS_DECRYPT_TO_CLEAR
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
NoRemoteAttestationNoVerificationMaskedDecrypt,
|
||||
RemoteAttestationAndVerificationTest,
|
||||
::testing::Combine(
|
||||
::testing::Values(Padding::kNone, Padding::kPKSC8),
|
||||
::testing::Values(std::make_tuple(Key::kCrypto,
|
||||
RemoteAttestation::kUnavailable,
|
||||
VerificationStatus::kUnavailable,
|
||||
Mode::kMaskedDecryptPass),
|
||||
std::make_tuple(Key::kDecode,
|
||||
RemoteAttestation::kUnavailable,
|
||||
VerificationStatus::kUnavailable,
|
||||
Mode::kMaskedDecryptPass),
|
||||
std::make_tuple(Key::kHardware,
|
||||
RemoteAttestation::kUnavailable,
|
||||
VerificationStatus::kUnavailable,
|
||||
Mode::kMaskedDecryptFail))));
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
UnverifiedRemoteAttestationNoVerificationMaskedDecrypt,
|
||||
RemoteAttestationAndVerificationTest,
|
||||
::testing::Combine(
|
||||
::testing::Values(Padding::kNone, Padding::kPKSC8),
|
||||
::testing::Values(std::make_tuple(Key::kCrypto,
|
||||
RemoteAttestation::kUnverified,
|
||||
VerificationStatus::kUnavailable,
|
||||
Mode::kMaskedDecryptPass),
|
||||
std::make_tuple(Key::kDecode,
|
||||
RemoteAttestation::kUnverified,
|
||||
VerificationStatus::kUnavailable,
|
||||
Mode::kMaskedDecryptPass),
|
||||
std::make_tuple(Key::kHardware,
|
||||
RemoteAttestation::kUnverified,
|
||||
VerificationStatus::kUnavailable,
|
||||
Mode::kMaskedDecryptFail))));
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
VerifiedRemoteAttestationNoVerificationMaskedDecrypt,
|
||||
RemoteAttestationAndVerificationTest,
|
||||
::testing::Combine(
|
||||
::testing::Values(Padding::kNone, Padding::kPKSC8),
|
||||
::testing::Values(std::make_tuple(Key::kCrypto,
|
||||
RemoteAttestation::kVerified,
|
||||
VerificationStatus::kUnavailable,
|
||||
Mode::kMaskedDecryptPass),
|
||||
std::make_tuple(Key::kDecode,
|
||||
RemoteAttestation::kVerified,
|
||||
VerificationStatus::kUnavailable,
|
||||
Mode::kMaskedDecryptPass),
|
||||
std::make_tuple(Key::kHardware,
|
||||
RemoteAttestation::kVerified,
|
||||
VerificationStatus::kUnavailable,
|
||||
Mode::kMaskedDecryptPass))));
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
NoRemoteAttestationHardwareVerificationMaskedDecrypt,
|
||||
RemoteAttestationAndVerificationTest,
|
||||
::testing::Combine(
|
||||
::testing::Values(Padding::kNone, Padding::kPKSC8),
|
||||
::testing::Values(std::make_tuple(Key::kCrypto,
|
||||
RemoteAttestation::kUnavailable,
|
||||
VerificationStatus::kHardwareVerified,
|
||||
Mode::kMaskedDecryptPass),
|
||||
std::make_tuple(Key::kDecode,
|
||||
RemoteAttestation::kUnavailable,
|
||||
VerificationStatus::kHardwareVerified,
|
||||
Mode::kMaskedDecryptPass),
|
||||
std::make_tuple(Key::kHardware,
|
||||
RemoteAttestation::kUnavailable,
|
||||
VerificationStatus::kHardwareVerified,
|
||||
Mode::kMaskedDecryptPass))));
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
UnverifiedRemoteAttestationHardwareVerificationMaskedDecrypt,
|
||||
RemoteAttestationAndVerificationTest,
|
||||
::testing::Combine(
|
||||
::testing::Values(Padding::kNone, Padding::kPKSC8),
|
||||
::testing::Values(std::make_tuple(Key::kCrypto,
|
||||
RemoteAttestation::kUnverified,
|
||||
VerificationStatus::kHardwareVerified,
|
||||
Mode::kMaskedDecryptPass),
|
||||
std::make_tuple(Key::kDecode,
|
||||
RemoteAttestation::kUnverified,
|
||||
VerificationStatus::kHardwareVerified,
|
||||
Mode::kMaskedDecryptPass),
|
||||
std::make_tuple(Key::kHardware,
|
||||
RemoteAttestation::kUnverified,
|
||||
VerificationStatus::kHardwareVerified,
|
||||
Mode::kMaskedDecryptFail))));
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
VerifiedRemoteAttestationHardwareVerificationMaskedDecrypt,
|
||||
RemoteAttestationAndVerificationTest,
|
||||
::testing::Combine(
|
||||
::testing::Values(Padding::kNone, Padding::kPKSC8),
|
||||
::testing::Values(std::make_tuple(Key::kCrypto,
|
||||
RemoteAttestation::kVerified,
|
||||
VerificationStatus::kHardwareVerified,
|
||||
Mode::kMaskedDecryptPass),
|
||||
std::make_tuple(Key::kDecode,
|
||||
RemoteAttestation::kVerified,
|
||||
VerificationStatus::kHardwareVerified,
|
||||
Mode::kMaskedDecryptPass),
|
||||
std::make_tuple(Key::kHardware,
|
||||
RemoteAttestation::kVerified,
|
||||
VerificationStatus::kHardwareVerified,
|
||||
Mode::kMaskedDecryptPass))));
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
NoRemoteAttestationOtherVerificationMaskedDecrypt,
|
||||
RemoteAttestationAndVerificationTest,
|
||||
::testing::Combine(
|
||||
::testing::Values(Padding::kNone, Padding::kPKSC8),
|
||||
::testing::Values(std::make_tuple(Key::kCrypto,
|
||||
RemoteAttestation::kUnavailable,
|
||||
VerificationStatus::kOther,
|
||||
Mode::kMaskedDecryptPass),
|
||||
std::make_tuple(Key::kDecode,
|
||||
RemoteAttestation::kUnavailable,
|
||||
VerificationStatus::kOther,
|
||||
Mode::kMaskedDecryptPass),
|
||||
std::make_tuple(Key::kHardware,
|
||||
RemoteAttestation::kUnavailable,
|
||||
VerificationStatus::kOther,
|
||||
Mode::kMaskedDecryptFail))));
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
UnverifiedRemoteAttestationOtherVerificationMaskedDecrypt,
|
||||
RemoteAttestationAndVerificationTest,
|
||||
@@ -586,24 +606,6 @@ INSTANTIATE_TEST_SUITE_P(
|
||||
VerificationStatus::kOther,
|
||||
Mode::kMaskedDecryptFail))));
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
VerifiedRemoteAttestationOtherVerificationDecrypt,
|
||||
RemoteAttestationAndVerificationTest,
|
||||
::testing::Combine(
|
||||
::testing::Values(Padding::kNone, Padding::kPKSC8),
|
||||
::testing::Values(std::make_tuple(Key::kCrypto,
|
||||
RemoteAttestation::kVerified,
|
||||
VerificationStatus::kOther,
|
||||
Mode::kDecryptPass),
|
||||
std::make_tuple(Key::kDecode,
|
||||
RemoteAttestation::kVerified,
|
||||
VerificationStatus::kOther,
|
||||
Mode::kDecryptFail),
|
||||
std::make_tuple(Key::kHardware,
|
||||
RemoteAttestation::kVerified,
|
||||
VerificationStatus::kOther,
|
||||
Mode::kDecryptFail))));
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
VerifiedRemoteAttestationOtherVerificationMaskedDecrypt,
|
||||
RemoteAttestationAndVerificationTest,
|
||||
@@ -621,4 +623,5 @@ INSTANTIATE_TEST_SUITE_P(
|
||||
RemoteAttestation::kVerified,
|
||||
VerificationStatus::kOther,
|
||||
Mode::kMaskedDecryptFail))));
|
||||
#endif
|
||||
} // namespace widevine
|
||||
|
||||
@@ -33,8 +33,9 @@ typedef enum {
|
||||
// exists for the given key id.
|
||||
WB_RESULT_KEY_UNAVAILABLE = 6,
|
||||
|
||||
// The requested key's security level is not sufficient for the operation.
|
||||
WB_RESULT_INSUFFICIENT_SECURITY_LEVEL = 7,
|
||||
// The requested key's security level or flags do not allow the operation.
|
||||
WB_RESULT_INSUFFICIENT_PERMISSIONS = 7,
|
||||
WB_RESULT_INSUFFICIENT_SECURITY_LEVEL = WB_RESULT_INSUFFICIENT_PERMISSIONS,
|
||||
|
||||
// The input data failed to be verified. This may happen if the data was
|
||||
// corrupted or tampered.
|
||||
|
||||
@@ -49,6 +49,16 @@ struct EntitlementKeyData {
|
||||
Aes256Key key;
|
||||
};
|
||||
|
||||
struct GenericKeyData {
|
||||
// The unique key id for this key. Any instance with this id should contain
|
||||
// the same level and key as this.
|
||||
KeyId id;
|
||||
|
||||
Aes256Key key;
|
||||
uint32_t kcb_flags = 0;
|
||||
uint8_t key_size = 32u;
|
||||
};
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
#endif // WHITEBOX_API_TEST_KEY_TYPES_H_
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <array>
|
||||
#include <ctime>
|
||||
#include <map>
|
||||
#include <type_traits>
|
||||
|
||||
#include "api/test_license_provider_keys.h"
|
||||
#include "base/check.h"
|
||||
@@ -140,8 +141,7 @@ video_widevine::License_KeyContainer_SecurityLevel SecurityLevelToProto(
|
||||
}
|
||||
|
||||
KeyControlBlock CreateKeyControlBlock(
|
||||
video_widevine::License_KeyContainer_SecurityLevel level,
|
||||
video_widevine::License_KeyContainer_KeyControl* key_control) {
|
||||
video_widevine::License_KeyContainer_SecurityLevel level) {
|
||||
// The key control block is an 128 bit structure containing the following
|
||||
// fields. The fields are defined to be in big-endian byte order.
|
||||
//
|
||||
@@ -331,10 +331,13 @@ void AddContentKeyToContainer(const ContentKeyData& key_data,
|
||||
break;
|
||||
|
||||
case TestLicenseBuilder::KeyControlBlock::kClear: {
|
||||
auto* key_control = container->mutable_key_control();
|
||||
const auto key_control_block = CreateKeyControlBlock(
|
||||
SecurityLevelToProto(key_data.level), key_control);
|
||||
auto key_control_block =
|
||||
CreateKeyControlBlock(SecurityLevelToProto(key_data.level));
|
||||
if (settings.kcb_header_error) {
|
||||
key_control_block[0] ^= 0xaa;
|
||||
}
|
||||
|
||||
auto* key_control = container->mutable_key_control();
|
||||
key_control->set_key_control_block(key_control_block.data(),
|
||||
key_control_block.size());
|
||||
|
||||
@@ -345,14 +348,17 @@ void AddContentKeyToContainer(const ContentKeyData& key_data,
|
||||
// It is only when the key control block is encrypted will the IV be set.
|
||||
// The key control block is encrypted with the content key. This will no
|
||||
// longer be the case in OEMCrypto 17.
|
||||
auto* key_control = container->mutable_key_control();
|
||||
const auto key_control_block = CreateKeyControlBlock(
|
||||
SecurityLevelToProto(key_data.level), key_control);
|
||||
auto key_control_block =
|
||||
CreateKeyControlBlock(SecurityLevelToProto(key_data.level));
|
||||
if (settings.kcb_header_error) {
|
||||
key_control_block[0] ^= 0xaa;
|
||||
}
|
||||
|
||||
const auto key_control_block_iv = DeriveIV(key_control_block);
|
||||
const auto encrypted_key_control_block =
|
||||
Encrypt(key_data.key, key_control_block_iv, key_control_block);
|
||||
|
||||
auto* key_control = container->mutable_key_control();
|
||||
key_control->set_iv(key_control_block_iv);
|
||||
key_control->set_key_control_block(encrypted_key_control_block.data(),
|
||||
encrypted_key_control_block.size());
|
||||
@@ -381,9 +387,39 @@ void AddEntitlementKeyToContainer(
|
||||
auto encrypted_key = Encrypt(container_key, key_iv, key);
|
||||
container->set_key(encrypted_key);
|
||||
|
||||
auto* key_control = container->mutable_key_control();
|
||||
const auto key_control_block =
|
||||
CreateKeyControlBlock(SecurityLevelToProto(key_data.level), key_control);
|
||||
CreateKeyControlBlock(SecurityLevelToProto(key_data.level));
|
||||
auto* key_control = container->mutable_key_control();
|
||||
key_control->set_key_control_block(key_control_block.data(),
|
||||
key_control_block.size());
|
||||
}
|
||||
|
||||
void AddGenericKeyToContainer(
|
||||
const GenericKeyData& key_data, const std::string& container_key,
|
||||
video_widevine::License_KeyContainer* container) {
|
||||
container->set_type(
|
||||
video_widevine::License_KeyContainer_KeyType_OPERATOR_SESSION);
|
||||
container->set_id(key_data.id.data(), key_data.id.size());
|
||||
|
||||
// To avoid having to define a key iv for each key, derive a key iv from the
|
||||
// key. This will allows us to have a different IVs between keys but keep it
|
||||
// deterministic.
|
||||
const auto key_iv = DeriveIV(key_data.key);
|
||||
|
||||
container->set_iv(key_iv);
|
||||
|
||||
auto encrypted_key = Encrypt(container_key, key_iv, key_data.key);
|
||||
CHECK_LE(key_data.key_size, encrypted_key.size());
|
||||
encrypted_key.resize(key_data.key_size);
|
||||
container->set_key(encrypted_key);
|
||||
|
||||
auto key_control_block = CreateKeyControlBlock(
|
||||
video_widevine::License_KeyContainer_SecurityLevel_SW_SECURE_CRYPTO);
|
||||
key_control_block[12] |= key_data.kcb_flags >> 24;
|
||||
key_control_block[13] |= key_data.kcb_flags >> 16;
|
||||
key_control_block[14] |= key_data.kcb_flags >> 8;
|
||||
key_control_block[15] |= key_data.kcb_flags;
|
||||
auto* key_control = container->mutable_key_control();
|
||||
key_control->set_key_control_block(key_control_block.data(),
|
||||
key_control_block.size());
|
||||
}
|
||||
@@ -485,6 +521,10 @@ void TestLicenseBuilder::AddEntitlementKey(const EntitlementKeyData& key) {
|
||||
entitlement_keys_.push_back(key);
|
||||
}
|
||||
|
||||
void TestLicenseBuilder::AddGenericKey(const GenericKeyData& key) {
|
||||
generic_keys_.push_back(key);
|
||||
}
|
||||
|
||||
void TestLicenseBuilder::AddOperatorSessionKey(const KeyId& id) {
|
||||
operator_session_keys_.push_back(id);
|
||||
}
|
||||
@@ -532,8 +572,12 @@ void TestLicenseBuilder::Build(const TestServer& server,
|
||||
response.add_key());
|
||||
}
|
||||
|
||||
// Cannot have both content keys and entitlement keys.
|
||||
CHECK(content_keys_.empty() || entitlement_keys_.empty());
|
||||
// Can only have one of content, entitlement, or generic keys.
|
||||
const int num_key_types =
|
||||
(content_keys_.empty() ? 0 : 1) +
|
||||
(entitlement_keys_.empty() ? 0 : 1) +
|
||||
(generic_keys_.empty() ? 0 : 1);
|
||||
CHECK_LE(num_key_types, 1);
|
||||
|
||||
for (const auto& key : content_keys_) {
|
||||
AddContentKeyToContainer(key, settings_, container_key_,
|
||||
@@ -542,6 +586,9 @@ void TestLicenseBuilder::Build(const TestServer& server,
|
||||
for (const auto& key : entitlement_keys_) {
|
||||
AddEntitlementKeyToContainer(key, container_key_, response.add_key());
|
||||
}
|
||||
for (const auto& key : generic_keys_) {
|
||||
AddGenericKeyToContainer(key, container_key_, response.add_key());
|
||||
}
|
||||
|
||||
for (const auto& key : operator_session_keys_) {
|
||||
AddOperatorSessionKeyToContainer(key, response.add_key());
|
||||
|
||||
@@ -86,6 +86,9 @@ class TestLicenseBuilder {
|
||||
bool include_signing_key_key = true;
|
||||
bool include_signing_key_type = true;
|
||||
|
||||
// Whether to insert an error in the KCB header
|
||||
bool kcb_header_error = false;
|
||||
|
||||
// Our content key's key and ivs should always be 16 bytes (see AesKey
|
||||
// definition), but we can use these controls to cut them short. If these
|
||||
// values are larger than 16, the key/iv with be padded. In order to
|
||||
@@ -122,6 +125,8 @@ class TestLicenseBuilder {
|
||||
|
||||
void AddEntitlementKey(const EntitlementKeyData& key);
|
||||
|
||||
void AddGenericKey(const GenericKeyData& key);
|
||||
|
||||
// The key id will matter as we will need to reference it, but the key won't
|
||||
// matter since we are only using it as a means to verify that a non-content
|
||||
// key can't be used as a content key.
|
||||
@@ -149,6 +154,7 @@ class TestLicenseBuilder {
|
||||
|
||||
std::vector<ContentKeyData> content_keys_;
|
||||
std::vector<EntitlementKeyData> entitlement_keys_;
|
||||
std::vector<GenericKeyData> generic_keys_;
|
||||
std::vector<SigningKey> signing_keys_;
|
||||
std::vector<KeyId> operator_session_keys_;
|
||||
};
|
||||
|
||||
@@ -41,6 +41,7 @@ const int kAes128KeySizeBits = 128;
|
||||
const int kAes128KeySizeBytes = 16;
|
||||
const int kAes256KeySizeBytes = 32;
|
||||
const char kKeyboxV3Label[] = "Keyboxv3";
|
||||
const int kSha256HmacSizeBytes = SHA256_DIGEST_LENGTH;
|
||||
|
||||
const int kAesBlockSizeBits = AES_BLOCK_SIZE * 8;
|
||||
const int kAesMaxDerivedBlocks = 255;
|
||||
|
||||
@@ -35,6 +35,7 @@ extern const int kIvSizeBits;
|
||||
extern const int kAes128KeySizeBits;
|
||||
extern const int kAes128KeySizeBytes;
|
||||
extern const char kKeyboxV3Label[];
|
||||
extern const int kSha256HmacSizeBytes;
|
||||
|
||||
extern const uint32_t kCENCSchemeID; // 'cenc' (AES-CTR): 0x63656E63
|
||||
extern const uint32_t kCBC1SchemeID; // 'cbc1' (AES-CBC): 0x63626331
|
||||
|
||||
@@ -174,6 +174,7 @@ cc_library(
|
||||
"//chromium_deps/cdm/keys:dev_certs",
|
||||
"//chromium_deps/cdm/protos:license_protocol_proto",
|
||||
"//crypto_utils:aes_cbc_decryptor",
|
||||
"//crypto_utils:aes_cbc_encryptor",
|
||||
"//crypto_utils:aes_ctr_encryptor",
|
||||
"//crypto_utils:crypto_util",
|
||||
"//crypto_utils:rsa_key",
|
||||
|
||||
@@ -16,6 +16,7 @@ enum class KeyType {
|
||||
kInvalid,
|
||||
kContentKey,
|
||||
kEntitlementKey,
|
||||
kGenericCryptoKey,
|
||||
};
|
||||
|
||||
struct InternalKey {
|
||||
@@ -23,6 +24,7 @@ struct InternalKey {
|
||||
WB_KeyStatus status = WB_KEY_STATUS_INVALID;
|
||||
KeyType type = KeyType::kInvalid;
|
||||
std::array<uint8_t, 32u> key;
|
||||
uint32_t kcb_flags = 0;
|
||||
|
||||
// These are the permission flags that will be used internally to check if
|
||||
// we can use a key.
|
||||
|
||||
@@ -103,10 +103,14 @@ WB_KeyStatus LicenseParser::GetKeyStatus(
|
||||
|
||||
InternalKey LicenseParser::CreateInternalKey(
|
||||
KeyType key_type, video_widevine::License_KeyContainer_SecurityLevel level,
|
||||
bool is_hw_verified, const std::string& key) {
|
||||
bool is_hw_verified, const std::string& key, uint32_t kcb_flags) {
|
||||
CHECK((kcb_flags & WB_KCB_FLAGS_GENERIC_MASK) == 0 ||
|
||||
key_type == KeyType::kGenericCryptoKey);
|
||||
|
||||
InternalKey internal_key;
|
||||
internal_key.type = key_type;
|
||||
internal_key.status = GetKeyStatus(level, is_hw_verified);
|
||||
internal_key.kcb_flags = kcb_flags;
|
||||
|
||||
// Unless we are going to use the key, we don't want to save this key as it
|
||||
// will only risk exposing it. We only have an entry for it so we can handle
|
||||
|
||||
@@ -42,6 +42,7 @@ class LicenseParser {
|
||||
virtual const std::map<std::string, InternalKey>& GetContentKeys() const = 0;
|
||||
virtual const std::map<std::string, InternalKey>&
|
||||
GetEntitlementKeys() const = 0;
|
||||
virtual const std::map<std::string, InternalKey>& GetGenericKeys() const = 0;
|
||||
|
||||
protected:
|
||||
static bool Decrypt(const std::string& key,
|
||||
@@ -69,7 +70,8 @@ class LicenseParser {
|
||||
KeyType key_type,
|
||||
video_widevine::License_KeyContainer_SecurityLevel level,
|
||||
bool is_hw_verified,
|
||||
const std::string& key);
|
||||
const std::string& key,
|
||||
uint32_t kcb_flags = 0);
|
||||
|
||||
static WB_KeyStatus GetKeyStatus(
|
||||
video_widevine::License_KeyContainer_SecurityLevel level,
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "base/check_op.h"
|
||||
#include "base/logging.h"
|
||||
#include "crypto_utils/aes_cbc_decryptor.h"
|
||||
#include "crypto_utils/aes_cbc_encryptor.h"
|
||||
#include "crypto_utils/aes_ctr_encryptor.h"
|
||||
#include "crypto_utils/crypto_util.h"
|
||||
#include "crypto_utils/rsa_key.h"
|
||||
@@ -40,6 +41,7 @@
|
||||
|
||||
namespace {
|
||||
using AesCbcDecryptor = widevine::AesCbcDecryptor;
|
||||
using AesCbcEncryptor = widevine::AesCbcEncryptor;
|
||||
using AesCtrDecryptor = widevine::AesCtrEncryptor;
|
||||
using KeyContainer = video_widevine::License_KeyContainer;
|
||||
using RsaPrivateKey = widevine::RsaPrivateKey;
|
||||
@@ -113,6 +115,7 @@ struct WB_License_Whitebox {
|
||||
|
||||
std::map<std::string, widevine::InternalKey> content_keys;
|
||||
std::map<std::string, widevine::InternalKey> entitlement_keys;
|
||||
std::map<std::string, widevine::InternalKey> generic_keys;
|
||||
|
||||
std::vector<widevine::LicenseParser::ProviderKey> provider_keys;
|
||||
};
|
||||
@@ -495,6 +498,7 @@ WB_Result WB_License_ProcessLicenseResponse(WB_License_Whitebox* whitebox,
|
||||
|
||||
whitebox->content_keys = parser->GetContentKeys();
|
||||
whitebox->entitlement_keys = parser->GetEntitlementKeys();
|
||||
whitebox->generic_keys = parser->GetGenericKeys();
|
||||
|
||||
whitebox->initialized = true;
|
||||
|
||||
@@ -561,6 +565,30 @@ WB_Result WB_License_LoadEntitledContentKey(WB_License_Whitebox* whitebox,
|
||||
return WB_RESULT_OK;
|
||||
}
|
||||
|
||||
WB_Result WB_License_RemoveEntitledContentKey(WB_License_Whitebox* whitebox,
|
||||
const uint8_t* content_key_id,
|
||||
size_t content_key_id_size) {
|
||||
if (!whitebox || !content_key_id) {
|
||||
DVLOG(1) << "Invalid parameter: null pointer.";
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
if (!whitebox->initialized) {
|
||||
DVLOG(1) << "Invalid state: no license loaded.";
|
||||
return WB_RESULT_INVALID_STATE;
|
||||
}
|
||||
if (whitebox->entitlement_keys.empty()) {
|
||||
DVLOG(1) << "Invalid state: can only remove when using entitlement keys.";
|
||||
return WB_RESULT_INVALID_STATE;
|
||||
}
|
||||
|
||||
if (whitebox->content_keys.erase(MakeString(content_key_id,
|
||||
content_key_id_size)) == 0) {
|
||||
DVLOG(1) << "Content key not found.";
|
||||
return WB_RESULT_KEY_UNAVAILABLE;
|
||||
}
|
||||
return WB_RESULT_OK;
|
||||
}
|
||||
|
||||
|
||||
WB_Result WB_License_QueryKeyStatus(const WB_License_Whitebox* whitebox,
|
||||
WB_KeyQueryType type,
|
||||
@@ -587,6 +615,27 @@ WB_Result WB_License_QueryKeyStatus(const WB_License_Whitebox* whitebox,
|
||||
return WB_RESULT_OK;
|
||||
}
|
||||
|
||||
case WB_KEY_QUERY_TYPE_GENERIC_KEY: {
|
||||
if (key_id == nullptr) {
|
||||
DVLOG(1) << "Invalid parameter: null pointer.";
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (key_id_size == 0) {
|
||||
DVLOG(1) << "Invalid parameter: array size 0.";
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
auto it = whitebox->generic_keys.find({key_id, key_id + key_id_size});
|
||||
|
||||
if (it == whitebox->generic_keys.end()) {
|
||||
return WB_RESULT_KEY_UNAVAILABLE;
|
||||
}
|
||||
|
||||
*key_status = it->second.status;
|
||||
return WB_RESULT_OK;
|
||||
}
|
||||
|
||||
case WB_KEY_QUERY_TYPE_CONTENT_KEY: {
|
||||
if (key_id == nullptr) {
|
||||
DVLOG(1) << "Invalid parameter: null pointer.";
|
||||
@@ -740,6 +789,9 @@ WB_Result WB_License_GetSecretString(const WB_License_Whitebox* whitebox,
|
||||
size_t key_id_size,
|
||||
uint8_t* secret_string,
|
||||
size_t* secret_string_size) {
|
||||
#ifdef ALWAYS_DECRYPT_TO_CLEAR
|
||||
return WB_RESULT_NOT_IMPLEMENTED;
|
||||
#else
|
||||
if (!whitebox || !key_id || !secret_string_size) {
|
||||
DVLOG(1) << "Invalid parameter: null pointer.";
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
@@ -787,12 +839,213 @@ WB_Result WB_License_GetSecretString(const WB_License_Whitebox* whitebox,
|
||||
if (!content_key->can_masked_decrypt()) {
|
||||
DVLOG(1) << "Insufficient security level: key policy does not allow use "
|
||||
"with MaskedDecrypt().";
|
||||
return WB_RESULT_INSUFFICIENT_SECURITY_LEVEL;
|
||||
return WB_RESULT_INSUFFICIENT_PERMISSIONS;
|
||||
}
|
||||
|
||||
CHECK(widevine::MemCopy(secret_pattern.data(), secret_pattern.size(),
|
||||
secret_string, *secret_string_size));
|
||||
return WB_RESULT_OK;
|
||||
#endif
|
||||
}
|
||||
|
||||
WB_Result WB_License_GenericEncrypt(const WB_License_Whitebox* whitebox,
|
||||
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) {
|
||||
if (!whitebox || !key_id || !output_data_size) {
|
||||
DVLOG(1) << "Invalid parameter: null pointer.";
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
if (!CheckAndUpdateSize(input_data_size, output_data_size)) {
|
||||
return WB_RESULT_BUFFER_TOO_SMALL;
|
||||
}
|
||||
|
||||
if (!whitebox->initialized) {
|
||||
DVLOG(1) << "Invalid state: no license loaded.";
|
||||
return WB_RESULT_INVALID_STATE;
|
||||
}
|
||||
|
||||
if (key_id_size == 0) {
|
||||
DVLOG(1) << "Invalid parameter: array size 0.";
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
auto it = whitebox->generic_keys.find({key_id, key_id + key_id_size});
|
||||
if (it == whitebox->generic_keys.end()) {
|
||||
DVLOG(1) << "Generic crypto key not found.";
|
||||
return WB_RESULT_KEY_UNAVAILABLE;
|
||||
}
|
||||
if (!it->second.is_valid()) {
|
||||
DVLOG(1) << "Generic crypto key not valid.";
|
||||
return WB_RESULT_KEY_UNAVAILABLE;
|
||||
}
|
||||
if (!(it->second.kcb_flags & WB_KCB_FLAGS_ALLOW_ENCRYPT)) {
|
||||
return WB_RESULT_INSUFFICIENT_PERMISSIONS;
|
||||
}
|
||||
|
||||
AesCbcEncryptor encryptor;
|
||||
if (!encryptor.SetKey(it->second.key.data(), 16u)) {
|
||||
DVLOG(1) << "Error setting AES key.";
|
||||
return WB_RESULT_INVALID_STATE;
|
||||
}
|
||||
if (!encryptor.Encrypt(iv, iv_size, input_data, input_data_size,
|
||||
output_data)) {
|
||||
DVLOG(1) << "Error encrypting data.";
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
return WB_RESULT_OK;
|
||||
}
|
||||
|
||||
WB_Result WB_License_GenericDecrypt(const WB_License_Whitebox* whitebox,
|
||||
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) {
|
||||
if (!whitebox || !key_id || !output_data_size) {
|
||||
DVLOG(1) << "Invalid parameter: null pointer.";
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
if (!CheckAndUpdateSize(input_data_size, output_data_size)) {
|
||||
return WB_RESULT_BUFFER_TOO_SMALL;
|
||||
}
|
||||
|
||||
if (!whitebox->initialized) {
|
||||
DVLOG(1) << "Invalid state: no license loaded.";
|
||||
return WB_RESULT_INVALID_STATE;
|
||||
}
|
||||
|
||||
if (key_id_size == 0) {
|
||||
DVLOG(1) << "Invalid parameter: array size 0.";
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
auto it = whitebox->generic_keys.find({key_id, key_id + key_id_size});
|
||||
if (it == whitebox->generic_keys.end()) {
|
||||
DVLOG(1) << "Generic crypto key not found.";
|
||||
return WB_RESULT_KEY_UNAVAILABLE;
|
||||
}
|
||||
if (!it->second.is_valid()) {
|
||||
DVLOG(1) << "Generic crypto key not valid.";
|
||||
return WB_RESULT_KEY_UNAVAILABLE;
|
||||
}
|
||||
if (!(it->second.kcb_flags & WB_KCB_FLAGS_ALLOW_DECRYPT)) {
|
||||
return WB_RESULT_INSUFFICIENT_PERMISSIONS;
|
||||
}
|
||||
|
||||
AesCbcDecryptor decryptor;
|
||||
if (!decryptor.SetKey(it->second.key.data(), 16u)) {
|
||||
DVLOG(1) << "Error setting AES key.";
|
||||
return WB_RESULT_INVALID_STATE;
|
||||
}
|
||||
if (!decryptor.Decrypt(iv, iv_size, input_data, input_data_size,
|
||||
output_data)) {
|
||||
DVLOG(1) << "Error decrypting data.";
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
return WB_RESULT_OK;
|
||||
}
|
||||
|
||||
WB_Result WB_License_GenericSign(const WB_License_Whitebox* whitebox,
|
||||
const uint8_t* key_id,
|
||||
size_t key_id_size,
|
||||
const uint8_t* message,
|
||||
size_t message_size,
|
||||
uint8_t* output_data,
|
||||
size_t* output_data_size) {
|
||||
if (!whitebox || !key_id || !message || !output_data_size) {
|
||||
DVLOG(1) << "Invalid parameter: null pointer.";
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
if (!CheckAndUpdateSize(widevine::crypto_util::kSha256HmacSizeBytes,
|
||||
output_data_size)) {
|
||||
return WB_RESULT_BUFFER_TOO_SMALL;
|
||||
}
|
||||
if (!output_data) {
|
||||
DVLOG(1) << "Invalid parameter: null pointer.";
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (!whitebox->initialized) {
|
||||
DVLOG(1) << "Invalid state: no license loaded.";
|
||||
return WB_RESULT_INVALID_STATE;
|
||||
}
|
||||
|
||||
if (key_id_size == 0) {
|
||||
DVLOG(1) << "Invalid parameter: array size 0.";
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
auto it = whitebox->generic_keys.find({key_id, key_id + key_id_size});
|
||||
if (it == whitebox->generic_keys.end()) {
|
||||
DVLOG(1) << "Generic crypto key not found.";
|
||||
return WB_RESULT_KEY_UNAVAILABLE;
|
||||
}
|
||||
if (!it->second.is_valid()) {
|
||||
DVLOG(1) << "Generic crypto key not valid.";
|
||||
return WB_RESULT_KEY_UNAVAILABLE;
|
||||
}
|
||||
if (!(it->second.kcb_flags & WB_KCB_FLAGS_ALLOW_SIGN)) {
|
||||
return WB_RESULT_INSUFFICIENT_PERMISSIONS;
|
||||
}
|
||||
|
||||
std::string result = widevine::crypto_util::CreateSignatureHmacSha256(
|
||||
MakeString(it->second.key.data(), it->second.key.size()),
|
||||
MakeString(message, message_size));
|
||||
memcpy(output_data, result.data(), result.size());
|
||||
return WB_RESULT_OK;
|
||||
}
|
||||
|
||||
WB_Result WB_License_GenericVerify(const WB_License_Whitebox* whitebox,
|
||||
const uint8_t* key_id,
|
||||
size_t key_id_size,
|
||||
const uint8_t* message,
|
||||
size_t message_size,
|
||||
const uint8_t* signature,
|
||||
size_t signature_size) {
|
||||
if (!whitebox || !key_id || !message || !signature) {
|
||||
DVLOG(1) << "Invalid parameter: null pointer.";
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (!whitebox->initialized) {
|
||||
DVLOG(1) << "Invalid state: no license loaded.";
|
||||
return WB_RESULT_INVALID_STATE;
|
||||
}
|
||||
|
||||
if (key_id_size == 0) {
|
||||
DVLOG(1) << "Invalid parameter: array size 0.";
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
auto it = whitebox->generic_keys.find({key_id, key_id + key_id_size});
|
||||
if (it == whitebox->generic_keys.end()) {
|
||||
DVLOG(1) << "Generic crypto key not found.";
|
||||
return WB_RESULT_KEY_UNAVAILABLE;
|
||||
}
|
||||
if (!it->second.is_valid()) {
|
||||
DVLOG(1) << "Generic crypto key not valid.";
|
||||
return WB_RESULT_KEY_UNAVAILABLE;
|
||||
}
|
||||
if (!(it->second.kcb_flags & WB_KCB_FLAGS_ALLOW_VERIFY)) {
|
||||
return WB_RESULT_INSUFFICIENT_PERMISSIONS;
|
||||
}
|
||||
|
||||
if (!widevine::crypto_util::VerifySignatureHmacSha256(
|
||||
MakeString(it->second.key.data(), it->second.key.size()),
|
||||
MakeString(signature, signature_size),
|
||||
MakeString(message, message_size))) {
|
||||
return WB_RESULT_INVALID_SIGNATURE;
|
||||
}
|
||||
return WB_RESULT_OK;
|
||||
}
|
||||
|
||||
WB_Result WB_License_Decrypt(const WB_License_Whitebox* whitebox,
|
||||
@@ -835,7 +1088,7 @@ WB_Result WB_License_Decrypt(const WB_License_Whitebox* whitebox,
|
||||
if (!content_key->can_decrypt()) {
|
||||
DVLOG(1) << "Insufficient security level: key policy does not allow use "
|
||||
"with Decrypt().";
|
||||
return WB_RESULT_INSUFFICIENT_SECURITY_LEVEL;
|
||||
return WB_RESULT_INSUFFICIENT_PERMISSIONS;
|
||||
}
|
||||
|
||||
// DecryptBuffer() will validate the remaining decryption parameters.
|
||||
@@ -854,6 +1107,9 @@ WB_Result WB_License_MaskedDecrypt(const WB_License_Whitebox* whitebox,
|
||||
size_t iv_size,
|
||||
uint8_t* masked_output_data,
|
||||
size_t* masked_output_data_size) {
|
||||
#ifdef ALWAYS_DECRYPT_TO_CLEAR
|
||||
return WB_RESULT_NOT_IMPLEMENTED;
|
||||
#else
|
||||
if (!whitebox || !key_id || !masked_output_data || !masked_output_data_size) {
|
||||
DVLOG(1) << "Invalid parameter: null pointer.";
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
@@ -884,7 +1140,7 @@ WB_Result WB_License_MaskedDecrypt(const WB_License_Whitebox* whitebox,
|
||||
if (!content_key->can_masked_decrypt()) {
|
||||
DVLOG(1) << "Insufficient security level: key policy does not allow use "
|
||||
"with MaskedDecrypt().";
|
||||
return WB_RESULT_INSUFFICIENT_SECURITY_LEVEL;
|
||||
return WB_RESULT_INSUFFICIENT_PERMISSIONS;
|
||||
}
|
||||
|
||||
// DecryptBuffer() will validate all the parameters, so just make sure it is
|
||||
@@ -914,6 +1170,7 @@ WB_Result WB_License_MaskedDecrypt(const WB_License_Whitebox* whitebox,
|
||||
}
|
||||
|
||||
return WB_RESULT_OK;
|
||||
#endif
|
||||
}
|
||||
|
||||
void WB_License_Unmask(const uint8_t* masked_data,
|
||||
|
||||
@@ -9,9 +9,10 @@
|
||||
namespace widevine {
|
||||
namespace {
|
||||
|
||||
bool ExtractLevel(
|
||||
bool ExtractKcbFields(
|
||||
const std::string& key_control_block,
|
||||
video_widevine::License_KeyContainer_SecurityLevel* security_level) {
|
||||
video_widevine::License_KeyContainer_SecurityLevel* security_level,
|
||||
uint32_t* kcb_flags) {
|
||||
// The key control block is an 128 bit structure containing the following
|
||||
// fields. The fields are defined to be in big-endian byte order.
|
||||
//
|
||||
@@ -33,8 +34,12 @@ bool ExtractLevel(
|
||||
return false;
|
||||
}
|
||||
|
||||
auto* kcb = reinterpret_cast<const uint8_t*>(key_control_block.data());
|
||||
*kcb_flags = (kcb[12] << 24u) | (kcb[13] << 16u) | (kcb[14] << 8u) | kcb[15];
|
||||
|
||||
// Extract bits 26..27 from Control Bits.
|
||||
switch ((key_control_block[12] & 0x0C) >> 2) {
|
||||
switch ((*kcb_flags & WB_KCB_FLAGS_SECURITY_LEVEL_MASK) >>
|
||||
WB_KCB_FLAGS_SECURITY_LEVEL_SHIFT) {
|
||||
case 0:
|
||||
*security_level =
|
||||
video_widevine::License_KeyContainer_SecurityLevel_SW_SECURE_CRYPTO;
|
||||
@@ -94,11 +99,17 @@ WB_Result OdkLicenseParser::Parse(const std::string& decryption_key,
|
||||
// Add the key right away. The key will be invalid, so if we fail to
|
||||
// parse the key, we'll already have an entry for the invalid key.
|
||||
switch (odk_context.license.license_type) {
|
||||
case OEMCrypto_ContentLicense:
|
||||
content_keys_[key_id] = ParseInternalKey(
|
||||
case OEMCrypto_ContentLicense: {
|
||||
auto temp_key = ParseInternalKey(
|
||||
KeyType::kContentKey, decryption_key, message, key, provider_keys,
|
||||
provider_key_id);
|
||||
if (temp_key.type == KeyType::kGenericCryptoKey) {
|
||||
generic_keys_[key_id] = temp_key;
|
||||
} else {
|
||||
content_keys_[key_id] = temp_key;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OEMCrypto_EntitlementLicense:
|
||||
entitlement_keys_[key_id] = ParseInternalKey(
|
||||
KeyType::kEntitlementKey, decryption_key, message, key,
|
||||
@@ -128,6 +139,11 @@ OdkLicenseParser::GetEntitlementKeys() const {
|
||||
return entitlement_keys_;
|
||||
}
|
||||
|
||||
const std::map<std::string, InternalKey>&
|
||||
OdkLicenseParser::GetGenericKeys() const {
|
||||
return generic_keys_;
|
||||
}
|
||||
|
||||
RenewalKey OdkLicenseParser::ParseSigningKeys(const std::string& decryption_key,
|
||||
const std::string& key,
|
||||
const std::string& iv) const {
|
||||
@@ -176,6 +192,19 @@ InternalKey OdkLicenseParser::ParseInternalKey(
|
||||
// This should have been verified before calling the parser.
|
||||
CHECK_EQ(decryption_key.size(), 16u) << "Incorrect decryption key size.";
|
||||
|
||||
const std::string key_control_block = ExtractItem(key.key_control, message);
|
||||
if (key_control_block.empty()) {
|
||||
VLOG(3) << "Empty key control block.";
|
||||
return InternalKey();
|
||||
}
|
||||
|
||||
video_widevine::License_KeyContainer_SecurityLevel security_level;
|
||||
uint32_t kcb_flags;
|
||||
if (!ExtractKcbFields(key_control_block, &security_level, &kcb_flags)) {
|
||||
VLOG(3) << "Invalid KCB.";
|
||||
return InternalKey();
|
||||
}
|
||||
|
||||
const std::string iv = ExtractItem(key.key_data_iv, message);
|
||||
|
||||
if (iv.size() != 16u) {
|
||||
@@ -186,8 +215,23 @@ InternalKey OdkLicenseParser::ParseInternalKey(
|
||||
std::string wrapped_key = ExtractItem(key.key_data, message);
|
||||
|
||||
// Unlike with protobufs, we don't need to handle padding here. The ODK will
|
||||
// not include the padding as part of the key's size.
|
||||
const size_t key_size = key_type == KeyType::kEntitlementKey ? 32u : 16u;
|
||||
// not include the padding as part of the key's size. Additionally, generic
|
||||
// keys look like content keys, but can have either 16 or 32 byte key.
|
||||
size_t key_size;
|
||||
if (kcb_flags & WB_KCB_FLAGS_GENERIC_MASK) {
|
||||
const bool is_aes =
|
||||
kcb_flags & (WB_KCB_FLAGS_ALLOW_ENCRYPT | WB_KCB_FLAGS_ALLOW_DECRYPT);
|
||||
const bool is_hmac =
|
||||
kcb_flags & (WB_KCB_FLAGS_ALLOW_SIGN | WB_KCB_FLAGS_ALLOW_VERIFY);
|
||||
if (is_aes && is_hmac) {
|
||||
VLOG(3) << "Cannot use both encrypt/decrypt and sign/verify";
|
||||
return InternalKey();
|
||||
}
|
||||
key_type = KeyType::kGenericCryptoKey;
|
||||
key_size = is_aes ? 16u : 32u;
|
||||
} else {
|
||||
key_size = key_type == KeyType::kContentKey ? 16u : 32u;
|
||||
}
|
||||
if (wrapped_key.size() != key_size) {
|
||||
VLOG(3) << "Invalid key size (" << wrapped_key.size() << ").";
|
||||
return InternalKey();
|
||||
@@ -200,22 +244,13 @@ InternalKey OdkLicenseParser::ParseInternalKey(
|
||||
return InternalKey();
|
||||
}
|
||||
|
||||
const std::string key_control_block = ExtractItem(key.key_control, message);
|
||||
if (key_control_block.empty()) {
|
||||
VLOG(3) << "Empty key control block.";
|
||||
return InternalKey();
|
||||
}
|
||||
|
||||
video_widevine::License_KeyContainer_SecurityLevel security_level;
|
||||
CHECK(ExtractLevel(key_control_block, &security_level));
|
||||
|
||||
// When the platform is hardware verified, all keys are unlocked and are
|
||||
// available to be used with either decrypt function. The license server
|
||||
// adjusts the level returned inside the key control block to handle
|
||||
// this.
|
||||
const bool is_hw_verified = false;
|
||||
return CreateInternalKey(
|
||||
key_type, security_level, is_hw_verified, unwrapped_key);
|
||||
key_type, security_level, is_hw_verified, unwrapped_key, kcb_flags);
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
@@ -22,6 +22,7 @@ class OdkLicenseParser : public LicenseParser {
|
||||
|
||||
const std::map<std::string, InternalKey>& GetContentKeys() const override;
|
||||
const std::map<std::string, InternalKey>& GetEntitlementKeys() const override;
|
||||
const std::map<std::string, InternalKey>& GetGenericKeys() const override;
|
||||
|
||||
private:
|
||||
RenewalKey ParseSigningKeys(const std::string& decryption_key,
|
||||
@@ -39,6 +40,7 @@ class OdkLicenseParser : public LicenseParser {
|
||||
std::vector<widevine::RenewalKey> renewal_keys_;
|
||||
std::map<std::string, InternalKey> content_keys_;
|
||||
std::map<std::string, InternalKey> entitlement_keys_;
|
||||
std::map<std::string, InternalKey> generic_keys_;
|
||||
};
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
@@ -149,6 +149,12 @@ ProtobufLicenseParser::GetEntitlementKeys() const {
|
||||
return entitlement_keys_;
|
||||
}
|
||||
|
||||
const std::map<std::string, InternalKey>&
|
||||
ProtobufLicenseParser::GetGenericKeys() const {
|
||||
static std::map<std::string, InternalKey> ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
RenewalKey ProtobufLicenseParser::ParseSigningKey(
|
||||
const std::string& decryption_key,
|
||||
const video_widevine::License_KeyContainer& key) const {
|
||||
|
||||
@@ -22,6 +22,8 @@ class ProtobufLicenseParser : public LicenseParser {
|
||||
const std::map<std::string, InternalKey>& GetContentKeys() const override;
|
||||
const std::map<std::string, InternalKey>&
|
||||
GetEntitlementKeys() const override;
|
||||
const std::map<std::string, InternalKey>&
|
||||
GetGenericKeys() const override;
|
||||
|
||||
RenewalKey ParseSigningKey(
|
||||
const std::string& decryption_key,
|
||||
|
||||
Reference in New Issue
Block a user