From 66820d41c5f205b9d8df99b61871ff3421f4ce5d Mon Sep 17 00:00:00 2001 From: Jacob Trimble Date: Wed, 16 Nov 2022 11:37:39 -0800 Subject: [PATCH] Update partner repo This adds: - Requires WB_RESULT_NOT_IMPLEMENTED for masked in CE - WB_License_RemoveEntitledContentKey - WB_License_Generic* methods --- whitebox/WORKSPACE | 6 +- whitebox/api/BUILD | 5 +- whitebox/api/golden_data.cc | 66 ++ whitebox/api/golden_data.h | 22 + whitebox/api/license_whitebox.h | 257 ++++++- .../api/license_whitebox_decrypt_benchmark.cc | 2 + whitebox/api/license_whitebox_decrypt_test.cc | 4 +- ...e_whitebox_entitlement_content_key_test.cc | 47 ++ .../license_whitebox_generic_crypto_test.cc | 699 +++++++++++++++++ ...license_whitebox_get_secret_string_test.cc | 262 ++++--- ...license_whitebox_key_control_block_test.cc | 47 ++ .../license_whitebox_masked_decrypt_test.cc | 714 ++++++++++-------- whitebox/api/license_whitebox_proxy_impl.cc | 44 ++ .../license_whitebox_security_level_test.cc | 15 +- ...emote_attestation_and_verification_test.cc | 289 +++---- whitebox/api/result.h | 5 +- whitebox/api/test_key_types.h | 10 + whitebox/api/test_license_builder.cc | 71 +- whitebox/api/test_license_builder.h | 6 + whitebox/crypto_utils/crypto_util.cc | 1 + whitebox/crypto_utils/crypto_util.h | 1 + whitebox/reference/impl/BUILD | 1 + whitebox/reference/impl/content_key.h | 2 + whitebox/reference/impl/license_parser.cc | 6 +- whitebox/reference/impl/license_parser.h | 4 +- .../reference/impl/license_whitebox_impl.cc | 263 ++++++- whitebox/reference/impl/odk_license_parser.cc | 69 +- whitebox/reference/impl/odk_license_parser.h | 2 + .../reference/impl/protobuf_license_parser.cc | 6 + .../reference/impl/protobuf_license_parser.h | 2 + 30 files changed, 2326 insertions(+), 602 deletions(-) create mode 100644 whitebox/api/license_whitebox_generic_crypto_test.cc diff --git a/whitebox/WORKSPACE b/whitebox/WORKSPACE index 3d8b36e..942c524 100644 --- a/whitebox/WORKSPACE +++ b/whitebox/WORKSPACE @@ -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 diff --git a/whitebox/api/BUILD b/whitebox/api/BUILD index dd59483..6fa4388 100644 --- a/whitebox/api/BUILD +++ b/whitebox/api/BUILD @@ -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", diff --git a/whitebox/api/golden_data.cc b/whitebox/api/golden_data.cc index 73cac2a..827e686 100644 --- a/whitebox/api/golden_data.cc +++ b/whitebox/api/golden_data.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() { diff --git a/whitebox/api/golden_data.h b/whitebox/api/golden_data.h index aa955b1..d6a9b17 100644 --- a/whitebox/api/golden_data.h +++ b/whitebox/api/golden_data.h @@ -43,6 +43,25 @@ class GoldenData { EntitlementKeyData entitlement_key; }; + struct GenericContent { + std::vector plaintext; + // |plaintext| encrypted with |encryption_key| + std::vector encrypted; + // |plaintext| signed with |signing_key| + std::vector 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; }; diff --git a/whitebox/api/license_whitebox.h b/whitebox/api/license_whitebox.h index e157399..31f553a 100644 --- a/whitebox/api/license_whitebox.h +++ b/whitebox/api/license_whitebox.h @@ -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 diff --git a/whitebox/api/license_whitebox_decrypt_benchmark.cc b/whitebox/api/license_whitebox_decrypt_benchmark.cc index 1268bc7..92c46d1 100644 --- a/whitebox/api/license_whitebox_decrypt_benchmark.cc +++ b/whitebox/api/license_whitebox_decrypt_benchmark.cc @@ -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 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( diff --git a/whitebox/api/license_whitebox_decrypt_test.cc b/whitebox/api/license_whitebox_decrypt_test.cc index 7df3786..d3187c0 100644 --- a/whitebox/api/license_whitebox_decrypt_test.cc +++ b/whitebox/api/license_whitebox_decrypt_test.cc @@ -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, diff --git a/whitebox/api/license_whitebox_entitlement_content_key_test.cc b/whitebox/api/license_whitebox_entitlement_content_key_test.cc index d0b1ff1..0f8a810 100644 --- a/whitebox/api/license_whitebox_entitlement_content_key_test.cc +++ b/whitebox/api/license_whitebox_entitlement_content_key_test.cc @@ -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 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 diff --git a/whitebox/api/license_whitebox_generic_crypto_test.cc b/whitebox/api/license_whitebox_generic_crypto_test.cc new file mode 100644 index 0000000..528e006 --- /dev/null +++ b/whitebox/api/license_whitebox_generic_crypto_test.cc @@ -0,0 +1,699 @@ +// Copyright 2021 Google LLC. All Rights Reserved. + +#include "api/license_whitebox.h" + +#include +#include + +#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 { + protected: + void SetUp() { + LicenseWhiteboxTestBase::SetUp(); + server_ = TestServer::CreateDualKey(); + } + + std::unique_ptr 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 { + 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 diff --git a/whitebox/api/license_whitebox_get_secret_string_test.cc b/whitebox/api/license_whitebox_get_secret_string_test.cc index 3129618..94b23c0 100644 --- a/whitebox/api/license_whitebox_get_secret_string_test.cc +++ b/whitebox/api/license_whitebox_get_secret_string_test.cc @@ -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(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 diff --git a/whitebox/api/license_whitebox_key_control_block_test.cc b/whitebox/api/license_whitebox_key_control_block_test.cc index 79f8f4c..e3eebb6 100644 --- a/whitebox/api/license_whitebox_key_control_block_test.cc +++ b/whitebox/api/license_whitebox_key_control_block_test.cc @@ -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 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 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 diff --git a/whitebox/api/license_whitebox_masked_decrypt_test.cc b/whitebox/api/license_whitebox_masked_decrypt_test.cc index 982c816..a3bc03f 100644 --- a/whitebox/api/license_whitebox_masked_decrypt_test.cc +++ b/whitebox/api/license_whitebox_masked_decrypt_test.cc @@ -113,16 +113,18 @@ TEST_P(LicenseWhiteboxMaskedDecryptTest, DecodeKeyWithCbcDataInCbcMode) { if (!LoadLicense(settings, provider_key_id_)) GTEST_SKIP(); - ASSERT_EQ(WB_License_MaskedDecrypt( - whitebox_, WB_CIPHER_MODE_CBC, - golden_data_.CBCContent().software_decode_key.id.data(), - golden_data_.CBCContent().software_decode_key.id.size(), - golden_data_.CBCContent().ciphertext.data(), - golden_data_.CBCContent().ciphertext.size(), - golden_data_.CBCContent().iv.data(), - golden_data_.CBCContent().iv.size(), masked_text_.data(), - &masked_text_size_), - WB_RESULT_OK); + auto result = WB_License_MaskedDecrypt( + whitebox_, WB_CIPHER_MODE_CBC, + golden_data_.CBCContent().software_decode_key.id.data(), + golden_data_.CBCContent().software_decode_key.id.size(), + golden_data_.CBCContent().ciphertext.data(), + golden_data_.CBCContent().ciphertext.size(), + golden_data_.CBCContent().iv.data(), golden_data_.CBCContent().iv.size(), + masked_text_.data(), &masked_text_size_); +#ifdef ALWAYS_DECRYPT_TO_CLEAR + ASSERT_EQ(result, WB_RESULT_NOT_IMPLEMENTED); +#else + ASSERT_EQ(result, WB_RESULT_OK); masked_text_.resize(masked_text_size_); // Returned data is masked, so it should be the correct size but not @@ -145,6 +147,7 @@ TEST_P(LicenseWhiteboxMaskedDecryptTest, DecodeKeyWithCbcDataInCbcMode) { plaintext_.data()); ASSERT_EQ(plaintext_, golden_data_.CBCContent().plaintext); +#endif } TEST_P(LicenseWhiteboxMaskedDecryptTest, DecodeKeyInPlaceCbc) { @@ -156,15 +159,17 @@ TEST_P(LicenseWhiteboxMaskedDecryptTest, DecodeKeyInPlaceCbc) { memcpy(masked_text_.data(), golden_data_.CBCContent().ciphertext.data(), golden_data_.CBCContent().ciphertext.size()); - ASSERT_EQ(WB_License_MaskedDecrypt( - whitebox_, WB_CIPHER_MODE_CBC, - golden_data_.CBCContent().software_decode_key.id.data(), - golden_data_.CBCContent().software_decode_key.id.size(), - masked_text_.data(), masked_text_.size(), - golden_data_.CBCContent().iv.data(), - golden_data_.CBCContent().iv.size(), masked_text_.data(), - &masked_text_size_), - WB_RESULT_OK); + auto result = WB_License_MaskedDecrypt( + whitebox_, WB_CIPHER_MODE_CBC, + golden_data_.CBCContent().software_decode_key.id.data(), + golden_data_.CBCContent().software_decode_key.id.size(), + masked_text_.data(), masked_text_.size(), + golden_data_.CBCContent().iv.data(), golden_data_.CBCContent().iv.size(), + masked_text_.data(), &masked_text_size_); +#ifdef ALWAYS_DECRYPT_TO_CLEAR + ASSERT_EQ(result, WB_RESULT_NOT_IMPLEMENTED); +#else + ASSERT_EQ(result, WB_RESULT_OK); masked_text_.resize(masked_text_size_); // Returned data is masked, so it should be the correct size but not @@ -187,6 +192,7 @@ TEST_P(LicenseWhiteboxMaskedDecryptTest, DecodeKeyInPlaceCbc) { plaintext_.data()); ASSERT_EQ(plaintext_, golden_data_.CBCContent().plaintext); +#endif } TEST_P(LicenseWhiteboxMaskedDecryptTest, DecodeKeyWithCtrDataInCtrMode) { @@ -195,16 +201,18 @@ TEST_P(LicenseWhiteboxMaskedDecryptTest, DecodeKeyWithCtrDataInCtrMode) { if (!LoadLicense(settings, provider_key_id_)) GTEST_SKIP(); - ASSERT_EQ(WB_License_MaskedDecrypt( - whitebox_, WB_CIPHER_MODE_CTR, - golden_data_.CTRContent().software_decode_key.id.data(), - golden_data_.CTRContent().software_decode_key.id.size(), - golden_data_.CTRContent().ciphertext.data(), - golden_data_.CTRContent().ciphertext.size(), - golden_data_.CTRContent().iv.data(), - golden_data_.CTRContent().iv.size(), masked_text_.data(), - &masked_text_size_), - WB_RESULT_OK); + auto result = WB_License_MaskedDecrypt( + whitebox_, WB_CIPHER_MODE_CTR, + golden_data_.CTRContent().software_decode_key.id.data(), + golden_data_.CTRContent().software_decode_key.id.size(), + golden_data_.CTRContent().ciphertext.data(), + golden_data_.CTRContent().ciphertext.size(), + golden_data_.CTRContent().iv.data(), golden_data_.CTRContent().iv.size(), + masked_text_.data(), &masked_text_size_); +#ifdef ALWAYS_DECRYPT_TO_CLEAR + ASSERT_EQ(result, WB_RESULT_NOT_IMPLEMENTED); +#else + ASSERT_EQ(result, WB_RESULT_OK); masked_text_.resize(masked_text_size_); // Returned data is masked, so it should be the correct size but not @@ -227,6 +235,7 @@ TEST_P(LicenseWhiteboxMaskedDecryptTest, DecodeKeyWithCtrDataInCtrMode) { plaintext_.data()); ASSERT_EQ(plaintext_, golden_data_.CTRContent().plaintext); +#endif } TEST_P(LicenseWhiteboxMaskedDecryptTest, DecodeKeyInPlaceCtr) { @@ -238,15 +247,17 @@ TEST_P(LicenseWhiteboxMaskedDecryptTest, DecodeKeyInPlaceCtr) { memcpy(masked_text_.data(), golden_data_.CTRContent().ciphertext.data(), golden_data_.CTRContent().ciphertext.size()); - ASSERT_EQ(WB_License_MaskedDecrypt( - whitebox_, WB_CIPHER_MODE_CTR, - golden_data_.CTRContent().software_decode_key.id.data(), - golden_data_.CTRContent().software_decode_key.id.size(), - masked_text_.data(), masked_text_.size(), - golden_data_.CTRContent().iv.data(), - golden_data_.CTRContent().iv.size(), masked_text_.data(), - &masked_text_size_), - WB_RESULT_OK); + auto result = WB_License_MaskedDecrypt( + whitebox_, WB_CIPHER_MODE_CTR, + golden_data_.CTRContent().software_decode_key.id.data(), + golden_data_.CTRContent().software_decode_key.id.size(), + masked_text_.data(), masked_text_.size(), + golden_data_.CTRContent().iv.data(), golden_data_.CTRContent().iv.size(), + masked_text_.data(), &masked_text_size_); +#ifdef ALWAYS_DECRYPT_TO_CLEAR + ASSERT_EQ(result, WB_RESULT_NOT_IMPLEMENTED); +#else + ASSERT_EQ(result, WB_RESULT_OK); masked_text_.resize(masked_text_size_); // Returned data is masked, so it should be the correct size but not @@ -269,6 +280,7 @@ TEST_P(LicenseWhiteboxMaskedDecryptTest, DecodeKeyInPlaceCtr) { plaintext_.data()); ASSERT_EQ(plaintext_, golden_data_.CTRContent().plaintext); +#endif } // We try to decrypt CBC encrypted data in CTR mode. All operations should be @@ -279,16 +291,18 @@ TEST_P(LicenseWhiteboxMaskedDecryptTest, DecodeKeyWithCbcDataInCtrMode) { if (!LoadLicense(settings, provider_key_id_)) GTEST_SKIP(); - ASSERT_EQ(WB_License_MaskedDecrypt( - whitebox_, WB_CIPHER_MODE_CTR, - golden_data_.CBCContent().software_decode_key.id.data(), - golden_data_.CBCContent().software_decode_key.id.size(), - golden_data_.CBCContent().ciphertext.data(), - golden_data_.CBCContent().ciphertext.size(), - golden_data_.CBCContent().iv.data(), - golden_data_.CBCContent().iv.size(), masked_text_.data(), - &masked_text_size_), - WB_RESULT_OK); + auto result = WB_License_MaskedDecrypt( + whitebox_, WB_CIPHER_MODE_CTR, + golden_data_.CBCContent().software_decode_key.id.data(), + golden_data_.CBCContent().software_decode_key.id.size(), + golden_data_.CBCContent().ciphertext.data(), + golden_data_.CBCContent().ciphertext.size(), + golden_data_.CBCContent().iv.data(), golden_data_.CBCContent().iv.size(), + masked_text_.data(), &masked_text_size_); +#ifdef ALWAYS_DECRYPT_TO_CLEAR + ASSERT_EQ(result, WB_RESULT_NOT_IMPLEMENTED); +#else + ASSERT_EQ(result, WB_RESULT_OK); masked_text_.resize(masked_text_size_); // Whatever is returned must not be the original text. @@ -310,6 +324,7 @@ TEST_P(LicenseWhiteboxMaskedDecryptTest, DecodeKeyWithCbcDataInCtrMode) { plaintext_.data()); ASSERT_NE(masked_text_, golden_data_.CBCContent().plaintext); +#endif } // We try to decrypt CTR encrypted data in CBC mode. All operations should be @@ -320,16 +335,18 @@ TEST_P(LicenseWhiteboxMaskedDecryptTest, DecodeKeyWithCtrDataInCbcMode) { if (!LoadLicense(settings, provider_key_id_)) GTEST_SKIP(); - ASSERT_EQ(WB_License_MaskedDecrypt( - whitebox_, WB_CIPHER_MODE_CBC, - golden_data_.CTRContent().software_decode_key.id.data(), - golden_data_.CTRContent().software_decode_key.id.size(), - golden_data_.CTRContent().ciphertext.data(), - golden_data_.CTRContent().ciphertext.size(), - golden_data_.CTRContent().iv.data(), - golden_data_.CTRContent().iv.size(), masked_text_.data(), - &masked_text_size_), - WB_RESULT_OK); + auto result = WB_License_MaskedDecrypt( + whitebox_, WB_CIPHER_MODE_CBC, + golden_data_.CTRContent().software_decode_key.id.data(), + golden_data_.CTRContent().software_decode_key.id.size(), + golden_data_.CTRContent().ciphertext.data(), + golden_data_.CTRContent().ciphertext.size(), + golden_data_.CTRContent().iv.data(), golden_data_.CTRContent().iv.size(), + masked_text_.data(), &masked_text_size_); +#ifdef ALWAYS_DECRYPT_TO_CLEAR + ASSERT_EQ(result, WB_RESULT_NOT_IMPLEMENTED); +#else + ASSERT_EQ(result, WB_RESULT_OK); masked_text_.resize(masked_text_size_); // Whatever is returned must not be the original text. @@ -351,6 +368,7 @@ TEST_P(LicenseWhiteboxMaskedDecryptTest, DecodeKeyWithCtrDataInCbcMode) { plaintext_.data()); ASSERT_NE(masked_text_, golden_data_.CTRContent().plaintext); +#endif } TEST_P(LicenseWhiteboxMaskedDecryptTest, CryptoKeyWithCbcDataInCbcMode) { @@ -359,16 +377,18 @@ TEST_P(LicenseWhiteboxMaskedDecryptTest, CryptoKeyWithCbcDataInCbcMode) { if (!LoadLicense(settings, provider_key_id_)) GTEST_SKIP(); - ASSERT_EQ(WB_License_MaskedDecrypt( - whitebox_, WB_CIPHER_MODE_CBC, - golden_data_.CBCContent().software_crypto_key.id.data(), - golden_data_.CBCContent().software_crypto_key.id.size(), - golden_data_.CBCContent().ciphertext.data(), - golden_data_.CBCContent().ciphertext.size(), - golden_data_.CBCContent().iv.data(), - golden_data_.CBCContent().iv.size(), masked_text_.data(), - &masked_text_size_), - WB_RESULT_OK); + auto result = WB_License_MaskedDecrypt( + whitebox_, WB_CIPHER_MODE_CBC, + golden_data_.CBCContent().software_crypto_key.id.data(), + golden_data_.CBCContent().software_crypto_key.id.size(), + golden_data_.CBCContent().ciphertext.data(), + golden_data_.CBCContent().ciphertext.size(), + golden_data_.CBCContent().iv.data(), golden_data_.CBCContent().iv.size(), + masked_text_.data(), &masked_text_size_); +#ifdef ALWAYS_DECRYPT_TO_CLEAR + ASSERT_EQ(result, WB_RESULT_NOT_IMPLEMENTED); +#else + ASSERT_EQ(result, WB_RESULT_OK); masked_text_.resize(masked_text_size_); // Returned data is masked, so it should be the correct size but not @@ -391,6 +411,7 @@ TEST_P(LicenseWhiteboxMaskedDecryptTest, CryptoKeyWithCbcDataInCbcMode) { plaintext_.data()); ASSERT_EQ(plaintext_, golden_data_.CBCContent().plaintext); +#endif } TEST_P(LicenseWhiteboxMaskedDecryptTest, CryptoKeyWithCtrDataInCtrMode) { @@ -399,16 +420,18 @@ TEST_P(LicenseWhiteboxMaskedDecryptTest, CryptoKeyWithCtrDataInCtrMode) { if (!LoadLicense(settings, provider_key_id_)) GTEST_SKIP(); - ASSERT_EQ(WB_License_MaskedDecrypt( - whitebox_, WB_CIPHER_MODE_CTR, - golden_data_.CTRContent().software_crypto_key.id.data(), - golden_data_.CTRContent().software_crypto_key.id.size(), - golden_data_.CTRContent().ciphertext.data(), - golden_data_.CTRContent().ciphertext.size(), - golden_data_.CTRContent().iv.data(), - golden_data_.CTRContent().iv.size(), masked_text_.data(), - &masked_text_size_), - WB_RESULT_OK); + auto result = WB_License_MaskedDecrypt( + whitebox_, WB_CIPHER_MODE_CTR, + golden_data_.CTRContent().software_crypto_key.id.data(), + golden_data_.CTRContent().software_crypto_key.id.size(), + golden_data_.CTRContent().ciphertext.data(), + golden_data_.CTRContent().ciphertext.size(), + golden_data_.CTRContent().iv.data(), golden_data_.CTRContent().iv.size(), + masked_text_.data(), &masked_text_size_); +#ifdef ALWAYS_DECRYPT_TO_CLEAR + ASSERT_EQ(result, WB_RESULT_NOT_IMPLEMENTED); +#else + ASSERT_EQ(result, WB_RESULT_OK); masked_text_.resize(masked_text_size_); // Returned data is masked, so it should be the correct size but not @@ -431,6 +454,7 @@ TEST_P(LicenseWhiteboxMaskedDecryptTest, CryptoKeyWithCtrDataInCtrMode) { plaintext_.data()); ASSERT_EQ(plaintext_, golden_data_.CTRContent().plaintext); +#endif } // We try to decrypt CBC encrypted data in CTR mode. All operations should be @@ -441,16 +465,18 @@ TEST_P(LicenseWhiteboxMaskedDecryptTest, CryptoKeyWithCbcDataInCtrMode) { if (!LoadLicense(settings, provider_key_id_)) GTEST_SKIP(); - ASSERT_EQ(WB_License_MaskedDecrypt( - whitebox_, WB_CIPHER_MODE_CTR, - golden_data_.CBCContent().software_crypto_key.id.data(), - golden_data_.CBCContent().software_crypto_key.id.size(), - golden_data_.CBCContent().ciphertext.data(), - golden_data_.CBCContent().ciphertext.size(), - golden_data_.CBCContent().iv.data(), - golden_data_.CBCContent().iv.size(), masked_text_.data(), - &masked_text_size_), - WB_RESULT_OK); + auto result = WB_License_MaskedDecrypt( + whitebox_, WB_CIPHER_MODE_CTR, + golden_data_.CBCContent().software_crypto_key.id.data(), + golden_data_.CBCContent().software_crypto_key.id.size(), + golden_data_.CBCContent().ciphertext.data(), + golden_data_.CBCContent().ciphertext.size(), + golden_data_.CBCContent().iv.data(), golden_data_.CBCContent().iv.size(), + masked_text_.data(), &masked_text_size_); +#ifdef ALWAYS_DECRYPT_TO_CLEAR + ASSERT_EQ(result, WB_RESULT_NOT_IMPLEMENTED); +#else + ASSERT_EQ(result, WB_RESULT_OK); masked_text_.resize(masked_text_size_); // Whatever is returned must not be the original text. @@ -472,6 +498,7 @@ TEST_P(LicenseWhiteboxMaskedDecryptTest, CryptoKeyWithCbcDataInCtrMode) { plaintext_.data()); ASSERT_NE(masked_text_, golden_data_.CBCContent().plaintext); +#endif } // We try to decrypt CTR encrypted data in CBC mode. All operations should be @@ -482,16 +509,18 @@ TEST_P(LicenseWhiteboxMaskedDecryptTest, CryptoKeyWithCtrDataInCbcMode) { if (!LoadLicense(settings, provider_key_id_)) GTEST_SKIP(); - ASSERT_EQ(WB_License_MaskedDecrypt( - whitebox_, WB_CIPHER_MODE_CBC, - golden_data_.CTRContent().software_crypto_key.id.data(), - golden_data_.CTRContent().software_crypto_key.id.size(), - golden_data_.CTRContent().ciphertext.data(), - golden_data_.CTRContent().ciphertext.size(), - golden_data_.CTRContent().iv.data(), - golden_data_.CTRContent().iv.size(), masked_text_.data(), - &masked_text_size_), - WB_RESULT_OK); + auto result = WB_License_MaskedDecrypt( + whitebox_, WB_CIPHER_MODE_CBC, + golden_data_.CTRContent().software_crypto_key.id.data(), + golden_data_.CTRContent().software_crypto_key.id.size(), + golden_data_.CTRContent().ciphertext.data(), + golden_data_.CTRContent().ciphertext.size(), + golden_data_.CTRContent().iv.data(), golden_data_.CTRContent().iv.size(), + masked_text_.data(), &masked_text_size_); +#ifdef ALWAYS_DECRYPT_TO_CLEAR + ASSERT_EQ(result, WB_RESULT_NOT_IMPLEMENTED); +#else + ASSERT_EQ(result, WB_RESULT_OK); masked_text_.resize(masked_text_size_); // Whatever is returned must not be the original text. @@ -513,6 +542,7 @@ TEST_P(LicenseWhiteboxMaskedDecryptTest, CryptoKeyWithCtrDataInCbcMode) { plaintext_.data()); ASSERT_NE(masked_text_, golden_data_.CTRContent().plaintext); +#endif } TEST_P(LicenseWhiteboxMaskedDecryptTest, CryptoKeyWithCbcDataAndPKCS8Padding) { @@ -521,16 +551,18 @@ TEST_P(LicenseWhiteboxMaskedDecryptTest, CryptoKeyWithCbcDataAndPKCS8Padding) { if (!LoadLicense(settings, provider_key_id_)) GTEST_SKIP(); - ASSERT_EQ(WB_License_MaskedDecrypt( - whitebox_, WB_CIPHER_MODE_CBC, - golden_data_.CBCContent().software_crypto_key.id.data(), - golden_data_.CBCContent().software_crypto_key.id.size(), - golden_data_.CBCContent().ciphertext.data(), - golden_data_.CBCContent().ciphertext.size(), - golden_data_.CBCContent().iv.data(), - golden_data_.CBCContent().iv.size(), masked_text_.data(), - &masked_text_size_), - WB_RESULT_OK); + auto result = WB_License_MaskedDecrypt( + whitebox_, WB_CIPHER_MODE_CBC, + golden_data_.CBCContent().software_crypto_key.id.data(), + golden_data_.CBCContent().software_crypto_key.id.size(), + golden_data_.CBCContent().ciphertext.data(), + golden_data_.CBCContent().ciphertext.size(), + golden_data_.CBCContent().iv.data(), golden_data_.CBCContent().iv.size(), + masked_text_.data(), &masked_text_size_); +#ifdef ALWAYS_DECRYPT_TO_CLEAR + ASSERT_EQ(result, WB_RESULT_NOT_IMPLEMENTED); +#else + ASSERT_EQ(result, WB_RESULT_OK); masked_text_.resize(masked_text_size_); // Returned data is masked, so it should be the correct size but not @@ -553,6 +585,7 @@ TEST_P(LicenseWhiteboxMaskedDecryptTest, CryptoKeyWithCbcDataAndPKCS8Padding) { plaintext_.data()); ASSERT_EQ(plaintext_, golden_data_.CBCContent().plaintext); +#endif } TEST_P(LicenseWhiteboxMaskedDecryptTest, CryptoKeyWithCtrDataAndPKCS8Padding) { @@ -561,16 +594,18 @@ TEST_P(LicenseWhiteboxMaskedDecryptTest, CryptoKeyWithCtrDataAndPKCS8Padding) { if (!LoadLicense(settings, provider_key_id_)) GTEST_SKIP(); - ASSERT_EQ(WB_License_MaskedDecrypt( - whitebox_, WB_CIPHER_MODE_CTR, - golden_data_.CTRContent().software_crypto_key.id.data(), - golden_data_.CTRContent().software_crypto_key.id.size(), - golden_data_.CTRContent().ciphertext.data(), - golden_data_.CTRContent().ciphertext.size(), - golden_data_.CTRContent().iv.data(), - golden_data_.CTRContent().iv.size(), masked_text_.data(), - &masked_text_size_), - WB_RESULT_OK); + auto result = WB_License_MaskedDecrypt( + whitebox_, WB_CIPHER_MODE_CTR, + golden_data_.CTRContent().software_crypto_key.id.data(), + golden_data_.CTRContent().software_crypto_key.id.size(), + golden_data_.CTRContent().ciphertext.data(), + golden_data_.CTRContent().ciphertext.size(), + golden_data_.CTRContent().iv.data(), golden_data_.CTRContent().iv.size(), + masked_text_.data(), &masked_text_size_); +#ifdef ALWAYS_DECRYPT_TO_CLEAR + ASSERT_EQ(result, WB_RESULT_NOT_IMPLEMENTED); +#else + ASSERT_EQ(result, WB_RESULT_OK); masked_text_.resize(masked_text_size_); // Returned data is masked, so it should be the correct size but not @@ -593,6 +628,7 @@ TEST_P(LicenseWhiteboxMaskedDecryptTest, CryptoKeyWithCtrDataAndPKCS8Padding) { plaintext_.data()); ASSERT_EQ(plaintext_, golden_data_.CTRContent().plaintext); +#endif } // Try decrypting two different sets of content to make sure that two @@ -606,16 +642,18 @@ TEST_P(LicenseWhiteboxMaskedDecryptTest, SuccessWithMultipleKeys) { if (!LoadLicense(settings, provider_key_id_)) GTEST_SKIP(); - ASSERT_EQ(WB_License_MaskedDecrypt( - whitebox_, WB_CIPHER_MODE_CBC, - golden_data_.CBCContent().software_decode_key.id.data(), - golden_data_.CBCContent().software_decode_key.id.size(), - golden_data_.CBCContent().ciphertext.data(), - golden_data_.CBCContent().ciphertext.size(), - golden_data_.CBCContent().iv.data(), - golden_data_.CBCContent().iv.size(), masked_text_.data(), - &masked_text_size_), - WB_RESULT_OK); + auto result = WB_License_MaskedDecrypt( + whitebox_, WB_CIPHER_MODE_CBC, + golden_data_.CBCContent().software_decode_key.id.data(), + golden_data_.CBCContent().software_decode_key.id.size(), + golden_data_.CBCContent().ciphertext.data(), + golden_data_.CBCContent().ciphertext.size(), + golden_data_.CBCContent().iv.data(), golden_data_.CBCContent().iv.size(), + masked_text_.data(), &masked_text_size_); +#ifdef ALWAYS_DECRYPT_TO_CLEAR + ASSERT_EQ(result, WB_RESULT_NOT_IMPLEMENTED); +#else + ASSERT_EQ(result, WB_RESULT_OK); masked_text_.resize(masked_text_size_); // Returned data is masked, so it should be the correct size but not @@ -679,6 +717,7 @@ TEST_P(LicenseWhiteboxMaskedDecryptTest, SuccessWithMultipleKeys) { plaintext_.data()); ASSERT_EQ(plaintext_, golden_data_.CTRContent().plaintext); +#endif } TEST_P(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullWhitebox) { @@ -687,16 +726,19 @@ TEST_P(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullWhitebox) { if (!LoadLicense(settings, provider_key_id_)) GTEST_SKIP(); - ASSERT_EQ(WB_License_MaskedDecrypt( - nullptr, WB_CIPHER_MODE_CBC, - golden_data_.CBCContent().software_decode_key.id.data(), - golden_data_.CBCContent().software_decode_key.id.size(), - golden_data_.CBCContent().ciphertext.data(), - golden_data_.CBCContent().ciphertext.size(), - golden_data_.CBCContent().iv.data(), - golden_data_.CBCContent().iv.size(), masked_text_.data(), - &masked_text_size_), - WB_RESULT_INVALID_PARAMETER); + auto result = WB_License_MaskedDecrypt( + nullptr, WB_CIPHER_MODE_CBC, + golden_data_.CBCContent().software_decode_key.id.data(), + golden_data_.CBCContent().software_decode_key.id.size(), + golden_data_.CBCContent().ciphertext.data(), + golden_data_.CBCContent().ciphertext.size(), + golden_data_.CBCContent().iv.data(), golden_data_.CBCContent().iv.size(), + masked_text_.data(), &masked_text_size_); +#ifdef ALWAYS_DECRYPT_TO_CLEAR + ASSERT_EQ(result, WB_RESULT_NOT_IMPLEMENTED); +#else + ASSERT_EQ(result, WB_RESULT_INVALID_PARAMETER); +#endif } TEST_P(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForInvalidCipherMode) { @@ -710,16 +752,19 @@ TEST_P(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForInvalidCipherMode) { // the compiler tries to save us. const WB_CipherMode invalid_mode = static_cast(0xFF); - ASSERT_EQ(WB_License_MaskedDecrypt( - whitebox_, invalid_mode, - golden_data_.CBCContent().software_decode_key.id.data(), - golden_data_.CBCContent().software_decode_key.id.size(), - golden_data_.CBCContent().ciphertext.data(), - golden_data_.CBCContent().ciphertext.size(), - golden_data_.CBCContent().iv.data(), - golden_data_.CBCContent().iv.size(), masked_text_.data(), - &masked_text_size_), - WB_RESULT_INVALID_PARAMETER); + auto result = WB_License_MaskedDecrypt( + whitebox_, invalid_mode, + golden_data_.CBCContent().software_decode_key.id.data(), + golden_data_.CBCContent().software_decode_key.id.size(), + golden_data_.CBCContent().ciphertext.data(), + golden_data_.CBCContent().ciphertext.size(), + golden_data_.CBCContent().iv.data(), golden_data_.CBCContent().iv.size(), + masked_text_.data(), &masked_text_size_); +#ifdef ALWAYS_DECRYPT_TO_CLEAR + ASSERT_EQ(result, WB_RESULT_NOT_IMPLEMENTED); +#else + ASSERT_EQ(result, WB_RESULT_INVALID_PARAMETER); +#endif } TEST_P(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullKeyId) { @@ -728,15 +773,18 @@ TEST_P(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullKeyId) { if (!LoadLicense(settings, provider_key_id_)) GTEST_SKIP(); - ASSERT_EQ(WB_License_MaskedDecrypt( - whitebox_, WB_CIPHER_MODE_CBC, nullptr, - golden_data_.CBCContent().software_decode_key.id.size(), - golden_data_.CBCContent().ciphertext.data(), - golden_data_.CBCContent().ciphertext.size(), - golden_data_.CBCContent().iv.data(), - golden_data_.CBCContent().iv.size(), masked_text_.data(), - &masked_text_size_), - WB_RESULT_INVALID_PARAMETER); + auto result = WB_License_MaskedDecrypt( + whitebox_, WB_CIPHER_MODE_CBC, nullptr, + golden_data_.CBCContent().software_decode_key.id.size(), + golden_data_.CBCContent().ciphertext.data(), + golden_data_.CBCContent().ciphertext.size(), + golden_data_.CBCContent().iv.data(), golden_data_.CBCContent().iv.size(), + masked_text_.data(), &masked_text_size_); +#ifdef ALWAYS_DECRYPT_TO_CLEAR + ASSERT_EQ(result, WB_RESULT_NOT_IMPLEMENTED); +#else + ASSERT_EQ(result, WB_RESULT_INVALID_PARAMETER); +#endif } TEST_P(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullZeroKeyIdSize) { @@ -745,15 +793,18 @@ TEST_P(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullZeroKeyIdSize) { if (!LoadLicense(settings, provider_key_id_)) GTEST_SKIP(); - ASSERT_EQ(WB_License_MaskedDecrypt( - whitebox_, WB_CIPHER_MODE_CBC, - golden_data_.CBCContent().software_decode_key.id.data(), 0, - golden_data_.CBCContent().ciphertext.data(), - golden_data_.CBCContent().ciphertext.size(), - golden_data_.CBCContent().iv.data(), - golden_data_.CBCContent().iv.size(), masked_text_.data(), - &masked_text_size_), - WB_RESULT_INVALID_PARAMETER); + auto result = WB_License_MaskedDecrypt( + whitebox_, WB_CIPHER_MODE_CBC, + golden_data_.CBCContent().software_decode_key.id.data(), 0, + golden_data_.CBCContent().ciphertext.data(), + golden_data_.CBCContent().ciphertext.size(), + golden_data_.CBCContent().iv.data(), golden_data_.CBCContent().iv.size(), + masked_text_.data(), &masked_text_size_); +#ifdef ALWAYS_DECRYPT_TO_CLEAR + ASSERT_EQ(result, WB_RESULT_NOT_IMPLEMENTED); +#else + ASSERT_EQ(result, WB_RESULT_INVALID_PARAMETER); +#endif } TEST_P(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullInputData) { @@ -762,15 +813,18 @@ TEST_P(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullInputData) { if (!LoadLicense(settings, provider_key_id_)) GTEST_SKIP(); - ASSERT_EQ(WB_License_MaskedDecrypt( - whitebox_, WB_CIPHER_MODE_CBC, - golden_data_.CBCContent().software_decode_key.id.data(), - golden_data_.CBCContent().software_decode_key.id.size(), - nullptr, golden_data_.CBCContent().ciphertext.size(), - golden_data_.CBCContent().iv.data(), - golden_data_.CBCContent().iv.size(), masked_text_.data(), - &masked_text_size_), - WB_RESULT_INVALID_PARAMETER); + auto result = WB_License_MaskedDecrypt( + whitebox_, WB_CIPHER_MODE_CBC, + golden_data_.CBCContent().software_decode_key.id.data(), + golden_data_.CBCContent().software_decode_key.id.size(), nullptr, + golden_data_.CBCContent().ciphertext.size(), + golden_data_.CBCContent().iv.data(), golden_data_.CBCContent().iv.size(), + masked_text_.data(), &masked_text_size_); +#ifdef ALWAYS_DECRYPT_TO_CLEAR + ASSERT_EQ(result, WB_RESULT_NOT_IMPLEMENTED); +#else + ASSERT_EQ(result, WB_RESULT_INVALID_PARAMETER); +#endif } // AES CBC requires that the input be block aligned (multiple of 16). CTR does @@ -782,15 +836,18 @@ TEST_P(LicenseWhiteboxMaskedDecryptTest, if (!LoadLicense(settings, provider_key_id_)) GTEST_SKIP(); - ASSERT_EQ(WB_License_MaskedDecrypt( - whitebox_, WB_CIPHER_MODE_CBC, - golden_data_.CBCContent().software_decode_key.id.data(), - golden_data_.CBCContent().software_decode_key.id.size(), - golden_data_.CBCContent().ciphertext.data(), 14, - golden_data_.CBCContent().iv.data(), - golden_data_.CBCContent().iv.size(), masked_text_.data(), - &masked_text_size_), - WB_RESULT_INVALID_PARAMETER); + auto result = WB_License_MaskedDecrypt( + whitebox_, WB_CIPHER_MODE_CBC, + golden_data_.CBCContent().software_decode_key.id.data(), + golden_data_.CBCContent().software_decode_key.id.size(), + golden_data_.CBCContent().ciphertext.data(), 14, + golden_data_.CBCContent().iv.data(), golden_data_.CBCContent().iv.size(), + masked_text_.data(), &masked_text_size_); +#ifdef ALWAYS_DECRYPT_TO_CLEAR + ASSERT_EQ(result, WB_RESULT_NOT_IMPLEMENTED); +#else + ASSERT_EQ(result, WB_RESULT_INVALID_PARAMETER); +#endif } // The white-box (using any cipher mode) should reject input with size zero. @@ -800,15 +857,18 @@ TEST_P(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForZeroInputDataSize) { if (!LoadLicense(settings, provider_key_id_)) GTEST_SKIP(); - ASSERT_EQ(WB_License_MaskedDecrypt( - whitebox_, WB_CIPHER_MODE_CBC, - golden_data_.CBCContent().software_decode_key.id.data(), - golden_data_.CBCContent().software_decode_key.id.size(), - golden_data_.CBCContent().ciphertext.data(), 0, - golden_data_.CBCContent().iv.data(), - golden_data_.CBCContent().iv.size(), masked_text_.data(), - &masked_text_size_), - WB_RESULT_INVALID_PARAMETER); + auto result = WB_License_MaskedDecrypt( + whitebox_, WB_CIPHER_MODE_CBC, + golden_data_.CBCContent().software_decode_key.id.data(), + golden_data_.CBCContent().software_decode_key.id.size(), + golden_data_.CBCContent().ciphertext.data(), 0, + golden_data_.CBCContent().iv.data(), golden_data_.CBCContent().iv.size(), + masked_text_.data(), &masked_text_size_); +#ifdef ALWAYS_DECRYPT_TO_CLEAR + ASSERT_EQ(result, WB_RESULT_NOT_IMPLEMENTED); +#else + ASSERT_EQ(result, WB_RESULT_INVALID_PARAMETER); +#endif } TEST_P(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullIV) { @@ -817,15 +877,19 @@ TEST_P(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullIV) { if (!LoadLicense(settings, provider_key_id_)) GTEST_SKIP(); - ASSERT_EQ(WB_License_MaskedDecrypt( - whitebox_, WB_CIPHER_MODE_CBC, - golden_data_.CBCContent().software_decode_key.id.data(), - golden_data_.CBCContent().software_decode_key.id.size(), - golden_data_.CBCContent().ciphertext.data(), - golden_data_.CBCContent().ciphertext.size(), nullptr, - golden_data_.CBCContent().iv.size(), masked_text_.data(), - &masked_text_size_), - WB_RESULT_INVALID_PARAMETER); + auto result = WB_License_MaskedDecrypt( + whitebox_, WB_CIPHER_MODE_CBC, + golden_data_.CBCContent().software_decode_key.id.data(), + golden_data_.CBCContent().software_decode_key.id.size(), + golden_data_.CBCContent().ciphertext.data(), + golden_data_.CBCContent().ciphertext.size(), nullptr, + golden_data_.CBCContent().iv.size(), masked_text_.data(), + &masked_text_size_); +#ifdef ALWAYS_DECRYPT_TO_CLEAR + ASSERT_EQ(result, WB_RESULT_NOT_IMPLEMENTED); +#else + ASSERT_EQ(result, WB_RESULT_INVALID_PARAMETER); +#endif } // IV size should be 16. Any number other than 16 should fail. @@ -835,15 +899,19 @@ TEST_P(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForInvalidIVSize) { if (!LoadLicense(settings, provider_key_id_)) GTEST_SKIP(); - ASSERT_EQ(WB_License_MaskedDecrypt( - whitebox_, WB_CIPHER_MODE_CBC, - golden_data_.CBCContent().software_decode_key.id.data(), - golden_data_.CBCContent().software_decode_key.id.size(), - golden_data_.CBCContent().ciphertext.data(), - golden_data_.CBCContent().ciphertext.size(), - golden_data_.CBCContent().iv.data(), 9, masked_text_.data(), - &masked_text_size_), - WB_RESULT_INVALID_PARAMETER); + auto result = WB_License_MaskedDecrypt( + whitebox_, WB_CIPHER_MODE_CBC, + golden_data_.CBCContent().software_decode_key.id.data(), + golden_data_.CBCContent().software_decode_key.id.size(), + golden_data_.CBCContent().ciphertext.data(), + golden_data_.CBCContent().ciphertext.size(), + golden_data_.CBCContent().iv.data(), 9, masked_text_.data(), + &masked_text_size_); +#ifdef ALWAYS_DECRYPT_TO_CLEAR + ASSERT_EQ(result, WB_RESULT_NOT_IMPLEMENTED); +#else + ASSERT_EQ(result, WB_RESULT_INVALID_PARAMETER); +#endif } TEST_P(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullOutput) { @@ -852,16 +920,19 @@ TEST_P(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullOutput) { if (!LoadLicense(settings, provider_key_id_)) GTEST_SKIP(); - ASSERT_EQ( - WB_License_MaskedDecrypt( - whitebox_, WB_CIPHER_MODE_CBC, - golden_data_.CBCContent().software_decode_key.id.data(), - golden_data_.CBCContent().software_decode_key.id.size(), - golden_data_.CBCContent().ciphertext.data(), - golden_data_.CBCContent().ciphertext.size(), - golden_data_.CBCContent().iv.data(), - golden_data_.CBCContent().iv.size(), nullptr, &masked_text_size_), - WB_RESULT_INVALID_PARAMETER); + auto result = WB_License_MaskedDecrypt( + whitebox_, WB_CIPHER_MODE_CBC, + golden_data_.CBCContent().software_decode_key.id.data(), + golden_data_.CBCContent().software_decode_key.id.size(), + golden_data_.CBCContent().ciphertext.data(), + golden_data_.CBCContent().ciphertext.size(), + golden_data_.CBCContent().iv.data(), golden_data_.CBCContent().iv.size(), + nullptr, &masked_text_size_); +#ifdef ALWAYS_DECRYPT_TO_CLEAR + ASSERT_EQ(result, WB_RESULT_NOT_IMPLEMENTED); +#else + ASSERT_EQ(result, WB_RESULT_INVALID_PARAMETER); +#endif } TEST_P(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullOutputSize) { @@ -870,16 +941,19 @@ TEST_P(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullOutputSize) { if (!LoadLicense(settings, provider_key_id_)) GTEST_SKIP(); - ASSERT_EQ( - WB_License_MaskedDecrypt( - whitebox_, WB_CIPHER_MODE_CBC, - golden_data_.CBCContent().software_decode_key.id.data(), - golden_data_.CBCContent().software_decode_key.id.size(), - golden_data_.CBCContent().ciphertext.data(), - golden_data_.CBCContent().ciphertext.size(), - golden_data_.CBCContent().iv.data(), - golden_data_.CBCContent().iv.size(), masked_text_.data(), nullptr), - WB_RESULT_INVALID_PARAMETER); + auto result = WB_License_MaskedDecrypt( + whitebox_, WB_CIPHER_MODE_CBC, + golden_data_.CBCContent().software_decode_key.id.data(), + golden_data_.CBCContent().software_decode_key.id.size(), + golden_data_.CBCContent().ciphertext.data(), + golden_data_.CBCContent().ciphertext.size(), + golden_data_.CBCContent().iv.data(), golden_data_.CBCContent().iv.size(), + masked_text_.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 @@ -891,15 +965,17 @@ TEST_P(LicenseWhiteboxMaskedDecryptTest, KeyUnavailableForMissingKeyId) { if (!LoadLicense(settings, provider_key_id_)) GTEST_SKIP(); - ASSERT_EQ( - WB_License_MaskedDecrypt(whitebox_, WB_CIPHER_MODE_CBC, - missing_key_id_.data(), missing_key_id_.size(), - golden_data_.CBCContent().ciphertext.data(), - golden_data_.CBCContent().ciphertext.size(), - golden_data_.CBCContent().iv.data(), - golden_data_.CBCContent().iv.size(), - masked_text_.data(), &masked_text_size_), - WB_RESULT_KEY_UNAVAILABLE); + auto result = WB_License_MaskedDecrypt( + whitebox_, WB_CIPHER_MODE_CBC, missing_key_id_.data(), + missing_key_id_.size(), golden_data_.CBCContent().ciphertext.data(), + golden_data_.CBCContent().ciphertext.size(), + golden_data_.CBCContent().iv.data(), golden_data_.CBCContent().iv.size(), + masked_text_.data(), &masked_text_size_); +#ifdef ALWAYS_DECRYPT_TO_CLEAR + ASSERT_EQ(result, WB_RESULT_NOT_IMPLEMENTED); +#else + ASSERT_EQ(result, WB_RESULT_KEY_UNAVAILABLE); +#endif } TEST_P(LicenseWhiteboxMaskedDecryptTest, KeyUnavailableForNonContentKey) { @@ -908,15 +984,17 @@ TEST_P(LicenseWhiteboxMaskedDecryptTest, KeyUnavailableForNonContentKey) { if (!LoadLicense(settings, provider_key_id_)) GTEST_SKIP(); - ASSERT_EQ(WB_License_MaskedDecrypt( - whitebox_, WB_CIPHER_MODE_CBC, non_content_key_id_.data(), - non_content_key_id_.size(), - golden_data_.CBCContent().ciphertext.data(), - golden_data_.CBCContent().ciphertext.size(), - golden_data_.CBCContent().iv.data(), - golden_data_.CBCContent().iv.size(), masked_text_.data(), - &masked_text_size_), - WB_RESULT_KEY_UNAVAILABLE); + auto result = WB_License_MaskedDecrypt( + whitebox_, WB_CIPHER_MODE_CBC, non_content_key_id_.data(), + non_content_key_id_.size(), golden_data_.CBCContent().ciphertext.data(), + golden_data_.CBCContent().ciphertext.size(), + golden_data_.CBCContent().iv.data(), golden_data_.CBCContent().iv.size(), + masked_text_.data(), &masked_text_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 @@ -928,32 +1006,38 @@ TEST_P(LicenseWhiteboxMaskedDecryptTest, if (!LoadLicense(settings, provider_key_id_)) GTEST_SKIP(); - ASSERT_EQ( - WB_License_MaskedDecrypt(whitebox_, WB_CIPHER_MODE_CBC, - golden_data_.CBCContent().hardware_key.id.data(), - golden_data_.CBCContent().hardware_key.id.size(), - golden_data_.CBCContent().ciphertext.data(), - golden_data_.CBCContent().ciphertext.size(), - golden_data_.CBCContent().iv.data(), - golden_data_.CBCContent().iv.size(), - masked_text_.data(), &masked_text_size_), - WB_RESULT_INSUFFICIENT_SECURITY_LEVEL); + auto result = WB_License_MaskedDecrypt( + whitebox_, WB_CIPHER_MODE_CBC, + golden_data_.CBCContent().hardware_key.id.data(), + golden_data_.CBCContent().hardware_key.id.size(), + golden_data_.CBCContent().ciphertext.data(), + golden_data_.CBCContent().ciphertext.size(), + golden_data_.CBCContent().iv.data(), golden_data_.CBCContent().iv.size(), + masked_text_.data(), &masked_text_size_); +#ifdef ALWAYS_DECRYPT_TO_CLEAR + ASSERT_EQ(result, WB_RESULT_NOT_IMPLEMENTED); +#else + ASSERT_EQ(result, WB_RESULT_INSUFFICIENT_PERMISSIONS); +#endif } // Unlike the other tests, we do not call LoadLicense() as the criteria for // WB_RESULT_INVALID_STATE is that no key can be found and keys are provided // via a license. TEST_P(LicenseWhiteboxMaskedDecryptTest, InvalidState) { - ASSERT_EQ(WB_License_MaskedDecrypt( - whitebox_, WB_CIPHER_MODE_CBC, - golden_data_.CBCContent().software_decode_key.id.data(), - golden_data_.CBCContent().software_decode_key.id.size(), - golden_data_.CBCContent().ciphertext.data(), - golden_data_.CBCContent().ciphertext.size(), - golden_data_.CBCContent().iv.data(), - golden_data_.CBCContent().iv.size(), masked_text_.data(), - &masked_text_size_), - WB_RESULT_INVALID_STATE); + auto result = WB_License_MaskedDecrypt( + whitebox_, WB_CIPHER_MODE_CBC, + golden_data_.CBCContent().software_decode_key.id.data(), + golden_data_.CBCContent().software_decode_key.id.size(), + golden_data_.CBCContent().ciphertext.data(), + golden_data_.CBCContent().ciphertext.size(), + golden_data_.CBCContent().iv.data(), golden_data_.CBCContent().iv.size(), + masked_text_.data(), &masked_text_size_); +#ifdef ALWAYS_DECRYPT_TO_CLEAR + ASSERT_EQ(result, WB_RESULT_NOT_IMPLEMENTED); +#else + ASSERT_EQ(result, WB_RESULT_INVALID_STATE); +#endif } TEST_P(LicenseWhiteboxMaskedDecryptTest, BufferTooSmall) { @@ -966,20 +1050,23 @@ TEST_P(LicenseWhiteboxMaskedDecryptTest, BufferTooSmall) { // using a constant here. masked_text_size_ = 8; - ASSERT_EQ(WB_License_MaskedDecrypt( - whitebox_, WB_CIPHER_MODE_CBC, - golden_data_.CBCContent().software_decode_key.id.data(), - golden_data_.CBCContent().software_decode_key.id.size(), - golden_data_.CBCContent().ciphertext.data(), - golden_data_.CBCContent().ciphertext.size(), - golden_data_.CBCContent().iv.data(), - golden_data_.CBCContent().iv.size(), masked_text_.data(), - &masked_text_size_), - WB_RESULT_BUFFER_TOO_SMALL); + auto result = WB_License_MaskedDecrypt( + whitebox_, WB_CIPHER_MODE_CBC, + golden_data_.CBCContent().software_decode_key.id.data(), + golden_data_.CBCContent().software_decode_key.id.size(), + golden_data_.CBCContent().ciphertext.data(), + golden_data_.CBCContent().ciphertext.size(), + golden_data_.CBCContent().iv.data(), golden_data_.CBCContent().iv.size(), + masked_text_.data(), &masked_text_size_); +#ifdef ALWAYS_DECRYPT_TO_CLEAR + ASSERT_EQ(result, WB_RESULT_NOT_IMPLEMENTED); +#else + ASSERT_EQ(result, WB_RESULT_BUFFER_TOO_SMALL); // We don't use padding so the reported plaintext size should be the same as // the cipher text size. ASSERT_EQ(masked_text_size_, golden_data_.CBCContent().ciphertext.size()); +#endif } // Check that the result of unmasking only a small portion of the data is the @@ -990,16 +1077,18 @@ TEST_P(LicenseWhiteboxMaskedDecryptTest, SuccessForSubRangeUnmask) { if (!LoadLicense(settings, provider_key_id_)) GTEST_SKIP(); - ASSERT_EQ(WB_License_MaskedDecrypt( - whitebox_, WB_CIPHER_MODE_CBC, - golden_data_.CBCContent().software_decode_key.id.data(), - golden_data_.CBCContent().software_decode_key.id.size(), - golden_data_.CBCContent().ciphertext.data(), - golden_data_.CBCContent().ciphertext.size(), - golden_data_.CBCContent().iv.data(), - golden_data_.CBCContent().iv.size(), masked_text_.data(), - &masked_text_size_), - WB_RESULT_OK); + auto result = WB_License_MaskedDecrypt( + whitebox_, WB_CIPHER_MODE_CBC, + golden_data_.CBCContent().software_decode_key.id.data(), + golden_data_.CBCContent().software_decode_key.id.size(), + golden_data_.CBCContent().ciphertext.data(), + golden_data_.CBCContent().ciphertext.size(), + golden_data_.CBCContent().iv.data(), golden_data_.CBCContent().iv.size(), + masked_text_.data(), &masked_text_size_); +#ifdef ALWAYS_DECRYPT_TO_CLEAR + ASSERT_EQ(result, WB_RESULT_NOT_IMPLEMENTED); +#else + ASSERT_EQ(result, WB_RESULT_OK); ASSERT_EQ(WB_License_GetSecretString( whitebox_, WB_CIPHER_MODE_CBC, @@ -1022,6 +1111,7 @@ TEST_P(LicenseWhiteboxMaskedDecryptTest, SuccessForSubRangeUnmask) { ASSERT_EQ(full_unmask[4], partial_unmask[0]); ASSERT_EQ(full_unmask[5], partial_unmask[1]); ASSERT_EQ(full_unmask[6], partial_unmask[2]); +#endif } TEST_P(LicenseWhiteboxMaskedDecryptTest, KeyUnavailableForInvalidKey) { @@ -1033,16 +1123,19 @@ TEST_P(LicenseWhiteboxMaskedDecryptTest, KeyUnavailableForInvalidKey) { if (!LoadLicense(settings, provider_key_id_)) GTEST_SKIP(); - ASSERT_EQ(WB_License_MaskedDecrypt( - whitebox_, WB_CIPHER_MODE_CBC, - golden_data_.CBCContent().software_decode_key.id.data(), - golden_data_.CBCContent().software_decode_key.id.size(), - golden_data_.CBCContent().ciphertext.data(), - golden_data_.CBCContent().ciphertext.size(), - golden_data_.CBCContent().iv.data(), - golden_data_.CBCContent().iv.size(), masked_text_.data(), - &masked_text_size_), - WB_RESULT_KEY_UNAVAILABLE); + auto result = WB_License_MaskedDecrypt( + whitebox_, WB_CIPHER_MODE_CBC, + golden_data_.CBCContent().software_decode_key.id.data(), + golden_data_.CBCContent().software_decode_key.id.size(), + golden_data_.CBCContent().ciphertext.data(), + golden_data_.CBCContent().ciphertext.size(), + golden_data_.CBCContent().iv.data(), golden_data_.CBCContent().iv.size(), + masked_text_.data(), &masked_text_size_); +#ifdef ALWAYS_DECRYPT_TO_CLEAR + ASSERT_EQ(result, WB_RESULT_NOT_IMPLEMENTED); +#else + ASSERT_EQ(result, WB_RESULT_KEY_UNAVAILABLE); +#endif } // For this test, we create a license using the test specified Provider Key @@ -1072,16 +1165,18 @@ TEST_P(LicenseWhiteboxMaskedDecryptTest, MismatchProviderKeyId) { GTEST_SKIP(); // Decryption should succeed, but the plaintext should be incorrect. - ASSERT_EQ(WB_License_MaskedDecrypt( - whitebox_, WB_CIPHER_MODE_CBC, - golden_data_.CBCContent().software_decode_key.id.data(), - golden_data_.CBCContent().software_decode_key.id.size(), - golden_data_.CBCContent().ciphertext.data(), - golden_data_.CBCContent().ciphertext.size(), - golden_data_.CBCContent().iv.data(), - golden_data_.CBCContent().iv.size(), masked_text_.data(), - &masked_text_size_), - WB_RESULT_OK); + auto result = WB_License_MaskedDecrypt( + whitebox_, WB_CIPHER_MODE_CBC, + golden_data_.CBCContent().software_decode_key.id.data(), + golden_data_.CBCContent().software_decode_key.id.size(), + golden_data_.CBCContent().ciphertext.data(), + golden_data_.CBCContent().ciphertext.size(), + golden_data_.CBCContent().iv.data(), golden_data_.CBCContent().iv.size(), + masked_text_.data(), &masked_text_size_); +#ifdef ALWAYS_DECRYPT_TO_CLEAR + ASSERT_EQ(result, WB_RESULT_NOT_IMPLEMENTED); +#else + ASSERT_EQ(result, WB_RESULT_OK); masked_text_.resize(masked_text_size_); // Returned data is masked, so it should be the correct size but not @@ -1105,6 +1200,7 @@ TEST_P(LicenseWhiteboxMaskedDecryptTest, MismatchProviderKeyId) { plaintext_.data()); ASSERT_NE(plaintext_, golden_data_.CBCContent().plaintext); +#endif } INSTANTIATE_TEST_SUITE_P( diff --git a/whitebox/api/license_whitebox_proxy_impl.cc b/whitebox/api/license_whitebox_proxy_impl.cc index 815b896..1b86ab2 100644 --- a/whitebox/api/license_whitebox_proxy_impl.cc +++ b/whitebox/api/license_whitebox_proxy_impl.cc @@ -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 GetLicenseWhiteboxProviderKeysInitData() { diff --git a/whitebox/api/license_whitebox_security_level_test.cc b/whitebox/api/license_whitebox_security_level_test.cc index bbf7fc6..8faa466 100644 --- a/whitebox/api/license_whitebox_security_level_test.cc +++ b/whitebox/api/license_whitebox_security_level_test.cc @@ -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( diff --git a/whitebox/api/remote_attestation_and_verification_test.cc b/whitebox/api/remote_attestation_and_verification_test.cc index d6f5ec4..5392367 100644 --- a/whitebox/api/remote_attestation_and_verification_test.cc +++ b/whitebox/api/remote_attestation_and_verification_test.cc @@ -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 diff --git a/whitebox/api/result.h b/whitebox/api/result.h index 9a29dfe..74ad7c9 100644 --- a/whitebox/api/result.h +++ b/whitebox/api/result.h @@ -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. diff --git a/whitebox/api/test_key_types.h b/whitebox/api/test_key_types.h index 095ae38..78d563a 100644 --- a/whitebox/api/test_key_types.h +++ b/whitebox/api/test_key_types.h @@ -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_ diff --git a/whitebox/api/test_license_builder.cc b/whitebox/api/test_license_builder.cc index 4d4db26..5f83439 100644 --- a/whitebox/api/test_license_builder.cc +++ b/whitebox/api/test_license_builder.cc @@ -5,6 +5,7 @@ #include #include #include +#include #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()); diff --git a/whitebox/api/test_license_builder.h b/whitebox/api/test_license_builder.h index d5eb6e6..3a7a4f9 100644 --- a/whitebox/api/test_license_builder.h +++ b/whitebox/api/test_license_builder.h @@ -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 content_keys_; std::vector entitlement_keys_; + std::vector generic_keys_; std::vector signing_keys_; std::vector operator_session_keys_; }; diff --git a/whitebox/crypto_utils/crypto_util.cc b/whitebox/crypto_utils/crypto_util.cc index cde009e..05b8539 100644 --- a/whitebox/crypto_utils/crypto_util.cc +++ b/whitebox/crypto_utils/crypto_util.cc @@ -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; diff --git a/whitebox/crypto_utils/crypto_util.h b/whitebox/crypto_utils/crypto_util.h index fbc11bb..bd29bc9 100644 --- a/whitebox/crypto_utils/crypto_util.h +++ b/whitebox/crypto_utils/crypto_util.h @@ -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 diff --git a/whitebox/reference/impl/BUILD b/whitebox/reference/impl/BUILD index 1dff330..e730f2d 100644 --- a/whitebox/reference/impl/BUILD +++ b/whitebox/reference/impl/BUILD @@ -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", diff --git a/whitebox/reference/impl/content_key.h b/whitebox/reference/impl/content_key.h index 042c315..7fbe46c 100644 --- a/whitebox/reference/impl/content_key.h +++ b/whitebox/reference/impl/content_key.h @@ -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 key; + uint32_t kcb_flags = 0; // These are the permission flags that will be used internally to check if // we can use a key. diff --git a/whitebox/reference/impl/license_parser.cc b/whitebox/reference/impl/license_parser.cc index cacfcb8..9189239 100644 --- a/whitebox/reference/impl/license_parser.cc +++ b/whitebox/reference/impl/license_parser.cc @@ -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 diff --git a/whitebox/reference/impl/license_parser.h b/whitebox/reference/impl/license_parser.h index 356565e..0d281f3 100644 --- a/whitebox/reference/impl/license_parser.h +++ b/whitebox/reference/impl/license_parser.h @@ -42,6 +42,7 @@ class LicenseParser { virtual const std::map& GetContentKeys() const = 0; virtual const std::map& GetEntitlementKeys() const = 0; + virtual const std::map& 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, diff --git a/whitebox/reference/impl/license_whitebox_impl.cc b/whitebox/reference/impl/license_whitebox_impl.cc index 900470d..1b00c06 100644 --- a/whitebox/reference/impl/license_whitebox_impl.cc +++ b/whitebox/reference/impl/license_whitebox_impl.cc @@ -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 content_keys; std::map entitlement_keys; + std::map generic_keys; std::vector 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, diff --git a/whitebox/reference/impl/odk_license_parser.cc b/whitebox/reference/impl/odk_license_parser.cc index 49292ea..6a37d9d 100644 --- a/whitebox/reference/impl/odk_license_parser.cc +++ b/whitebox/reference/impl/odk_license_parser.cc @@ -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(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& +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 diff --git a/whitebox/reference/impl/odk_license_parser.h b/whitebox/reference/impl/odk_license_parser.h index c77ee85..c954a5c 100644 --- a/whitebox/reference/impl/odk_license_parser.h +++ b/whitebox/reference/impl/odk_license_parser.h @@ -22,6 +22,7 @@ class OdkLicenseParser : public LicenseParser { const std::map& GetContentKeys() const override; const std::map& GetEntitlementKeys() const override; + const std::map& GetGenericKeys() const override; private: RenewalKey ParseSigningKeys(const std::string& decryption_key, @@ -39,6 +40,7 @@ class OdkLicenseParser : public LicenseParser { std::vector renewal_keys_; std::map content_keys_; std::map entitlement_keys_; + std::map generic_keys_; }; } // namespace widevine diff --git a/whitebox/reference/impl/protobuf_license_parser.cc b/whitebox/reference/impl/protobuf_license_parser.cc index 5352c56..7d15635 100644 --- a/whitebox/reference/impl/protobuf_license_parser.cc +++ b/whitebox/reference/impl/protobuf_license_parser.cc @@ -149,6 +149,12 @@ ProtobufLicenseParser::GetEntitlementKeys() const { return entitlement_keys_; } +const std::map& +ProtobufLicenseParser::GetGenericKeys() const { + static std::map ret; + return ret; +} + RenewalKey ProtobufLicenseParser::ParseSigningKey( const std::string& decryption_key, const video_widevine::License_KeyContainer& key) const { diff --git a/whitebox/reference/impl/protobuf_license_parser.h b/whitebox/reference/impl/protobuf_license_parser.h index 8fb59d5..14347df 100644 --- a/whitebox/reference/impl/protobuf_license_parser.h +++ b/whitebox/reference/impl/protobuf_license_parser.h @@ -22,6 +22,8 @@ class ProtobufLicenseParser : public LicenseParser { const std::map& GetContentKeys() const override; const std::map& GetEntitlementKeys() const override; + const std::map& + GetGenericKeys() const override; RenewalKey ParseSigningKey( const std::string& decryption_key,